Skip to content

Integration test runner #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .buildkite/rest-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
steps:
- label: ":elasticsearch: :python: ES Serverless ({{ matrix.python }}/{{ matrix.connection_class }}) Python Test Suite: {{ matrix.suite }}"
agents:
provider: gcp
env:
PYTHON_VERSION: "{{ matrix.python }}"
TEST_SUITE: "{{ matrix.suite }}"
PYTHON_CONNECTION_CLASS: "{{ matrix.connection_class }}"
# TEMPORARY for 3.11
# https://github.com/aio-libs/aiohttp/issues/6600
AIOHTTP_NO_EXTENSIONS: 1
# https://github.com/aio-libs/frozenlist/issues/285
FROZENLIST_NO_EXTENSIONS: 1
# https://github.com/aio-libs/yarl/issues/680
YARL_NO_EXTENSIONS: 1
EC_REGISTER_BACKEND: "appex-qa-team-cluster"
EC_ENV: "qa"
EC_REGION: "aws-eu-west-1"
EC_PROJECT_NAME: "esv-client-python-test-{{ matrix.python }}-{{ matrix.suite }}-{{ matrix.connection_class }}-{{ key }}"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another improvement I hope to make soon is to spin up a single serverless project for the whole matrix, ensuring each test run in the matrix uses a unique prefix (probably the Buildkite step ID) on all resources created in Elasticsearch. It's slow, ugly and expensive to set up several serverless instances every time this test suite is run.

matrix:
setup:
suite:
- free
- platinum
python:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
connection_class:
- urllib3
- requests
command: ./.buildkite/run-tests
artifact_paths: "junit/*-junit.xml"
- wait: ~
continue_on_failure: true
- label: ":junit: Test results"
agents:
provider: gcp
image: family/core-ubuntu-2204
plugins:
- junit-annotate#v2.4.1:
artifacts: "junit/*-junit.xml"
job-uuid-file-pattern: "(.*)-junit.xml"
fail-build-on-error: true
failure-format: file
76 changes: 76 additions & 0 deletions .buildkite/run-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash

# Default environment variables
export TEST_SUITE="${TEST_SUITE:=platinum}"
export PYTHON_VERSION="${PYTHON_VERSION:=3.9}"
export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}"

set -euo pipefail

# fetch cloud creds used by qaf
CLOUD_ACCESS_KEY=$(vault read -field="$EC_ENV" secret/ci/elastic-elasticsearch-serverless-python/cloud-access)
echo "{\"api_key\":{\"$EC_ENV\":\"$CLOUD_ACCESS_KEY\"}}" > "$(pwd)/cloud.json"

run_qaf() {
cmd=$1
docker run --rm \
-e EC_REGISTER_BACKEND \
-e EC_ENV \
-e EC_REGION \
-e EC_PROJECT_NAME \
-e VAULT_TOKEN \
-v "$(pwd)/cloud.json:/root/.elastic/cloud.json" \
docker.elastic.co/employees/dolaru/qaf:latest \
bash -c "$cmd"
}

# ensure serverless instance is deleted even if script errors
cleanup() {
echo -e "--- :elasticsearch: Tear down serverless instance EC_PROJECT_NAME"
run_qaf 'qaf elastic-cloud projects delete'
rm -rf "$(pwd)/cloud.json"
}
trap cleanup EXIT

echo -e "--- :elasticsearch: Start serverless instance"

run_qaf "qaf elastic-cloud projects create --project-type elasticsearch"
deployment=$(run_qaf "qaf elastic-cloud projects describe --as-json --show-credentials")

ES_API_SECRET_KEY=$(echo "$deployment" | jq -r '.credentials.api_key')
ELASTICSEARCH_URL=$(echo "$deployment" | jq -r '.elasticsearch.url')
export ELASTICSEARCH_URL

echo -e "--- :computer: Environment variables"
echo -e "ELASTICSEARCH_URL $ELASTICSEARCH_URL"
echo -e "TEST_SUITE $TEST_SUITE"
echo -e "PYTHON_VERSION $PYTHON_VERSION"
echo -e "PYTHON_CONNECTION_CLASS $PYTHON_CONNECTION_CLASS"

echo -e "--- :docker: Build elasticsearch-serverless-python container"

docker build \
--file .ci/Dockerfile \
--tag elasticsearch-serverless-python \
--build-arg "PYTHON_VERSION=$PYTHON_VERSION" \
.

echo -e "--- :docker: :python: Run integration tests for Python $PYTHON_VERSION"

GITHUB_TOKEN=$(vault read -field=token secret/ci/elastic-elasticsearch-serverless-python/github-token)
export GITHUB_TOKEN

docker run \
-e ELASTICSEARCH_URL \
-e "ES_API_KEY=$ES_API_SECRET_KEY" \
-e PYTHON_CONNECTION_CLASS \
-e TEST_SUITE \
-e GITHUB_TOKEN \
-e AIOHTTP_NO_EXTENSIONS \
-e FROZENLIST_NO_EXTENSIONS \
-e YARL_NO_EXTENSIONS \
--name elasticsearch-serverless-python-tests \
--volume "$(pwd)/junit:/code/elasticsearch-serverless-python/junit" \
--rm \
elasticsearch-serverless-python \
nox -s "test-$PYTHON_VERSION"
20 changes: 5 additions & 15 deletions .ci/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
ARG PYTHON_VERSION=3.8
ARG PYTHON_VERSION=3.9
FROM python:${PYTHON_VERSION}

# Default UID/GID to 1000
# it can be overridden at build time
ARG BUILDER_UID=1000
ARG BUILDER_GID=1000
ENV BUILDER_USER elastic
ENV BUILDER_GROUP elastic

WORKDIR /code/elasticsearch-serverless-python
RUN mkdir -p /code/elasticsearch-serverless-python/build

# Create user
RUN groupadd --system -g ${BUILDER_GID} ${BUILDER_GROUP} \
&& useradd --system --shell /bin/bash -u ${BUILDER_UID} -g ${BUILDER_GROUP} -d /var/lib/elastic -m elastic 1>/dev/null 2>/dev/null \
&& mkdir /code/elasticsearch-serverless-python/build \
&& chown -R ${BUILDER_USER}:${BUILDER_GROUP} /code/
USER ${BUILDER_USER}:${BUILDER_GROUP}
COPY --chown=$BUILDER_USER:$BUILDER_GROUP . .
COPY pyproject.toml README.rst .
RUN python -m pip install \
-U --no-cache-dir \
--disable-pip-version-check \
.[dev]

COPY . .
35 changes: 0 additions & 35 deletions .ci/run-elasticsearch.sh

This file was deleted.

44 changes: 44 additions & 0 deletions catalog-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/catalog-info.json
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: elasticsearch-serverless-python
spec:
type: library
owner: group:clients-team
lifecycle: alpha

---
# yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/e57ee3bed7a6f73077a3f55a38e76e40ec87a7cf/rre.schema.json
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: elasticsearch-serverless-python-rest-tests
description: elasticsearch-serverless-python - rest tests
spec:
type: buildkite-pipeline
owner: group:clients-team
system: buildkite
implementation:
apiVersion: buildkite.elastic.dev/v1
kind: Pipeline
metadata:
name: elasticsearch-serverless-python - rest tests
spec:
repository: elastic/elasticsearch-serverless-python
pipeline_file: .buildkite/rest-tests.yaml
teams:
clients-team:
access_level: MANAGE_BUILD_AND_READ
everyone:
access_level: READ_ONLY
provider_settings:
build_pull_requests: true
build_branches: true
cancel_intermediate_builds: true
cancel_intermediate_builds_branch_filter: '!main'
schedules:
main_semi_daily:
branch: 'main'
cronline: '0 */12 * * *'
5 changes: 1 addition & 4 deletions elasticsearch_serverless/_async/client/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
)
from elastic_transport.client_utils import DEFAULT, DefaultType

from ..._version import __versionstr__
from ...compat import warn_stacklevel
from ...exceptions import (
HTTP_EXCEPTIONS,
Expand All @@ -42,9 +41,7 @@
from .utils import _base64_auth_header, _quote_query

_WARNING_RE = re.compile(r"\"([^\"]*)\"")
_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=" + str(
__versionstr__.partition(".")[0]
)
_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=8"
_COMPAT_MIMETYPE_RE = re.compile(r"application/(json|x-ndjson|vnd\.mapbox-vector-tile)")
_COMPAT_MIMETYPE_SUB = _COMPAT_MIMETYPE_TEMPLATE % (r"\g<1>",)

Expand Down
5 changes: 1 addition & 4 deletions elasticsearch_serverless/_sync/client/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
)
from elastic_transport.client_utils import DEFAULT, DefaultType

from ..._version import __versionstr__
from ...compat import warn_stacklevel
from ...exceptions import (
HTTP_EXCEPTIONS,
Expand All @@ -42,9 +41,7 @@
from .utils import _base64_auth_header, _quote_query

_WARNING_RE = re.compile(r"\"([^\"]*)\"")
_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=" + str(
__versionstr__.partition(".")[0]
)
_COMPAT_MIMETYPE_TEMPLATE = "application/vnd.elasticsearch+%s; compatible-with=8"
_COMPAT_MIMETYPE_RE = re.compile(r"application/(json|x-ndjson|vnd\.mapbox-vector-tile)")
_COMPAT_MIMETYPE_SUB = _COMPAT_MIMETYPE_TEMPLATE % (r"\g<1>",)

Expand Down
2 changes: 1 addition & 1 deletion elasticsearch_serverless/_sync/client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def host_to_node_config(host: _TYPE_HOST) -> NodeConfig:
if isinstance(host, NodeConfig):
return host
elif isinstance(host, str):
return url_to_node_config(host)
return url_to_node_config(host, use_default_ports_for_scheme=True)
elif isinstance(host, Mapping):
return host_mapping_to_node_config(host)
else:
Expand Down
4 changes: 2 additions & 2 deletions elasticsearch_serverless/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@

# This file exists for backwards compatibility.
warnings.warn(
"Importing from the 'elasticsearch.client' module is deprecated. "
"Instead use 'elasticsearch' module for importing the client.",
"Importing from the 'elasticsearch_serverless.client' module is deprecated. "
"Instead use 'elasticsearch_serverless' module for importing the client.",
category=DeprecationWarning,
stacklevel=2,
)
Expand Down
13 changes: 8 additions & 5 deletions test_elasticsearch_serverless/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from elasticsearch_serverless import Elasticsearch

from .utils import CA_CERTS, es_url, es_version
from .utils import es_api_key, es_url, es_version


@pytest.fixture(scope="session")
Expand All @@ -33,11 +33,14 @@ def elasticsearch_url():


@pytest.fixture(scope="session")
def ca_certs():
return CA_CERTS
def elasticsearch_api_key():
try:
return es_api_key()
except RuntimeError as e:
pytest.skip(str(e))


@pytest.fixture(scope="session")
def elasticsearch_version(elasticsearch_url, ca_certs) -> Tuple[int, ...]:
def elasticsearch_version(elasticsearch_url) -> Tuple[int, ...]:
"""Returns the version of the current Elasticsearch cluster"""
return es_version(Elasticsearch(elasticsearch_url, ca_certs=ca_certs))
return es_version(Elasticsearch(elasticsearch_url))
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
# under the License.

import pytest
import pytest_asyncio

import elasticsearch_serverless

from ...utils import CA_CERTS, wipe_cluster
from ...utils import wipe_cluster

pytestmark = pytest.mark.asyncio


@pytest.fixture(scope="function")
@pytest_asyncio.fixture(scope="function")
@pytest.mark.usefixtures("sync_client")
async def async_client(elasticsearch_url):
async def async_client(elasticsearch_url, elasticsearch_api_key):
# 'sync_client' fixture is used for the guaranteed wipe_cluster() call.

if not hasattr(elasticsearch_serverless, "AsyncElasticsearch"):
Expand All @@ -38,7 +39,7 @@ async def async_client(elasticsearch_url):
client = None
try:
client = elasticsearch_serverless.AsyncElasticsearch(
elasticsearch_url, request_timeout=3, ca_certs=CA_CERTS
elasticsearch_url, api_key=elasticsearch_api_key, request_timeout=3
)
yield client
finally:
Expand Down
Loading