Skip to content

Commit 62c6820

Browse files
authored
airbyte-ci: introduce a SecretStore abstraction (#38322)
1 parent fa43741 commit 62c6820

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+726
-520
lines changed

airbyte-ci/connectors/connector_ops/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ requests = "^2.31"
1515
PyYAML = "^6.0"
1616
GitPython = "^3.1.29"
1717
pydantic = "^1.9"
18-
PyGithub = "^1.58.0"
18+
PyGithub = "^2"
1919
rich = "^13.0.0"
2020
pydash = "^6.0.2"
2121
google-cloud-storage = "^2.8.0"

airbyte-ci/connectors/pipelines/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ E.G.: running Poe tasks on the modified internal packages of the current branch:
748748

749749
| Version | PR | Description |
750750
| ------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
751+
| 4.15.0 | [#38322](https://github.com/airbytehq/airbyte/pull/38322) | Introduce a SecretStore abstraction to fetch connector secrets from metadata files. |
751752
| 4.14.1 | [#38582](https://github.com/airbytehq/airbyte/pull/38582) | Fixed bugs in `up_to_date` flags, `pull_request` version change logic. |
752753
| 4.14.0 | [#38281](https://github.com/airbytehq/airbyte/pull/38281) | Conditionally run test suites according to `connectorTestSuitesOptions` in metadata files. |
753754
| 4.13.3 | [#38221](https://github.com/airbytehq/airbyte/pull/38221) | Add dagster cloud dev deployment pipeline opitions |

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,11 @@ async def build(ctx: click.Context, use_host_gradle_dist_tar: bool, build_archit
5454
git_repo_url=ctx.obj["git_repo_url"],
5555
ci_report_bucket=ctx.obj["ci_report_bucket_name"],
5656
report_output_prefix=ctx.obj["report_output_prefix"],
57-
use_remote_secrets=ctx.obj["use_remote_secrets"],
5857
gha_workflow_run_url=ctx.obj.get("gha_workflow_run_url"),
5958
dagger_logs_url=ctx.obj.get("dagger_logs_url"),
6059
pipeline_start_timestamp=ctx.obj.get("pipeline_start_timestamp"),
6160
ci_context=ctx.obj.get("ci_context"),
62-
ci_gcs_credentials=ctx.obj["ci_gcs_credentials"],
61+
ci_gcp_credentials=ctx.obj["ci_gcp_credentials"],
6362
use_local_cdk=ctx.obj.get("use_local_cdk"),
6463
enable_report_auto_open=ctx.obj.get("enable_report_auto_open"),
6564
use_host_gradle_dist_tar=use_host_gradle_dist_tar,

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@ async def bump_version(
3333
git_repo_url=ctx.obj["git_repo_url"],
3434
ci_report_bucket=ctx.obj["ci_report_bucket_name"],
3535
report_output_prefix=ctx.obj["report_output_prefix"],
36-
use_remote_secrets=ctx.obj["use_remote_secrets"],
3736
gha_workflow_run_url=ctx.obj.get("gha_workflow_run_url"),
3837
dagger_logs_url=ctx.obj.get("dagger_logs_url"),
3938
pipeline_start_timestamp=ctx.obj.get("pipeline_start_timestamp"),
4039
ci_context=ctx.obj.get("ci_context"),
41-
ci_gcs_credentials=ctx.obj["ci_gcs_credentials"],
40+
ci_gcp_credentials=ctx.obj["ci_gcp_credentials"],
4241
ci_git_user=ctx.obj["ci_git_user"],
4342
ci_github_access_token=ctx.obj["ci_github_access_token"],
4443
enable_report_auto_open=False,

airbyte-ci/connectors/pipelines/pipelines/airbyte_ci/connectors/bump_version/pipeline.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,9 @@ def __init__(
153153

154154
async def get_repo_dir(self) -> Directory:
155155
if not self.repo_dir:
156-
self.repo_dir = await self.context.get_repo_dir()
157-
return self.repo_dir
156+
repo_dir = await self.context.get_repo_dir()
157+
self.repo_dir = repo_dir
158+
return repo_dir
158159

159160
async def _run(self) -> StepResult:
160161
result = await self.update_metadata()

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

+5-40
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
import os
66
from pathlib import Path
7-
from typing import List, Optional, Set, Tuple
7+
from typing import List, Set, Tuple
88

99
import asyncclick as click
1010
from connector_ops.utils import ConnectorLanguage, SupportLevelEnum, get_all_connectors_in_repo # type: ignore
1111
from pipelines import main_logger
12-
from pipelines.cli.click_decorators import click_append_to_context_object, click_ignore_unused_kwargs, click_merge_args_into_context_obj
12+
from pipelines.cli.airbyte_ci import wrap_in_secret
13+
from pipelines.cli.click_decorators import click_ignore_unused_kwargs, click_merge_args_into_context_obj
1314
from pipelines.cli.lazy_group import LazyGroup
1415
from pipelines.helpers.connectors.modifed import ConnectorWithModifiedFiles, get_connector_modified_files, get_modified_connectors
1516
from pipelines.helpers.git import get_modified_files
@@ -108,37 +109,6 @@ def validate_environment(is_local: bool) -> None:
108109
raise click.UsageError(f"When running in a CI context a {required_env_var} environment variable must be set.")
109110

110111

111-
def should_use_remote_secrets(use_remote_secrets: Optional[bool]) -> bool:
112-
"""Check if the connector secrets should be loaded from Airbyte GSM or from the local secrets directory.
113-
114-
Args:
115-
use_remote_secrets (Optional[bool]): Whether to use remote connector secrets or local connector secrets according to user inputs.
116-
117-
Raises:
118-
click.UsageError: If the --use-remote-secrets flag was provided but no GCP_GSM_CREDENTIALS environment variable was found.
119-
120-
Returns:
121-
bool: Whether to use remote connector secrets (True) or local connector secrets (False).
122-
"""
123-
gcp_gsm_credentials_is_set = bool(os.getenv("GCP_GSM_CREDENTIALS"))
124-
if use_remote_secrets is None:
125-
if gcp_gsm_credentials_is_set:
126-
main_logger.info("GCP_GSM_CREDENTIALS environment variable found, using remote connector secrets.")
127-
return True
128-
else:
129-
main_logger.info("No GCP_GSM_CREDENTIALS environment variable found, using local connector secrets.")
130-
return False
131-
if use_remote_secrets:
132-
if gcp_gsm_credentials_is_set:
133-
main_logger.info("GCP_GSM_CREDENTIALS environment variable found, using remote connector secrets.")
134-
return True
135-
else:
136-
raise click.UsageError("The --use-remote-secrets flag was provided but no GCP_GSM_CREDENTIALS environment variable was found.")
137-
else:
138-
main_logger.info("Using local connector secrets as the --use-local-secrets flag was provided")
139-
return False
140-
141-
142112
@click.group(
143113
cls=LazyGroup,
144114
help="Commands related to connectors and connector acceptance tests.",
@@ -157,12 +127,6 @@ def should_use_remote_secrets(use_remote_secrets: Optional[bool]) -> bool:
157127
"pull_request": "pipelines.airbyte_ci.connectors.pull_request.commands.pull_request",
158128
},
159129
)
160-
@click.option(
161-
"--use-remote-secrets/--use-local-secrets",
162-
help="Use Airbyte GSM connector secrets or local connector secrets.",
163-
type=bool,
164-
default=None,
165-
)
166130
@click.option(
167131
"--name",
168132
"names",
@@ -223,16 +187,17 @@ def should_use_remote_secrets(use_remote_secrets: Optional[bool]) -> bool:
223187
type=click.STRING,
224188
required=False,
225189
envvar="DOCKER_HUB_USERNAME",
190+
callback=wrap_in_secret,
226191
)
227192
@click.option(
228193
"--docker-hub-password",
229194
help="Your password to connect to DockerHub.",
230195
type=click.STRING,
231196
required=False,
232197
envvar="DOCKER_HUB_PASSWORD",
198+
callback=wrap_in_secret,
233199
)
234200
@click_merge_args_into_context_obj
235-
@click_append_to_context_object("use_remote_secrets", lambda ctx: should_use_remote_secrets(ctx.obj["use_remote_secrets"]))
236201
@click.pass_context
237202
@click_ignore_unused_kwargs
238203
async def connectors(

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

+23-42
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import yaml # type: ignore
1616
from asyncer import asyncify
17-
from dagger import Directory, Platform, Secret
17+
from dagger import Directory, Platform
1818
from github import PullRequest
1919
from pipelines.airbyte_ci.connectors.consts import CONNECTOR_TEST_STEP_ID
2020
from pipelines.airbyte_ci.connectors.reports import ConnectorReport
@@ -26,6 +26,7 @@
2626
from pipelines.helpers.slack import send_message_to_webhook
2727
from pipelines.helpers.utils import METADATA_FILE_NAME
2828
from pipelines.models.contexts.pipeline_context import PipelineContext
29+
from pipelines.models.secrets import LocalDirectorySecretStore, Secret, SecretStore
2930

3031
if TYPE_CHECKING:
3132
from pathlib import Path as NativePath
@@ -54,11 +55,10 @@ def __init__(
5455
diffed_branch: str,
5556
git_repo_url: str,
5657
report_output_prefix: str,
57-
use_remote_secrets: bool = True,
5858
ci_report_bucket: Optional[str] = None,
59-
ci_gcs_credentials: Optional[str] = None,
59+
ci_gcp_credentials: Optional[Secret] = None,
6060
ci_git_user: Optional[str] = None,
61-
ci_github_access_token: Optional[str] = None,
61+
ci_github_access_token: Optional[Secret] = None,
6262
connector_acceptance_test_image: str = DEFAULT_CONNECTOR_ACCEPTANCE_TEST_IMAGE,
6363
gha_workflow_run_url: Optional[str] = None,
6464
dagger_logs_url: Optional[str] = None,
@@ -72,13 +72,14 @@ def __init__(
7272
use_local_cdk: bool = False,
7373
use_host_gradle_dist_tar: bool = False,
7474
enable_report_auto_open: bool = True,
75-
docker_hub_username: Optional[str] = None,
76-
docker_hub_password: Optional[str] = None,
77-
s3_build_cache_access_key_id: Optional[str] = None,
78-
s3_build_cache_secret_key: Optional[str] = None,
75+
docker_hub_username: Optional[Secret] = None,
76+
docker_hub_password: Optional[Secret] = None,
77+
s3_build_cache_access_key_id: Optional[Secret] = None,
78+
s3_build_cache_secret_key: Optional[Secret] = None,
7979
concurrent_cat: Optional[bool] = False,
8080
run_step_options: RunStepOptions = RunStepOptions(),
8181
targeted_platforms: Sequence[Platform] = BUILD_PLATFORMS,
82+
secret_stores: Dict[str, SecretStore] | None = None,
8283
) -> None:
8384
"""Initialize a connector context.
8485
@@ -90,7 +91,6 @@ def __init__(
9091
diffed_branch: str: The branch to compare the current branch against.
9192
git_repo_url: str: The URL of the git repository.
9293
report_output_prefix (str): The S3 key to upload the test report to.
93-
use_remote_secrets (bool, optional): Whether to download secrets for GSM or use the local secrets. Defaults to True.
9494
connector_acceptance_test_image (Optional[str], optional): The image to use to run connector acceptance tests. Defaults to DEFAULT_CONNECTOR_ACCEPTANCE_TEST_IMAGE.
9595
gha_workflow_run_url (Optional[str], optional): URL to the github action workflow run. Only valid for CI run. Defaults to None.
9696
dagger_logs_url (Optional[str], optional): URL to the dagger logs. Only valid for CI run. Defaults to None.
@@ -102,17 +102,16 @@ def __init__(
102102
code_tests_only (bool, optional): Whether to ignore non-code tests like QA and metadata checks. Defaults to False.
103103
use_host_gradle_dist_tar (bool, optional): Used when developing java connectors with gradle. Defaults to False.
104104
enable_report_auto_open (bool, optional): Open HTML report in browser window. Defaults to True.
105-
docker_hub_username (Optional[str], optional): Docker Hub username to use to read registries. Defaults to None.
106-
docker_hub_password (Optional[str], optional): Docker Hub password to use to read registries. Defaults to None.
107-
s3_build_cache_access_key_id (Optional[str], optional): Gradle S3 Build Cache credentials. Defaults to None.
108-
s3_build_cache_secret_key (Optional[str], optional): Gradle S3 Build Cache credentials. Defaults to None.
105+
docker_hub_username (Optional[Secret], optional): Docker Hub username to use to read registries. Defaults to None.
106+
docker_hub_password (Optional[Secret], optional): Docker Hub password to use to read registries. Defaults to None.
107+
s3_build_cache_access_key_id (Optional[Secret], optional): Gradle S3 Build Cache credentials. Defaults to None.
108+
s3_build_cache_secret_key (Optional[Secret], optional): Gradle S3 Build Cache credentials. Defaults to None.
109109
concurrent_cat (bool, optional): Whether to run the CAT tests in parallel. Defaults to False.
110110
targeted_platforms (Optional[Iterable[Platform]], optional): The platforms to build the connector image for. Defaults to BUILD_PLATFORMS.
111111
"""
112112

113113
self.pipeline_name = pipeline_name
114114
self.connector = connector
115-
self.use_remote_secrets = use_remote_secrets
116115
self.connector_acceptance_test_image = connector_acceptance_test_image
117116
self._secrets_dir: Optional[Directory] = None
118117
self._updated_secrets_dir: Optional[Directory] = None
@@ -127,7 +126,6 @@ def __init__(
127126
self.s3_build_cache_access_key_id = s3_build_cache_access_key_id
128127
self.s3_build_cache_secret_key = s3_build_cache_secret_key
129128
self.concurrent_cat = concurrent_cat
130-
self._connector_secrets: Optional[Dict[str, Secret]] = None
131129
self.targeted_platforms = targeted_platforms
132130

133131
super().__init__(
@@ -146,25 +144,14 @@ def __init__(
146144
reporting_slack_channel=reporting_slack_channel,
147145
pull_request=pull_request,
148146
ci_report_bucket=ci_report_bucket,
149-
ci_gcs_credentials=ci_gcs_credentials,
147+
ci_gcp_credentials=ci_gcp_credentials,
150148
ci_git_user=ci_git_user,
151149
ci_github_access_token=ci_github_access_token,
152150
run_step_options=self._skip_metadata_disabled_test_suites(run_step_options),
153151
enable_report_auto_open=enable_report_auto_open,
152+
secret_stores=secret_stores,
154153
)
155154

156-
@property
157-
def s3_build_cache_access_key_id_secret(self) -> Optional[Secret]:
158-
if self.s3_build_cache_access_key_id:
159-
return self.dagger_client.set_secret("s3_build_cache_access_key_id", self.s3_build_cache_access_key_id)
160-
return None
161-
162-
@property
163-
def s3_build_cache_secret_key_secret(self) -> Optional[Secret]:
164-
if self.s3_build_cache_access_key_id and self.s3_build_cache_secret_key:
165-
return self.dagger_client.set_secret("s3_build_cache_secret_key", self.s3_build_cache_secret_key)
166-
return None
167-
168155
@property
169156
def modified_files(self) -> FrozenSet[NativePath]:
170157
return self.connector.modified_files
@@ -195,7 +182,7 @@ def live_tests_dir(self) -> Directory:
195182

196183
@property
197184
def should_save_updated_secrets(self) -> bool:
198-
return self.use_remote_secrets and self.updated_secrets_dir is not None
185+
return self.ci_gcp_credentials is not None and self.updated_secrets_dir is not None
199186

200187
@property
201188
def host_image_export_dir_path(self) -> str:
@@ -222,21 +209,15 @@ def docker_image(self) -> str:
222209
return f"{self.docker_repository}:{self.docker_image_tag}"
223210

224211
@property
225-
def docker_hub_username_secret(self) -> Optional[Secret]:
226-
if self.docker_hub_username is None:
227-
return None
228-
return self.dagger_client.set_secret("docker_hub_username", self.docker_hub_username)
212+
def local_secret_store_name(self) -> str:
213+
return f"{self.connector.technical_name}-local"
229214

230215
@property
231-
def docker_hub_password_secret(self) -> Optional[Secret]:
232-
if self.docker_hub_password is None:
233-
return None
234-
return self.dagger_client.set_secret("docker_hub_password", self.docker_hub_password)
235-
236-
async def get_connector_secrets(self) -> Dict[str, Secret]:
237-
if self._connector_secrets is None:
238-
self._connector_secrets = await secrets.get_connector_secrets(self)
239-
return self._connector_secrets
216+
def local_secret_store(self) -> Optional[LocalDirectorySecretStore]:
217+
connector_secrets_path = self.connector.code_directory / "secrets"
218+
if connector_secrets_path.is_dir():
219+
return LocalDirectorySecretStore(connector_secrets_path)
220+
return None
240221

241222
async def get_connector_dir(self, exclude: Optional[List[str]] = None, include: Optional[List[str]] = None) -> Directory:
242223
"""Get the connector under test source code directory.

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ async def migrate_to_base_image(
5656
git_repo_url=ctx.obj["git_repo_url"],
5757
ci_report_bucket=ctx.obj["ci_report_bucket_name"],
5858
report_output_prefix=ctx.obj["report_output_prefix"],
59-
use_remote_secrets=ctx.obj["use_remote_secrets"],
6059
gha_workflow_run_url=ctx.obj.get("gha_workflow_run_url"),
6160
dagger_logs_url=ctx.obj.get("dagger_logs_url"),
6261
pipeline_start_timestamp=ctx.obj.get("pipeline_start_timestamp"),
6362
ci_context=ctx.obj.get("ci_context"),
64-
ci_gcs_credentials=ctx.obj["ci_gcs_credentials"],
63+
ci_gcp_credentials=ctx.obj["ci_gcp_credentials"],
6564
ci_git_user=ctx.obj["ci_git_user"],
6665
ci_github_access_token=ctx.obj["ci_github_access_token"],
6766
enable_report_auto_open=False,

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,11 @@ async def migrate_to_poetry(ctx: click.Context, changelog: bool, bump: str | Non
4343
git_repo_url=ctx.obj["git_repo_url"],
4444
ci_report_bucket=ctx.obj["ci_report_bucket_name"],
4545
report_output_prefix=ctx.obj["report_output_prefix"],
46-
use_remote_secrets=ctx.obj["use_remote_secrets"],
4746
gha_workflow_run_url=ctx.obj.get("gha_workflow_run_url"),
4847
dagger_logs_url=ctx.obj.get("dagger_logs_url"),
4948
pipeline_start_timestamp=ctx.obj.get("pipeline_start_timestamp"),
5049
ci_context=ctx.obj.get("ci_context"),
51-
ci_gcs_credentials=ctx.obj["ci_gcs_credentials"],
50+
ci_gcp_credentials=ctx.obj["ci_gcp_credentials"],
5251
ci_git_user=ctx.obj["ci_git_user"],
5352
ci_github_access_token=ctx.obj["ci_github_access_token"],
5453
enable_report_auto_open=False,

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,7 @@ async def run_connectors_pipelines(
9898
docker_hub_password = contexts[0].docker_hub_password
9999

100100
if docker_hub_username and docker_hub_password:
101-
docker_hub_username_secret = dagger_client.set_secret("DOCKER_HUB_USERNAME", docker_hub_username)
102-
docker_hub_password_secret = dagger_client.set_secret("DOCKER_HUB_PASSWORD", docker_hub_password)
103-
dockerd_service = docker.with_global_dockerd_service(dagger_client, docker_hub_username_secret, docker_hub_password_secret)
101+
dockerd_service = docker.with_global_dockerd_service(dagger_client, docker_hub_username, docker_hub_password)
104102
else:
105103
dockerd_service = docker.with_global_dockerd_service(dagger_client)
106104

0 commit comments

Comments
 (0)