Skip to content
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

Regression tests: run with airbyte-ci #37440

Merged
merged 15 commits into from
Apr 25, 2024
Merged

Conversation

clnoll
Copy link
Contributor

@clnoll clnoll commented Apr 19, 2024

Adds a new command and pipeline for running regression tests in airbyte-ci.

This requires us to make a couple of changes to the live tests to bypass prompts. For this reason, test artifacts are cleaned up immediately when the test run is complete. If artifacts are needed the user should run this with pytest (as documented here). (When run in Github Actions, artifacts will be uploaded to GCS.)

TODO:

  • Use a service account for gcloud auth.

@alafanechere responding to a couple of your questions/comments:

Can we make the RegressionTests step inherit from PytestStep?

PytestStep mostly concerns itself with running tests that exist in a connector container. We'd have to refactor/overwrite a lot of the existing functionality to make it reusable here.

I don't think there's a need for a specific regression test command in airbyte ci. I believe we should use airbyte-ci connectors test. If we want to pass custom parameters to regression test this can be done via parameters passing: `--regression-test.--=

To me it feels premature to have regression tests be part of the testsuite so IMO we should hold off until we've decided on a cadence to run them on and can verify that we're getting meaningful, non-noisy results. I'm not personally totally sure we'll ever want to run them as part of "normal" tests, so would rather give them their own command than pollute test with conditional logic.

@clnoll clnoll requested a review from a team as a code owner April 19, 2024 18:34
Copy link

vercel bot commented Apr 19, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Ignored Deployment
Name Status Preview Comments Updated (UTC)
airbyte-docs ⬜️ Ignored (Inspect) Visit Preview Apr 25, 2024 9:41pm

@alafanechere
Copy link
Contributor

To me it feels premature to have regression tests be part of the testsuite so IMO we should hold off until we've decided on a cadence to run them on and can verify that we're getting meaningful, non-noisy results. I'm not personally totally sure we'll ever want to run them as part of "normal" tests, so would rather give them their own command than pollute test with conditional logic.

@clnoll this makes sense to me and I think this is where I miss details to give a review: how do you plan to orchestrate the CI run?

I'd like to avoid the proliferation of commands on airbyte-ci and would prefer:

  1. Changing our existing "normal test suites CI command" from airbyte-ci connectors --modified test to airbyte-connectors --modified test --skip-step=regression
  2. Declaring a specific regression test workflow which runs airbyte-connectors --modified test --only-step=regression

This will make it easier to eventually integrate the regression test as part of the automated test suite and avoid introducing new command and code.

@clnoll
Copy link
Contributor Author

clnoll commented Apr 23, 2024

@alafanechere my hesitation with --skip-step=regression is that for anyone who is running airbyte-ci connectors test locally, they'll have to actively remember to skip regression tests, or else we could end up running them many many times. Since we haven't implemented rate limiting per connection ID yet and don't have a perfect story for what it means for a regression test to fail, I don't think we should allow this to happen.

If we're agreed on that, I'm happy either keeping them in airbyte-ci connectors test but defaulting to skipping them (which could get a little bit ugly in the code but would address my concern) or give them a separate command as they have in this PR. I personally still have a slight preference for a separate command because I don't see them ever being part of the normal CI test cadence, but can understand the aversion to giving airbyte-ci too many commands and don't feel too strongly, as long as we have sensible defaults.

@alafanechere
Copy link
Contributor

alafanechere commented Apr 23, 2024

If we're agreed on that, I'm happy either keeping them in airbyte-ci connectors test but defaulting to skipping them (which could get a little bit ugly in the code but would address my concern) or give them a separate command as they have in this PR. I personally still have a slight preference for a separate command because I don't see them ever being part of the normal CI test cadence, but can understand the aversion to giving airbyte-ci too many commands and don't feel too strongly, as long as we have sensible defaults.

I think a reasonable tradeoff could be to introduce a --run-regression-tests flag to the test command. It would default to False to address the "do no run it locally" concern. I'd love to keep the regression test run in the main test pipeline so that enabling it later by default will be very easy and we do not risk to introduce diverging code paths to refactor later.

We could also make the default --skip-step value be ["regression"]. I think I prefer this 😄

@clnoll
Copy link
Contributor Author

clnoll commented Apr 23, 2024

Just closing the loop on the conversation - we've decided to keep regression tests in the main test pipeline.

To avoid regression tests getting run unintentionally, we'll always add them to the --skip-step list, so they'll only run when explicitly specified via --only-step.

@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from 80b808d to df0ed5b Compare April 23, 2024 19:48
@@ -96,10 +96,9 @@ async def get_connector_container(dagger_client: dagger.Client, image_name_with_
# If a container_id.txt file is available, we'll use it to load the connector container
# We use a txt file as container ids can be too long to be passed as env vars
# It's used for dagger-in-dagger use case with airbyte-ci, when the connector container is built via an upstream dagger operation
connector_container_id_path = Path("/tmp/container_id.txt")
if connector_container_id_path.exists():
# If the CONNECTOR_CONTAINER_ID env var is set, we'll use it to load the connector container
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're not getting it from the env var so I deleted this comment.

self.should_read_with_state = self._get_item_or_default(options, "should-read-with-state", True)

@staticmethod
def _get_item_or_default(options: Dict[str, Any], key: str, default: Any):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alafanechere is this something we're already doing elsewhere?

Copy link
Contributor

@alafanechere alafanechere left a comment

Choose a reason for hiding this comment

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

I'm mainly requesting changes for the minor suggestions. LGTM logic wise.
Can you please bump both project versions and changelogs 🙏

Comment on lines +31 to +34
if user_id:
analytics.identify(user_id)
else:
user_id = "airbyte-ci"
Copy link
Contributor

Choose a reason for hiding this comment

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

I would find it cleaner to explicitely pass airbyte-ci as the user id.
The user_id could also be fetched from an environment variable as it'd make it possible to track different CI use of the tool.

Comment on lines 81 to 98
start_timestamp = int(time.time())
start_timestamp = int(config.getoption("--start-timestamp") or time.time())
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why we want to override the start_timestamp? Is it to get predictable session path?
I would suggest creating a function which would produce the test_artifacts_directory and uses the github run id if we're in CI.
Something like:

def get_artifacts_directory(start_timestamp):
  suffix = os.environ["GITHUB_RUN_ID"] if  "GITHUB_RUN_ID" in os.environ else str(start_timestamp)
  return  MAIN_OUTPUT_DIRECTORY / f"session_{suffix}"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it to get predictable session path?

Yes that's right.
Using "GITHUB_RUN_ID" will work when this is run in GhA, but when we run it locally with airbyte-ci we don't have that. I'm not seeing anything else in the environment that makes sense to use.

I agree it feels a little hacky to pass in start time, but we do need something that's known by both the external dagger process and the conftest.

To address this I made a change to pass --run-id to pytest instead of --start-timestamp. Then I let the dagger process look for GITHUB_RUN_ID and fall back to a start timestamp if it's not present. Does this look better to you?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why this file changed

self.should_read_with_state = self._get_item_or_default(options, "should-read-with-state", True)

@staticmethod
def _get_item_or_default(options: Dict[str, Any], key: str, default: Any):
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think so but it can go to the helpers / utils. I'd be more inclined to use dpath instead of writing this kind of function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use dpath and made it a staticmethod on RunStepOptions.

"""
super().__init__(context)
self.connector_image = context.docker_image.split(":")[0]
print(self.context.run_step_options.step_params)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we avoid printing here in case there's a secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, deleted.

Comment on lines 397 to 398
await container.directory(regression_tests_artifacts_dir).export(regression_tests_artifacts_dir)
path_to_report = f"{regression_tests_artifacts_dir}/session_{int(start_timestamp)}/report.html"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we just export the report in CI to minimize sensitive data exposure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, done.

Comment on lines +423 to +416
.with_mounted_file("/root/.ssh/id_rsa", self.dagger_client.host().file(str(Path("~/.ssh/id_rsa").expanduser()))) # TODO
.with_mounted_file(
"/root/.ssh/known_hosts", self.dagger_client.host().file(str(Path("~/.ssh/known_hosts").expanduser())) # TODO
)
Copy link
Contributor

Choose a reason for hiding this comment

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

not 💯 sure this will work in GHA runners

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't 😄 . But I'll make the needed update in the gha PR instead of here.

container = (
(
container.with_exec(["apt-get", "update"])
.with_exec(["apt-get", "install", "-y", "git", "openssh-client", "curl", "docker.io"])
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit annoying that we have to install git and openssh-client just because we have a private dependency to install...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. I'm trying to make this go away in the gha follow up PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

But I did not mean I have a better suggestion. 😄

@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from bd1287b to d57c2dd Compare April 24, 2024 17:29
Copy link
Contributor

@alafanechere alafanechere left a comment

Choose a reason for hiding this comment

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

Thanks for your replies and changes 🙏 Please bump the versions and add changelog entries before merging.

container = (
(
container.with_exec(["apt-get", "update"])
.with_exec(["apt-get", "install", "-y", "git", "openssh-client", "curl", "docker.io"])
Copy link
Contributor

Choose a reason for hiding this comment

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

But I did not mean I have a better suggestion. 😄

@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch 3 times, most recently from 1ec9282 to 51bcc33 Compare April 25, 2024 12:22
# If a package from `airbyte-platform-internal` is required, modify the entry in pyproject.toml to use https instead of ssh, when run in Github Actions
if is_ci:
container = container.with_exec(
["sed", "-i", "-E", r"s,git@github\.com:airbytehq/airbyte-platform-internal,https://github.com/airbytehq/airbyte-platform-internal.git,", "pyproject.toml"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make the URL https:// in the checked in pyproject.toml.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we did that poetry install wouldn't work for regression tests locally (whether running with pytest or airbyte-ci) unless the user has a PAT in their environment.

Comment on lines 198 to 227
).with_exec(
["poetry", "source", "add", "--priority=supplemental", "airbyte-platform-internal-source",
"https://github.com/airbytehq/airbyte-platform-internal.git"]
).with_exec(
[
"poetry", "config", "http-basic.airbyte-platform-internal-source", "octavia-squidington-iii",
pipeline_context_params["ci_github_access_token"],
]
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain in a comment why this is for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 done.

@@ -190,6 +191,20 @@ def prepare_container_for_poe_tasks(
# Set working directory to the poetry package directory
container = container.with_workdir(f"/airbyte/{poetry_package_path}")

# If a package from `airbyte-platform-internal` is required, modify the entry in pyproject.toml to use https instead of ssh, when run in Github Actions
Copy link
Contributor

Choose a reason for hiding this comment

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

A simpler but hackier option would be to download the private package from the private repo outside of airbyte-ci in the GHA workflow and edit the pyproject.toml to point to a local path instead of using a git URL.

The best approach would be to host the private packages in a private PyPi repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll look into moving it into the GHA when I'm wrapping up the GHA-specific PR.

Agree re hosting the private package, but it didn't seem worth it for just this one thing.

@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from 51bcc33 to d819889 Compare April 25, 2024 13:45
@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch 7 times, most recently from 853f9ca to 363cbb6 Compare April 25, 2024 16:48
@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from 363cbb6 to fe882c5 Compare April 25, 2024 17:13
@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from 892a88c to c37aad4 Compare April 25, 2024 18:28
@clnoll
Copy link
Contributor Author

clnoll commented Apr 25, 2024

/format-fix

Format-fix job started... Check job output.

❌ Job failed.

@clnoll clnoll force-pushed the catherine/live-tests-with-airbyte-ci branch from e5e899f to 7c2b897 Compare April 25, 2024 21:41
@clnoll clnoll merged commit 7bd0324 into master Apr 25, 2024
35 checks passed
@clnoll clnoll deleted the catherine/live-tests-with-airbyte-ci branch April 25, 2024 21:59
Copy link

sentry-io bot commented Apr 25, 2024

Suspect Issues

This pull request was deployed and Sentry observed the following issues:

  • ‼️ QueryError: host directory /home/runner/.config/gcloud: rpc error: code = Unknown desc = resolve : lstat /hom... dagger.client._core in execute View Issue
  • ‼️ ExceptionGroup: 2 exceptions were raised in the task group: anyio._backends._asyncio in __aexit__ View Issue
  • ‼️ ExceptionGroup: 2 exceptions were raised in the task group: anyio._backends._asyncio in __aexit__ View Issue
  • ‼️ QueryError: host directory /home/runner/.config/gcloud: rpc error: code = Unknown desc = resolve : lstat /hom... pipelines.airbyte_ci.connectors.test.steps.comm... View Issue
  • ‼️ QueryError: lstat /tmp/regression_tests_artifacts/session_8886483582/report.html: no such file or directory pipelines.airbyte_ci.connectors.test.steps.comm... View Issue

Did you find this useful? React with a 👍 or 👎

[tool.airbyte_ci]
optional_poetry_groups = ["dev"]
poe_tasks = []
required_environment_variables = ["DOCKER_HUB_USERNAME", "DOCKER_HUB_PASSWORD"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you tell me more about how we are using those credentials in live-tests? Are we using any private images today, and/or publishing? Are we logging in to dockerhub in the flow, and do we have to do it? /cc @alafanechere

@@ -1,4 +1,5 @@
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
from __future__ import annotations
Copy link
Contributor

Choose a reason for hiding this comment

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

What version of Python are we running this on, mainly? Seemingly that is obsolete in 3.10, right?

FVidalCarneiro pushed a commit to AgiData/airbyte that referenced this pull request May 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants