|
6 | 6 |
|
7 | 7 | from __future__ import annotations
|
8 | 8 |
|
9 |
| -from copy import deepcopy |
10 | 9 | from datetime import datetime
|
11 | 10 | from pathlib import Path
|
12 | 11 | from types import TracebackType
|
|
16 | 15 | from asyncer import asyncify
|
17 | 16 | from dagger import Directory, Platform
|
18 | 17 | from github import PullRequest
|
19 |
| -from pipelines.airbyte_ci.connectors.consts import CONNECTOR_TEST_STEP_ID |
20 | 18 | from pipelines.airbyte_ci.connectors.reports import ConnectorReport
|
21 | 19 | from pipelines.consts import BUILD_PLATFORMS
|
22 | 20 | from pipelines.dagger.actions import secrets
|
|
26 | 24 | from pipelines.helpers.slack import send_message_to_webhook
|
27 | 25 | from pipelines.helpers.utils import METADATA_FILE_NAME
|
28 | 26 | from pipelines.models.contexts.pipeline_context import PipelineContext
|
29 |
| -from pipelines.models.secrets import LocalDirectorySecretStore, Secret, SecretNotFoundError, SecretStore |
30 |
| -from pydash import find # type: ignore |
| 27 | +from pipelines.models.secrets import LocalDirectorySecretStore, Secret, SecretStore |
31 | 28 |
|
32 | 29 | if TYPE_CHECKING:
|
33 |
| - from logging import Logger |
34 | 30 | from pathlib import Path as NativePath
|
35 | 31 | from typing import Dict, FrozenSet, List, Optional, Sequence
|
36 |
| -# These test suite names are declared in metadata.yaml files |
37 |
| -TEST_SUITE_NAME_TO_STEP_ID = { |
38 |
| - "unitTests": CONNECTOR_TEST_STEP_ID.UNIT, |
39 |
| - "integrationTests": CONNECTOR_TEST_STEP_ID.INTEGRATION, |
40 |
| - "acceptanceTests": CONNECTOR_TEST_STEP_ID.ACCEPTANCE, |
41 |
| -} |
42 | 32 |
|
43 | 33 |
|
44 | 34 | class ConnectorContext(PipelineContext):
|
@@ -147,11 +137,10 @@ def __init__(
|
147 | 137 | ci_gcp_credentials=ci_gcp_credentials,
|
148 | 138 | ci_git_user=ci_git_user,
|
149 | 139 | ci_github_access_token=ci_github_access_token,
|
150 |
| - run_step_options=self._skip_metadata_disabled_test_suites(run_step_options), |
| 140 | + run_step_options=run_step_options, |
151 | 141 | enable_report_auto_open=enable_report_auto_open,
|
152 | 142 | secret_stores=secret_stores,
|
153 | 143 | )
|
154 |
| - self.step_id_to_secrets_mapping = self._get_step_id_to_secret_mapping() |
155 | 144 |
|
156 | 145 | @property
|
157 | 146 | def modified_files(self) -> FrozenSet[NativePath]:
|
@@ -233,124 +222,6 @@ async def get_connector_dir(self, exclude: Optional[List[str]] = None, include:
|
233 | 222 | vanilla_connector_dir = self.get_repo_dir(str(self.connector.code_directory), exclude=exclude, include=include)
|
234 | 223 | return await vanilla_connector_dir.with_timestamps(1)
|
235 | 224 |
|
236 |
| - @staticmethod |
237 |
| - def _handle_missing_secret_store( |
238 |
| - secret_info: Dict[str, str | Dict[str, str]], raise_on_missing: bool, logger: Optional[Logger] = None |
239 |
| - ) -> None: |
240 |
| - assert isinstance(secret_info["secretStore"], dict), "The secretStore field must be a dict" |
241 |
| - message = f"Secret {secret_info['name']} can't be retrieved as {secret_info['secretStore']['alias']} is not available" |
242 |
| - if raise_on_missing: |
243 |
| - raise SecretNotFoundError(message) |
244 |
| - if logger is not None: |
245 |
| - logger.warn(message) |
246 |
| - |
247 |
| - @staticmethod |
248 |
| - def _process_secret( |
249 |
| - secret_info: Dict[str, str | Dict[str, str]], |
250 |
| - secret_stores: Dict[str, SecretStore], |
251 |
| - raise_on_missing: bool, |
252 |
| - logger: Optional[Logger] = None, |
253 |
| - ) -> Optional[Secret]: |
254 |
| - assert isinstance(secret_info["secretStore"], dict), "The secretStore field must be a dict" |
255 |
| - secret_store_alias = secret_info["secretStore"]["alias"] |
256 |
| - if secret_store_alias not in secret_stores: |
257 |
| - ConnectorContext._handle_missing_secret_store(secret_info, raise_on_missing, logger) |
258 |
| - return None |
259 |
| - else: |
260 |
| - # All these asserts and casting are there to make MyPy happy |
261 |
| - # The dict structure being nested MyPy can't figure if the values are str or dict |
262 |
| - assert isinstance(secret_info["name"], str), "The secret name field must be a string" |
263 |
| - if file_name := secret_info.get("fileName"): |
264 |
| - assert isinstance(secret_info["fileName"], str), "The secret fileName must be a string" |
265 |
| - file_name = str(secret_info["fileName"]) |
266 |
| - else: |
267 |
| - file_name = None |
268 |
| - return Secret(secret_info["name"], secret_stores[secret_store_alias], file_name=file_name) |
269 |
| - |
270 |
| - @staticmethod |
271 |
| - def get_secrets_from_connector_test_suites_option( |
272 |
| - connector_test_suites_options: List[Dict[str, str | Dict[str, List[Dict[str, str | Dict[str, str]]]]]], |
273 |
| - suite_name: str, |
274 |
| - secret_stores: Dict[str, SecretStore], |
275 |
| - raise_on_missing_secret_store: bool = True, |
276 |
| - logger: Logger | None = None, |
277 |
| - ) -> List[Secret]: |
278 |
| - """Get secrets declared in metadata connectorTestSuitesOptions for a test suite name. |
279 |
| - It will use the secret store alias declared in connectorTestSuitesOptions. |
280 |
| - If the secret store is not available a warning or and error could be raised according to the raise_on_missing_secret_store parameter value. |
281 |
| - We usually want to raise an error when running in CI context and log a warning when running locally, as locally we can fallback on local secrets. |
282 |
| -
|
283 |
| - Args: |
284 |
| - connector_test_suites_options (List[Dict[str, str | Dict]]): The connector under test test suite options |
285 |
| - suite_name (str): The test suite name |
286 |
| - secret_stores (Dict[str, SecretStore]): The available secrets stores |
287 |
| - raise_on_missing_secret_store (bool, optional): Raise an error if the secret store declared in the connectorTestSuitesOptions is not available. Defaults to True. |
288 |
| - logger (Logger | None, optional): Logger to log a warning if the secret store declared in the connectorTestSuitesOptions is not available. Defaults to None. |
289 |
| -
|
290 |
| - Raises: |
291 |
| - SecretNotFoundError: Raised if the secret store declared in the connectorTestSuitesOptions is not available and raise_on_missing_secret_store is truthy. |
292 |
| -
|
293 |
| - Returns: |
294 |
| - List[Secret]: List of secrets declared in the connectorTestSuitesOptions for a test suite name. |
295 |
| - """ |
296 |
| - secrets: List[Secret] = [] |
297 |
| - enabled_test_suite = find(connector_test_suites_options, lambda x: x["suite"] == suite_name) |
298 |
| - |
299 |
| - if enabled_test_suite and "testSecrets" in enabled_test_suite: |
300 |
| - for secret_info in enabled_test_suite["testSecrets"]: |
301 |
| - if secret := ConnectorContext._process_secret(secret_info, secret_stores, raise_on_missing_secret_store, logger): |
302 |
| - secrets.append(secret) |
303 |
| - |
304 |
| - return secrets |
305 |
| - |
306 |
| - def get_connector_secrets_for_test_suite( |
307 |
| - self, test_suite_name: str, connector_test_suites_options: List, local_secrets: List[Secret] |
308 |
| - ) -> List[Secret]: |
309 |
| - """Get secrets to use for a test suite. |
310 |
| - Always merge secrets declared in metadata's connectorTestSuiteOptions with secrets declared locally. |
311 |
| -
|
312 |
| - Args: |
313 |
| - test_suite_name (str): Name of the test suite to get secrets for |
314 |
| - context (ConnectorContext): The current connector context |
315 |
| - connector_test_suites_options (Dict): The current connector test suite options (from metadata) |
316 |
| - local_secrets (List[Secret]): The local connector secrets. |
317 |
| -
|
318 |
| - Returns: |
319 |
| - List[Secret]: Secrets to use to run the passed test suite name. |
320 |
| - """ |
321 |
| - return ( |
322 |
| - self.get_secrets_from_connector_test_suites_option( |
323 |
| - connector_test_suites_options, |
324 |
| - test_suite_name, |
325 |
| - self.secret_stores, |
326 |
| - raise_on_missing_secret_store=self.is_ci, |
327 |
| - logger=self.logger, |
328 |
| - ) |
329 |
| - + local_secrets |
330 |
| - ) |
331 |
| - |
332 |
| - def _get_step_id_to_secret_mapping(self) -> Dict[CONNECTOR_TEST_STEP_ID, List[Secret]]: |
333 |
| - step_id_to_secrets: Dict[CONNECTOR_TEST_STEP_ID, List[Secret]] = { |
334 |
| - CONNECTOR_TEST_STEP_ID.UNIT: [], |
335 |
| - CONNECTOR_TEST_STEP_ID.INTEGRATION: [], |
336 |
| - CONNECTOR_TEST_STEP_ID.ACCEPTANCE: [], |
337 |
| - } |
338 |
| - local_secrets = self.local_secret_store.get_all_secrets() if self.local_secret_store else [] |
339 |
| - connector_test_suites_options = self.metadata.get("connectorTestSuitesOptions", []) |
340 |
| - |
341 |
| - keep_steps = set(self.run_step_options.keep_steps or []) |
342 |
| - skip_steps = set(self.run_step_options.skip_steps or []) |
343 |
| - |
344 |
| - for test_suite_name, step_id in TEST_SUITE_NAME_TO_STEP_ID.items(): |
345 |
| - if step_id in keep_steps or (not keep_steps and step_id not in skip_steps): |
346 |
| - step_id_to_secrets[step_id] = self.get_connector_secrets_for_test_suite( |
347 |
| - test_suite_name, connector_test_suites_options, local_secrets |
348 |
| - ) |
349 |
| - return step_id_to_secrets |
350 |
| - |
351 |
| - def get_secrets_for_step_id(self, step_id: CONNECTOR_TEST_STEP_ID) -> List[Secret]: |
352 |
| - return self.step_id_to_secrets_mapping.get(step_id, []) |
353 |
| - |
354 | 225 | async def __aexit__(
|
355 | 226 | self, exception_type: Optional[type[BaseException]], exception_value: Optional[BaseException], traceback: Optional[TracebackType]
|
356 | 227 | ) -> bool:
|
@@ -395,30 +266,3 @@ async def __aexit__(
|
395 | 266 |
|
396 | 267 | def create_slack_message(self) -> str:
|
397 | 268 | raise NotImplementedError
|
398 |
| - |
399 |
| - def _get_step_id_to_skip_according_to_metadata(self) -> List[CONNECTOR_TEST_STEP_ID]: |
400 |
| - """The connector metadata have a connectorTestSuitesOptions field. |
401 |
| - It allows connector developers to declare the test suites that are enabled for a connector. |
402 |
| - This function retrieved enabled test suites according to this field value and returns the test suites steps that are skipped (because they're not declared in this field.) |
403 |
| - The skippable test suites steps are declared in TEST_SUITE_NAME_TO_STEP_ID. |
404 |
| -
|
405 |
| - Returns: |
406 |
| - List[CONNECTOR_TEST_STEP_ID]: List of step ids that should be skipped according to connector metadata. |
407 |
| - """ |
408 |
| - enabled_test_suites = [option["suite"] for option in self.metadata.get("connectorTestSuitesOptions", [])] |
409 |
| - return [step_id for test_suite_name, step_id in TEST_SUITE_NAME_TO_STEP_ID.items() if test_suite_name not in enabled_test_suites] |
410 |
| - |
411 |
| - def _skip_metadata_disabled_test_suites(self, run_step_options: RunStepOptions) -> RunStepOptions: |
412 |
| - """Updated the original run_step_options to skip the disabled test suites according to connector metadata. |
413 |
| -
|
414 |
| - Args: |
415 |
| - run_step_options (RunStepOptions): Original run step options. |
416 |
| -
|
417 |
| - Returns: |
418 |
| - RunStepOptions: Updated run step options. |
419 |
| - """ |
420 |
| - run_step_options = deepcopy(run_step_options) |
421 |
| - # If any `skip_steps` are present, we will run everything except the skipped steps, instead of just `keep_steps`. |
422 |
| - if not run_step_options.keep_steps: |
423 |
| - run_step_options.skip_steps += self._get_step_id_to_skip_according_to_metadata() |
424 |
| - return run_step_options |
0 commit comments