-
Notifications
You must be signed in to change notification settings - Fork 11
Surface inaccessible root folders in SourceCrawler status #304
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -139,14 +139,29 @@ func (r *SourceCrawlerReconciler) Reconcile(ctx context.Context, req ctrl.Reques | |||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| storedFiles, err := source.SyncFilesToFilestore(ctx, r.fileStore) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| var failedRootFolders []operatorv1alpha1.InaccessibleRootFolder | ||||||||||||||||||||||||||||
| if gds, ok := source.(*unstructured.GDriveSource); ok && len(gds.FailedRootFolders) > 0 { | ||||||||||||||||||||||||||||
| failedRootFolders = buildInaccessibleRootFolders(gds.FailedRootFolders, sourceCrawlerConfig.GoogleDriveConfig) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| 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)) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| logger.Info("successfully stored files to filestore", "count", len(storedFiles)) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| successMessage := fmt.Sprintf("successfully reconciled source crawler: %s", sourceCrawlerCR.Name) | ||||||||||||||||||||||||||||
| if len(failedRootFolders) > 0 { | ||||||||||||||||||||||||||||
| successMessage += fmt.Sprintf(", %d inaccessible root folders", len(failedRootFolders)) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
158
to
+161
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| if err := controllerutils.StatusPatch(ctx, r.Client, sourceCrawlerCR, func() { | ||||||||||||||||||||||||||||
| sourceCrawlerCR.Status.FilesProcessed += int64(len(storedFiles)) | ||||||||||||||||||||||||||||
| sourceCrawlerCR.Status.InaccessibleRootFolders = failedRootFolders | ||||||||||||||||||||||||||||
| sourceCrawlerCR.UpdateStatus(successMessage, nil) | ||||||||||||||||||||||||||||
| }); err != nil { | ||||||||||||||||||||||||||||
| logger.Error(err, "failed to update SourceCrawler CR status") | ||||||||||||||||||||||||||||
|
|
@@ -295,6 +310,25 @@ func extractGDriveFolderID(rawURL string) (string, error) { | |||||||||||||||||||||||||||
| return "", fmt.Errorf("could not extract folder ID from URL path: %s", u.Path) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| func buildInaccessibleRootFolders(failed []unstructured.FailedRootFolder, gdriveConfig *operatorv1alpha1.GoogleDriveConfig) []operatorv1alpha1.InaccessibleRootFolder { | ||||||||||||||||||||||||||||
| folderIDToURL := make(map[string]string, len(gdriveConfig.Folders)) | ||||||||||||||||||||||||||||
| for _, f := range gdriveConfig.Folders { | ||||||||||||||||||||||||||||
| id, err := extractGDriveFolderID(f.URL) | ||||||||||||||||||||||||||||
| if err == nil { | ||||||||||||||||||||||||||||
| folderIDToURL[id] = f.URL | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| result := make([]operatorv1alpha1.InaccessibleRootFolder, 0, len(failed)) | ||||||||||||||||||||||||||||
| for _, f := range failed { | ||||||||||||||||||||||||||||
| result = append(result, operatorv1alpha1.InaccessibleRootFolder{ | ||||||||||||||||||||||||||||
| FolderID: f.FolderID, | ||||||||||||||||||||||||||||
| URL: folderIDToURL[f.FolderID], | ||||||||||||||||||||||||||||
| Error: f.Error, | ||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return result | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| func (r *SourceCrawlerReconciler) handleError(ctx context.Context, sourceCrawlerCR *operatorv1alpha1.SourceCrawler, err error) error { | ||||||||||||||||||||||||||||
| logger := log.FromContext(ctx) | ||||||||||||||||||||||||||||
| logger.Error(err, "encountered error") | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -236,6 +236,11 @@ func (s *S3BucketSource) s3Key(filestorePath string) string { | |
| return path.Join(s.Prefix, baseName) | ||
| } | ||
|
|
||
| type FailedRootFolder struct { | ||
| FolderID string | ||
| Error string | ||
| } | ||
|
|
||
| // GDriveSource implements DataSource for Google Drive folders. | ||
| type GDriveSource struct { | ||
| GDriveClient *gdrive.Client | ||
|
|
@@ -245,6 +250,7 @@ type GDriveSource struct { | |
| ConcurrentFolders int | ||
| ConcurrentDownloads int | ||
| OutputDir string | ||
| FailedRootFolders []FailedRootFolder | ||
| } | ||
|
|
||
| // Close releases resources held by the underlying clients. | ||
|
|
@@ -285,6 +291,10 @@ func (g *GDriveSource) SyncFilesToFilestore(ctx context.Context, fs *filestore.F | |
| for i, r := range results { | ||
| if r.err != nil { | ||
| logger.Error(r.err, "folder crawl failed", "folderID", g.FolderIDs[i]) | ||
| g.FailedRootFolders = append(g.FailedRootFolders, FailedRootFolder{ | ||
| FolderID: g.FolderIDs[i], | ||
| Error: r.err.Error(), | ||
| }) | ||
|
Comment on lines
+294
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 |
||
| continue | ||
| } | ||
| for _, record := range r.result.Records { | ||
|
|
@@ -307,6 +317,10 @@ func (g *GDriveSource) SyncFilesToFilestore(ctx context.Context, fs *filestore.F | |
| } | ||
| } | ||
|
|
||
| if len(g.FailedRootFolders) == len(g.FolderIDs) { | ||
| return nil, errors.New("all configured root folders are inaccessible (service account may lack access)") | ||
| } | ||
|
|
||
| logger.Info("gdrive crawl complete, starting file download", | ||
| "discoveredFiles", len(fileRecords), | ||
| "concurrentDownloads", g.ConcurrentDownloads, | ||
|
|
||
There was a problem hiding this comment.
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:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 2707
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 815
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 3920
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 185
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 185
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 185
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 4885
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 326
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 6024
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 2449
🏁 Script executed:
Repository: redhat-data-and-ai/unstructured-data-controller
Length of output: 1720
Include the inaccessible-root count in the error-path status message.
When
SyncFilesToFilestorereturns an error,failedRootFoldersis populated and stored in the CR status. However, the error message passed tohandleErrorat line 154 contains only the storage error, not the count of inaccessible roots. TheUpdateStatusmethod 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 tohandleErrorto includelen(failedRootFolders)when constructing the error message.🤖 Prompt for AI Agents