Skip to content

Add e2e tests for gdrive - #306

Open
gshikhar2021 wants to merge 1 commit into
redhat-data-and-ai:mainfrom
gshikhar2021:gdrive-e2e
Open

Add e2e tests for gdrive#306
gshikhar2021 wants to merge 1 commit into
redhat-data-and-ai:mainfrom
gshikhar2021:gdrive-e2e

Conversation

@gshikhar2021

@gshikhar2021 gshikhar2021 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added end-to-end Google Drive pipeline validation, covering crawling, document conversion, chunking, embeddings, permissions, and storage synchronization.
    • Added support for configuring Google Drive service-account credentials and pipeline settings in end-to-end environments.
  • Bug Fixes

    • Improved operation when LDAP is unavailable by preserving cached or display-name identities and allowing Google Drive processing to continue where supported.
  • Tests

    • Added cleanup and diagnostic reporting for failed Google Drive pipeline runs.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes enable Google Drive e2e execution with LocalStack S3. They configure service-account secrets and pipeline stages, permit e2e operation without LDAP, and validate crawled files, permissions, converted documents, chunks, and destination embeddings.

Changes

Google Drive pipeline end-to-end coverage

Layer / File(s) Summary
Cache and Google Drive runtime initialization
internal/controller/controllerconfig_controller.go, internal/controller/sourcecrawler_controller.go, pkg/gdrive/permissions.go
Initializes the cache independently of LDAP. Allows a nil LDAP client only in e2e-test. Avoids LDAP lookups when no client exists.
Google Drive e2e configuration and pipeline wiring
test/e2e/main_test.go, test/resources/unstructured/unstructured-secret.yaml, test/utils/utils_function.go
Decodes the service-account JSON, injects operator and pipeline secrets, selects Google Drive settings, and builds the five-stage pipeline.
Pipeline execution and output validation
test/e2e/gdrive_test.go
Waits for pipeline and crawler progress. Validates crawl objects, permissions, converted documents, chunks, and destination objects.
Pipeline and bucket teardown
test/e2e/gdrive_test.go
Deletes the pipeline and cleans the output bucket after the test.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: concaf, piyush-garg

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: adding end-to-end tests for Google Drive support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
test/e2e/gdrive_test.go (2)

505-570: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Extend garbage-collection assertions to metadata and permission sidecars.

The GC assessment verifies that file2ID's primary object is removed (Lines 554-557) and that 2 data files remain (Lines 561-568), but it does not check whether the metadata sidecar (file2ID.json) or the permission file (permissions/file2ID.json) for the deleted source file are also removed. If production GC only deletes the primary object, an orphaned permission sidecar containing resolved UIDs/emails would remain in S3 indefinitely, undetected by this test.

🔧 Proposed additional assertions
 		for _, obj := range output.Contents {
 			key := aws.ToString(obj.Key)
 			assert.NotContains(t, key, file2ID, "data.csv should be garbage collected from S3")
+			assert.NotContains(t, key, outputDir+"/permissions/"+file2ID+".json",
+				"data.csv permission sidecar should be garbage collected from S3")
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gdrive_test.go` around lines 505 - 570, Extend the
garbage-collection checks in the “garbage collection removes files deleted from
source” assessment to assert that both the metadata sidecar for file2ID and the
permissions/file2ID.json sidecar are absent from the listed S3 objects. Keep the
existing primary-object and remaining-data-file assertions unchanged, and
identify each sidecar by its expected key pattern.

234-264: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Harden the S3 bucket lifecycle against leftover state.

gdriveDataStorageBucket uses a fixed name, and CreateBucket (Line 260) uses require.NoError, which fails the test outright if the bucket already exists from a prior run whose teardown did not complete. The Teardown (Lines 572-590) ignores all errors from DeleteObject and DeleteBucket with _, _ =, so a failed cleanup leaves the bucket present for the next run without surfacing a warning.

Combine a failed teardown with a fixed bucket name, and the next test run fails at setup with a confusing "bucket already exists" error instead of a clear signal about the actual regression.

🔧 Proposed fix: tolerate a pre-existing bucket
 		_, err = s3Client.CreateBucket(ctx, &s3.CreateBucketInput{
 			Bucket: aws.String(gdriveDataStorageBucket),
 		})
-		require.NoError(t, err)
+		if err != nil {
+			var alreadyOwned *types.BucketAlreadyOwnedByYou
+			var alreadyExists *types.BucketAlreadyExists
+			require.True(t, errors.As(err, &alreadyOwned) || errors.As(err, &alreadyExists), "unexpected error creating bucket: %v", err)
+			t.Logf("bucket %s already exists, reusing", gdriveDataStorageBucket)
+		}

Also applies to: 572-590

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gdrive_test.go` around lines 234 - 264, Update the bucket setup
around gdriveDataStorageBucket and CreateBucket to tolerate an already-existing
bucket while still failing on other S3 errors. Preserve the existing bucket
initialization for new runs, and surface unexpected creation failures clearly;
do not treat all errors as successful setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/e2e/gdrive_test.go`:
- Around line 505-570: Extend the garbage-collection checks in the “garbage
collection removes files deleted from source” assessment to assert that both the
metadata sidecar for file2ID and the permissions/file2ID.json sidecar are absent
from the listed S3 objects. Keep the existing primary-object and
remaining-data-file assertions unchanged, and identify each sidecar by its
expected key pattern.
- Around line 234-264: Update the bucket setup around gdriveDataStorageBucket
and CreateBucket to tolerate an already-existing bucket while still failing on
other S3 errors. Preserve the existing bucket initialization for new runs, and
surface unexpected creation failures clearly; do not treat all errors as
successful setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Enterprise

Run ID: 116ec05f-b26f-4f50-9979-ab4ba63f0cd6

📥 Commits

Reviewing files that changed from the base of the PR and between 7abca92 and 46da3ac.

📒 Files selected for processing (1)
  • test/e2e/gdrive_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/e2e/gdrive_test.go`:
- Around line 321-339: Update the cleanup flow after deleting the pipeline to
wait for the pipeline and its child resources to be fully absent before touching
the output bucket, using foreground deletion or an equivalent wait. In the
bucket cleanup block, handle and surface errors from ListObjectsV2, each
DeleteObject, and DeleteBucket instead of discarding them, while preserving the
existing object-removal sequence.
- Around line 110-114: Update the error handling in the GDrive pipeline
readiness check using WaitForResourceReady to call t.Fatal(err) instead of
t.Error(err), ensuring the setup stops immediately when the pipeline is not
ready.

In `@test/e2e/main_test.go`:
- Around line 156-163: Update the scale-down and scale-up command handling in
testSetup to return p.Err() immediately when either utils.RunCommandContext
invocation fails, instead of only logging and continuing. Preserve the existing
scale command order so setup stops before proceeding after an unsuccessful
operator restart.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Enterprise

Run ID: 76e5dcce-14d2-4001-80db-b1a7bb507d34

📥 Commits

Reviewing files that changed from the base of the PR and between 46da3ac and 22f7c70.

📒 Files selected for processing (7)
  • internal/controller/controllerconfig_controller.go
  • internal/controller/sourcecrawler_controller.go
  • pkg/gdrive/permissions.go
  • test/e2e/gdrive_test.go
  • test/e2e/main_test.go
  • test/resources/unstructured/unstructured-secret.yaml
  • test/utils/utils_function.go

Comment thread test/e2e/gdrive_test.go
Comment on lines +110 to +114
if err := operatorUtils.WaitForResourceReady(ctx, v1alpha1.UnstructuredDataPipelineCondition,
"unstructureddatapipelines.operator.dataverse.redhat.com", pipelineName, testNamespace); err != nil {
t.Error(err)
}
t.Log("GDrive pipeline CR is healthy")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file="test/e2e/gdrive_test.go"
printf '%s\n' '--- candidate file outline ---'
ast-grep outline "$file" --lang go || true

printf '%s\n' '--- relevant test section ---'
sed -n '70,145p' "$file"

printf '%s\n' '--- readiness helper usages in this test ---'
rg -n -C 4 'WaitForResourceReady|t\.(Error|Fatal)\(' "$file"

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 4624


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- complete test structure ---'
sed -n '1,230p' test/e2e/gdrive_test.go

printf '%s\n' '--- feature setup and assessment definitions ---'
rg -n -C 5 'feature\.Setup|feature\.Assess|type Feature|func .*Setup|Assess\(' test internal . \
  -g '*.go' \
  -g '!vendor' | head -n 300

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 31641


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- e2e-framework dependency version ---'
rg -n 'sigs\.k8s\.io/e2e-framework' go.mod go.sum

printf '%s\n' '--- local dependency source, if present ---'
find "${GOMODCACHE:-/nonexistent}" -path '*sigs.k8s.io*e2e-framework*' -type f \
  \( -name '*.go' -o -name 'README*' \) -print 2>/dev/null | head -n 40

printf '%s\n' '--- test runner invocation ---'
sed -n '1,145p' test/e2e/main_test.go
rg -n -C 4 'feature\.Run|features\.New|Run\(' test/e2e -g '*.go'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 6338


🌐 Web query:

sigs.k8s.io/e2e-framework v0.7.0 features.Setup Assess implementation setup failure t.Error

💡 Result:

In the sigs.k8s.io/e2e-framework, the Assess method is used within a feature definition to perform specific test assertions against the environment [1][2]. The function signature for an assessment is: func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context Within this assessment function, you use the standard Go *testing.T object to handle test failures [1][3][2]. To signal a failure within an Assess block, you should call methods on the *testing.T object, such as: 1. t.Error: Reports an error but allows the execution of the current assessment (and subsequent assessments in the feature) to continue [2]. 2. t.Fatal: Reports an error and stops the execution of the current test/assessment immediately [1][4]. 3. t.Fail: Marks the test as failed but continues execution. 4. t.FailNow: Marks the test as failed and stops execution immediately [3]. Using t.Fatal or t.FailNow is often preferred if the failure prevents subsequent steps or assessments from running correctly [3]. When using the --fail-fast flag with the e2e-framework, invoking t.Fail or t.FailNow ensures the framework properly stops execution of the remaining assessments for that feature and skips subsequent features [3]. Example implementation: f:= features.New("example feature"). Assess("check condition", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { if conditionNotMet { t.Error("condition not met") // Continues execution // OR t.Fatal("critical failure") // Stops execution } return ctx }).Feature

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

url='https://raw.githubusercontent.com/kubernetes-sigs/e2e-framework/v0.7.0/pkg/features/feature.go'
curl -fsSL "$url" | sed -n '1,260p'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 2927


🏁 Script executed:

#!/bin/bash
set -eu

for path in pkg/features/builder.go pkg/features/feature_builder.go pkg/types/feature.go pkg/types/step.go pkg/env/run.go pkg/env/environment.go; do
  url="https://raw.githubusercontent.com/kubernetes-sigs/e2e-framework/v0.7.0/$path"
  body="$(curl -fsSL "$url" 2>/dev/null || true)"
  if [ -n "$body" ]; then
    printf '\n--- %s ---\n' "$path"
    printf '%s\n' "$body" | rg -n -C 8 'func .*Setup|func .*Assess|LevelSetup|LevelAssess|Steps\(\)|FailNow|Run' || true
  fi
done

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 1928


🏁 Script executed:

#!/bin/bash
set -eu

curl -fsSL 'https://api.github.com/repos/kubernetes-sigs/e2e-framework/git/trees/v0.7.0?recursive=1' |
  jq -r '.tree[].path' |
  rg '(^|/)(run|runner|environment|feature|step).*\.go$|pkg/types'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 234


🌐 Web query:

site:github.com/kubernetes-sigs/e2e-framework/blob/v0.7.0/pkg "GetStepsByLevel" "LevelSetup"

💡 Result:

Found in pkg/features/feature.go for v0.7.0:

  • LevelSetup is defined as an alias of types.LevelSetup.
  • GetStepsByLevel filters steps whose Level() matches the requested level.
  • Relevant lines: 542–545 and 653–673. (github.com)
func GetStepsByLevel(steps []types.Step, l types.Level) []types.Step {
    if steps == nil {
        return nil
    }

    var result []Step
    for _, s := range steps {
        if s.Level() == l {
            result = append(result, s)
        }
    }

    return result
}

Citations:


🌐 Web query:

kubernetes-sigs e2e-framework v0.7.0 Feature Steps LevelSetup LevelAssess testenv.Run source

💡 Result:

The kubernetes-sigs/e2e-framework (v0.7.0) manages test execution via a defined set of steps within features [1][2]. In the e2e-framework architecture, steps are defined as having a Level, which determines their execution phase [1]. These levels are: - Setup: Actions to prepare the environment or feature [1]. - Assessment: Actions to verify or assert on the system state [1]. - Teardown: Actions to clean up resources [1]. The features package allows builders to add these steps using methods like Setup(...), Assess(...), and Teardown(...) [1][3]. These methods accept a function that typically follows the signature func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context [4][5][3]. Regarding testenv.Run(m), this function is the standard entry point used within a Go TestMain(m *testing.M) function to execute the defined testing environment, including setup and teardown tasks configured at the environment level, and to trigger the execution of tests in the package [4][5][6]. It returns an integer status code that is typically passed to os.Exit() [4][3][6]. Note: The terms "LevelSetup" and "LevelAssess" as paired phrases are not native to the kubernetes-sigs/e2e-framework API. They appear to be confused with components from the "Level CI" accessibility testing tools (e.g., @level-ci/a11y-playwright), which utilize functions named levelSetup and levelAnalyze [7][8][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

for path in \
  pkg/env/env.go \
  pkg/env/environment.go \
  pkg/env/runner.go \
  pkg/env/run.go \
  pkg/env/funcs.go \
  pkg/types/types.go \
  pkg/types/feature.go; do
  url="https://raw.githubusercontent.com/kubernetes-sigs/e2e-framework/v0.7.0/$path"
  if body="$(curl -fsSL "$url" 2>/dev/null)"; then
    printf '\n--- %s ---\n' "$path"
    printf '%s\n' "$body" | rg -n -C 12 'func .*Run|GetStepsByLevel|LevelSetup|LevelAssess|Step\(' || true
  fi
done

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 5957


🏁 Script executed:

#!/bin/bash
set -eu

url='https://raw.githubusercontent.com/kubernetes-sigs/e2e-framework/v0.7.0/pkg/env/env.go'
curl -fsSL "$url" | sed -n '430,550p'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 4013


Stop after pipeline readiness failure.

If WaitForResourceReady fails, t.Error(err) records the failure and the setup callback continues. The feature then runs later assessments against an unhealthy pipeline. Replace t.Error(err) with t.Fatal(err).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gdrive_test.go` around lines 110 - 114, Update the error handling in
the GDrive pipeline readiness check using WaitForResourceReady to call
t.Fatal(err) instead of t.Error(err), ensuring the setup stops immediately when
the pipeline is not ready.

Comment thread test/e2e/gdrive_test.go
Comment on lines +321 to +339
if err := kubeClient.Resources(testNamespace).Delete(ctx, pipeline); err != nil {
t.Logf("failed to delete GDrive pipeline: %v", err)
}

// cleanup output bucket
output, _ := s3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(outputBucketName),
})
if output != nil {
for _, obj := range output.Contents {
_, _ = s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(outputBucketName),
Key: obj.Key,
})
}
}
_, _ = s3Client.DeleteBucket(ctx, &s3.DeleteBucketInput{
Bucket: aws.String(outputBucketName),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target file outline ---'
ast-grep outline test/e2e/gdrive_test.go
printf '%s\n' '--- target section ---'
sed -n '260,365p' test/e2e/gdrive_test.go
printf '%s\n' '--- cleanup and pipeline deletion references ---'
rg -n -C 4 'Delete\(ctx, pipeline\)|outputBucketName|Wait|pipeline deletion|DeleteBucket|ListObjectsV2' test/e2e

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 25124


🏁 Script executed:

#!/bin/bash
set -e
sed -n '260,365p' test/e2e/gdrive_test.go
rg -n -C 4 'Delete\(ctx, pipeline\)|outputBucketName|Wait|pipeline deletion|DeleteBucket|ListObjectsV2' test/e2e

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 24956


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- pipeline and stage deletion logic ---'
rg -n -C 5 'DeletionTimestamp|Finalizer|finaliz|Delete.*Stage|UnstructuredDataPipeline|GDrive' controllers api internal pkg test -g '*.go' -g '*.yaml' 2>/dev/null | head -n 500
printf '%s\n' '--- client and wait helper definitions/usages ---'
rg -n -C 4 'func .*WaitForResourceReady|WaitForResourceReady|type Client interface|func .* Delete\(' . -g '*.go' | head -n 500
printf '%s\n' '--- test framework teardown semantics ---'
rg -n -C 5 'func \(.*Teardown|Teardown\(|Feature\(\).*Teardown|type .*Teardown' . -g '*.go' | head -n 300

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 50403


🏁 Script executed:

#!/bin/bash
set -e
rg -n -C 5 'DeletionTimestamp|Finalizer|finaliz|Delete.*Stage|UnstructuredDataPipeline|GDrive' controllers api internal pkg test -g '*.go' -g '*.yaml' 2>/dev/null | head -n 500
rg -n -C 4 'func .*WaitForResourceReady|WaitForResourceReady|type Client interface|func .* Delete\(' . -g '*.go' | head -n 500
rg -n -C 5 'func \(.*Teardown|Teardown\(|Feature\(\).*Teardown|type .*Teardown' . -g '*.go' | head -n 300

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 50403


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- pipeline reconciliation and orphan cleanup ---'
sed -n '70,225p' internal/controller/unstructureddatapipeline_controller.go
printf '%s\n' '--- child resource creation and ownership ---'
sed -n '224,356p' internal/controller/unstructureddatapipeline_controller.go
printf '%s\n' '--- child controller reconcile entry points ---'
for f in internal/controller/sourcecrawler_controller.go internal/controller/documentprocessor_controller.go internal/controller/chunksgenerator_controller.go internal/controller/vectorembeddingsgenerator_controller.go internal/controller/destinationsyncer_controller.go; do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    sed -n '55,120p' "$f"
  fi
done
printf '%s\n' '--- teardown implementation and dependency versions ---'
sed -n '225,255p' test/utils/utils_function.go
rg -n 'sigs.k8s.io/controller-runtime|sigs.k8s.io/e2e-framework' go.mod go.sum
printf '%s\n' '--- precise deletion/finalizer matches in controllers ---'
rg -n 'DeletionTimestamp|Finalizer|SetControllerReference|OwnerReferences|Delete\(ctx' internal/controller -g '*.go'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 29534


🏁 Script executed:

#!/bin/bash
set -e
sed -n '70,225p' internal/controller/unstructureddatapipeline_controller.go
sed -n '224,356p' internal/controller/unstructureddatapipeline_controller.go
for f in internal/controller/sourcecrawler_controller.go internal/controller/documentprocessor_controller.go internal/controller/chunksgenerator_controller.go internal/controller/vectorembeddingsgenerator_controller.go internal/controller/destinationsyncer_controller.go; do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    sed -n '55,120p' "$f"
  fi
done
sed -n '225,255p' test/utils/utils_function.go
rg -n 'sigs.k8s.io/controller-runtime|sigs.k8s.io/e2e-framework' go.mod go.sum
rg -n 'DeletionTimestamp|Finalizer|SetControllerReference|OwnerReferences|Delete\(ctx' internal/controller -g '*.go'

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 29275


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- destination writes and completion status ---'
rg -n -C 6 'S3Destination|PutObject|Upload|Sync|UpdateStatus|StatusPatch' internal/controller/destinationsyncer_controller.go pkg -g '*.go' | head -n 300
printf '%s\n' '--- read-only structural verifier ---'
python3 - <<'PY'
from pathlib import Path

pipeline = Path("internal/controller/unstructureddatapipeline_controller.go").read_text()
gdrive = Path("test/e2e/gdrive_test.go").read_text()
children = [
    Path("internal/controller/sourcecrawler_controller.go"),
    Path("internal/controller/documentprocessor_controller.go"),
    Path("internal/controller/chunksgenerator_controller.go"),
    Path("internal/controller/vectorembeddingsgenerator_controller.go"),
    Path("internal/controller/destinationsyncer_controller.go"),
]

checks = {
    "pipeline has no deletion handler": "DeletionTimestamp" not in pipeline,
    "pipeline creates owner references": pipeline.count("SetControllerReference(") == 5,
    "gdrive teardown ignores list errors": "output, _ := s3Client.ListObjectsV2" in gdrive,
    "gdrive teardown ignores object-delete errors": "_, _ = s3Client.DeleteObject" in gdrive,
    "gdrive teardown ignores bucket-delete errors": "_, _ = s3Client.DeleteBucket" in gdrive,
}
for name, result in checks.items():
    print(f"{'PASS' if result else 'FAIL'}: {name}")

for path in children:
    text = path.read_text()
    reconcile = text[text.find("func ("):text.find("func (", text.find("func (") + 1)]
    print(f"{path}: deletion guard in first reconcile slice = {'DeletionTimestamp' in reconcile}")
PY

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 26101


🏁 Script executed:

#!/bin/bash
set -e
rg -n -C 6 'S3Destination|PutObject|Upload|Sync|UpdateStatus|StatusPatch' internal/controller/destinationsyncer_controller.go pkg -g '*.go' | head -n 300
python3 - <<'PY'
from pathlib import Path

pipeline = Path("internal/controller/unstructureddatapipeline_controller.go").read_text()
gdrive = Path("test/e2e/gdrive_test.go").read_text()
children = [
    Path("internal/controller/sourcecrawler_controller.go"),
    Path("internal/controller/documentprocessor_controller.go"),
    Path("internal/controller/chunksgenerator_controller.go"),
    Path("internal/controller/vectorembeddingsgenerator_controller.go"),
    Path("internal/controller/destinationsyncer_controller.go"),
]

checks = {
    "pipeline has no deletion handler": "DeletionTimestamp" not in pipeline,
    "pipeline creates owner references": pipeline.count("SetControllerReference(") == 5,
    "gdrive teardown ignores list errors": "output, _ := s3Client.ListObjectsV2" in gdrive,
    "gdrive teardown ignores object-delete errors": "_, _ = s3Client.DeleteObject" in gdrive,
    "gdrive teardown ignores bucket-delete errors": "_, _ = s3Client.DeleteBucket" in gdrive,
}
for name, result in checks.items():
    print(f"{'PASS' if result else 'FAIL'}: {name}")

for path in children:
    text = path.read_text()
    reconcile = text[text.find("func ("):text.find("func (", text.find("func (") + 1)]
    print(f"{path}: deletion guard in first reconcile slice = {'DeletionTimestamp' in reconcile}")
PY

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 26018


Wait for pipeline and child CR deletion before cleaning the bucket. Use foreground deletion or wait until all resources are absent, then surface ListObjectsV2, DeleteObject, and DeleteBucket errors. Otherwise, a child stage can write after cleanup and leave gdrive-output-bucket for the next test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/gdrive_test.go` around lines 321 - 339, Update the cleanup flow
after deleting the pipeline to wait for the pipeline and its child resources to
be fully absent before touching the output bucket, using foreground deletion or
an equivalent wait. In the bucket cleanup block, handle and surface errors from
ListObjectsV2, each DeleteObject, and DeleteBucket instead of discarding them,
while preserving the existing object-removal sequence.

Comment thread test/e2e/main_test.go
Comment on lines +156 to +163
scaleDown := fmt.Sprintf("kubectl scale deployment/%s -n %s --replicas=0", deploymentName, testNamespace)
if p := utils.RunCommandContext(ctx, scaleDown); p.Err() != nil {
log.Printf("Failed to scale down: %s", p.Err())
}
scaleUp := fmt.Sprintf("kubectl scale deployment/%s -n %s --replicas=1", deploymentName, testNamespace)
if p := utils.RunCommandContext(ctx, scaleUp); p.Err() != nil {
log.Printf("Failed to scale up: %s", p.Err())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# Map the test file and inspect the setup function around the scale commands.
ast-grep outline test/e2e/main_test.go
sed -n '100,190p' test/e2e/main_test.go

# Find the enclosing function's callers and the command-result API usage.
rg -n -C 3 'RunCommandContext|scaleDown|scaleUp|CURRENT_ENV|func .*Setup|func .*setup' test/e2e

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 11254


🏁 Script executed:

# Inspect the command-result implementation and the setup caller to confirm that
# returning p.Err() aborts test setup and that Err() reflects command failure.
rg -n -C 5 'func RunCommandContext|type .*Process|func \(.*\) Err\(\)|testSetup\(' .

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 50403


🏁 Script executed:

# Narrow the lookup to the vendored e2e-framework command implementation.
rg -n -C 8 'RunProcWithContext|type Proc|func \(.*Proc.*\) Err|Wait\(' vendor/sigs.k8s.io/e2e-framework/pkg

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 3291


🏁 Script executed:

# Resolve the imported exec package and inspect only its process methods.
sed -n '1,45p' vendor/sigs.k8s.io/e2e-framework/pkg/utils/command.go
rg -n -C 6 'package exec|type Proc|RunProcWithContext|func \(.*\) Err' vendor/sigs.k8s.io/e2e-framework vendor | head -200

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 17185


🏁 Script executed:

# Read-only verifier: check the two scale branches and the testSetup caller
# without executing repository code.
python3 - <<'PY'
from pathlib import Path
import re

path = Path("test/e2e/main_test.go")
text = path.read_text()

for name in ("scaleDown", "scaleUp"):
    match = re.search(
        rf'if p := utils\.RunCommandContext\(ctx, {name}\); p\.Err\(\) != nil \{{(?P<body>.*?)\n\t\t\}}',
        text,
        re.S,
    )
    if not match:
        raise SystemExit(f"{name}: branch not found")
    body = match.group("body")
    print(f"{name}: returns p.Err() = {'return p.Err()' in body}")
    print(f"{name}: logs failure = {'log.Printf' in body}")

caller = re.search(
    r'if err := testSetup\(ctx, &runningProcesses, config\); err != nil \{(?P<body>.*?)\n\t\t\}',
    text,
    re.S,
)
if not caller:
    raise SystemExit("testSetup caller not found")
print(f"testSetup caller terminates on error = {'log.Fatalf' in caller.group('body')}")
PY

Repository: redhat-data-and-ai/unstructured-data-controller

Length of output: 357


Propagate scale command failures.

If either scale command fails, return p.Err() so testSetup stops before continuing with an unverified operator restart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/main_test.go` around lines 156 - 163, Update the scale-down and
scale-up command handling in testSetup to return p.Err() immediately when either
utils.RunCommandContext invocation fails, instead of only logging and
continuing. Preserve the existing scale command order so setup stops before
proceeding after an unsuccessful operator restart.

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