Skip to content

Commit 299a543

Browse files
committed
airbyte-ci: Add regression_tests option
1 parent eb16b58 commit 299a543

File tree

25 files changed

+822
-58
lines changed

25 files changed

+822
-58
lines changed

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/build_image/steps/common.py

+30-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from __future__ import annotations
55

66
from abc import ABC
7-
from typing import TYPE_CHECKING
7+
from typing import TYPE_CHECKING, Optional
88

99
import docker # type: ignore
1010
from dagger import Container, ExecError, Platform, QueryError
@@ -26,28 +26,40 @@ class BuildConnectorImagesBase(Step, ABC):
2626
def title(self) -> str:
2727
return f"Build {self.context.connector.technical_name} docker image for platform(s) {', '.join(self.build_platforms)}"
2828

29-
def __init__(self, context: ConnectorContext) -> None:
29+
def __init__(self, context: ConnectorContext, docker_image_name: Optional[str] = None) -> None:
3030
self.build_platforms = context.targeted_platforms
31+
self.docker_image_name = docker_image_name
3132
super().__init__(context)
3233

3334
async def _run(self, *args: Any) -> StepResult:
34-
build_results_per_platform = {}
35-
for platform in self.build_platforms:
36-
try:
37-
connector = await self._build_connector(platform, *args)
35+
if self.docker_image_name:
36+
self.logger.info(f"Pulling connector {self.docker_image_name}")
37+
build_results_per_platform = {
38+
platform: self.dagger_client.container(platform=platform).from_(self.docker_image_name)
39+
for platform in self.build_platforms
40+
}
41+
success_message = (
42+
f"The {self.docker_image_name} images "
43+
f"were successfully pulled for platform(s) {', '.join(self.build_platforms)}"
44+
)
45+
else:
46+
build_results_per_platform = {}
47+
for platform in self.build_platforms:
3848
try:
39-
await connector.with_exec(["spec"])
40-
except ExecError as e:
41-
return StepResult(
42-
step=self, status=StepStatus.FAILURE, stderr=str(e), stdout=f"Failed to run the spec command on the connector container for platform {platform}."
43-
)
44-
build_results_per_platform[platform] = connector
45-
except QueryError as e:
46-
return StepResult(step=self, status=StepStatus.FAILURE, stderr=f"Failed to build connector image for platform {platform}: {e}")
47-
success_message = (
48-
f"The {self.context.connector.technical_name} docker image "
49-
f"was successfully built for platform(s) {', '.join(self.build_platforms)}"
50-
)
49+
connector = await self._build_connector(platform, *args)
50+
try:
51+
await connector.with_exec(["spec"])
52+
except ExecError as e:
53+
return StepResult(
54+
step=self, status=StepStatus.FAILURE, stderr=str(e), stdout=f"Failed to run the spec command on the connector container for platform {platform}."
55+
)
56+
build_results_per_platform[platform] = connector
57+
except QueryError as e:
58+
return StepResult(step=self, status=StepStatus.FAILURE, stderr=f"Failed to build connector image for platform {platform}: {e}")
59+
success_message = (
60+
f"The {self.context.connector.technical_name} docker image "
61+
f"was successfully built for platform(s) {', '.join(self.build_platforms)}"
62+
)
5163
return StepResult(step=self, status=StepStatus.SUCCESS, stdout=success_message, output_artifact=build_results_per_platform)
5264

5365
async def _build_connector(self, platform: Platform, *args: Any, **kwargs: Any) -> Container:

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/commands.py

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def should_use_remote_secrets(use_remote_secrets: Optional[bool]) -> bool:
153153
"migrate_to_base_image": "pipelines.airbyte_ci.connectors.migrate_to_base_image.commands.migrate_to_base_image",
154154
"upgrade_base_image": "pipelines.airbyte_ci.connectors.upgrade_base_image.commands.upgrade_base_image",
155155
"upgrade_cdk": "pipelines.airbyte_ci.connectors.upgrade_cdk.commands.bump_version",
156+
"regression_test": "pipelines.airbyte_ci.connectors.regression_test.commands.regression_test",
156157
},
157158
)
158159
@click.option(

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/consts.py

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class CONNECTOR_TEST_STEP_ID(str, Enum):
2323
VERSION_INC_CHECK = "version_inc_check"
2424
TEST_ORCHESTRATOR = "test_orchestrator"
2525
DEPLOY_ORCHESTRATOR = "deploy_orchestrator"
26+
REGRESSION_TEST_BUILD_CONTROL = "regression_test_build_control"
27+
REGRESSION_TEST_BUILD_TARGET = "regression_test_build_target"
28+
REGRESSION_TEST_CONTROL = "regression_test_control"
29+
REGRESSION_TEST_TARGET = "regression_test_target"
2630

2731
def __str__(self) -> str:
2832
return self.value

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/context.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from datetime import datetime
1010
from types import TracebackType
11-
from typing import TYPE_CHECKING
11+
from typing import TYPE_CHECKING, Tuple
1212

1313
import yaml # type: ignore
1414
from anyio import Path
@@ -68,6 +68,7 @@ def __init__(
6868
concurrent_cat: Optional[bool] = False,
6969
run_step_options: RunStepOptions = RunStepOptions(),
7070
targeted_platforms: Sequence[Platform] = BUILD_PLATFORMS,
71+
versions_to_test: Optional[Tuple[str, str]] = None,
7172
) -> None:
7273
"""Initialize a connector context.
7374
@@ -95,6 +96,7 @@ def __init__(
9596
s3_build_cache_secret_key (Optional[str], optional): Gradle S3 Build Cache credentials. Defaults to None.
9697
concurrent_cat (bool, optional): Whether to run the CAT tests in parallel. Defaults to False.
9798
targeted_platforms (Optional[Iterable[Platform]], optional): The platforms to build the connector image for. Defaults to BUILD_PLATFORMS.
99+
versions_to_test (Optional[Tuple[str, str]]): The control and target version of the connector to be used in regression tests.
98100
"""
99101

100102
self.pipeline_name = pipeline_name
@@ -116,6 +118,7 @@ def __init__(
116118
self.concurrent_cat = concurrent_cat
117119
self._connector_secrets: Optional[Dict[str, Secret]] = None
118120
self.targeted_platforms = targeted_platforms
121+
self.versions_to_test = versions_to_test
119122

120123
super().__init__(
121124
pipeline_name=pipeline_name,

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/regression_test/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#
2+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3+
#
4+
5+
from typing import Dict, List
6+
7+
import asyncclick as click
8+
from pipelines import main_logger
9+
from pipelines.airbyte_ci.connectors.consts import CONNECTOR_TEST_STEP_ID
10+
from pipelines.airbyte_ci.connectors.context import ConnectorContext
11+
from pipelines.airbyte_ci.connectors.pipeline import run_connectors_pipelines
12+
from pipelines.airbyte_ci.connectors.regression_test.pipeline import run_connector_regression_test_pipeline
13+
from pipelines.cli.click_decorators import click_ci_requirements_option
14+
from pipelines.cli.dagger_pipeline_command import DaggerPipelineCommand
15+
from pipelines.consts import LOCAL_BUILD_PLATFORM, ContextState
16+
from pipelines.helpers.execution import argument_parsing
17+
from pipelines.helpers.execution.run_steps import RunStepOptions
18+
from pipelines.helpers.github import update_global_commit_status_check_for_tests
19+
from pipelines.helpers.utils import fail_if_missing_docker_hub_creds
20+
from pipelines.models.steps import STEP_PARAMS
21+
22+
23+
@click.command(
24+
cls=DaggerPipelineCommand,
25+
help="Test all the selected connectors.",
26+
context_settings=dict(
27+
ignore_unknown_options=True,
28+
),
29+
)
30+
@click_ci_requirements_option()
31+
@click.option(
32+
"--control-version",
33+
help=(
34+
"Control version of the connector to be tested. Records will be downloaded from this container and used as expected records for the target version."
35+
),
36+
default="latest",
37+
type=str,
38+
)
39+
@click.option(
40+
"--target-version",
41+
help=("Target version of the connector being tested."),
42+
default="dev",
43+
type=str,
44+
)
45+
@click.option(
46+
"--fail-fast",
47+
help="When enabled, tests will fail fast.",
48+
default=False,
49+
type=bool,
50+
is_flag=True,
51+
)
52+
@click.pass_context
53+
async def regression_test(ctx: click.Context, control_version: str, target_version: str, fail_fast: bool) -> bool:
54+
"""
55+
Runs a regression test pipeline for the selected connectors.
56+
"""
57+
if ctx.obj["is_ci"]:
58+
fail_if_missing_docker_hub_creds(ctx)
59+
60+
if ctx.obj["selected_connectors_with_modified_files"]:
61+
update_global_commit_status_check_for_tests(ctx.obj, "pending")
62+
else:
63+
main_logger.warn("No connector were selected for testing.")
64+
update_global_commit_status_check_for_tests(ctx.obj, "success")
65+
return True
66+
67+
run_step_options = RunStepOptions(fail_fast=fail_fast)
68+
connectors_tests_contexts = []
69+
for connector in ctx.obj["selected_connectors_with_modified_files"]:
70+
if control_version in ("dev", "latest"):
71+
control_version = f"airbyte/{connector.technical_name}:{control_version}"
72+
73+
if target_version in ("dev", "latest"):
74+
target_version = f"airbyte/{connector.technical_name}:{target_version}"
75+
76+
connectors_tests_contexts.append(
77+
ConnectorContext(
78+
pipeline_name=f"Testing connector {connector.technical_name}",
79+
connector=connector,
80+
is_local=ctx.obj["is_local"],
81+
git_branch=ctx.obj["git_branch"],
82+
git_revision=ctx.obj["git_revision"],
83+
ci_report_bucket=ctx.obj["ci_report_bucket_name"],
84+
report_output_prefix=ctx.obj["report_output_prefix"],
85+
use_remote_secrets=ctx.obj["use_remote_secrets"],
86+
gha_workflow_run_url=ctx.obj.get("gha_workflow_run_url"),
87+
dagger_logs_url=ctx.obj.get("dagger_logs_url"),
88+
pipeline_start_timestamp=ctx.obj.get("pipeline_start_timestamp"),
89+
ci_context=ctx.obj.get("ci_context"),
90+
pull_request=ctx.obj.get("pull_request"),
91+
ci_gcs_credentials=ctx.obj["ci_gcs_credentials"],
92+
use_local_cdk=ctx.obj.get("use_local_cdk"),
93+
s3_build_cache_access_key_id=ctx.obj.get("s3_build_cache_access_key_id"),
94+
s3_build_cache_secret_key=ctx.obj.get("s3_build_cache_secret_key"),
95+
docker_hub_username=ctx.obj.get("docker_hub_username"),
96+
docker_hub_password=ctx.obj.get("docker_hub_password"),
97+
run_step_options=run_step_options,
98+
targeted_platforms=[LOCAL_BUILD_PLATFORM],
99+
versions_to_test=(control_version, target_version),
100+
)
101+
)
102+
103+
try:
104+
await run_connectors_pipelines(
105+
[connector_context for connector_context in connectors_tests_contexts],
106+
run_connector_regression_test_pipeline,
107+
"Regression Test Pipeline",
108+
ctx.obj["concurrency"],
109+
ctx.obj["dagger_logs_path"],
110+
ctx.obj["execute_timeout"],
111+
)
112+
except Exception as e:
113+
main_logger.error("An error occurred while running the regression test pipeline", exc_info=e)
114+
return False
115+
116+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
2+
3+
"""This module groups factory like functions to dispatch tests steps according to the connector under test language."""
4+
5+
from __future__ import annotations
6+
7+
from typing import TYPE_CHECKING
8+
9+
import anyio
10+
from connector_ops.utils import ConnectorLanguage # type: ignore
11+
from pipelines.airbyte_ci.connectors.context import ConnectorContext
12+
from pipelines.airbyte_ci.connectors.regression_test.steps.regression_cats import get_test_steps
13+
from pipelines.airbyte_ci.connectors.reports import ConnectorReport
14+
from pipelines.helpers.execution.run_steps import StepToRun, run_steps
15+
16+
if TYPE_CHECKING:
17+
18+
from pipelines.helpers.execution.run_steps import STEP_TREE
19+
20+
21+
async def run_connector_regression_test_pipeline(context: ConnectorContext, semaphore: anyio.Semaphore) -> ConnectorReport:
22+
"""
23+
Compute the steps to run for a connector test pipeline.
24+
"""
25+
steps_to_run: STEP_TREE = get_test_steps(context)
26+
27+
async with semaphore:
28+
async with context:
29+
result_dict = await run_steps(
30+
runnables=steps_to_run,
31+
options=context.run_step_options,
32+
)
33+
34+
results = list(result_dict.values())
35+
report = ConnectorReport(context, steps_results=results, name="TEST RESULTS")
36+
context.report = report
37+
38+
return report

0 commit comments

Comments
 (0)