Skip to content

feat(platform): add gcloud WIF credential helper command (PYSDK-146)#689

Open
dima-aignostics wants to merge 10 commits into
mainfrom
feat/PYSDK-146-add-user-token-command
Open

feat(platform): add gcloud WIF credential helper command (PYSDK-146)#689
dima-aignostics wants to merge 10 commits into
mainfrom
feat/PYSDK-146-add-user-token-command

Conversation

@dima-aignostics

Copy link
Copy Markdown
Contributor

Adds aignostics user token CLI command implementing the gcloud pluggable external credential source contract. Outputs an Aignostics id_token as JSON so gcloud can use it for Workload Identity Federation when accessing GCS.

Adds `aignostics user token` CLI command implementing the gcloud pluggable
external credential source contract. Outputs an Aignostics id_token as JSON
so gcloud can use it for Workload Identity Federation when accessing GCS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dima-aignostics dima-aignostics requested a review from a team as a code owner July 1, 2026 10:47
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
962 1 961 15
View the top 1 failed test(s) by shortest run time
tests.aignostics.application.cli_test::test_cli_run_submit_and_describe_and_cancel_and_download_and_delete
Stack Traces | 67.5s run time
runner = <typer.testing.CliRunner object at 0x7f07b08727b0>
tmp_path = PosixPath('.../pytest-20/popen-gw3/test_cli_run_submit_and_descri0')
silent_logging = None
record_property = <function record_property.<locals>.append_property at 0x7f07b0de1170>

    @pytest.mark.e2e
    @pytest.mark.long_running
    @pytest.mark.flaky(retries=3, delay=5)
    @pytest.mark.timeout(timeout=60 * 10)
    @pytest.mark.skipif(
        (platform.system() == "Linux" and platform.machine() in {"aarch64", "arm64"})
        or (platform.system() in {"Darwin", "Windows"}),
        reason=(
            "Only run on Linux x86_64 / GitHub Actions ubuntu-latest to avoid creating unnecessary load on the platform."
        ),
    )
    def test_cli_run_submit_and_describe_and_cancel_and_download_and_delete(  # noqa: PLR0915
        runner: CliRunner, tmp_path: Path, silent_logging, record_property
    ) -> None:
        """Check run submit command runs successfully."""
        record_property("tested-item-id", "TC-APPLICATION-CLI-02")
        with submitted_run(
            runner,
            tmp_path,
            CSV_CONTENT_SPOT0,
            extra_args=[
                "--note",
                "note_of_this_complex_test",
                "--tags",
                "cli-test,test_cli_run_submit_and_describe_and_cancel_and_download_and_delete,further-tag",
                "--deadline",
                (datetime.now(tz=UTC) + timedelta(minutes=10)).isoformat(),
                "--onboard-to-aignostics-portal",
                "--gpu-type",
                PIPELINE_GPU_TYPE,
                "--force",
            ],
        ) as run_id:
            # Test that we can find this run by it's note via the query parameter
            list_result = runner.invoke(
                cli,
                [
                    "application",
                    "run",
                    "list",
                    "--query",
                    "note_of_this_complex_test",
                    "--limit",
                    str(LIST_APPLICATION_RUNS_MAX_PAGE_SIZE),
                ],
            )
            assert list_result.exit_code == 0
            list_output = normalize_output(list_result.stdout)
            assert run_id in list_output, f"Run ID '{run_id}' not found when filtering by note via query"
    
            # Test that we can find this run by it's tag via the query parameter
            list_result = runner.invoke(
                cli,
                [
                    "application",
                    "run",
                    "list",
                    "--query",
                    "test_cli_run_submit_and_describe_and_cancel_and_download_and_delete",
                    "--limit",
                    str(LIST_APPLICATION_RUNS_MAX_PAGE_SIZE),
                ],
            )
            assert list_result.exit_code == 0
            list_output = normalize_output(list_result.stdout)
>           assert run_id in list_output, f"Run ID '{run_id}' not found when filtering by tag via query"
E           AssertionError: Run ID '1029fba8-b14e-48e5-a58a-41ac7a0c21dd' not found when filtering by tag via query
E           assert '1029fba8-b14e-48e5-a58a-41ac7a0c21dd' in 'Error: Failed to list runs: Failed to retrieve application runs: (503)Reason: Service UnavailableHTTP response headers: HTTPHeaderDict({\'date\': \'Mon, 06 Jul 2026 14:21:53 GMT\', \'server\': \'envoy\', \'content-length\': \'85\', \'content-type\': \'application/json\', \'x-trace-id\': \'2e3cf899d6d3238426481e3b19bd4626\', \'x-envoy-upstream-service-time\': \'5049\', \'vary\': \'Accept-Encoding\'})HTTP response body: {"detail":"Query timed out. Add additional column filters to narrow the result set."}'

.../aignostics/application/cli_test.py:586: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

dima-aignostics and others added 8 commits July 1, 2026 14:30
Replace get_token mock with a real token file on disk so that
get_token's cache-hit path (file existence check, expiry parsing,
5-minute window validation) executes for real.

For success tests: write token:future_expiry to tmp_path and mock
settings at the authentication layer; only _inform_sentry_about_user
is suppressed to avoid JWT network calls.

For failure tests: point settings at a non-existent file so the cache
miss occurs naturally, then mock _authenticate to raise so get_token's
full failure path is exercised without real OAuth flows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove stray backslash line continuation and add missing return type
annotation on _settings_with_token_file staticmethod.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The combined upload was timing out (500) because the CycloneDX SBOM
had no root component, causing Ketryx to do excessive synchronous
dependency-relation work before returning.

Split into two parallel builds per Ketryx support recommendation:
- ci-cd-tests: JUnit XML + coverage reports
- ci-cd-audit: CycloneDX SBOM + SPDX + licenses + vulnerabilities

Each upload is now smaller and independently scoped, avoiding the
timeout on the SBOM parsing path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass --pyproject pyproject.toml to cyclonedx-py so the SBOM includes
aignostics as its root component. Without it, Ketryx has no dependency
tree anchor and performs excessive synchronous graph work, causing the
ci-cd-audit upload to time out with a 500 error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract _invoke_success and _invoke_failure helpers that own all
patching and invocation logic, so each test method only contains
its single assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract mock_user_info pytest fixture shared by test_whoami_success
and test_whoami_with_relogin_flag, and _assert_valid_json_output
helper shared by both no-pretty schema tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add space before colon in slice expression per ruff E203 style rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add RecordProperty type alias (Callable[[str, object], None]) and
Path import to satisfy mypy strict no-untyped-def on all test
methods that accept record_property or tmp_path fixtures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

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.

1 participant