Skip to content

Surface inaccessible root folders in SourceCrawler status - #304

Open
gshikhar2021 wants to merge 1 commit into
redhat-data-and-ai:mainfrom
gshikhar2021:failed-root-folders
Open

Surface inaccessible root folders in SourceCrawler status#304
gshikhar2021 wants to merge 1 commit into
redhat-data-and-ai:mainfrom
gshikhar2021:failed-root-folders

Conversation

@gshikhar2021

@gshikhar2021 gshikhar2021 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

When crawling Google Drive folders, service account might not have access to folders mentioned by the user in the unstructured data pipeline

Example status

status:
  conditions:
    - lastTransitionTime: "2026-07-20T10:15:07Z"
      message: 'successfully reconciled source crawler: gdrive-pipeline-26-crawl, 2
        inaccessible items (service account may lack access)'
      reason: SuccessfullyReconciled
      status: "True"
      type: SourceCrawlerReady
  filesProcessed: 3
  inaccessibleRootFolders:
  - error: 'failed to get root folder metadata for 1or5z3gVLPuhZDMiMnYiitE5Z6JQn5I:
          failed to get metadata for file 1or5z3gVLPuhZDMiMnYiitE5Z6JQ38n: googleapi:
          Error 404: File not found: 1or5z3gVLPuhZDMiMnYiitE5Z6JQ38n., notFound'
    folderID: 1or5z3gVLPuhZMiMnYiitE5Z6JQ38
    url: https://drive.google.com/drive/u/0/folders/1or5z3gVLPuhZDMiMnYiitE5Z6JQ38x
  lastAppliedGeneration: 1

Summary by CodeRabbit

  • New Features

    • Source crawling now reports inaccessible root folders, including folder IDs, URLs, and error details.
    • Crawling continues for accessible folders when only some roots are unavailable.
    • Status information includes the number and details of inaccessible folders.
  • Bug Fixes

    • Crawling now returns an error when all configured root folders are inaccessible.

@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 Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change tracks inaccessible Google Drive root folders, continues partial crawls, and records folder IDs, URLs, and errors in SourceCrawler status. The API type, deep-copy methods, and CRD schema expose the new status field.

Changes

Root folder failure reporting

Layer / File(s) Summary
Status contract and generated copying
api/v1alpha1/sourcecrawler_types.go, api/v1alpha1/zz_generated.deepcopy.go, config/crd/bases/operator.dataverse.redhat.com_sourcecrawlers.yaml
The API and CRD define InaccessibleRootFolders entries with required folder IDs and errors, optional URLs, and deep-copy support.
GDrive failure tracking
pkg/unstructured/source.go
GDriveSource records failed root folders, continues when some roots succeed, and returns an error when all configured roots fail.
Reconciliation status persistence
internal/controller/sourcecrawler_controller.go
Reconciliation maps failed folder IDs to configured URLs and persists the resulting inaccessible-folder records in SourceCrawler status.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SourceCrawlerController
  participant GDriveSource
  participant GoogleDrive
  participant SourceCrawlerStatus
  SourceCrawlerController->>GDriveSource: reconcile source
  GDriveSource->>GoogleDrive: crawl configured root folders
  GoogleDrive-->>GDriveSource: crawl results and failures
  GDriveSource-->>SourceCrawlerController: FailedRootFolders and results
  SourceCrawlerController->>SourceCrawlerController: map folder IDs to URLs
  SourceCrawlerController->>SourceCrawlerStatus: persist inaccessibleRootFolders
Loading

Possibly related PRs

Suggested reviewers: concaf

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: reporting inaccessible root folders in SourceCrawler status.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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 `@internal/controller/sourcecrawler_controller.go`:
- Around line 158-161: Update the successMessage construction in the source
crawler reconciliation flow to use singular wording when failedRootFolders
contains exactly one item, while retaining the plural wording for multiple
inaccessible root folders.
- Around line 148-155: Update the error message passed to handleError in the
SyncFilesToFilestore error path to include the inaccessible root count using the
same format as the success path. Include len(failedRootFolders) in the
fmt.Errorf call so the message contains both the storage error and the count of
inaccessible roots, matching the consistency and visibility of the success-path
message construction.

In `@pkg/unstructured/source.go`:
- Around line 294-297: Update the crawl and garbage-collection flow around
FailedRootFolders so files belonging to inaccessible roots are preserved: either
skip garbage collection whenever any root crawl fails, or restrict deletion to
successfully crawled roots. Ensure cached files and permission files from failed
roots are not deleted, and add a regression test covering a cached file from a
failed root.
🪄 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: c78c1ca6-476e-48e5-a2aa-33071b9ea5be

📥 Commits

Reviewing files that changed from the base of the PR and between 7abca92 and 8b7d05c.

📒 Files selected for processing (5)
  • api/v1alpha1/sourcecrawler_types.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • config/crd/bases/operator.dataverse.redhat.com_sourcecrawlers.yaml
  • internal/controller/sourcecrawler_controller.go
  • pkg/unstructured/source.go

Comment on lines 148 to 155
if err != nil {
if err := controllerutils.StatusPatch(ctx, r.Client, sourceCrawlerCR, func() {
sourceCrawlerCR.Status.InaccessibleRootFolders = failedRootFolders
}); err != nil {
logger.Error(err, "failed to update SourceCrawler CR status with inaccessible root folders")
}
return ctrl.Result{}, r.handleError(ctx, sourceCrawlerCR, fmt.Errorf("failed to store files to filestore: %w", 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:

#!/bin/bash
set -euo pipefail

rg -n -C 12 'func \(r \*SourceCrawlerReconciler\) handleError|UpdateStatus\(' \
  internal/controller/sourcecrawler_controller.go

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

Length of output: 2707


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, let's see the full function to understand the context
rg -n 'failedRootFolders' internal/controller/sourcecrawler_controller.go

# Also search for where roots are crawled and where errors from that are handled
rg -n 'source\.' internal/controller/sourcecrawler_controller.go | head -20

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

Length of output: 815


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full Reconcile function to see the exact control flow
sed -n '120,200p' internal/controller/sourcecrawler_controller.go

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

Length of output: 3920


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Let's look at what UpdateStatus does
rg -n 'func.*UpdateStatus' internal/controller/sourcecrawler_controller.go

# Also check if there's a definition in the SourceCrawler type
fd -t f '\.go$' | xargs rg -l 'func.*UpdateStatus' | head -5

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

Length of output: 185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for UpdateStatus definition across the codebase
rg -n 'UpdateStatus' --type go | grep -E 'func.*UpdateStatus|UpdateStatus\s*\(' | head -20

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

Length of output: 185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for UpdateStatus definition
rg 'UpdateStatus' --type go -n | head -20

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

Length of output: 185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all go files and search for UpdateStatus
find . -name "*.go" -type f | xargs grep -n "UpdateStatus" 2>/dev/null | head -30

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

Length of output: 4885


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the SourceCrawler type definition and its UpdateStatus method
find . -name "*.go" -type f | xargs grep -l "type SourceCrawler" | grep -v vendor

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

Length of output: 326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look at the SourceCrawler type definition and UpdateStatus method
cat -n api/v1alpha1/sourcecrawler_types.go

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

Length of output: 6024


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Now let's verify the exact scenario: when SyncFilesToFilestore returns an error, 
# are failedRootFolders available and is the count included in the message?

# Looking at lines 141-155 in context:
sed -n '141,170p' internal/controller/sourcecrawler_controller.go

echo "---"
echo "The UpdateStatus method from the types file:"
sed -n '122,145p' api/v1alpha1/sourcecrawler_types.go

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

Length of output: 2449


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Let's trace what happens with failedRootFolders in both paths
# 1. Error path (lines 147-155): failedRootFolders is set in status but not in the error message
# 2. Success path (lines 158-165): failedRootFolders is set in status AND included in the message

# Let's check: in the error path, what message is being passed to handleError?
# Line 154 passes: fmt.Errorf("failed to store files to filestore: %w", err)
# This doesn't include failedRootFolders count

# Let's verify by looking at what message is actually constructed
echo "=== Error path message construction ==="
sed -n '148,155p' internal/controller/sourcecrawler_controller.go

echo ""
echo "=== Success path message construction ==="
sed -n '158,165p' internal/controller/sourcecrawler_controller.go

echo ""
echo "=== What handleError does with the message ==="
sed -n '332,343p' internal/controller/sourcecrawler_controller.go

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

Length of output: 1720


Include the inaccessible-root count in the error-path status message.

When SyncFilesToFilestore returns an error, failedRootFolders is populated and stored in the CR status. However, the error message passed to handleError at line 154 contains only the storage error, not the count of inaccessible roots. The UpdateStatus method concatenates the message with the error text, so the condition message becomes ", error: ..." without the count.

In contrast, the success path at lines 159–160 explicitly includes the count: fmt.Sprintf(", %d inaccessible root folders", len(failedRootFolders)). For consistency and visibility, pass the count in the error path as well. Modify the message passed to handleError to include len(failedRootFolders) when constructing the error message.

🤖 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 `@internal/controller/sourcecrawler_controller.go` around lines 148 - 155,
Update the error message passed to handleError in the SyncFilesToFilestore error
path to include the inaccessible root count using the same format as the success
path. Include len(failedRootFolders) in the fmt.Errorf call so the message
contains both the storage error and the count of inaccessible roots, matching
the consistency and visibility of the success-path message construction.

Comment on lines 158 to +161
successMessage := fmt.Sprintf("successfully reconciled source crawler: %s", sourceCrawlerCR.Name)
if len(failedRootFolders) > 0 {
successMessage += fmt.Sprintf(", %d inaccessible root folders", len(failedRootFolders))
}

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

Use singular wording for one inaccessible root folder.

For one failure, the message is 1 inaccessible root folders. Handle the singular form.

Proposed fix
-		successMessage += fmt.Sprintf(", %d inaccessible root folders", len(failedRootFolders))
+		count := len(failedRootFolders)
+		label := "folders"
+		if count == 1 {
+			label = "folder"
+		}
+		successMessage += fmt.Sprintf(", %d inaccessible root %s", count, label)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
successMessage := fmt.Sprintf("successfully reconciled source crawler: %s", sourceCrawlerCR.Name)
if len(failedRootFolders) > 0 {
successMessage += fmt.Sprintf(", %d inaccessible root folders", len(failedRootFolders))
}
successMessage := fmt.Sprintf("successfully reconciled source crawler: %s", sourceCrawlerCR.Name)
if len(failedRootFolders) > 0 {
count := len(failedRootFolders)
label := "folders"
if count == 1 {
label = "folder"
}
successMessage += fmt.Sprintf(", %d inaccessible root %s", count, label)
}
🤖 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 `@internal/controller/sourcecrawler_controller.go` around lines 158 - 161,
Update the successMessage construction in the source crawler reconciliation flow
to use singular wording when failedRootFolders contains exactly one item, while
retaining the plural wording for multiple inaccessible root folders.

Comment on lines +294 to +297
g.FailedRootFolders = append(g.FailedRootFolders, FailedRootFolder{
FolderID: g.FolderIDs[i],
Error: r.err.Error(),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Prevent garbage collection from deleting files from failed roots.

When one root crawl fails, this code continues with records from successful roots. The later garbage-collection phase builds currentFiles only from those records and deletes local files and permission files that are not present. Cached files from the inaccessible root are therefore deleted during a partial crawl.

Skip garbage collection while any root folder has failed, or limit deletion to roots whose crawls completed successfully. Add a regression test for a cached file from a failed root.

🤖 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 `@pkg/unstructured/source.go` around lines 294 - 297, Update the crawl and
garbage-collection flow around FailedRootFolders so files belonging to
inaccessible roots are preserved: either skip garbage collection whenever any
root crawl fails, or restrict deletion to successfully crawled roots. Ensure
cached files and permission files from failed roots are not deleted, and add a
regression test covering a cached file from a failed root.

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