Skip to content

OAPE-824: add the agentic docs in must-gather-operator repo#370

Open
praveencodes wants to merge 2 commits into
openshift:masterfrom
praveencodes:OAPE-824
Open

OAPE-824: add the agentic docs in must-gather-operator repo#370
praveencodes wants to merge 2 commits into
openshift:masterfrom
praveencodes:OAPE-824

Conversation

@praveencodes

@praveencodes praveencodes commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Compared the below 2 tools/plugins and created the agentic docs with Gwen's agent-readiness tool.

  1. agent-readiness - https://github.com/gwenneg/claude-engineering-toolkit/tree/main/skills/agent-readiness
  2. agentic-docs - https://github.com/openshift-eng/ai-helpers/tree/main/plugins/agentic-docs

Summary by CodeRabbit

  • Documentation

    • Expanded and added several guides covering architecture, contribution workflow, onboarding, testing, security, performance, error handling, API contracts, and integration behavior.
    • Clarified the main README with updated usage details, setup steps, and a more organized documentation index.
  • Chores

    • Added repository guidance files for AI-assisted contributors and code review workflows.
    • Introduced configuration for automated documentation-aware tooling.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 6, 2026
@openshift-ci

openshift-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci-robot

openshift-ci-robot commented Jul 6, 2026

Copy link
Copy Markdown

@praveencodes: This pull request references OAPE-824 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Compared the below 2 tools/plugins and created the agentic docs with Gwen's agent-readiness tool.

  1. agent-readiness - https://github.com/gwenneg/claude-engineering-toolkit/tree/main/skills/agent-readiness
  2. agentic-docs - https://github.com/openshift-eng/ai-helpers/tree/main/plugins/agentic-docs

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds a CodeRabbit configuration file and a comprehensive set of documentation files for the must-gather-operator repository, including an AGENTS.md onboarding guide, a rewritten CLAUDE.md, a new CONTRIBUTING.md, updated README.md, and six new docs/ guideline files (architecture, API contracts, error handling, integration, performance, security, testing).

Changes

Documentation and configuration additions

Layer / File(s) Summary
CodeRabbit config
.coderabbit.yaml
Enables knowledge_base code_guidelines matching docs/*-guidelines.md.
AGENTS.md onboarding guide
AGENTS.md
Adds guideline index, make targets, repo layout, architectural notes, code conventions, pitfalls, and dev checklist.
CLAUDE.md simplification
CLAUDE.md
Points to AGENTS.md and replaces architecture content with behavioral preferences checklist.
Contribution workflow
CONTRIBUTING.md
Documents prerequisites, setup, workflow, commit conventions, PR review process, reference table, and checklist.
README updates
README.md
Rewrites intro, audit logs, garbage collection, requirements table, local dev steps, build commands, and docs table.
Architecture doc
docs/ARCHITECTURE.md
Adds architecture decisions, data flow, component interactions, constraints, history, and future considerations.
API contracts guidelines
docs/api-contracts-guidelines.md
Documents immutability, union patterns, CEL rules, status conventions, ownership/finalizers, predicates, checklists.
Error handling guidelines
docs/error-handling-guidelines.md
Documents error classification, retry behavior, wrapping conventions, cleanup semantics, logging, and testing.
Integration guidelines
docs/integration-guidelines.md
Documents SFTP flow, proxy support, secrets, job lifecycle, trusted CA, metrics, and env vars.
Performance guidelines
docs/performance-guidelines.md
Documents concurrency, timeouts, cleanup ordering, scheduling, metrics, and retry rules.
Security guidelines
docs/security-guidelines.md
Documents credential handling, RBAC, CRD validation, trusted CA, FIPS mode, and cleanup security.
Testing guidelines
docs/testing-guidelines.md
Documents unit/E2E test conventions, mocking patterns, and checklist for new tests.

Estimated code review effort: 2 (Simple) | ~12 minutes

🚥 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 accurately summarizes the main change: adding agentic documentation to the must-gather-operator repo.
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.

@openshift-ci

openshift-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: praveencodes
Once this PR has been reviewed and has the lgtm label, please assign dustman9000 for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@praveencodes praveencodes marked this pull request as ready for review July 6, 2026 11:49
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 6, 2026
@openshift-ci openshift-ci Bot requested review from dustman9000 and rbhilare July 6, 2026 11:50
@praveencodes

Copy link
Copy Markdown
Contributor Author

/cc @shivprakashmuley

@openshift-ci openshift-ci Bot requested a review from shivprakashmuley July 6, 2026 11:52
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.32%. Comparing base (6860bae) to head (18592f6).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #370   +/-   ##
=======================================
  Coverage   82.32%   82.32%           
=======================================
  Files           8        8           
  Lines         905      905           
=======================================
  Hits          745      745           
  Misses        148      148           
  Partials       12       12           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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: 12

🧹 Nitpick comments (2)
docs/api-contracts-guidelines.md (2)

110-126: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Spell out the cleanup order and ownership check.

The contract here should be explicit: verify the target object's OwnerReferences against the current MustGather UID, delete Pods first, then delete the Job. The current wording is loose enough to be misread into the wrong cleanup sequence.

🤖 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 `@docs/api-contracts-guidelines.md` around lines 110 - 126, Clarify the
Finalizer Pattern cleanup flow by explicitly stating that the controller must
first verify the target object's OwnerReferences against the current MustGather
instance UID, then delete the Job's Pods before deleting the Job itself. Update
the wording around the cleanup steps in the MustGather contract so the ownership
check and delete order are unambiguous and tied to the MustGather UID and job
cleanup logic.

Source: Learnings


33-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Don't describe Storage as a union.

Storage is just an enum plus a required nested config here; it doesn't use the union/discriminator markers. Keeping it in this section will steer future changes toward the wrong validation pattern.

🤖 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 `@docs/api-contracts-guidelines.md` at line 33, The documentation currently
describes Storage like a union, but it should be documented as a simple enum
plus required nested config instead. Update the Storage guidance in the
api-contracts section to remove any union/discriminator framing and make it
clear that StorageType is the only enum involved today, with PersistentVolume as
the current value. Keep the reference to Storage and StorageType so the correct
section is easy to find.
🤖 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 `@AGENTS.md`:
- Around line 30-47: Add language tags to the two fenced Markdown blocks in
AGENTS.md so markdownlint MD040 is satisfied; update the unlabeled fences in the
repository layout section and the owner-reference block to use an explicit
language such as text, keeping the existing content unchanged. Use the
surrounding markdown list and the nearby owner-reference section to locate both
fences.

In `@CONTRIBUTING.md`:
- Around line 100-102: The fenced example in the CONTRIBUTING markdown needs a
language tag so markdownlint MD040 does not flag it. Update the fenced block
containing “MG-NNN: Short description of the change” to use a text language
label, and keep the example otherwise unchanged; locate the snippet by its
fenced markdown example rather than line numbers.

In `@docs/ARCHITECTURE.md`:
- Around line 17-25: The architecture note hardcodes the operator namespace as a
fixed value, but it should describe a configurable operator namespace instead.
Update the wording in the decision/rationale around the operator namespace to
refer to OPERATOR_NAMESPACE as the required startup setting, and mention that
config.OperatorName is only a local-mode fallback. Keep the rest of the RBAC and
watch/job placement explanation intact while removing the impression that
must-gather-operator is an invariant.
- Around line 31-39: The architecture note in the MustGather Job description
overstates permission isolation for the upload container; it shares the same
ServiceAccount as gather and does not have a separate Kubernetes identity.
Update the wording around the gather/upload container split to emphasize that
upload is trusted operator-controlled code that relies on process namespace
sharing for completion detection and does not need Kubernetes API access, rather
than claiming distinct pod-level permissions.
- Around line 41-43: Update the explanation in the architecture doc to reflect
the real init-container limitation: in the section near the sequential
containers discussion, clarify that init containers do share volumes and can use
shareProcessNamespace, but they run before app containers so they cannot wait
for gather to finish or observe the long-running gather process this design
relies on. Keep the reference to the sidecar vs. sequential container rationale
in ARCHITECTURE.md aligned with that distinction.

In `@docs/error-handling-guidelines.md`:
- Around line 29-35: The decision-tree block in the error-handling guidelines
uses a bare fenced code block, which triggers markdownlint MD040. Update the
fence to use a language tag such as text (or convert the block to a list) so the
documentation passes lint; the relevant section is the decision-tree snippet in
the guidelines doc.
- Around line 39-42: The transient error check in IsTransientError uses an
invalid errors.As target for net.Error, so update the validation.go logic to
populate a concrete net.Error variable before calling Timeout(). Keep the
existing context.DeadlineExceeded and context.Canceled checks, and replace the
interface target with a local variable inside IsTransientError so the SFTP retry
classification works correctly.

In `@docs/integration-guidelines.md`:
- Around line 50-54: The Secret-not-found handling described in the integration
guidelines does not match the actual reconcile flow in MustGatherController.
Update the MustGather controller logic around ManageError and the Secret lookup
path so a missing Secret is treated consistently with the documented behavior,
or change the doc bullet to describe it as a retried runtime error instead of
terminal. Use the MustGather reconciliation code and its Secret existence check
to align the behavior and wording.

In `@docs/security-guidelines.md`:
- Around line 70-72: Add a language tag to the fenced example in the docs so the
Markdown lint rule is satisfied; update the fence around the kubebuilder
validation snippet to include a Go language hint using the nearby markdown
example block, and keep the content unchanged in the security-guidelines
example.
- Around line 112-118: `build/bin/upload` is building the remote upload path
from user-controlled `caseid` and, for internal uploads, `username` without
validation; update the upload path assembly to normalize and allowlist these
components before they are combined into `REMOTE_FILENAME`. Use the existing
upload flow in `build/bin/upload` and the internal path construction logic to
reject unsafe characters or path traversal sequences, then only concatenate
sanitized values so the remote SFTP target stays predictable.

In `@docs/testing-guidelines.md`:
- Around line 153-160: The async assertions guidance is out of sync with the e2e
tests: the job-completion timeout recommendation no longer matches the behavior
in must_gather_operator_test. Update the wording in the testing guidelines near
the Async Assertions section to reflect the current job-completion wait used by
the e2e suite, or explicitly state that 5 minutes is only an upper bound for
exceptional cases. Make sure the guidance stays aligned with the job-related
assertions in must_gather_operator_test.

In `@README.md`:
- Around line 78-103: The local development example is missing the runtime
environment variable required by the controller, so the startup recipe is
incomplete. Update the local-run steps in README.md near the local development
workflow to also export OPERATOR_IMAGE alongside DEFAULT_MUST_GATHER_IMAGE
before invoking operator-sdk run, matching the deployment contract documented by
the operator. Use the local-dev section and the operator-sdk run example as the
reference points so the example remains consistent with the controller’s
expected environment.

---

Nitpick comments:
In `@docs/api-contracts-guidelines.md`:
- Around line 110-126: Clarify the Finalizer Pattern cleanup flow by explicitly
stating that the controller must first verify the target object's
OwnerReferences against the current MustGather instance UID, then delete the
Job's Pods before deleting the Job itself. Update the wording around the cleanup
steps in the MustGather contract so the ownership check and delete order are
unambiguous and tied to the MustGather UID and job cleanup logic.
- Line 33: The documentation currently describes Storage like a union, but it
should be documented as a simple enum plus required nested config instead.
Update the Storage guidance in the api-contracts section to remove any
union/discriminator framing and make it clear that StorageType is the only enum
involved today, with PersistentVolume as the current value. Keep the reference
to Storage and StorageType so the correct section is easy to find.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 10a7761f-b2d5-4587-a9d7-1602743eb487

📥 Commits

Reviewing files that changed from the base of the PR and between 6860bae and 18592f6.

📒 Files selected for processing (12)
  • .coderabbit.yaml
  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • README.md
  • docs/ARCHITECTURE.md
  • docs/api-contracts-guidelines.md
  • docs/error-handling-guidelines.md
  • docs/integration-guidelines.md
  • docs/performance-guidelines.md
  • docs/security-guidelines.md
  • docs/testing-guidelines.md

Comment thread AGENTS.md
Comment on lines +30 to +47
```
api/v1alpha1/ # CRD types, deepcopy, OpenAPI generation
controllers/mustgather/ # Controller, Job template, predicates, SFTP validation
mustgather_controller.go # Main reconcile loop
template.go # Job/container spec generation
predicates.go # Event filters for controller-runtime
validation.go # SFTP credential and proxy validation
constant.go # Shared constants (validation types, env var names)
config/ # Operator name, OLM constants
pkg/localmetrics/ # Prometheus counter definitions
pkg/k8sutil/ # Namespace detection utility
build/bin/upload # Shell script: compress + SFTP upload (runs in container)
deploy/ # Kubernetes manifests (Deployment, RBAC, CRD)
test/e2e/ # E2E tests (Ginkgo/Gomega, build tag: e2e)
test/library/ # E2E test helpers (dynamic resource loader, kube clients)
examples/ # Sample MustGather CR YAML files
boilerplate/ # OpenShift-eng convention system (do not edit directly)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language tags to the fenced Markdown blocks.

These two non-code fences trigger the markdownlint MD040 warning. Mark them as text (or another appropriate language) so the docs stay lint-clean.

Suggested fix
-```
+```text

Apply the same change to the owner-reference block below.

Also applies to: 66-69

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 30-30: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@AGENTS.md` around lines 30 - 47, Add language tags to the two fenced Markdown
blocks in AGENTS.md so markdownlint MD040 is satisfied; update the unlabeled
fences in the repository layout section and the owner-reference block to use an
explicit language such as text, keeping the existing content unchanged. Use the
surrounding markdown list and the nearby owner-reference section to locate both
fences.

Source: Linters/SAST tools

Comment thread CONTRIBUTING.md
Comment on lines +100 to +102
```
MG-NNN: Short description of the change
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Label this fenced example for markdownlint.

Add a language tag like text to the fence so MD040 doesn't flag the file.

Suggested fix
-```
+```text
 MG-NNN: Short description of the change
-```
+```
📝 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
```
MG-NNN: Short description of the change
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 100-100: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@CONTRIBUTING.md` around lines 100 - 102, The fenced example in the
CONTRIBUTING markdown needs a language tag so markdownlint MD040 does not flag
it. Update the fenced block containing “MG-NNN: Short description of the change”
to use a text language label, and keep the example otherwise unchanged; locate
the snippet by its fenced markdown example rather than line numbers.

Source: Linters/SAST tools

Comment thread docs/ARCHITECTURE.md
Comment on lines +17 to +25
**Decision**: The operator runs in a single namespace (`must-gather-operator`) but watches MustGather CRs across all namespaces.

**Rationale**:
- Jobs are created in the CR's namespace, not the operator's namespace
- This allows users to leverage namespace-scoped RBAC for must-gather Jobs
- The operator needs cluster-scoped RBAC only for watching CRs and creating Jobs
- Avoids requiring elevated permissions in every namespace

**Trade-off**: More complex RBAC setup (cluster-scoped ClusterRole) vs. simpler namespace-scoped deployment.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Don't hardcode the operator namespace.

must-gather-operator reads like a deployment default, not an architectural invariant. Rephrase this as a configurable operator namespace instead of a fixed name.

Based on learnings, OPERATOR_NAMESPACE is required at startup and only falls back to config.OperatorName in local mode.

♻️ Proposed fix
- The operator runs in a single namespace (`must-gather-operator`) but watches MustGather CRs across all namespaces.
+ The operator runs in a single deployment-defined namespace but watches MustGather CRs across all namespaces.
📝 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
**Decision**: The operator runs in a single namespace (`must-gather-operator`) but watches MustGather CRs across all namespaces.
**Rationale**:
- Jobs are created in the CR's namespace, not the operator's namespace
- This allows users to leverage namespace-scoped RBAC for must-gather Jobs
- The operator needs cluster-scoped RBAC only for watching CRs and creating Jobs
- Avoids requiring elevated permissions in every namespace
**Trade-off**: More complex RBAC setup (cluster-scoped ClusterRole) vs. simpler namespace-scoped deployment.
**Decision**: The operator runs in a single deployment-defined namespace but watches MustGather CRs across all namespaces.
**Rationale**:
- Jobs are created in the CR's namespace, not the operator's namespace
- This allows users to leverage namespace-scoped RBAC for must-gather Jobs
- The operator needs cluster-scoped RBAC only for watching CRs and creating Jobs
- Avoids requiring elevated permissions in every namespace
**Trade-off**: More complex RBAC setup (cluster-scoped ClusterRole) vs. simpler namespace-scoped deployment.
🤖 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 `@docs/ARCHITECTURE.md` around lines 17 - 25, The architecture note hardcodes
the operator namespace as a fixed value, but it should describe a configurable
operator namespace instead. Update the wording in the decision/rationale around
the operator namespace to refer to OPERATOR_NAMESPACE as the required startup
setting, and mention that config.OperatorName is only a local-mode fallback.
Keep the rest of the RBAC and watch/job placement explanation intact while
removing the impression that must-gather-operator is an invariant.

Source: Learnings

Comment thread docs/ARCHITECTURE.md
Comment on lines +31 to +39
**Decision**: Each MustGather Job uses two containers sharing a process namespace:
1. **gather** - Runs the must-gather image, writes to `/must-gather`
2. **upload** - Runs the operator image, waits for gather completion, compresses and uploads via SFTP

**Rationale**:
- **Separation of concerns**: Gather container runs user-specified images (potentially untrusted), upload container runs operator-controlled code
- **Permission isolation**: Gather runs with cluster-admin via ServiceAccount, upload runs with minimal permissions
- **Process detection**: `ShareProcessNamespace: true` allows upload to detect gather completion via `pgrep -a gather`
- **No gather image modification**: Must-gather images don't need built-in upload logic

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the referenced section with line numbers
sed -n '1,120p' docs/ARCHITECTURE.md | cat -n

# Search for related permission / service account wording in the repo
rg -n "ServiceAccount|cluster-admin|ShareProcessNamespace|pgrep -a gather|must-gather|SFTP|upload container|gather container" docs . -g '!**/.git/**'

Repository: openshift/must-gather-operator

Length of output: 50387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the job template and the lines that set service account / container fields
rg -n "ServiceAccountName|serviceAccountName|upload container|gather container|ShareProcessNamespace|automountServiceAccountToken|must-gather-admin|OperatorImage" -- glob '*.go' -- glob '*.md' -- glob '*.yaml'

# Inspect the most likely template/controller files around the relevant logic
fd -a "template.go|mustgather_controller.go" .

Repository: openshift/must-gather-operator

Length of output: 526


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant source files by name
fd -a 'template.go' .
fd -a 'mustgather_controller.go' .

# Search for the job template construction and service account settings
rg -n "ServiceAccountName|automountServiceAccountToken|ShareProcessNamespace|OperatorImage|upload container|gather container|must-gather-admin" -- ./api ./controllers ./pkg ./internal ./test ./docs 2>/dev/null || true

Repository: openshift/must-gather-operator

Length of output: 18236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the job template construction around the ServiceAccount and containers
sed -n '1,320p' controllers/mustgather/template.go | cat -n

# Show the controller validation that references the operator's ServiceAccount
sed -n '130,190p' controllers/mustgather/mustgather_controller.go | cat -n

# Read the security guidance that mentions the upload container's trust/permissions model
sed -n '1,170p' docs/security-guidelines.md | cat -n

Repository: openshift/must-gather-operator

Length of output: 25400


Tighten the container permission model. The upload container shares the pod’s ServiceAccount with the gather container, so it doesn’t have a separate Kubernetes identity. Describe it as trusted operator-controlled code that doesn’t need API access, rather than as isolated permissions.

🤖 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 `@docs/ARCHITECTURE.md` around lines 31 - 39, The architecture note in the
MustGather Job description overstates permission isolation for the upload
container; it shares the same ServiceAccount as gather and does not have a
separate Kubernetes identity. Update the wording around the gather/upload
container split to emphasize that upload is trusted operator-controlled code
that relies on process namespace sharing for completion detection and does not
need Kubernetes API access, rather than claiming distinct pod-level permissions.

Comment thread docs/ARCHITECTURE.md
Comment on lines +41 to +43
**Why not a sidecar?**: Sidecars would require gather to signal completion. Process namespace sharing avoids modifying gather images.

**Why not sequential containers?**: Kubernetes doesn't support sequential container execution within a single pod. Init containers can't share volumes with main containers effectively for this use case.

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
printf '== architecture excerpt ==\n'
sed -n '30,55p' docs/ARCHITECTURE.md
printf '\n== line count ==\n'
wc -l docs/ARCHITECTURE.md

Repository: openshift/must-gather-operator

Length of output: 1841


🌐 Web query:

Kubernetes init containers share volumes with app containers process namespace init containers run before app containers documentation

💡 Result:

Init containers in Kubernetes run before application containers and can share volumes, network resources, and process namespaces with them, depending on the Pod configuration [1][2][3]. Init Containers and Execution Order Init containers are specialized containers that run during Pod startup, before any application containers start [2][3]. If multiple init containers are defined, they run sequentially in the order specified in the Pod's configuration [4][3]. Each init container must complete successfully before the next one starts, and all init containers must finish successfully before the application containers can begin [4][3]. Shared Volumes Init containers and application containers can share volumes [5][6]. By mounting the same volume in both the init container and the application container, you can use the init container to perform pre-processing tasks, such as generating configuration files, setting permissions, or pulling data, which are then accessed by the application container [7][6]. Process Namespace Sharing Kubernetes allows containers within a Pod to share a process namespace [8]. When this feature is enabled (by setting shareProcessNamespace: true in the Pod spec), all containers in the Pod—including init containers and regular application containers—share a single process namespace [9][10]. This allows processes in one container to see and interact with processes in other containers [8]. Resource Sharing Init containers share the same network, resource limits (CPU/memory), and, if configured, process namespace as the main application containers [1]. However, unlike sidecar containers (which are initialized using the restartPolicy: Always configuration), init containers are not intended to run continuously alongside the main application; they are designed to run to completion [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('docs/ARCHITECTURE.md')
text = p.read_text()
for i, line in enumerate(text.splitlines(), 1):
    if 38 <= i <= 46:
        print(f"{i}: {line}")
PY

Repository: openshift/must-gather-operator

Length of output: 783


🌐 Web query:

Kubernetes shareProcessNamespace init containers include init containers same process namespace documentation

💡 Result:

Yes, when the shareProcessNamespace field is set to true in a Pod specification, all containers in the Pod, including init containers, share the same process namespace [1][2]. When this setting is enabled: - All containers in the Pod can see the processes of all other containers, including processes that were started by init containers and are still running (if any), as well as those in the main application containers [1][3]. - Processes are visible to other containers in the Pod, and users can view process information in /proc, signal processes (if they have the necessary permissions), and access other containers' filesystems via the /proc/$pid/root link [1]. It is important to note that because init containers run to completion before the main application containers start, an init container's processes will only be visible to subsequent containers if they continue to run after the init container has terminated (e.g., if the init container spawns a background process that persists) [3]. Technical documentation and design specifications confirm that the kubelet translates the shareProcessNamespace setting into a single namespace mode for the pod sandbox and all regular and init containers [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "shareProcessNamespace|init containers|sequential containers|gather completion" docs/ARCHITECTURE.md

Repository: openshift/must-gather-operator

Length of output: 683


Clarify the init-container limitation.

Init containers do share volumes, and shareProcessNamespace applies to them too; the real limitation is that they run before app containers, so they can’t wait for gather completion or observe the long-running gather process this design depends on.

🤖 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 `@docs/ARCHITECTURE.md` around lines 41 - 43, Update the explanation in the
architecture doc to reflect the real init-container limitation: in the section
near the sequential containers discussion, clarify that init containers do share
volumes and can use shareProcessNamespace, but they run before app containers so
they cannot wait for gather to finish or observe the long-running gather process
this design relies on. Keep the reference to the sidecar vs. sequential
container rationale in ARCHITECTURE.md aligned with that distinction.

Comment on lines +50 to +54
- **No cross-namespace secret copy.** The Secret must exist in the same namespace as the MustGather CR. The Job references it via `SecretKeyRef` env vars.
- **Required fields:** `username` and `password`, both must be non-empty byte slices.
- **Validation order:** Check Secret exists -> check fields present and non-empty -> validate SFTP credentials -> create Job.
- **Secret not-found is terminal;** transient Get errors requeue with `Requeue: true`.

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 | 🟠 Major | ⚡ Quick win

Align the Secret-not-found behavior with the controller.

controllers/mustgather/mustgather_controller.go:141-241 still routes a missing Secret through ManageError(...), so calling it terminal here is inaccurate. Either update the reconcile logic too, or rewrite this bullet as a retried runtime error.

Suggested doc fix
-- **Secret not-found is terminal;** transient Get errors requeue with `Requeue: true`.
+- **Secret not-found is retried via `ManageError`;** transient Get errors requeue with `Requeue: true`.
📝 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
- **No cross-namespace secret copy.** The Secret must exist in the same namespace as the MustGather CR. The Job references it via `SecretKeyRef` env vars.
- **Required fields:** `username` and `password`, both must be non-empty byte slices.
- **Validation order:** Check Secret exists -> check fields present and non-empty -> validate SFTP credentials -> create Job.
- **Secret not-found is terminal;** transient Get errors requeue with `Requeue: true`.
- **No cross-namespace secret copy.** The Secret must exist in the same namespace as the MustGather CR. The Job references it via `SecretKeyRef` env vars.
- **Required fields:** `username` and `password`, both must be non-empty byte slices.
- **Validation order:** Check Secret exists -> check fields present and non-empty -> validate SFTP credentials -> create Job.
- **Secret not-found is retried via `ManageError`;** transient Get errors requeue with `Requeue: true`.
🤖 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 `@docs/integration-guidelines.md` around lines 50 - 54, The Secret-not-found
handling described in the integration guidelines does not match the actual
reconcile flow in MustGatherController. Update the MustGather controller logic
around ManageError and the Secret lookup path so a missing Secret is treated
consistently with the documented behavior, or change the doc bullet to describe
it as a retried runtime error instead of terminal. Use the MustGather
reconciliation code and its Secret existence check to align the behavior and
wording.

Comment on lines +70 to +72
```
+kubebuilder:validation:XValidation:rule="!has(oldSelf.spec) || self.spec == oldSelf.spec"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to this fenced example.

Markdownlint already flags this block (MD040). A language hint keeps docs checks green.

Proposed fix
-```
+```go
📝 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
```
+kubebuilder:validation:XValidation:rule="!has(oldSelf.spec) || self.spec == oldSelf.spec"
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 70-70: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@docs/security-guidelines.md` around lines 70 - 72, Add a language tag to the
fenced example in the docs so the Markdown lint rule is satisfied; update the
fence around the kubebuilder validation snippet to include a Go language hint
using the nearby markdown example block, and keep the content unchanged in the
security-guidelines example.

Source: Linters/SAST tools

Comment on lines +112 to +118
The upload script (`build/bin/upload`) has these security-relevant behaviors:

- `StrictHostKeyChecking=no` with a dedicated `UserKnownHostsFile` at `/tmp/must-gather-operator/.ssh/known_hosts`. The SSH directory and known_hosts file are created by the upload container via `mkdir -p` and `touch` commands with restricted permissions (`chmod 700` for the directory, `chmod 600` for known_hosts) in `template.go`.
- Password is passed via `SSHPASS` env var with `sshpass -e`, never on the command line.
- Proxy credentials may appear in `ProxyCommand` arguments for HTTP proxies (via `--proxy-auth`). For HTTPS proxies, credentials are passed via exported env vars to `https-proxy-connect-util`.
- Internal users upload to a different SFTP path (`username/caseID_filename`). This path construction is not sanitized beyond the SFTP server's own controls.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant docs section ==\n'
nl -ba docs/security-guidelines.md | sed -n '100,130p'

printf '\n== Locate upload path construction ==\n'
rg -n --hidden --glob '!**/.git/**' 'caseID|username/caseID|SFTP|upload path|ProxyCommand|sshpass|known_hosts' .

printf '\n== Candidate files around upload logic ==\n'
fd -a 'upload|template\.go|bin' .

Repository: openshift/must-gather-operator

Length of output: 237


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant docs section ==\n'
cat -n docs/security-guidelines.md | sed -n '100,130p'

printf '\n== Search for path construction and upload logic ==\n'
rg -n --hidden --glob '!**/.git/**' 'caseID|username/|upload.*sftp|SFTP|ProxyCommand|sshpass|known_hosts|ProxyCommand|proxy-auth|https-proxy-connect-util' .

printf '\n== File names that look relevant ==\n'
fd -a 'template.go|upload|security-guidelines.md' .

Repository: openshift/must-gather-operator

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Outline template.go ==\n'
ast-grep outline controllers/mustgather/template.go --view expanded || true

printf '\n== Relevant slices from template.go ==\n'
cat -n controllers/mustgather/template.go | sed -n '130,240p'

printf '\n== Outline upload script if present ==\n'
fd -a 'upload' build controllers test || true

printf '\n== Search for remote path assembly details ==\n'
rg -n --hidden --glob '!**/.git/**' 'caseID.*filename|filename.*caseID|username.*filename|InternalUser|remote.*path|sftp.*Put|Put\(|Rename\(|Mkdir\(|Create\(' build controllers test

Repository: openshift/must-gather-operator

Length of output: 11538


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== getUploadContainer section ==\n'
cat -n controllers/mustgather/template.go | sed -n '318,430p'

printf '\n== upload script ==\n'
cat -n build/bin/upload | sed -n '1,260p'

printf '\n== caseID validation rules ==\n'
cat -n api/v1alpha1/mustgather_types.go | sed -n '118,170p'

Repository: openshift/must-gather-operator

Length of output: 11206


Normalize the path components before upload. build/bin/upload concatenates the user-controlled caseid (and username for internal uploads) directly into REMOTE_FILENAME; the tarball name itself is generated locally. Allowlist/normalize those inputs before assembling the remote path instead of relying on SFTP server-side rejection.

🤖 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 `@docs/security-guidelines.md` around lines 112 - 118, `build/bin/upload` is
building the remote upload path from user-controlled `caseid` and, for internal
uploads, `username` without validation; update the upload path assembly to
normalize and allowlist these components before they are combined into
`REMOTE_FILENAME`. Use the existing upload flow in `build/bin/upload` and the
internal path construction logic to reject unsafe characters or path traversal
sequences, then only concatenate sanitized values so the remote SFTP target
stays predictable.

Comment on lines +153 to +160
### Async Assertions

```go
Eventually(func() bool { ... }).
WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue())
```

Timeouts: 1-2 min for resource creation, 5 min for job completion, 30s for `Consistently`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the job-completion timeout with the current e2e suite.

test/e2e/must_gather_operator_test.go:491-532 currently waits 2 minutes for job creation/completion, so the “5 min for job completion” guidance is out of sync. Either update the recommendation or call out that 5 minutes is only an upper bound for exceptional cases.

🛠️ Possible wording
- Timeouts: 1-2 min for resource creation, 5 min for job completion, 30s for `Consistently`.
+ Timeouts: 1-2 min for resource creation, 2 min for job completion in the current suite, 30s for `Consistently`.
📝 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
### Async Assertions
```go
Eventually(func() bool { ... }).
WithTimeout(2 * time.Minute).WithPolling(5 * time.Second).Should(BeTrue())
```
Timeouts: 1-2 min for resource creation, 5 min for job completion, 30s for `Consistently`.
### Async Assertions
🤖 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 `@docs/testing-guidelines.md` around lines 153 - 160, The async assertions
guidance is out of sync with the e2e tests: the job-completion timeout
recommendation no longer matches the behavior in must_gather_operator_test.
Update the wording in the testing guidelines near the Async Assertions section
to reflect the current job-completion wait used by the e2e suite, or explicitly
state that 5 minutes is only an upper bound for exceptional cases. Make sure the
guidance stays aligned with the job-related assertions in
must_gather_operator_test.

Comment thread README.md
Comment on lines 78 to +103
## Local Development

Execute the following steps to develop the functionality locally. It is recommended that development be done using a cluster with `cluster-admin` permissions.

In the operator's `Deployment.yaml` [file](deploy/99_must-gather-operator.Deployment.yaml), add a variable to the deployment's `spec.template.spec.containers.env` list called `OPERATOR_IMAGE` and set the value to your local copy of the image:
```shell
env:
- name: OPERATOR_IMAGE
value: "registry.example/repo/image:latest"
```
Then run:
```shell
go mod download
```
1. In the operator's `Deployment.yaml` [file](deploy/99_must-gather-operator.Deployment.yaml), add a variable to the deployment's `spec.template.spec.containers.env` list called `OPERATOR_IMAGE` and set the value to your local copy of the image:

Using the [operator-sdk](https://github.com/operator-framework/operator-sdk), run the operator locally:
```yaml
env:
- name: OPERATOR_IMAGE
value: "registry.example/repo/image:latest"
```

```shell
oc apply -f deploy/crds/operator.openshift.io_mustgathers_crd.yaml
oc new-project must-gather-operator
export DEFAULT_MUST_GATHER_IMAGE='quay.io/openshift/origin-must-gather:latest'
OPERATOR_NAME=must-gather-operator operator-sdk run --verbose --local --namespace ''
2. Download dependencies:

```shell
go mod download
```

3. Using the [operator-sdk](https://github.com/operator-framework/operator-sdk), apply the CRD and run the operator locally:

```shell
oc apply -f deploy/crds/operator.openshift.io_mustgathers_crd.yaml
oc new-project must-gather-operator
export DEFAULT_MUST_GATHER_IMAGE='quay.io/openshift/origin-must-gather:latest'
OPERATOR_NAME=must-gather-operator operator-sdk run --verbose --local --namespace ''
```

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

Export OPERATOR_IMAGE in the local-dev example.

Based on learnings, the controller still needs OPERATOR_IMAGE at startup, but this recipe only exports DEFAULT_MUST_GATHER_IMAGE, so the local-run example stops short of the documented runtime contract.

Suggested fix
 export DEFAULT_MUST_GATHER_IMAGE='quay.io/openshift/origin-must-gather:latest'
+export OPERATOR_IMAGE='quay.io/openshift/origin-must-gather-operator:latest'
 OPERATOR_NAME=must-gather-operator operator-sdk run --verbose --local --namespace ''
📝 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
## Local Development
Execute the following steps to develop the functionality locally. It is recommended that development be done using a cluster with `cluster-admin` permissions.
In the operator's `Deployment.yaml` [file](deploy/99_must-gather-operator.Deployment.yaml), add a variable to the deployment's `spec.template.spec.containers.env` list called `OPERATOR_IMAGE` and set the value to your local copy of the image:
```shell
env:
- name: OPERATOR_IMAGE
value: "registry.example/repo/image:latest"
```
Then run:
```shell
go mod download
```
1. In the operator's `Deployment.yaml` [file](deploy/99_must-gather-operator.Deployment.yaml), add a variable to the deployment's `spec.template.spec.containers.env` list called `OPERATOR_IMAGE` and set the value to your local copy of the image:
Using the [operator-sdk](https://github.com/operator-framework/operator-sdk), run the operator locally:
```yaml
env:
- name: OPERATOR_IMAGE
value: "registry.example/repo/image:latest"
```
```shell
oc apply -f deploy/crds/operator.openshift.io_mustgathers_crd.yaml
oc new-project must-gather-operator
export DEFAULT_MUST_GATHER_IMAGE='quay.io/openshift/origin-must-gather:latest'
OPERATOR_NAME=must-gather-operator operator-sdk run --verbose --local --namespace ''
2. Download dependencies:
```shell
go mod download
```
3. Using the [operator-sdk](https://github.com/operator-framework/operator-sdk), apply the CRD and run the operator locally:
```shell
oc apply -f deploy/crds/operator.openshift.io_mustgathers_crd.yaml
oc new-project must-gather-operator
export DEFAULT_MUST_GATHER_IMAGE='quay.io/openshift/origin-must-gather:latest'
OPERATOR_NAME=must-gather-operator operator-sdk run --verbose --local --namespace ''
```
## Local Development
Execute the following steps to develop the functionality locally. It is recommended that development be done using a cluster with `cluster-admin` permissions.
1. In the operator's `Deployment.yaml` [file](deploy/99_must-gather-operator.Deployment.yaml), add a variable to the deployment's `spec.template.spec.containers.env` list called `OPERATOR_IMAGE` and set the value to your local copy of the image:
🤖 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 `@README.md` around lines 78 - 103, The local development example is missing
the runtime environment variable required by the controller, so the startup
recipe is incomplete. Update the local-run steps in README.md near the local
development workflow to also export OPERATOR_IMAGE alongside
DEFAULT_MUST_GATHER_IMAGE before invoking operator-sdk run, matching the
deployment contract documented by the operator. Use the local-dev section and
the operator-sdk run example as the reference points so the example remains
consistent with the controller’s expected environment.

Source: Learnings

@openshift-ci

openshift-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

@praveencodes: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-operator 18592f6 link true /test e2e-gcp-operator
ci/prow/validate-boilerplate 18592f6 link false /test validate-boilerplate

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants