Skip to content

Tokenize Credential Request AWS#782

Open
desmax74 wants to merge 3 commits into
trustification:release/3.y.zfrom
desmax74:occ
Open

Tokenize Credential Request AWS#782
desmax74 wants to merge 3 commits into
trustification:release/3.y.zfrom
desmax74:occ

Conversation

@desmax74

@desmax74 desmax74 commented May 11, 2026

Copy link
Copy Markdown
Collaborator

https://redhat.atlassian.net/browse/TC-4306

Assisted-by: Claude: Opus 4.6

Summary by Sourcery

Introduce cloud-provider-specific credential requests and wire them into the trusted profile analyzer Helm chart.

New Features:

  • Add configurable cloudProvider and cloudCredentials values for AWS and GCP to the redhat-trusted-profile-analyzer Helm values.
  • Add a CredentialsRequest template that generates provider-specific credentials for AWS or GCP and exposes them via a secret to the chart.
  • Add a sample Deployment manifest that consumes generated AWS credentials as environment variables.
  • Add a development AWS CredentialsRequest manifest under devel/cco for local or example usage.

Summary by Sourcery

Integrate Cloud Credentials Operator support for cloud-provider-specific S3 credentials in the trusted profile analyzer operator and Helm chart.

New Features:

  • Add configurable cloudProvider, ccoMode, and cloudCredentials values to the Helm chart to drive cloud-provider-specific credential provisioning for AWS and GCP.
  • Generate a Cloud Credentials Operator CredentialsRequest resource and associated secret for use by the chart when a cloud provider is configured.
  • Expose CCO-provisioned AWS credentials to workloads via environment variables and volume mounts, including STS/manual token-based authentication support.
  • Declare and bind a ClusterRole granting the operator permission to manage CredentialsRequest resources.
  • Document how to use the Cloud Credentials Operator with the operator via a new extension guide and provide sample CredentialsRequest manifests for development and testing.

Enhancements:

  • Relax S3 storage configuration to allow omission of accessKey and secretKey when credentials are managed by the Cloud Credentials Operator.
  • Advertise AWS token-based authentication support through updated operator bundle labels.
  • Refine Helm and operator tests to cover CCO-related schema, RBAC, rendering behavior, and storage environment variable wiring.

Tests:

  • Add e2e Helm rendering tests for CCO integration across AWS and GCP providers and different CCO modes (mint, passthrough, manual, default).
  • Add integration tests to verify CCO-related RBAC resources and presence in the RBAC kustomization.

Summary by Sourcery

Integrate OpenShift Cloud Credential Operator support into the trusted profile analyzer operator and Helm chart to provision cloud-provider-specific credentials and enable AWS token-based auth and optional RDS IAM authentication.

New Features:

  • Add Helm values and templates for cloudProvider, ccoMode, cloudCredentials, and ccoRds to drive CCO-managed credentials for AWS and GCP.
  • Introduce a CredentialsRequest Helm template and CCO helper templates to inject cloud credentials and RDS IAM auth configuration into workloads.
  • Provide sample TrustedProfileAnalyzer CRs and a development CredentialsRequest manifest demonstrating CCO usage across modes and providers.
  • Document CCO integration, configuration, and RDS IAM usage in operator extension documentation.

Enhancements:

  • Relax S3 storage and database configuration so accessKey/secretKey and database password become optional when CCO-managed credentials or RDS IAM auth are enabled.
  • Wire CCO-provisioned credentials and RDS IAM settings into server, importer, and migrate-database jobs via environment variables, volumes, and mounts.
  • Advertise AWS token-based authentication support via updated operator bundle labels.
  • Extend RBAC with a ClusterRole and ClusterRoleBinding granting the operator permission to manage CredentialsRequest resources.

Tests:

  • Add e2e Helm rendering tests covering CCO integration for AWS and GCP, all CCO modes, and RDS IAM authentication behavior.
  • Add integration tests verifying CCO-related RBAC resources and their inclusion in the RBAC kustomization.
  • Add integration tests ensuring the Helm values schema includes new CCO-related fields and structure.

@sourcery-ai

sourcery-ai Bot commented May 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Integrates OpenShift Cloud Credential Operator (CCO) support into the Trusted Profile Analyzer Helm chart and operator, adding cloud-provider-specific credential requests (AWS/GCP), STS/manual token support, and RDS IAM auth wiring, along with corresponding RBAC, docs, and tests.

Sequence diagram for CCO-managed credentials and RDS IAM auth

sequenceDiagram
    actor Admin
    participant RhtpaOperator as rhtpa_operator
    participant CCO as cloud_credential_operator
    participant Cloud as CloudProvider
    participant Secret as RhtpaCloudCredsSecret
    participant TrustdPods as trustd_pods
    participant S3
    participant RDS

    Admin->>RhtpaOperator: Apply TrustedProfileAnalyzer CR
    RhtpaOperator->>RhtpaOperator: Render credentialrequest.yaml
    RhtpaOperator->>CCO: create CredentialsRequest
    CCO->>Cloud: provision credentials (mint/passthrough/manual)
    Cloud-->>CCO: cloud credentials
    CCO->>Secret: create Secret <release-name>-cloud-creds

    Note over TrustdPods,Secret: Pod startup using Helm templates

    RhtpaOperator->>TrustdPods: deploy pods with<br/>trustification.cco.volumes<br/>trustification.cco.volumeMounts<br/>trustification.cco.envVars<br/>trustification.cco.rds.envVars<br/>trustification.storage.envVars

    Secret-->>TrustdPods: aws_access_key_id, aws_secret_access_key

    alt non-manual CCO mode
        TrustdPods->>S3: use TRUSTD_S3_ACCESS_KEY / SECRET_KEY
        TrustdPods->>RDS: use TRUSTD_DB_PASSWORD (no IAM auth)
    else RDS IAM auth enabled
        TrustdPods->>RDS: use TRUSTD_DB_IAM_AUTH with AWS_* env vars
    end
Loading

File-Level Changes

Change Details Files
Add Helm values schema and templates for cloudProvider/ccoMode/ccoRds/cloudCredentials and generate provider-specific CredentialsRequest resources.
  • Extend values.schema.yaml with ccoMode, cloudProvider, ccoRds, and cloudCredentials objects (including AWS statementEntries/stsIAMRoleARN and GCP permissions).
  • Update S3 storage and database schema to make accessKey/secretKey and password optional when CCO is enabled.
  • Introduce credentialrequest.yaml Helm template that emits a CredentialsRequest in the CCO namespace with AWSProviderSpec or GCPProviderSpec based on cloudProvider and ccoMode (including manual-mode cloudTokenPath and stsIAMRoleARN).
  • Add default values wiring for new CCO-related fields in values.yaml.
helm-charts/redhat-trusted-profile-analyzer/values.schema.yaml
helm-charts/redhat-trusted-profile-analyzer/values.schema.json
helm-charts/redhat-trusted-profile-analyzer/values.yaml
helm-charts/redhat-trusted-profile-analyzer/templates/credentialrequest.yaml
Wire CCO-provisioned credentials and RDS IAM auth into application pods via Helm helper templates.
  • Add cco.tpl helpers to detect manual mode and RDS IAM enablement, define cloud-credentials and bound-sa-token volumes/mounts, and emit AWS* and TRUSTD_DB_* env vars as appropriate.
  • Update storage helper to source TRUSTD_S3_ACCESS_KEY/SECRET_KEY from the CCO secret when cloudProvider is set and not in manual mode, falling back to explicit values otherwise and omitting them entirely in manual mode.
  • Update Postgres helpers to skip PGPASSWORD/TRUSTD_DB_PASSWORD and force SSL mode to require when RDS IAM is enabled, while emitting IAM-related env vars via CCO helpers.
  • Inject CCO volumes, volumeMounts, and envVars into migrate-database Job and server/importer Deployments to ensure both S3 and RDS flows work under CCO.
helm-charts/redhat-trusted-profile-analyzer/templates/helpers/_cco.tpl
helm-charts/redhat-trusted-profile-analyzer/templates/helpers/_storage.tpl
helm-charts/redhat-trusted-profile-analyzer/templates/helpers/_postgres.tpl
helm-charts/redhat-trusted-profile-analyzer/templates/init/migrate-database/020-Job.yaml
helm-charts/redhat-trusted-profile-analyzer/templates/services/importer/030-Deployment.yaml
helm-charts/redhat-trusted-profile-analyzer/templates/services/server/030-Deployment.yaml
Document CCO integration, including modes, manual STS, and RDS IAM auth, and provide sample manifests.
  • Extend CLAUDE.md with a detailed "Cloud Credential Operator (CCO) Integration" section covering enabling/disabling CCO, ccoMode semantics, manual STS configuration, RDS IAM auth behavior, and key file references.
  • Add an extensions/README.md guide describing how to inspect CCO status, how the operator interacts with CCO, Helm integration steps, and manual mode workflows.
  • Add sample TrustedProfileAnalyzer CR fixtures for AWS (mint/default/passthrough/manual) and GCP CCO usage and RDS IAM auth in both mint and manual modes.
  • Add a devel/cco/credentialRequest.yaml example CredentialsRequest for development use.
CLAUDE.md
extensions/README.md
test/fixtures/aws_cco_cr.yaml
test/fixtures/aws_cco_default_cr.yaml
test/fixtures/aws_cco_passthrough_cr.yaml
test/fixtures/aws_cco_manual_cr.yaml
test/fixtures/aws_cco_rds_cr.yaml
test/fixtures/aws_cco_manual_rds_cr.yaml
test/fixtures/gcp_cco_cr.yaml
devel/cco/credentialRequest.yaml
Grant the operator RBAC to manage CredentialsRequest resources and ensure RBAC is wired into kustomize.
  • Introduce a ClusterRole granting full CRUD/watch on cloudcredential.openshift.io credentialsrequests.
  • Add a ClusterRoleBinding binding that ClusterRole to the rhtpa-operator-controller-manager ServiceAccount (namespace placeholder).
  • Register the new RBAC resources in config/rbac/kustomization.yaml so they are applied with other RBAC manifests.
  • Add integration tests to validate CCO ClusterRole and ClusterRoleBinding existence, permissions, subject wiring, and presence in kustomization.
config/rbac/clusterrole.yaml
config/rbac/clusterrolebinding_cco.yaml
config/rbac/kustomization.yaml
test/integration/cco_rbac_test.go
Advertise AWS token-based auth in the operator bundle metadata.
  • Flip features.operators.openshift.io/token-auth-aws from false to true in bundle.Dockerfile labels.
  • Update the operator ClusterServiceVersion manifests (both generated bundle and base config) and bundle metadata annotations to set token-auth-aws to true.
bundle.Dockerfile
bundle/manifests/rhtpa-operator.clusterserviceversion.yaml
bundle/metadata/annotations.yaml
config/manifests/bases/rhtpa-operator.clusterserviceversion.yaml
Add tests to cover new schema, CCO Helm rendering, and minor e2e helpers tweaks.
  • Add TestValuesSchemaContainsCCOFields to ensure values.schema.json exposes cloudProvider, ccoMode, cloudCredentials with expected enums and aws.stsIAMRoleARN.
  • Introduce a comprehensive cco_helm_rendering_test.go e2e suite that renders the chart with various AWS/GCP CCO configurations and asserts CredentialsRequest shapes, env var/volume wiring (including manual STS and RDS IAM), SSL behavior, and absence/presence of keys in different modes.
  • Add cco-related constants to helpers_test.go and reuse kindDeploymentResource constant in helm_rendering_test.go and new CCO tests.
  • Adjust operator_health_test metrics-port check to use the shared fieldMetrics constant for consistency.
test/integration/helm_chart_test.go
test/e2e/cco_helm_rendering_test.go
test/e2e/helpers_test.go
test/e2e/helm_rendering_test.go
test/e2e/operator_health_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@desmax74 desmax74 marked this pull request as draft May 11, 2026 16:26

@sourcery-ai sourcery-ai 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.

Hey - I've found 4 issues, and left some high level feedback:

  • The Helm template for credentialrequest.yaml has inconsistent indentation and mixed static/dynamic statementEntries/predefinedRoles blocks, which may produce invalid or unexpected YAML; consider simplifying to either fully static defaults or a cleanly merged toYaml block with carefully aligned indentation.
  • The deployment.yaml currently hardcodes an AWS-specific secret and environment variables; if cloudProvider can be GCP/Azure, it would be better to conditionally inject provider-specific env vars or secrets to avoid misconfigured deployments for non-AWS providers.
  • The values for AWS (s3:*) and the hardcoded s3:GetObject/PutObject/ListBucket actions in the credentials request may be redundant or conflicting; aligning the default actions with what the template actually renders will make the permissions model clearer and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The Helm template for `credentialrequest.yaml` has inconsistent indentation and mixed static/dynamic `statementEntries`/`predefinedRoles` blocks, which may produce invalid or unexpected YAML; consider simplifying to either fully static defaults or a cleanly merged `toYaml` block with carefully aligned indentation.
- The `deployment.yaml` currently hardcodes an AWS-specific secret and environment variables; if `cloudProvider` can be GCP/Azure, it would be better to conditionally inject provider-specific env vars or secrets to avoid misconfigured deployments for non-AWS providers.
- The values for AWS (`s3:*`) and the hardcoded `s3:GetObject`/`PutObject`/`ListBucket` actions in the credentials request may be redundant or conflicting; aligning the default actions with what the template actually renders will make the permissions model clearer and easier to maintain.

## Individual Comments

### Comment 1
<location path="helm-charts/redhat-trusted-profile-analyzer/values.yaml" line_range="82-85" />
<code_context>

+  cloudProvider: aws   # aws | gcp | azure
+  cloudCredentials:
+      aws:
+        statementEntries:
+          - effect: Allow
+            action: [ "s3:*" ]
+            resource: "*"
+      gcp:
</code_context>
<issue_to_address>
**🚨 issue (security):** AWS credentials default to full S3 access, which is overly permissive

The default `cloudCredentials.aws.statementEntries` grant `"s3:*"` on all resources, which is effectively admin-level S3 access and may be unacceptable in locked-down environments. Please scope the default to specific buckets and limited actions, or require users to provide these permissions explicitly rather than shipping such broad defaults.
</issue_to_address>

### Comment 2
<location path="helm-charts/redhat-trusted-profile-analyzer/templates/credentialrequest.yaml" line_range="11-20" />
<code_context>
+    name: {{ .Release.Name }}-cloud-creds
+    namespace: {{ .Release.Namespace }}
+
+  {{- if eq .Values.cloudProvider "aws" }}
+    providerSpec:
+      apiVersion: cloudcredential.openshift.io/v1
+      kind: AWSProviderSpec
+      statementEntries:
+        - effect: Allow
+          action:
+            - "s3:GetObject"
+            - "s3:PutObject"
+            - "s3:ListBucket"
+          resource: "*"
+         {{- toYaml .Values.cloudCredentials.aws.statementEntries | nindent 6 }}
+   {{- else if eq .Values.cloudProvider "gcp" }}
</code_context>
<issue_to_address>
**issue (bug_risk):** Indentation and mixed static/dynamic `statementEntries` may yield invalid or duplicated YAML

The `providerSpec` block is over-indented relative to the `if`, and `statementEntries` is both hardcoded and redefined via `toYaml .Values.cloudCredentials.aws.statementEntries | nindent 6`. This can break the YAML structure or create a malformed `statementEntries` field. Please align `providerSpec` with its peers and use a single, consistently indented `statementEntries` definition (either fully templated or merged cleanly).
</issue_to_address>

### Comment 3
<location path="helm-charts/redhat-trusted-profile-analyzer/templates/credentialrequest.yaml" line_range="23-27" />
<code_context>
+            - "s3:ListBucket"
+          resource: "*"
+         {{- toYaml .Values.cloudCredentials.aws.statementEntries | nindent 6 }}
+   {{- else if eq .Values.cloudProvider "gcp" }}
+      providerSpec:
+        apiVersion: cloudcredential.openshift.io/v1
+        kind: GCPProviderSpec
+        predefinedRoles:
+         {{- toYaml .Values.cloudCredentials.gcp.permissions | nindent 6 }}
+   {{- end }}
</code_context>
<issue_to_address>
**issue (bug_risk):** GCP `predefinedRoles` seems to be used with permission strings instead of role names

Here `predefinedRoles` is populated from `cloudCredentials.gcp.permissions`, but those values look like permission strings (e.g. `compute.instances.list`) instead of IAM role IDs (e.g. `roles/...`). Since `GCPProviderSpec.predefinedRoles` requires role names, this is likely to fail or not grant the required access. Either switch to the appropriate field for listing permissions (e.g. `serviceAccountPermissions`) or map these permissions to suitable predefined roles before using this field.
</issue_to_address>

### Comment 4
<location path="helm-charts/redhat-trusted-profile-analyzer/templates/deployment.yaml" line_range="11-20" />
<code_context>
+      containers:
+        - name: my-operator
+          env:
+            - name: AWS_ACCESS_KEY_ID
+              valueFrom:
+                secretKeyRef:
+                  name: {{ .Release.Name }}-cloud-creds
+                  key: aws_access_key_id
+            - name: AWS_SECRET_ACCESS_KEY
+              valueFrom:
+                secretKeyRef:
+                  name: {{ .Release.Name }}-cloud-creds
+                  key: aws_secret_access_key
</code_context>
<issue_to_address>
**issue (bug_risk):** Deployment always expects AWS-style secrets even when using non-AWS providers

Env vars are always populated from `{{ .Release.Name }}-cloud-creds` using AWS-specific keys. For `cloudProvider: gcp` (or others), the credentialsRequest will likely create a different secret schema, so this pod won’t receive valid creds. Please gate these env vars on the provider, or introduce provider-specific secret layouts/templates so non-AWS providers don’t break this deployment.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread helm-charts/redhat-trusted-profile-analyzer/values.yaml Outdated
Comment thread helm-charts/redhat-trusted-profile-analyzer/templates/credentialrequest.yaml Outdated
Comment thread helm-charts/redhat-trusted-profile-analyzer/templates/credentialrequest.yaml Outdated
Comment thread helm-charts/redhat-trusted-profile-analyzer/templates/deployment.yaml Outdated
@desmax74 desmax74 force-pushed the occ branch 3 times, most recently from b1a1eab to 49cc78e Compare May 12, 2026 07:52
@desmax74 desmax74 force-pushed the occ branch 4 times, most recently from bd39ddd to 5c4f03e Compare June 9, 2026 09:15
@desmax74

desmax74 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator Author

@sourcery-ai review

@sourcery-ai sourcery-ai 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.

Hey - I've found 13 issues, and left some high level feedback:

  • In templates/credentialrequest.yaml, the AWS/GCP branches assume .Values.cloudCredentials.aws.statementEntries or .Values.cloudCredentials.gcp.permissions are always present; consider guarding these with if checks or providing sane defaults so that setting cloudProvider without the corresponding cloudCredentials sub-structure does not cause a Helm render error.
  • The TRUSTD_S3_ACCESS_KEY/TRUSTD_S3_SECRET_KEY env var wiring in _storage.tpl always references aws_access_key_id/aws_secret_access_key from the CCO secret even when cloudProvider is gcp; if GCP CCO produces a different secret shape, you may need provider-specific key selection rather than hard‑coding AWS-style keys.
  • The new config/rbac/clusterrole.yaml defines a namespace on a ClusterRole, which is ignored for cluster-scoped resources; it would be clearer to drop the namespace field to avoid confusion about the scope of these permissions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `templates/credentialrequest.yaml`, the AWS/GCP branches assume `.Values.cloudCredentials.aws.statementEntries` or `.Values.cloudCredentials.gcp.permissions` are always present; consider guarding these with `if` checks or providing sane defaults so that setting `cloudProvider` without the corresponding `cloudCredentials` sub-structure does not cause a Helm render error.
- The `TRUSTD_S3_ACCESS_KEY`/`TRUSTD_S3_SECRET_KEY` env var wiring in `_storage.tpl` always references `aws_access_key_id`/`aws_secret_access_key` from the CCO secret even when `cloudProvider` is `gcp`; if GCP CCO produces a different secret shape, you may need provider-specific key selection rather than hard‑coding AWS-style keys.
- The new `config/rbac/clusterrole.yaml` defines a `namespace` on a `ClusterRole`, which is ignored for cluster-scoped resources; it would be clearer to drop the namespace field to avoid confusion about the scope of these permissions.

## Individual Comments

### Comment 1
<location path="test/integration/cco_rbac_test.go" line_range="110" />
<code_context>
+	assert.Equal(t, "cco-credentialsrequest-access", binding.RoleRef.Name, "roleRef name should match the CCO ClusterRole")
+}
+
+func TestCCOClusterRoleBindingSubjects(t *testing.T) {
+	bindingPath := filepath.Join("config", "rbac", "clusterrolebinding_cco.yaml")
+
</code_context>
<issue_to_address>
**suggestion (testing):** Make subject assertions robust to multiple or reordered subjects by checking all entries

This test currently assumes the first subject is the operator ServiceAccount, so added or reordered subjects could cause false failures or hide regressions. Instead, iterate over `binding.Subjects` and assert that at least one subject has the expected ServiceAccount name and kind (e.g., via a small helper like `any(binding.Subjects, matchesSA)`) so the test remains robust while still verifying the binding targets the correct ServiceAccount.

Suggested implementation:

```golang
func TestCCOClusterRoleBindingSubjects(t *testing.T) {
	bindingPath := filepath.Join("config", "rbac", "clusterrolebinding_cco.yaml")

	binding := loadClusterRoleBinding(t, bindingPath)

	expectedSAName := "cluster-cloud-controller-manager-operator"

	found := false
	for _, subject := range binding.Subjects {
		if subject.Kind == "ServiceAccount" && subject.Name == expectedSAName {
			found = true
			break
		}
	}

	assert.Truef(t, found, "expected at least one ServiceAccount subject named %q in ClusterRoleBinding %q", expectedSAName, binding.Metadata.Name)

```

1. Ensure there is a helper `loadClusterRoleBinding(t *testing.T, path string)` (or equivalent) that returns the parsed ClusterRoleBinding used by the other CCO RBAC tests. If the name differs, adjust the call accordingly.
2. If the expected ServiceAccount name is defined as a constant or used elsewhere in this test file, replace the hardcoded `"cluster-cloud-controller-manager-operator"` with that constant to keep things DRY and consistent.
3. Confirm that `binding.Metadata.Name` and `binding.Subjects` are the correct fields based on the struct type you are using (likely `rbacv1.ClusterRoleBinding` or a local wrapper); adjust field access if your type differs.
4. Make sure the necessary imports (`testing`, `path/filepath`, and `github.com/stretchr/testify/assert`) are present in the file’s import block.
</issue_to_address>

### Comment 2
<location path="extensions/README.md" line_range="9" />
<code_context>
+```console 
+oc get clusteroperator cloud-credential
+```
+it show somethig like
+
+```console
</code_context>
<issue_to_address>
**issue (typo):** Fix verb agreement and spelling in "it show somethig like".

Change to "it shows something like" to fix the verb form and spelling.

```suggestion
it shows something like
```
</issue_to_address>

### Comment 3
<location path="extensions/README.md" line_range="15" />
<code_context>
+NAME               VERSION   AVAILABLE   PROGRESSING   DEGRADED   SINCE
+cloud-credential   4.x.x     True        False         False      ...
+```
+Pod' status
+```console 
+oc get pods -n openshift-cloud-credential-operator
</code_context>
<issue_to_address>
**issue (typo):** Correct the apostrophe in "Pod' status".

Use "Pod status" or "Pods status" instead; the current apostrophe is incorrect.

```suggestion
Pod status
```
</issue_to_address>

### Comment 4
<location path="extensions/README.md" line_range="34-35" />
<code_context>
+oc get cloudcredential cluster -o yaml
+```
+On spec.credentialsMode will be the configured setting (Mint,
+Passthrough, Manual, o empty for default).
+
+## How the Operator interact with the CCO
</code_context>
<issue_to_address>
**issue (typo):** Replace "o" with "or" in the list of modes.

It should read: `Passthrough, Manual, or empty for default).`

```suggestion
On spec.credentialsMode will be the configured setting (Mint,
Passthrough, Manual, or empty for default).
```
</issue_to_address>

### Comment 5
<location path="extensions/README.md" line_range="37" />
<code_context>
+On spec.credentialsMode will be the configured setting (Mint,
+Passthrough, Manual, o empty for default).
+
+## How the Operator interact with the CCO
+
+1. Operator declare permissions needed in a CredentialsRequest CR in the namespace openshift-cloud-credential-operator
</code_context>
<issue_to_address>
**issue (typo):** Fix subject–verb agreement in the section title.

Please change the heading text to “How the Operator interacts with the CCO”.

```suggestion
## How the Operator interacts with the CCO
```
</issue_to_address>

### Comment 6
<location path="extensions/README.md" line_range="39" />
<code_context>
+
+## How the Operator interact with the CCO
+
+1. Operator declare permissions needed in a CredentialsRequest CR in the namespace openshift-cloud-credential-operator
+
+```console 
</code_context>
<issue_to_address>
**issue (typo):** Use "declares" instead of "declare" for subject–verb agreement.

Because "Operator" is singular, the verb form should be "declares."

Suggested implementation:

```
1. Operator declares permissions needed in a CredentialsRequest CR in the namespace openshift-cloud-credential-operator

```

If similar list items or descriptions elsewhere in this README use "Operator declare" with the singular "Operator," they should also be updated to "Operator declares" for consistent subject–verb agreement.
</issue_to_address>

### Comment 7
<location path="extensions/README.md" line_range="62" />
<code_context>
+          resource: "*"
+```
+
+2. CCO process the CR and create a  Kubernetes Secret with the cloud credentails 
+   in the  namespace specified in spec.secretRef.
+
</code_context>
<issue_to_address>
**issue (typo):** Fix verb form and spelling in "process" and "credentails".

Consider: “CCO processes the CR and creates a Kubernetes Secret with the cloud credentials …” to fix the verb agreement and spelling.

Suggested implementation:

```
2. CCO processes the CR and creates a Kubernetes Secret with the cloud credentials
   in the namespace specified in spec.secretRef.

```

If this same sentence or similar wording appears elsewhere in the documentation (e.g., in other language versions or duplicated sections), the same corrections to verb agreement (“processes”, “creates”) and spelling (“credentials”) should be applied there as well for consistency.
</issue_to_address>

### Comment 8
<location path="extensions/README.md" line_range="65-66" />
<code_context>
+2. CCO process the CR and create a  Kubernetes Secret with the cloud credentails 
+   in the  namespace specified in spec.secretRef.
+
+3. Operator reads the Secret and use the credentials to interact with the cloud API. 
+   Operator must tolerate the not immediate availability of the secret becasue take time to create.
+
+## How integrate the CCO with the Helm Chart Operator
</code_context>
<issue_to_address>
**issue (typo):** Improve verb agreement and fix the typo "becasue" in the description.

For example: “Operator reads the Secret and uses the credentials to interact with the cloud API. The Operator must tolerate the non-immediate availability of the Secret because it takes time to create.” This corrects the verb, spelling, and missing word.

```suggestion
3. Operator reads the Secret and uses the credentials to interact with the cloud API. 
   The Operator must tolerate the non-immediate availability of the Secret because it takes time to create.
```
</issue_to_address>

### Comment 9
<location path="extensions/README.md" line_range="68" />
<code_context>
+3. Operator reads the Secret and use the credentials to interact with the cloud API. 
+   Operator must tolerate the not immediate availability of the secret becasue take time to create.
+
+## How integrate the CCO with the Helm Chart Operator
+
+1. Define Credential Request in the chart
</code_context>
<issue_to_address>
**issue (typo):** Add "to" for correct phrasing in the section title.

Consider rephrasing the header as: "How to integrate the CCO with the Helm Chart Operator."

```suggestion
## How to integrate the CCO with the Helm Chart Operator
```
</issue_to_address>

### Comment 10
<location path="extensions/README.md" line_range="74-75" />
<code_context>
+2. 
+2. Configure Deployment to use the secret created by the CCO
+
+3. Handling the delay of the creation of the secret
+   first approact Init container
+```console
+ initContainers:
</code_context>
<issue_to_address>
**issue (typo):** Fix the typo "approact" and clarify the phrase about the first approach.

Consider correcting "approact" and rephrasing this line to something like "first approach: init container" for clarity.

```suggestion
3. Handling the delay of the creation of the secret
   first approach: init container
```
</issue_to_address>

### Comment 11
<location path="extensions/README.md" line_range="92" />
<code_context>
+```
+   second approach Retry in the Operator's code, but this is available on a full Go Operator
+
+4. Support different cloud provider with values configurations
+
+5. RBAC needed to create the CredentialsRequest
</code_context>
<issue_to_address>
**issue (typo):** Use the plural "providers" instead of "provider".

Because you mention “different” cloud vendors, this bullet should read: “Support different cloud providers with values configurations.”

```suggestion
4. Support different cloud providers with values configurations
```
</issue_to_address>

### Comment 12
<location path="extensions/README.md" line_range="94-95" />
<code_context>
+4. Support different cloud provider with values configurations
+
+5. RBAC needed to create the CredentialsRequest
+6. Supporto Manual Mode (STS/WIF) if the cluster use mode o STS, CCO didn't create the secret automatically.  
+   In STS mode the user must :
+   1. Extract CredentialsRequest from chart
</code_context>
<issue_to_address>
**issue (typo):** Fix multiple small language issues in the "Supporto Manual Mode" sentence.

Consider: “Support Manual Mode (STS/WIF): if the cluster uses STS mode, CCO doesn’t create the secret automatically.” This corrects “Supporto,” subject-verb agreement, “o” → “or,” and the tense of “didn’t.”

```suggestion
5. RBAC needed to create the CredentialsRequest
6. Support Manual Mode (STS/WIF): if the cluster uses STS mode, CCO doesn’t create the secret automatically.  
```
</issue_to_address>

### Comment 13
<location path="extensions/README.md" line_range="99" />
<code_context>
+   In STS mode the user must :
+   1. Extract CredentialsRequest from chart
+   2. Use ccoctl tool to generate the credentials
+   3. Create manually the secrets before install the chart
+
</code_context>
<issue_to_address>
**issue (typo):** Improve word order and verb form in "Create manually the secrets before install the chart".

Change this to: "Create the secrets manually before installing the chart."

```suggestion
   3. Create the secrets manually before installing the chart.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread test/integration/cco_rbac_test.go
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
Comment thread extensions/README.md Outdated
@desmax74 desmax74 force-pushed the occ branch 3 times, most recently from 191abed to 13fc004 Compare June 9, 2026 14:33
@desmax74

desmax74 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator Author

@sourcery-ai review

@sourcery-ai sourcery-ai 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.

Hey - I've found 7 issues, and left some high level feedback:

  • In credentialrequest.yaml, rendering assumes .Values.cloudCredentials.aws.statementEntries / .cloudCredentials.gcp.permissions are always set when cloudProvider is set; consider adding guards or defaults so the chart doesn’t fail to render if these subkeys are omitted or empty.
  • The _storage.tpl helper always references aws_access_key_id / aws_secret_access_key keys from the CCO secret even when cloudProvider is gcp; if GCP is intended to be supported, this template likely needs to branch on provider or use provider-agnostic keys.
  • There are multiple string constants for common field names (e.g., fieldMetrics, fieldStorage, etc.) spread across e2e and integration tests; consider centralizing these in a shared test helper to avoid divergence and make future refactors simpler.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `credentialrequest.yaml`, rendering assumes `.Values.cloudCredentials.aws.statementEntries` / `.cloudCredentials.gcp.permissions` are always set when `cloudProvider` is set; consider adding guards or defaults so the chart doesn’t fail to render if these subkeys are omitted or empty.
- The `_storage.tpl` helper always references `aws_access_key_id` / `aws_secret_access_key` keys from the CCO secret even when `cloudProvider` is `gcp`; if GCP is intended to be supported, this template likely needs to branch on provider or use provider-agnostic keys.
- There are multiple string constants for common field names (e.g., `fieldMetrics`, `fieldStorage`, etc.) spread across e2e and integration tests; consider centralizing these in a shared test helper to avoid divergence and make future refactors simpler.

## Individual Comments

### Comment 1
<location path="go.mod" line_range="3" />
<code_context>
 module github.com/trustification/trusted-profile-analyzer-operator

-go 1.25.0
+go 1.26

 require (
</code_context>
<issue_to_address>
**issue (bug_risk):** Targeting Go 1.26 is likely invalid and will break builds with current toolchains

Since Go 1.26 is not released yet, `go 1.26` will make `go` tool invocations (including CI) fail with an `unknown major version` error. Please instead target the latest stable version supported by your toolchain and pipeline (e.g. a current 1.22.x).
</issue_to_address>

### Comment 2
<location path="config/rbac/clusterrole.yaml" line_range="2-5" />
<code_context>
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: cco-credentialsrequest-access
+  namespace: placeholder
+rules:
+  - apiGroups: ["cloudcredential.openshift.io"]
</code_context>
<issue_to_address>
**nitpick:** ClusterRole resources are cluster-scoped and shouldn't include a namespace field

Kubernetes will ignore this field on a ClusterRole, but it can still mislead readers and trigger warnings in tooling. Please remove the namespace field from this manifest (and any other cluster-scoped RBAC resources) to keep it clean and avoid unnecessary validation noise.
</issue_to_address>

### Comment 3
<location path="test/integration/helm_chart_test.go" line_range="182" />
<code_context>
 	assert.NoError(t, err, ".helmignore should exist")
 }
+
+func TestValuesSchemaContainsCCOFields(t *testing.T) {
+	schemaPath := filepath.Join("helm-charts", "redhat-trusted-profile-analyzer", "values.schema.json")
+
</code_context>
<issue_to_address>
**suggestion (testing):** Extend schema test to cover ccoMode/cloudCredentials structure and constraints, not just presence

Right now this only checks the new top-level CCO fields and the `cloudProvider` enum. To better guard against regressions, please also assert:
- `ccoMode`’s type (and allowed values if it’s an enum/oneOf).
- That `cloudCredentials` is an object and, for each provider (e.g. `aws`, `gcp`), the expected sub-structure exists (e.g. `statementEntries`/`permissions` types).
- Any key required vs optional constraints needed for backwards compatibility.

This will ensure the test fails if the schema shape (types/enums/required fields) changes, not only when properties are removed.

Suggested implementation:

```golang
func TestValuesSchemaContainsCCOFields(t *testing.T) {
	schemaPath := filepath.Join("helm-charts", "redhat-trusted-profile-analyzer", "values.schema.json")

	data, err := os.ReadFile(schemaPath)
	require.NoError(t, err, "values.schema.json should be readable")

	var schema map[string]interface{}
	err = yaml.Unmarshal(data, &schema)
	require.NoError(t, err, "values.schema.json should be valid JSON")

	// top-level properties
	props, ok := schema["properties"].(map[string]interface{})
	require.True(t, ok, "schema.properties should be an object")

	// ccoMode field: type + enum (if present)
	ccoModeSchema, ok := props["ccoMode"].(map[string]interface{})
	require.True(t, ok, "schema.properties.ccoMode should be an object")

	ccoModeType, ok := ccoModeSchema["type"].(string)
	require.True(t, ok, "schema.properties.ccoMode.type should be a string")
	assert.Equal(t, "string", ccoModeType, "ccoMode should be of type string")

	if enumRaw, hasEnum := ccoModeSchema["enum"]; hasEnum {
		enumValues, ok := enumRaw.([]interface{})
		require.True(t, ok, "schema.properties.ccoMode.enum should be an array")
		require.NotEmpty(t, enumValues, "schema.properties.ccoMode.enum should not be empty")

		// If specific enums are defined, we assert the known values for compatibility.
		// Adjust this list if the schema defines a different set of allowed modes.
		expectedModes := map[string]struct{}{
			"manual":      {},
			"managed":     {},
			"mint":        {},
			"passthrough": {},
			"disabled":    {},
		}

		for _, v := range enumValues {
			s, ok := v.(string)
			require.True(t, ok, "ccoMode enum values should be strings")
			_, known := expectedModes[s]
			assert.Truef(t, known, "unexpected ccoMode enum value %q", s)
		}
	}

	// cloudCredentials object and provider-specific structure
	cloudCredsSchema, ok := props["cloudCredentials"].(map[string]interface{})
	require.True(t, ok, "schema.properties.cloudCredentials should be an object")

	cloudCredsType, ok := cloudCredsSchema["type"].(string)
	require.True(t, ok, "schema.properties.cloudCredentials.type should be a string")
	assert.Equal(t, "object", cloudCredsType, "cloudCredentials should be of type object")

	cloudCredsProps, ok := cloudCredsSchema["properties"].(map[string]interface{})
	require.True(t, ok, "schema.properties.cloudCredentials.properties should be an object")

	// AWS structure: cloudCredentials.aws.statementEntries
	if awsRaw, hasAWS := cloudCredsProps["aws"]; hasAWS {
		awsSchema, ok := awsRaw.(map[string]interface{})
		require.True(t, ok, "cloudCredentials.aws should be an object")

		awsProps, ok := awsSchema["properties"].(map[string]interface{})
		require.True(t, ok, "cloudCredentials.aws.properties should be an object")

		statementEntriesSchemaRaw, hasStatementEntries := awsProps["statementEntries"]
		require.True(t, hasStatementEntries, "cloudCredentials.aws.properties.statementEntries should exist")

		statementEntriesSchema, ok := statementEntriesSchemaRaw.(map[string]interface{})
		require.True(t, ok, "cloudCredentials.aws.properties.statementEntries should be an object")

		statementEntriesType, ok := statementEntriesSchema["type"].(string)
		require.True(t, ok, "cloudCredentials.aws.properties.statementEntries.type should be a string")
		assert.Equal(t, "array", statementEntriesType, "cloudCredentials.aws.statementEntries should be an array")

		if itemsRaw, hasItems := statementEntriesSchema["items"]; hasItems {
			itemsSchema, ok := itemsRaw.(map[string]interface{})
			require.True(t, ok, "cloudCredentials.aws.statementEntries.items should be an object")

			// We expect each statement entry to be an object.
			itemType, hasItemType := itemsSchema["type"].(string)
			if hasItemType {
				assert.Equal(t, "object", itemType, "cloudCredentials.aws.statementEntries.items.type should be object")
			}
		}

		// Check required keys for AWS if defined to ensure backward compatibility.
		if requiredRaw, hasRequired := awsSchema["required"]; hasRequired {
			requiredFields, ok := requiredRaw.([]interface{})
			require.True(t, ok, "cloudCredentials.aws.required should be an array")
			require.NotEmpty(t, requiredFields, "cloudCredentials.aws.required should not be empty")
		}
	}

	// GCP structure: cloudCredentials.gcp.permissions
	if gcpRaw, hasGCP := cloudCredsProps["gcp"]; hasGCP {
		gcpSchema, ok := gcpRaw.(map[string]interface{})
		require.True(t, ok, "cloudCredentials.gcp should be an object")

		gcpProps, ok := gcpSchema["properties"].(map[string]interface{})
		require.True(t, ok, "cloudCredentials.gcp.properties should be an object")

		permissionsSchemaRaw, hasPermissions := gcpProps["permissions"]
		require.True(t, hasPermissions, "cloudCredentials.gcp.properties.permissions should exist")

		permissionsSchema, ok := permissionsSchemaRaw.(map[string]interface{})
		require.True(t, ok, "cloudCredentials.gcp.properties.permissions should be an object")

		permissionsType, ok := permissionsSchema["type"].(string)
		require.True(t, ok, "cloudCredentials.gcp.properties.permissions.type should be a string")
		assert.Equal(t, "array", permissionsType, "cloudCredentials.gcp.permissions should be an array")

		if itemsRaw, hasItems := permissionsSchema["items"]; hasItems {
			itemsSchema, ok := itemsRaw.(map[string]interface{})
			require.True(t, ok, "cloudCredentials.gcp.permissions.items should be an object")

			itemType, hasItemType := itemsSchema["type"].(string)
			if hasItemType {
				assert.Equal(t, "string", itemType, "cloudCredentials.gcp.permissions.items.type should be string")
			}
		}

		// Check required keys for GCP if defined.
		if requiredRaw, hasRequired := gcpSchema["required"]; hasRequired {
			requiredFields, ok := requiredRaw.([]interface{})
			require.True(t, ok, "cloudCredentials.gcp.required should be an array")
			require.NotEmpty(t, requiredFields, "cloudCredentials.gcp.required should not be empty")
		}
	}

	// cloudCredentials required vs optional constraints:
	// If the schema defines a required array for cloudCredentials, assert it contains
	// the keys we rely on for backwards compatibility.
	if requiredRaw, hasRequired := cloudCredsSchema["required"]; hasRequired {
		requiredFields, ok := requiredRaw.([]interface{})
		require.True(t, ok, "cloudCredentials.required should be an array")

		requiredSet := map[string]struct{}{}
		for _, r := range requiredFields {
			s, ok := r.(string)
			require.True(t, ok, "cloudCredentials.required entries should be strings")
			requiredSet[s] = struct{}{}
		}

		// Adjust these expectations if specific providers must/must-not be required.
		// For now, we only assert that required is not empty if it exists.
		require.NotEmpty(t, requiredSet, "cloudCredentials.required should not be empty when present")
	}
}

```

1. The exact `ccoMode` enum values and the provider-specific field names (`statementEntries`, `permissions`, etc.) may differ from what is implemented in your `values.schema.json`. You should align the `expectedModes` map and the nested property names/types with the actual schema.
2. If additional cloud providers (e.g. `azure`) are present in `cloudCredentials.properties`, you may want to extend this test with similar structural assertions for those providers.
3. If your schema intentionally does not define `required` arrays for some objects, you can relax or remove the `require.NotEmpty` assertions for those scopes to better reflect the intended optionality.
</issue_to_address>

### Comment 4
<location path="test/e2e/cco_helm_rendering_test.go" line_range="243" />
<code_context>
+	assert.Nil(t, cr, "CredentialsRequest should NOT be rendered without cloudProvider")
+}
+
+func TestHelmRenderStorageEnvVarsWithCCO(t *testing.T) {
+	if testing.Short() {
+		t.Skip(skipE2ETest)
</code_context>
<issue_to_address>
**suggestion (testing):** Add a GCP variant of the storage env var test to ensure CCO-managed GCP credentials are wired correctly when supported

Since this PR mentions both AWS and GCP CCO flows, please either add a corresponding GCP-focused test (using `gcpValues()` or similar with CCO enabled and asserting the expected GCP credential secrets in the `Deployment`), or add a brief comment here clarifying that env var wiring is intentionally limited to AWS only.

Suggested implementation:

```golang
	metadata, _ := cr[fieldMetadata].(map[string]interface{})

func TestHelmRenderStorageEnvVarsWithCCOGCP(t *testing.T) {
	if testing.Short() {
		t.Skip(skipE2ETest)
	}

	chartPath := getChartPath(t)
	rendered := renderHelmChart(t, chartPath, gcpValues())
	docs := splitYAMLDocs(rendered)

	deploy := findDocByKindAndName(t, docs, kindDeployment, "observatorium-operator")
	require.NotNil(t, deploy, "Deployment should be rendered for GCP")

	spec, _ := deploy["spec"].(map[string]interface{})
	template, _ := spec["template"].(map[string]interface{})
	podSpec, _ := template["spec"].(map[string]interface{})
	containers, _ := podSpec["containers"].([]interface{})
	require.NotEmpty(t, containers, "Deployment should have containers")

	container, _ := containers[0].(map[string]interface{})
	envVars, _ := container["env"].([]interface{})

	// Assert that the expected CCO-managed GCP credentials secret is wired into the env vars.
	require.Condition(t, func() bool {
		for _, e := range envVars {
			env, _ := e.(map[string]interface{})
			name, _ := env["name"].(string)
			if name == "GOOGLE_APPLICATION_CREDENTIALS" {
				valueFrom, _ := env["valueFrom"].(map[string]interface{})
				secretRef, _ := valueFrom["secretKeyRef"].(map[string]interface{})
				secretName, _ := secretRef["name"].(string)
				secretKey, _ := secretRef["key"].(string)
				return secretName != "" && secretKey != ""
			}
		}
		return false
	}, "GCP CCO-managed credentials env var should be present")
}

```

To fully integrate this test with the existing suite, you should:
1. Ensure `gcpValues()` enables the CCO-managed GCP credentials flow in the same way that `awsValues()` enables the AWS CCO flow (e.g. setting the corresponding `cco`/`cloudProvider` values).
2. Confirm that `findDocByKindAndName` exists and matches the helper signature used elsewhere; if not, introduce a helper similar to `findDocByKind` that also filters on the `metadata.name` field.
3. Optionally, refine the `GOOGLE_APPLICATION_CREDENTIALS` env var name and expected secret name/key to exactly match what the Helm chart renders for CCO-managed GCP credentials.
</issue_to_address>

### Comment 5
<location path="test/e2e/cco_helm_rendering_test.go" line_range="312" />
<code_context>
+	t.Fatal("server deployment not found in rendered output")
+}
+
+func TestHelmRenderStorageEnvVarsWithoutCCO(t *testing.T) {
+	if testing.Short() {
+		t.Skip(skipE2ETest)
</code_context>
<issue_to_address>
**nitpick:** Use the ccoSecretName constant instead of a hardcoded substring when asserting no CCO secret references

The assertion currently checks for the substring "cloud-creds" while the constant is `ccoSecretName = "test-release-cloud-creds"`. To avoid divergence and keep the test resilient to naming changes, assert against `ccoSecretName` directly (e.g. `assert.NotContains(t, rendered, ccoSecretName, ...)`).
</issue_to_address>

### Comment 6
<location path="extensions/README.md" line_range="52-54" />
<code_context>
+      namespace: my-operator-namespace
+    providerSpec:
+      apiVersion: cloudcredential.openshift.io/v1
+      kind: AWSProviderSpec   # esempio per AWS
+      statementEntries:
+        - effect: Allow
</code_context>
<issue_to_address>
**suggestion (typo):** Use an English comment instead of "esempio" for consistency.

Specifically, update `# esempio per AWS` to an English version such as `# example for AWS` to match the rest of the documentation.

```suggestion
      apiVersion: cloudcredential.openshift.io/v1
      kind: AWSProviderSpec   # example for AWS
      statementEntries:
```
</issue_to_address>

### Comment 7
<location path="extensions/README.md" line_range="89-90" />
<code_context>
+            sleep 5
+          done
+```
+   second approach Retry in the Operator's code, but this is available on a full Go Operator
+
+4. Support different cloud providers with values configurations
</code_context>
<issue_to_address>
**suggestion (typo):** Clarify and fix grammar in the description of the second approach.

For example: "Second approach: retry in the Operator's code, but this is only available for a full Go Operator."

```suggestion
```
   Second approach: retry in the Operator's code, but this is only available for a full Go Operator.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread go.mod
Comment thread config/rbac/clusterrole.yaml Outdated
Comment thread test/integration/helm_chart_test.go
Comment thread test/e2e/cco_helm_rendering_test.go
Comment thread test/e2e/cco_helm_rendering_test.go
Comment thread extensions/README.md
Comment thread extensions/README.md
@desmax74 desmax74 force-pushed the occ branch 3 times, most recently from d511877 to 8c9d943 Compare June 22, 2026 12:45
@desmax74 desmax74 changed the title Tokenize Credential Request Tokenize Credential Request AWS Jun 22, 2026
@desmax74 desmax74 marked this pull request as ready for review June 22, 2026 17:19

@sourcery-ai sourcery-ai 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.

Hey - I've found 2 issues, and left some high level feedback:

  • The _storage.tpl helper always wires TRUSTD_S3_ACCESS_KEY/TRUSTD_S3_SECRET_KEY to aws_access_key_id / aws_secret_access_key, which looks AWS-specific; when cloudProvider: gcp is used the generated secret will likely have different keys, so the template should branch on provider and use provider-appropriate secret keys.
  • In templates/credentialrequest.yaml the template assumes .Values.cloudCredentials.aws / .Values.cloudCredentials.gcp are populated whenever cloudProvider is set; consider adding guards or defaults so the chart either fails fast with a clear message or omits the CredentialsRequest when these blocks are missing, instead of rendering incomplete specs.
  • The CCO helper _cco.tpl only emits manual-mode env vars for AWS; if cloudProvider: gcp is set with ccoMode: manual the chart will be in an unsupported/partially configured state, so you may want to either explicitly reject that combination or add a GCP manual-mode branch.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_storage.tpl` helper always wires `TRUSTD_S3_ACCESS_KEY`/`TRUSTD_S3_SECRET_KEY` to `aws_access_key_id` / `aws_secret_access_key`, which looks AWS-specific; when `cloudProvider: gcp` is used the generated secret will likely have different keys, so the template should branch on provider and use provider-appropriate secret keys.
- In `templates/credentialrequest.yaml` the template assumes `.Values.cloudCredentials.aws` / `.Values.cloudCredentials.gcp` are populated whenever `cloudProvider` is set; consider adding guards or defaults so the chart either fails fast with a clear message or omits the `CredentialsRequest` when these blocks are missing, instead of rendering incomplete specs.
- The CCO helper `_cco.tpl` only emits manual-mode env vars for AWS; if `cloudProvider: gcp` is set with `ccoMode: manual` the chart will be in an unsupported/partially configured state, so you may want to either explicitly reject that combination or add a GCP manual-mode branch.

## Individual Comments

### Comment 1
<location path="test/e2e/cco_helm_rendering_test.go" line_range="227-236" />
<code_context>
+func TestHelmRenderGCPCredentialsRequest(t *testing.T) {
</code_context>
<issue_to_address>
**suggestion (testing):** Add GCP-specific tests for how the generated secret is consumed by Deployments

Right now this only checks that the GCP `CredentialsRequest` has the expected provider spec. Unlike the AWS tests, it doesn’t verify that the resulting secret is actually used by the Deployments.

To align with the AWS coverage, please add tests that:
- Render the chart with `cloudProvider = gcp` and valid `cloudCredentials.gcp`.
- Find the relevant `Deployment` manifests (server/importer) in the rendered output.
- Assert that the env vars / volume mounts for storage credentials reference the secret name and keys created by the GCP `CredentialsRequest`.

That will validate the full GCP wiring, not just the CR generation.

Suggested implementation:

```golang
func TestHelmRenderGCPCredentialsRequest(t *testing.T) {
	if testing.Short() {
		t.Skip(skipE2ETest)
	}

	chartPath := getChartPath(t)
	rendered := renderHelmChart(t, chartPath, gcpValues())
	docs := splitYAMLDocs(rendered)

	cr := findCredentialsRequest(t, docs)
	require.NotNil(t, cr, "CredentialsRequest should be rendered for GCP")

	// Extract the secret name and keys from the GCP CredentialsRequest so we can
	// assert that the Deployments are wired up to use it.
	secretName, secretKeyClientEmail, secretKeyPrivateKey := extractGCPCredentialsRequestSecretRef(t, cr)

	// Verify that the server Deployment consumes the generated credentials secret
	serverDeployment := findDeployment(t, docs, "server") // align name with AWS tests / chart
	require.NotNil(t, serverDeployment, "server Deployment should be rendered for GCP")

	assertDeploymentUsesGCPCredentialsSecret(
		t,
		serverDeployment,
		secretName,
		secretKeyClientEmail,
		secretKeyPrivateKey,
	)

	// Verify that the importer Deployment (if rendered for GCP) also consumes the same secret.
	// If the importer is not rendered for GCP in this chart, this assertion can be relaxed
	// or the test can be updated to match the actual behavior.
	importerDeployment := findDeployment(t, docs, "importer") // align name with AWS tests / chart
	if importerDeployment != nil {
		assertDeploymentUsesGCPCredentialsSecret(
			t,
			importerDeployment,
			secretName,
			secretKeyClientEmail,
			secretKeyPrivateKey,
		)
	}
}

// extractGCPCredentialsRequestSecretRef extracts the secret name and the relevant
// key names from the GCP CredentialsRequest. This mirrors the logic used by the
// AWS tests to keep provider coverage aligned.
func extractGCPCredentialsRequestSecretRef(t *testing.T, cr map[string]interface{}) (secretName, keyClientEmail, keyPrivateKey string) {
	spec, ok := cr["spec"].(map[string]interface{})
	require.True(t, ok, "CredentialsRequest.spec should be a map")

	secretRef, ok := spec["secretRef"].(map[string]interface{})
	require.True(t, ok, "CredentialsRequest.spec.secretRef should be a map")

	name, ok := secretRef["name"].(string)
	require.True(t, ok, "CredentialsRequest.spec.secretRef.name should be a string")

	// The keys below should match what the GCP provider/ccoctl expects to write
	// into the generated Secret for workload identity credentials.
	keyClientEmail = "service_account_email"
	keyPrivateKey = "service_account_key"

	return name, keyClientEmail, keyPrivateKey
}

// assertDeploymentUsesGCPCredentialsSecret validates that the given Deployment
// has env vars / volume mounts wired to the provided credentials Secret.
// This is the GCP equivalent of the AWS wiring tests, ensuring end-to-end coverage
// from CredentialsRequest -> Secret -> Deployment.
func assertDeploymentUsesGCPCredentialsSecret(
	t *testing.T,
	deployment map[string]interface{},
	secretName string,
	keyClientEmail string,
	keyPrivateKey string,
) {
	containers := getDeploymentContainers(t, deployment)

	// Depending on how the chart is structured, credentials may be passed as
	// env vars, volume mounts, or both. The assertions below assume env vars
	// similar to the AWS tests and should be aligned with the actual chart.
	for _, c := range containers {
		env := getContainerEnv(t, c)

		// Assert env vars sourcing from the credentials Secret.
		assertEnvVarFromSecret(t, env, "GCP_SERVICE_ACCOUNT_EMAIL", secretName, keyClientEmail)
		assertEnvVarFromSecret(t, env, "GCP_SERVICE_ACCOUNT_KEY", secretName, keyPrivateKey)
	}
}

```

The snippet above assumes the existence of several helper functions that likely already exist in this file for the AWS tests (or in shared test utilities):

1. `findDeployment(t, docs, name string) map[string]interface{}` – used in AWS tests to locate a Deployment by name in the rendered YAML.
2. `getDeploymentContainers(t, deployment map[string]interface{}) []map[string]interface{}` – extracts the `.spec.template.spec.containers` list from a Deployment.
3. `getContainerEnv(t, container map[string]interface{}) []map[string]interface{}` – extracts the `.env` list from a container spec.
4. `assertEnvVarFromSecret(t, env []map[string]interface{}, envName, secretName, secretKey string)` – asserts that an env var with the given `envName` exists and references `secretName` / `secretKey` via `valueFrom.secretKeyRef`.

To fully integrate this change and align it with the existing AWS coverage, you should:

1. Reuse the actual helper names already present in the AWS tests instead of `findDeployment`, `getDeploymentContainers`, `getContainerEnv`, and `assertEnvVarFromSecret` if they differ.
2. Update the Deployment identifiers `"server"` and `"importer"` to match the real chart’s Deployment names used in the AWS tests (for example, `"rhtap-operator-server"` or similar).
3. Adjust the secret key names (`"service_account_email"` and `"service_account_key"`) and env var names (`"GCP_SERVICE_ACCOUNT_EMAIL"`, `"GCP_SERVICE_ACCOUNT_KEY"`) to match the actual keys and environment variable names used by the GCP storage configuration in your Helm chart.
4. If the importer Deployment is not rendered for GCP, either:
   - Remove the importer-related section, or
   - Change the conditional logic to match the chart behavior (e.g. assert it is `nil` and document that only the server uses the credentials).

Once these are aligned with the existing AWS tests and chart conventions, the GCP tests will validate the full wiring from the `CredentialsRequest` through to the Deployments’ consumption of the generated Secret.
</issue_to_address>

### Comment 2
<location path="extensions/README.md" line_range="20" />
<code_context>
+oc get pods -n openshift-cloud-credential-operator
+```
+
+Credential requests checks
+```console 
+oc get credentialsrequests -n openshift-cloud-credential-operator
</code_context>
<issue_to_address>
**suggestion (typo):** Rephrase this heading for clearer grammar (singular vs plural).

The phrase "Credential requests checks" is awkward. Consider "CredentialRequest checks" or "Credential request checks" for clearer, grammatically correct wording.

```suggestion
CredentialRequest checks
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +227 to +236
func TestHelmRenderGCPCredentialsRequest(t *testing.T) {
if testing.Short() {
t.Skip(skipE2ETest)
}

chartPath := getChartPath(t)
rendered := renderHelmChart(t, chartPath, gcpValues())
docs := splitYAMLDocs(rendered)

cr := findCredentialsRequest(t, docs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add GCP-specific tests for how the generated secret is consumed by Deployments

Right now this only checks that the GCP CredentialsRequest has the expected provider spec. Unlike the AWS tests, it doesn’t verify that the resulting secret is actually used by the Deployments.

To align with the AWS coverage, please add tests that:

  • Render the chart with cloudProvider = gcp and valid cloudCredentials.gcp.
  • Find the relevant Deployment manifests (server/importer) in the rendered output.
  • Assert that the env vars / volume mounts for storage credentials reference the secret name and keys created by the GCP CredentialsRequest.

That will validate the full GCP wiring, not just the CR generation.

Suggested implementation:

func TestHelmRenderGCPCredentialsRequest(t *testing.T) {
	if testing.Short() {
		t.Skip(skipE2ETest)
	}

	chartPath := getChartPath(t)
	rendered := renderHelmChart(t, chartPath, gcpValues())
	docs := splitYAMLDocs(rendered)

	cr := findCredentialsRequest(t, docs)
	require.NotNil(t, cr, "CredentialsRequest should be rendered for GCP")

	// Extract the secret name and keys from the GCP CredentialsRequest so we can
	// assert that the Deployments are wired up to use it.
	secretName, secretKeyClientEmail, secretKeyPrivateKey := extractGCPCredentialsRequestSecretRef(t, cr)

	// Verify that the server Deployment consumes the generated credentials secret
	serverDeployment := findDeployment(t, docs, "server") // align name with AWS tests / chart
	require.NotNil(t, serverDeployment, "server Deployment should be rendered for GCP")

	assertDeploymentUsesGCPCredentialsSecret(
		t,
		serverDeployment,
		secretName,
		secretKeyClientEmail,
		secretKeyPrivateKey,
	)

	// Verify that the importer Deployment (if rendered for GCP) also consumes the same secret.
	// If the importer is not rendered for GCP in this chart, this assertion can be relaxed
	// or the test can be updated to match the actual behavior.
	importerDeployment := findDeployment(t, docs, "importer") // align name with AWS tests / chart
	if importerDeployment != nil {
		assertDeploymentUsesGCPCredentialsSecret(
			t,
			importerDeployment,
			secretName,
			secretKeyClientEmail,
			secretKeyPrivateKey,
		)
	}
}

// extractGCPCredentialsRequestSecretRef extracts the secret name and the relevant
// key names from the GCP CredentialsRequest. This mirrors the logic used by the
// AWS tests to keep provider coverage aligned.
func extractGCPCredentialsRequestSecretRef(t *testing.T, cr map[string]interface{}) (secretName, keyClientEmail, keyPrivateKey string) {
	spec, ok := cr["spec"].(map[string]interface{})
	require.True(t, ok, "CredentialsRequest.spec should be a map")

	secretRef, ok := spec["secretRef"].(map[string]interface{})
	require.True(t, ok, "CredentialsRequest.spec.secretRef should be a map")

	name, ok := secretRef["name"].(string)
	require.True(t, ok, "CredentialsRequest.spec.secretRef.name should be a string")

	// The keys below should match what the GCP provider/ccoctl expects to write
	// into the generated Secret for workload identity credentials.
	keyClientEmail = "service_account_email"
	keyPrivateKey = "service_account_key"

	return name, keyClientEmail, keyPrivateKey
}

// assertDeploymentUsesGCPCredentialsSecret validates that the given Deployment
// has env vars / volume mounts wired to the provided credentials Secret.
// This is the GCP equivalent of the AWS wiring tests, ensuring end-to-end coverage
// from CredentialsRequest -> Secret -> Deployment.
func assertDeploymentUsesGCPCredentialsSecret(
	t *testing.T,
	deployment map[string]interface{},
	secretName string,
	keyClientEmail string,
	keyPrivateKey string,
) {
	containers := getDeploymentContainers(t, deployment)

	// Depending on how the chart is structured, credentials may be passed as
	// env vars, volume mounts, or both. The assertions below assume env vars
	// similar to the AWS tests and should be aligned with the actual chart.
	for _, c := range containers {
		env := getContainerEnv(t, c)

		// Assert env vars sourcing from the credentials Secret.
		assertEnvVarFromSecret(t, env, "GCP_SERVICE_ACCOUNT_EMAIL", secretName, keyClientEmail)
		assertEnvVarFromSecret(t, env, "GCP_SERVICE_ACCOUNT_KEY", secretName, keyPrivateKey)
	}
}

The snippet above assumes the existence of several helper functions that likely already exist in this file for the AWS tests (or in shared test utilities):

  1. findDeployment(t, docs, name string) map[string]interface{} – used in AWS tests to locate a Deployment by name in the rendered YAML.
  2. getDeploymentContainers(t, deployment map[string]interface{}) []map[string]interface{} – extracts the .spec.template.spec.containers list from a Deployment.
  3. getContainerEnv(t, container map[string]interface{}) []map[string]interface{} – extracts the .env list from a container spec.
  4. assertEnvVarFromSecret(t, env []map[string]interface{}, envName, secretName, secretKey string) – asserts that an env var with the given envName exists and references secretName / secretKey via valueFrom.secretKeyRef.

To fully integrate this change and align it with the existing AWS coverage, you should:

  1. Reuse the actual helper names already present in the AWS tests instead of findDeployment, getDeploymentContainers, getContainerEnv, and assertEnvVarFromSecret if they differ.
  2. Update the Deployment identifiers "server" and "importer" to match the real chart’s Deployment names used in the AWS tests (for example, "rhtap-operator-server" or similar).
  3. Adjust the secret key names ("service_account_email" and "service_account_key") and env var names ("GCP_SERVICE_ACCOUNT_EMAIL", "GCP_SERVICE_ACCOUNT_KEY") to match the actual keys and environment variable names used by the GCP storage configuration in your Helm chart.
  4. If the importer Deployment is not rendered for GCP, either:
    • Remove the importer-related section, or
    • Change the conditional logic to match the chart behavior (e.g. assert it is nil and document that only the server uses the credentials).

Once these are aligned with the existing AWS tests and chart conventions, the GCP tests will validate the full wiring from the CredentialsRequest through to the Deployments’ consumption of the generated Secret.

Comment thread extensions/README.md
oc get pods -n openshift-cloud-credential-operator
```

Credential requests checks

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (typo): Rephrase this heading for clearer grammar (singular vs plural).

The phrase "Credential requests checks" is awkward. Consider "CredentialRequest checks" or "Credential request checks" for clearer, grammatically correct wording.

Suggested change
Credential requests checks
CredentialRequest checks

Signed-off-by: desmax74 <mdessi@redhat.com>
desmax74 added 2 commits June 23, 2026 12:00
Signed-off-by: desmax74 <mdessi@redhat.com>
Signed-off-by: desmax74 <mdessi@redhat.com>
@desmax74

Copy link
Copy Markdown
Collaborator Author

@sourcery-ai review

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="test/integration/cco_rbac_test.go" line_range="111" />
<code_context>
+	assert.Equal(t, "cco-credentialsrequest-access", binding.RoleRef.Name, "roleRef name should match the CCO ClusterRole")
+}
+
+func TestCCOClusterRoleBindingSubjects(t *testing.T) {
+	bindingPath := filepath.Join("config", "rbac", "clusterrolebinding_cco.yaml")
+
</code_context>
<issue_to_address>
**suggestion (testing):** Make subject assertions resilient to ordering by matching the desired ServiceAccount rather than indexing the first subject.

This test currently assumes the desired ServiceAccount is always at `binding.Subjects[0]`, so it will break if subjects are reordered or new ones are added.

Instead, iterate over `binding.Subjects` and assert that at least one subject has `Kind == "ServiceAccount"` and `Name == "rhtpa-operator-controller-manager"` (and the expected namespace, if applicable). This keeps the test focused on verifying that the operator’s ServiceAccount is bound without relying on subject ordering.

Suggested implementation:

```golang
func TestCCOClusterRoleBindingSubjects(t *testing.T) {
	bindingPath := filepath.Join("config", "rbac", "clusterrolebinding_cco.yaml")

	data, err := os.ReadFile(bindingPath)
	require.NoError(t, err, "should be able to read CCO ClusterRoleBinding manifest")

	var binding rbacv1.ClusterRoleBinding
	err = yaml.Unmarshal(data, &binding)
	require.NoError(t, err, "should be able to unmarshal CCO ClusterRoleBinding")

	foundOperatorSA := false
	for _, subject := range binding.Subjects {
		if subject.Kind == "ServiceAccount" &&
			subject.Name == "rhtpa-operator-controller-manager" {
			foundOperatorSA = true
			break
		}
	}

	assert.True(t, foundOperatorSA,
		"ClusterRoleBinding should bind the operator ServiceAccount 'rhtpa-operator-controller-manager'")

```

To compile and run this test, ensure the following:
1. The necessary imports are present at the top of `test/integration/cco_rbac_test.go`:
   - `os` from the standard library
   - `k8s.io/api/rbac/v1` as `rbacv1`
   - `sigs.k8s.io/yaml` (or the YAML package you use elsewhere in this file)
   - `github.com/stretchr/testify/assert` and `github.com/stretchr/testify/require` if not already imported.
2. If the file already has a helper for loading `ClusterRoleBinding` YAML (e.g., `loadClusterRoleBinding(t, path)`), you may want to replace the explicit `os.ReadFile`/`yaml.Unmarshal` block with a call to that helper for consistency.
3. If the subject’s namespace should also be asserted, extend the `if` condition inside the loop to include `subject.Namespace == "<expected-namespace>"` and update the failure message accordingly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

assert.Equal(t, "cco-credentialsrequest-access", binding.RoleRef.Name, "roleRef name should match the CCO ClusterRole")
}

func TestCCOClusterRoleBindingSubjects(t *testing.T) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Make subject assertions resilient to ordering by matching the desired ServiceAccount rather than indexing the first subject.

This test currently assumes the desired ServiceAccount is always at binding.Subjects[0], so it will break if subjects are reordered or new ones are added.

Instead, iterate over binding.Subjects and assert that at least one subject has Kind == "ServiceAccount" and Name == "rhtpa-operator-controller-manager" (and the expected namespace, if applicable). This keeps the test focused on verifying that the operator’s ServiceAccount is bound without relying on subject ordering.

Suggested implementation:

func TestCCOClusterRoleBindingSubjects(t *testing.T) {
	bindingPath := filepath.Join("config", "rbac", "clusterrolebinding_cco.yaml")

	data, err := os.ReadFile(bindingPath)
	require.NoError(t, err, "should be able to read CCO ClusterRoleBinding manifest")

	var binding rbacv1.ClusterRoleBinding
	err = yaml.Unmarshal(data, &binding)
	require.NoError(t, err, "should be able to unmarshal CCO ClusterRoleBinding")

	foundOperatorSA := false
	for _, subject := range binding.Subjects {
		if subject.Kind == "ServiceAccount" &&
			subject.Name == "rhtpa-operator-controller-manager" {
			foundOperatorSA = true
			break
		}
	}

	assert.True(t, foundOperatorSA,
		"ClusterRoleBinding should bind the operator ServiceAccount 'rhtpa-operator-controller-manager'")

To compile and run this test, ensure the following:

  1. The necessary imports are present at the top of test/integration/cco_rbac_test.go:
    • os from the standard library
    • k8s.io/api/rbac/v1 as rbacv1
    • sigs.k8s.io/yaml (or the YAML package you use elsewhere in this file)
    • github.com/stretchr/testify/assert and github.com/stretchr/testify/require if not already imported.
  2. If the file already has a helper for loading ClusterRoleBinding YAML (e.g., loadClusterRoleBinding(t, path)), you may want to replace the explicit os.ReadFile/yaml.Unmarshal block with a call to that helper for consistency.
  3. If the subject’s namespace should also be asserted, extend the if condition inside the loop to include subject.Namespace == "<expected-namespace>" and update the failure message accordingly.

@desmax74 desmax74 changed the base branch from release/2.y.z to release/3.y.z June 30, 2026 18:44
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