Skip to content

Add new collect keyless params task#3186

Merged
simonbaird merged 4 commits intoconforma:mainfrom
simonbaird:keyless-params-task-redo
Mar 24, 2026
Merged

Add new collect keyless params task#3186
simonbaird merged 4 commits intoconforma:mainfrom
simonbaird:keyless-params-task-redo

Conversation

@simonbaird
Copy link
Member

@simonbaird simonbaird commented Mar 20, 2026

The task will look in a ConfigMap containing Konflux cluster config related to keyless signing. The idea is to use the task results as input params to the Conforma task so it can perform keyless signature verification using the correct params.

Ref: https://redhat.atlassian.net/browse/EC-1695

Note: This was split out from #3171 which has been blocked on some unrelated acceptance test problems because I was doing too much in the one PR. This PR has the task only, so should be green and ready to merge.

@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Adds a new Tekton Task collect-keyless-params (v0.1) that reads keyless-signing configuration from a Kubernetes ConfigMap and emits results; includes the task in bundle/workflow definitions, extends acceptance-test cluster API/impl and BDD steps, and adds feature scenarios and documentation.

Changes

Cohort / File(s) Summary
Release & Bundle Workflows
.github/workflows/release.yaml, .tekton/cli-main-pull-request.yaml, .tekton/cli-main-push.yaml
Appended tasks/collect-keyless-params/0.1/collect-keyless-params.yaml to TASKS/CONTEXT used for building/pushing Tekton bundles; added an inline deprecation comment for the quay.io/enterprise-contract bundle step.
New Tekton Task
tasks/collect-keyless-params/0.1/collect-keyless-params.yaml
Added Task collect-keyless-params (params: configMapName, configMapNamespace; results: keylessSigningEnabled, defaultOIDCIssuer, buildIdentityRegexp, tektonChainsIdentity, fulcioUrl, rekorUrl, tufUrl). Script uses kubectl + jq, initializes defaults, checks ConfigMap, sets outputs and prefers internal URLs with external fallbacks.
Acceptance-test cluster API & impl
acceptance/kubernetes/types/types.go, acceptance/kubernetes/kind/kubernetes.go, acceptance/kubernetes/stub/stub.go
Extended Cluster interface with CreateNamedNamespace and CreateConfigMap; kindCluster implements create-or-update ConfigMap (JSON-aware) plus cluster-wide RBAC creation; stubCluster exposes the new methods but returns unsupported errors.
Acceptance test steps
acceptance/kubernetes/kubernetes.go
Added helper functions and Godog step registrations to create named namespaces and ConfigMaps by delegating to the cluster implementation.
BDD Scenarios & Docs
features/task_validate_image.feature, docs/modules/ROOT/partials/tasks_nav.adoc, docs/modules/ROOT/pages/collect-keyless-params.adoc
Added multiple Gherkin scenarios covering enabled/disabled/malformed/missing ConfigMap cases for the task; added docs page and navigation entry describing task usage, params, and results.

Sequence Diagram(s)

sequenceDiagram
    participant Pipeline as Pipeline/Bundle
    participant TektonTask as Tekton Task\n(collect-keyless-params)
    participant Container as Task Container\n(quay.io/conforma/cli:latest)
    participant K8sAPI as Kubernetes API\n(ConfigMap)
    Pipeline->>TektonTask: invoke task with params
    TektonTask->>Container: start step (bash script)
    Container->>K8sAPI: kubectl get configmap\n-o json --ignore-not-found
    K8sAPI-->>Container: JSON object or empty/not-found
    Container->>Container: parse with jq,\ncompute results (internal→external URL fallback)
    Container-->>TektonTask: write Tekton result files
    TektonTask-->>Pipeline: publish results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a new Tekton task for collecting keyless signing parameters.
Description check ✅ Passed The PR description clearly explains the purpose of the changes: adding a new task that reads a ConfigMap for Konflux keyless signing configuration and emits results for use in the Conforma task. It is directly related to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Add collect-keyless-params task with ConfigMap support and acceptance tests

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add new collect-keyless-params Tekton task for reading keyless signing configuration from
  ConfigMaps
• Implement ConfigMap and namespace creation support in acceptance test framework with RBAC
  permissions
• Add comprehensive acceptance test scenarios covering multiple ConfigMap states and edge cases
• Include new task in bundle pushes for both Konflux and deprecated GitHub repositories
Diagram
flowchart LR
  A["New Task: collect-keyless-params"] -->|reads config from| B["ConfigMap in konflux-info namespace"]
  B -->|extracts parameters| C["Keyless signing configuration"]
  D["Test Framework Enhancements"] -->|supports| E["ConfigMap creation"]
  D -->|supports| F["Namespace creation"]
  D -->|manages| G["RBAC permissions"]
  H["Bundle Workflows"] -->|include| A
Loading

Grey Divider

File Changes

1. tasks/collect-keyless-params/0.1/collect-keyless-params.yaml ✨ Enhancement +176/-0

New Tekton task for collecting keyless signing parameters

tasks/collect-keyless-params/0.1/collect-keyless-params.yaml


2. acceptance/kubernetes/kind/kubernetes.go ✨ Enhancement +133/-0

Add ConfigMap and namespace creation with RBAC support

acceptance/kubernetes/kind/kubernetes.go


3. acceptance/kubernetes/kubernetes.go ✨ Enhancement +22/-0

Add step definitions for ConfigMap and namespace creation

acceptance/kubernetes/kubernetes.go


View more (7)
4. acceptance/kubernetes/types/types.go ✨ Enhancement +2/-0

Add interface methods for ConfigMap and namespace operations

acceptance/kubernetes/types/types.go


5. acceptance/kubernetes/stub/stub.go ✨ Enhancement +8/-0

Add stub implementations for ConfigMap and namespace creation

acceptance/kubernetes/stub/stub.go


6. features/task_validate_image.feature 🧪 Tests +118/-0

Add acceptance test scenarios for collect-keyless-params task

features/task_validate_image.feature


7. .github/workflows/release.yaml ⚙️ Configuration changes +3/-2

Include collect-keyless-params task in bundle pushes

.github/workflows/release.yaml


8. .tekton/cli-main-pull-request.yaml ⚙️ Configuration changes +4/-1

Add collect-keyless-params to pull request bundle build

.tekton/cli-main-pull-request.yaml


9. .tekton/cli-main-push.yaml ⚙️ Configuration changes +4/-1

Add collect-keyless-params to main push bundle build

.tekton/cli-main-push.yaml


10. docs/modules/ROOT/partials/tasks_nav.adoc 📝 Documentation +1/-0

Add collect-keyless-params to documentation navigation

docs/modules/ROOT/partials/tasks_nav.adoc


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Mar 20, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. Broken docs xref 🐞 Bug ✓ Correctness
Description
The docs navigation links to collect-keyless-params.adoc, but that page is not present in the docs
pages directory, causing a broken navigation entry (and often a docs build failure under strict
Antora settings).
Code

docs/modules/ROOT/partials/tasks_nav.adoc[2]

+** xref:collect-keyless-params.adoc[collect-keyless-params]
Evidence
tasks_nav adds an xref to collect-keyless-params.adoc and this partial is included in the published
nav, so the missing page directly affects rendered documentation navigation.

docs/modules/ROOT/partials/tasks_nav.adoc[1-5]
docs/modules/ROOT/nav.adoc[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Docs navigation references `xref:collect-keyless-params.adoc`, but there is no corresponding page file, creating a broken xref (and potentially breaking docs builds).

## Issue Context
The tasks navigation partial is included in the global Antora nav.

## Fix Focus Areas
- docs/modules/ROOT/partials/tasks_nav.adoc[1-5]
- docs/modules/ROOT/nav.adoc[1-4]

## Expected fix
- Add `docs/modules/ROOT/pages/collect-keyless-params.adoc` describing the new task (inputs/results/example usage), OR
- Remove/hold back the nav entry until the page exists.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. MinVersion annotation mismatch 🐞 Bug ✓ Correctness
Description
The new task declares apiVersion: tekton.dev/v1 but sets tekton.dev/pipelines.minVersion to
0.12.1, which is inconsistent with other v1 tasks in this repo and can mislead users/tooling about
compatibility.
Code

tasks/collect-keyless-params/0.1/collect-keyless-params.yaml[R18-25]

+apiVersion: tekton.dev/v1
+kind: Task
+metadata:
+  name: collect-keyless-params
+  annotations:
+    tekton.dev/displayName: Collect Keyless Signing Parameters
+    tekton.dev/pipelines.minVersion: "0.12.1"
+    tekton.dev/tags: ec, keyless, signing, configuration
Evidence
The new task’s minVersion differs from the established pattern used by existing tekton.dev/v1
tasks in this repo, indicating the new value is likely incorrect or at least inconsistent.

tasks/collect-keyless-params/0.1/collect-keyless-params.yaml[18-25]
tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[18-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`tasks/collect-keyless-params` sets `tekton.dev/pipelines.minVersion: "0.12.1"` while using `apiVersion: tekton.dev/v1`. This is inconsistent with other tasks in this repository and may cause incorrect compatibility signaling.

## Issue Context
Other tasks in `tasks/*/0.1/*.yaml` using `tekton.dev/v1` declare `tekton.dev/pipelines.minVersion: "0.19"`.

## Fix Focus Areas
- tasks/collect-keyless-params/0.1/collect-keyless-params.yaml[18-25]
- tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[18-25]

## Expected fix
- Update `tekton.dev/pipelines.minVersion` for `collect-keyless-params` to match the repo’s convention (likely `"0.19"`), or otherwise set it to the minimum version that actually supports the features/apiVersion used by this task.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Brittle AlreadyExists checks 🐞 Bug ⛯ Reliability
Description
Acceptance Kind helpers detect AlreadyExists by matching the error message text, which can cause
flaky failures if Kubernetes error strings change; typed checks are already used elsewhere in the
same file.
Code

acceptance/kubernetes/kind/kubernetes.go[R273-276]

+	if _, err := k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
+		// Ignore error if ClusterRole already exists
+		if !strings.Contains(err.Error(), "already exists") {
+			return fmt.Errorf("failed to create ClusterRole: %w", err)
Evidence
RBAC and namespace creation use strings.Contains(err.Error(), "already exists"), while ConfigMap
creation in the same file correctly uses apierrors.IsAlreadyExists(err), showing a safer,
consistent approach is already available.

acceptance/kubernetes/kind/kubernetes.go[273-277]
acceptance/kubernetes/kind/kubernetes.go[299-304]
acceptance/kubernetes/kind/kubernetes.go[309-323]
acceptance/kubernetes/kind/kubernetes.go[229-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ensureConfigMapRBAC` and `CreateNamedNamespace` ignore AlreadyExists errors by string-matching `err.Error()`. This is fragile and can break acceptance runs.

## Issue Context
The file already imports and uses `k8s.io/apimachinery/pkg/api/errors` (`apierrors.IsAlreadyExists`) for ConfigMap creation.

## Fix Focus Areas
- acceptance/kubernetes/kind/kubernetes.go[257-307]
- acceptance/kubernetes/kind/kubernetes.go[309-323]
- acceptance/kubernetes/kind/kubernetes.go[229-232]

## Expected fix
- Replace `strings.Contains(err.Error(), "already exists")` with `apierrors.IsAlreadyExists(err)` for:
 - ClusterRole create
 - ClusterRoleBinding create
 - Namespace create
- Keep returning wrapped errors for non-AlreadyExists cases.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
features/task_validate_image.feature (1)

495-517: Add missing buildIdentity assertion in the “ConfigMap does not exist” scenario.

Line [516]-[517] validates buildIdentityRegexp but this scenario does not assert buildIdentity, leaving one result unchecked in this failure path.

Proposed test assertion patch
     And the task result "tufExternalUrl" should equal ""
+    And the task result "buildIdentity" should equal ""
     And the task result "buildIdentityRegexp" should equal ""
     And the task result "keylessSigningEnabled" should equal "false"
As per coding guidelines, "-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@features/task_validate_image.feature` around lines 495 - 517, The "ConfigMap
does not exist" scenario for the task "collect-keyless-params" is missing an
assertion for the task result "buildIdentity", leaving that output unchecked;
add a new step asserting That the task result "buildIdentity" should equal ""
(empty string) alongside the existing assertions (e.g., after the
"buildIdentityRegexp" and before "keylessSigningEnabled") so the failure path
verifies all keyless signing fields produced by the "collect-signing-params"
step.
acceptance/kubernetes/kind/kubernetes.go (2)

317-320: Same issue: use apierrors.IsAlreadyExists instead of string matching.

♻️ Proposed fix
 	// Ignore error if namespace already exists
-	if err != nil && strings.Contains(err.Error(), "already exists") {
+	if err != nil && apierrors.IsAlreadyExists(err) {
 		return nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@acceptance/kubernetes/kind/kubernetes.go` around lines 317 - 320, Replace the
string-match check for an existing namespace with the Kubernetes API helper: use
apierrors.IsAlreadyExists(err) instead of strings.Contains(err.Error(), "already
exists") in the function that handles namespace creation (the block currently
checking err != nil && strings.Contains(...)); also add the import for
"k8s.io/apimachinery/pkg/api/errors" aliased as apierrors if not already present
so the check becomes apierrors.IsAlreadyExists(err) and returns nil when true.

273-278: Use apierrors.IsAlreadyExists consistently for error checking.

Lines 275 and 301 use string matching on the error message, while line 231 correctly uses apierrors.IsAlreadyExists(err). The string-based approach is fragile and inconsistent with the rest of this file.

♻️ Proposed fix
 	if _, err := k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
 		// Ignore error if ClusterRole already exists
-		if !strings.Contains(err.Error(), "already exists") {
+		if !apierrors.IsAlreadyExists(err) {
 			return fmt.Errorf("failed to create ClusterRole: %w", err)
 		}
 	}
 	if _, err := k.client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}); err != nil {
 		// Ignore error if ClusterRoleBinding already exists
-		if !strings.Contains(err.Error(), "already exists") {
+		if !apierrors.IsAlreadyExists(err) {
 			return fmt.Errorf("failed to create ClusterRoleBinding: %w", err)
 		}
 	}

Also applies to: 299-304

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@acceptance/kubernetes/kind/kubernetes.go` around lines 273 - 278, Replace
fragile string matching on err.Error() with Kubernetes API error helper: call
apierrors.IsAlreadyExists(err) to detect already-existing resources;
specifically update the error checks after
k.client.RbacV1().ClusterRoles().Create (and the similar check after
k.client.RbacV1().ClusterRoleBindings().Create) to use
apierrors.IsAlreadyExists(err) and only return an error when that helper returns
false, ensuring import of "k8s.io/apimachinery/pkg/api/errors" (alias apierrors)
is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@acceptance/kubernetes/kind/kubernetes.go`:
- Around line 317-320: Replace the string-match check for an existing namespace
with the Kubernetes API helper: use apierrors.IsAlreadyExists(err) instead of
strings.Contains(err.Error(), "already exists") in the function that handles
namespace creation (the block currently checking err != nil &&
strings.Contains(...)); also add the import for
"k8s.io/apimachinery/pkg/api/errors" aliased as apierrors if not already present
so the check becomes apierrors.IsAlreadyExists(err) and returns nil when true.
- Around line 273-278: Replace fragile string matching on err.Error() with
Kubernetes API error helper: call apierrors.IsAlreadyExists(err) to detect
already-existing resources; specifically update the error checks after
k.client.RbacV1().ClusterRoles().Create (and the similar check after
k.client.RbacV1().ClusterRoleBindings().Create) to use
apierrors.IsAlreadyExists(err) and only return an error when that helper returns
false, ensuring import of "k8s.io/apimachinery/pkg/api/errors" (alias apierrors)
is present.

In `@features/task_validate_image.feature`:
- Around line 495-517: The "ConfigMap does not exist" scenario for the task
"collect-keyless-params" is missing an assertion for the task result
"buildIdentity", leaving that output unchecked; add a new step asserting That
the task result "buildIdentity" should equal "" (empty string) alongside the
existing assertions (e.g., after the "buildIdentityRegexp" and before
"keylessSigningEnabled") so the failure path verifies all keyless signing fields
produced by the "collect-signing-params" step.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d989645f-b73b-44ac-bade-1a27a46a9f83

📥 Commits

Reviewing files that changed from the base of the PR and between e46c21e and e05fc8f.

⛔ Files ignored due to path filters (1)
  • features/__snapshots__/task_validate_image.snap is excluded by !**/*.snap
📒 Files selected for processing (10)
  • .github/workflows/release.yaml
  • .tekton/cli-main-pull-request.yaml
  • .tekton/cli-main-push.yaml
  • acceptance/kubernetes/kind/kubernetes.go
  • acceptance/kubernetes/kubernetes.go
  • acceptance/kubernetes/stub/stub.go
  • acceptance/kubernetes/types/types.go
  • docs/modules/ROOT/partials/tasks_nav.adoc
  • features/task_validate_image.feature
  • tasks/collect-keyless-params/0.1/collect-keyless-params.yaml

@codecov
Copy link

codecov bot commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
acceptance 55.16% <ø> (ø)
generative 17.90% <ø> (ø)
integration 26.63% <ø> (ø)
unit 69.01% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@simonbaird simonbaird force-pushed the keyless-params-task-redo branch from e05fc8f to 19b48d2 Compare March 20, 2026 17:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tasks/collect-keyless-params/0.1/collect-keyless-params.yaml (1)

142-146: Minor: Log message could be more precise.

The message states "enableKeylessSigning is not set" but the condition triggers when the value is anything other than "true" (including explicit "false"). Consider clarifying:

✏️ Suggested improvement
           else
             # Otherwise we ignore the rest of the ConfigMap
-            echo "enableKeylessSigning is not set, using default empty values"
+            echo "enableKeylessSigning is not 'true', using default empty values"
 
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tasks/collect-keyless-params/0.1/collect-keyless-params.yaml` around lines
142 - 146, The log is misleading because the else branch runs for any value
other than "true"; update the echo in the else branch that mentions
enableKeylessSigning so it clearly states the value isn't "true" (and ideally
print the actual variable), e.g. change the message currently printed by the
else branch to indicate enableKeylessSigning != "true" and include the variable
value (e.g., enableKeylessSigning="$enableKeylessSigning") while keeping the
note about using default empty values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tasks/collect-keyless-params/0.1/collect-keyless-params.yaml`:
- Around line 142-146: The log is misleading because the else branch runs for
any value other than "true"; update the echo in the else branch that mentions
enableKeylessSigning so it clearly states the value isn't "true" (and ideally
print the actual variable), e.g. change the message currently printed by the
else branch to indicate enableKeylessSigning != "true" and include the variable
value (e.g., enableKeylessSigning="$enableKeylessSigning") while keeping the
note about using default empty values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: caacea4b-f132-4178-821d-44b0d74c7866

📥 Commits

Reviewing files that changed from the base of the PR and between e05fc8f and 19b48d2.

⛔ Files ignored due to path filters (1)
  • features/__snapshots__/task_validate_image.snap is excluded by !**/*.snap
📒 Files selected for processing (11)
  • .github/workflows/release.yaml
  • .tekton/cli-main-pull-request.yaml
  • .tekton/cli-main-push.yaml
  • acceptance/kubernetes/kind/kubernetes.go
  • acceptance/kubernetes/kubernetes.go
  • acceptance/kubernetes/stub/stub.go
  • acceptance/kubernetes/types/types.go
  • docs/modules/ROOT/pages/collect-keyless-params.adoc
  • docs/modules/ROOT/partials/tasks_nav.adoc
  • features/task_validate_image.feature
  • tasks/collect-keyless-params/0.1/collect-keyless-params.yaml
✅ Files skipped from review due to trivial changes (3)
  • docs/modules/ROOT/partials/tasks_nav.adoc
  • docs/modules/ROOT/pages/collect-keyless-params.adoc
  • acceptance/kubernetes/kind/kubernetes.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • acceptance/kubernetes/types/types.go
  • .tekton/cli-main-pull-request.yaml
  • acceptance/kubernetes/stub/stub.go
  • .github/workflows/release.yaml
  • features/task_validate_image.feature

@gbenhaim
Copy link

@simonbaird for getting the identity of chains, please use the tektonChainsIdentity field from the configmap - https://github.com/redhat-appstudio/tsf-cli/blob/84561ca6c9a39272a671c2ba5aeafef648dfe566/installer/charts/tsf-konflux/templates/konflux.yaml#L56

@gbenhaim
Copy link

@simonbaird the default should be to use the internal Sigstore urls https://github.com/redhat-appstudio/tsf-cli/blob/84561ca6c9a39272a671c2ba5aeafef648dfe566/installer/charts/tsf-konflux/templates/konflux.yaml#L61

If those are not present in the configmap (which means sigstore runs out of the cluster), the external urls should be used.

simonbaird and others added 2 commits March 23, 2026 15:34
(A test will be added in some upcoming commits.)

Ref: https://redhat.atlassian.net/browse/EC-1695
Co-authored-by: Claude Code <noreply@anthropic.com>
Also namespaces, since we want the ConfigMap in a particular
namespace. An RBAC is created also so the ConfigMap is readable by
every service account.

This will be used in the acceptance test added in an upcoming
commit.

Ref: https://issues.redhat.com/browse/EC-1695
Co-authored-by: Claude Code <noreply@anthropic.com>
@simonbaird simonbaird force-pushed the keyless-params-task-redo branch from 19b48d2 to baf6e7d Compare March 23, 2026 19:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
acceptance/kubernetes/kind/kubernetes.go (2)

273-278: Use apierrors.IsAlreadyExists consistently.

String matching on error messages is fragile. Since apierrors is already imported and used correctly in CreateConfigMap (line 231), use the same approach here.

♻️ Proposed fix
 	if _, err := k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
-		// Ignore error if ClusterRole already exists
-		if !strings.Contains(err.Error(), "already exists") {
+		if !apierrors.IsAlreadyExists(err) {
 			return fmt.Errorf("failed to create ClusterRole: %w", err)
 		}
 	}
 	if _, err := k.client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}); err != nil {
-		// Ignore error if ClusterRoleBinding already exists
-		if !strings.Contains(err.Error(), "already exists") {
+		if !apierrors.IsAlreadyExists(err) {
 			return fmt.Errorf("failed to create ClusterRoleBinding: %w", err)
 		}
 	}

Also applies to: 299-304

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@acceptance/kubernetes/kind/kubernetes.go` around lines 273 - 278, Replace
fragile string matching with apierrors.IsAlreadyExists when handling create
errors for ClusterRole creation: in the block that calls
k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole,
metav1.CreateOptions{}), catch the error and use apierrors.IsAlreadyExists(err)
to ignore "already exists" cases and return other errors (same pattern used in
CreateConfigMap). Apply the same change to the other create block noted (the
ClusterRoleBinding/related create at the later section) so both use
apierrors.IsAlreadyExists instead of strings.Contains(err.Error(), "already
exists").

317-320: Same issue: use apierrors.IsAlreadyExists here as well.

♻️ Proposed fix
-	// Ignore error if namespace already exists
-	if err != nil && strings.Contains(err.Error(), "already exists") {
+	if apierrors.IsAlreadyExists(err) {
 		return nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@acceptance/kubernetes/kind/kubernetes.go` around lines 317 - 320, Replace the
string-based check for an existing namespace with the k8s API helper: instead of
checking strings.Contains(err.Error(), "already exists") in the
namespace-creation error handling (the block that currently does "if err != nil
&& strings.Contains(err.Error(), \"already exists\") { return nil }"), use
apierrors.IsAlreadyExists(err) to detect that condition; update imports to
include "k8s.io/apimachinery/pkg/api/errors" aliased as apierrors and keep the
logic to return nil when apierrors.IsAlreadyExists(err).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@acceptance/kubernetes/kind/kubernetes.go`:
- Around line 273-278: Replace fragile string matching with
apierrors.IsAlreadyExists when handling create errors for ClusterRole creation:
in the block that calls k.client.RbacV1().ClusterRoles().Create(ctx,
clusterRole, metav1.CreateOptions{}), catch the error and use
apierrors.IsAlreadyExists(err) to ignore "already exists" cases and return other
errors (same pattern used in CreateConfigMap). Apply the same change to the
other create block noted (the ClusterRoleBinding/related create at the later
section) so both use apierrors.IsAlreadyExists instead of
strings.Contains(err.Error(), "already exists").
- Around line 317-320: Replace the string-based check for an existing namespace
with the k8s API helper: instead of checking strings.Contains(err.Error(),
"already exists") in the namespace-creation error handling (the block that
currently does "if err != nil && strings.Contains(err.Error(), \"already
exists\") { return nil }"), use apierrors.IsAlreadyExists(err) to detect that
condition; update imports to include "k8s.io/apimachinery/pkg/api/errors"
aliased as apierrors and keep the logic to return nil when
apierrors.IsAlreadyExists(err).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 99e5328b-8c7e-41f8-8168-81187e26252f

📥 Commits

Reviewing files that changed from the base of the PR and between 19b48d2 and baf6e7d.

⛔ Files ignored due to path filters (1)
  • features/__snapshots__/task_validate_image.snap is excluded by !**/*.snap
📒 Files selected for processing (11)
  • .github/workflows/release.yaml
  • .tekton/cli-main-pull-request.yaml
  • .tekton/cli-main-push.yaml
  • acceptance/kubernetes/kind/kubernetes.go
  • acceptance/kubernetes/kubernetes.go
  • acceptance/kubernetes/stub/stub.go
  • acceptance/kubernetes/types/types.go
  • docs/modules/ROOT/pages/collect-keyless-params.adoc
  • docs/modules/ROOT/partials/tasks_nav.adoc
  • features/task_validate_image.feature
  • tasks/collect-keyless-params/0.1/collect-keyless-params.yaml
✅ Files skipped from review due to trivial changes (3)
  • docs/modules/ROOT/partials/tasks_nav.adoc
  • acceptance/kubernetes/types/types.go
  • docs/modules/ROOT/pages/collect-keyless-params.adoc
🚧 Files skipped from review as they are similar to previous changes (6)
  • .tekton/cli-main-pull-request.yaml
  • acceptance/kubernetes/stub/stub.go
  • .tekton/cli-main-push.yaml
  • .github/workflows/release.yaml
  • tasks/collect-keyless-params/0.1/collect-keyless-params.yaml
  • features/task_validate_image.feature

@simonbaird
Copy link
Member Author

@gbenhaim Thanks for the review. New revision adds the tektonChainsIdentity result which is pulled from the map key of the same name. Also I removed buildIdentity since it doesn't exist currently.

Also I adjusted the logic so we use the internal urls if they exist, and fall back to the external urls if there are no internal urls. There's a related change to the three url task results, e.g. I'm using tufUrl instead tufExernalUrl since the value might be from either of them. Similar for the rekor and fulcio task results.

@gbenhaim
Copy link

/review

@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

EC-1695 - Partially compliant

Compliant requirements:

  • Add a new Tekton task that looks up that ConfigMap, extracts required values, and writes them into task results.
  • Ensure those task results can be used as params for existing Conforma verification tasks.
  • Do not change behavior for existing clusters that are not configured for keyless signing; fall back to current public-key verification when keyless is not configured.

Non-compliant requirements:

  • Update the pipeline definition that includes the Conforma task to include this new task.

Requires further human verification:

  • Expect a ConfigMap to exist in the Konflux cluster with keyless-signing-related configuration.
  • Update the pipeline definition that includes the Conforma task to include this new task.
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 Security concerns

Overly broad RBAC:
ensureConfigMapRBAC creates a ClusterRole and ClusterRoleBinding granting the system:serviceaccounts group get/list on all configmaps cluster-wide. Even if this is only for kind-based acceptance tests, it’s still a high-privilege pattern; ensure it cannot leak into non-test environments and consider scoping to specific namespaces and/or a dedicated ServiceAccount.

Let @robnester-rh know what you think of this process.

⚡ Recommended focus areas for review

RBAC Scope

The acceptance helper creates a ClusterRoleBinding granting all service accounts cluster-wide read access to all ConfigMaps. This is very broad; validate it’s acceptable for the test environment and consider scoping it to the specific namespace(s) and/or a dedicated ServiceAccount used by Tekton in tests.

	// Create RBAC permissions for cross-namespace ConfigMap access
	// This allows any service account to read ConfigMaps from any namespace
	if err := k.ensureConfigMapRBAC(ctx); err != nil {
		return fmt.Errorf("failed to create RBAC permissions: %w", err)
	}

	return nil
}

// ensureConfigMapRBAC creates necessary RBAC permissions for ConfigMap access across namespaces
func (k *kindCluster) ensureConfigMapRBAC(ctx context.Context) error {
	// Create ClusterRole for ConfigMap reading (idempotent)
	clusterRole := &rbacv1.ClusterRole{
		ObjectMeta: metav1.ObjectMeta{
			Name: "acceptance-configmap-reader",
		},
		Rules: []rbacv1.PolicyRule{
			{
				APIGroups: []string{""},
				Resources: []string{"configmaps"},
				Verbs:     []string{"get", "list"},
			},
		},
	}

	if _, err := k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
		// Ignore error if ClusterRole already exists
		if !strings.Contains(err.Error(), "already exists") {
			return fmt.Errorf("failed to create ClusterRole: %w", err)
		}
	}

	// Create ClusterRoleBinding for all service accounts (idempotent)
	clusterRoleBinding := &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "acceptance-configmap-reader-binding",
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     "acceptance-configmap-reader",
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:     "Group",
				Name:     "system:serviceaccounts",
				APIGroup: "rbac.authorization.k8s.io",
			},
		},
	}

	if _, err := k.client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}); err != nil {
		// Ignore error if ClusterRoleBinding already exists
		if !strings.Contains(err.Error(), "already exists") {
			return fmt.Errorf("failed to create ClusterRoleBinding: %w", err)
		}
	}
Idempotency

Resource creation treats “already exists” via substring matching on the error message. Prefer typed checks (e.g., apierrors.IsAlreadyExists) for RBAC and namespace creation as well, to avoid brittleness across Kubernetes versions/locales.

	if _, err := k.client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
		// Ignore error if ClusterRole already exists
		if !strings.Contains(err.Error(), "already exists") {
			return fmt.Errorf("failed to create ClusterRole: %w", err)
		}
	}

	// Create ClusterRoleBinding for all service accounts (idempotent)
	clusterRoleBinding := &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name: "acceptance-configmap-reader-binding",
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     "acceptance-configmap-reader",
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:     "Group",
				Name:     "system:serviceaccounts",
				APIGroup: "rbac.authorization.k8s.io",
			},
		},
	}

	if _, err := k.client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}); err != nil {
		// Ignore error if ClusterRoleBinding already exists
		if !strings.Contains(err.Error(), "already exists") {
			return fmt.Errorf("failed to create ClusterRoleBinding: %w", err)
		}
	}

	return nil
}

// CreateNamedNamespace creates a namespace with the specified name
func (k *kindCluster) CreateNamedNamespace(ctx context.Context, name string) error {
	_, err := k.client.CoreV1().Namespaces().Create(ctx, &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}, metav1.CreateOptions{})

	// Ignore error if namespace already exists
	if err != nil && strings.Contains(err.Error(), "already exists") {
		return nil
	}

	return err
Image/Tooling

The task uses quay.io/conforma/cli:latest and relies on kubectl and jq being present. Pinning the image (or using Tekton’s injected $(params.*) image digest pattern) and confirming required binaries exist in the image would improve reproducibility and reduce breakage risk.

- name: collect-signing-params
  image: quay.io/conforma/cli:latest

  computeResources:
    limits:
      memory: 128Mi
      cpu: 50m
    requests:
      memory: 128Mi
      cpu: 50m

  env:
    - name: configMapNamespace
      value: "$(params.configMapNamespace)"
    - name: configMapName
      value: "$(params.configMapName)"

  script: |
    #!/bin/bash
    set -euo pipefail

    # Default value is "false"
    keylessSigningEnabled="false"

    # Default values are empty strings
    defaultOIDCIssuer=""
    buildIdentityRegexp=""
    tektonChainsIdentity=""
    fulcioUrl=""
    rekorUrl=""
    tufUrl=""

    # Read from the ConfigMap
    echo "Reading ConfigMap ${configMapNamespace}/${configMapName}"
    KFLX_CONFIG_PATH='/tmp/cluster-config.json'

    if kubectl get configmap "${configMapName}" -n "${configMapNamespace}" -o json --ignore-not-found > "${KFLX_CONFIG_PATH}"; then
      if [ -s "${KFLX_CONFIG_PATH}" ]; then
        echo "ConfigMap found, extracting keyless signing parameters"

        # First we read "keylessSigningEnabled"
        keylessSigningEnabled=$(jq -r '.data.enableKeylessSigning // "false"' "$KFLX_CONFIG_PATH")

        if [ "$keylessSigningEnabled" = "true" ]; then
          # If that is set to "true" then read the other values
          defaultOIDCIssuer=$(jq -r '.data.defaultOIDCIssuer // ""' "$KFLX_CONFIG_PATH")
          buildIdentityRegexp=$(jq -r '.data.buildIdentityRegexp // ""' "$KFLX_CONFIG_PATH")
          tektonChainsIdentity=$(jq -r '.data.tektonChainsIdentity // ""' "$KFLX_CONFIG_PATH")

          # For each of these we prefer the internal url if its present
          fulcioUrl=$(jq -r '.data.fulcioInternalUrl // ""' "$KFLX_CONFIG_PATH")
          if [ -z "$fulcioUrl" ]; then
            fulcioUrl=$(jq -r '.data.fulcioExternalUrl // ""' "$KFLX_CONFIG_PATH")
          fi

          rekorUrl=$(jq -r '.data.rekorInternalUrl // ""' "$KFLX_CONFIG_PATH")
          if [ -z "$rekorUrl" ]; then
            rekorUrl=$(jq -r '.data.rekorExternalUrl // ""' "$KFLX_CONFIG_PATH")
          fi

          tufUrl=$(jq -r '.data.tufInternalUrl // ""' "$KFLX_CONFIG_PATH")
          if [ -z "$tufUrl" ]; then
            tufUrl=$(jq -r '.data.tufExternalUrl // ""' "$KFLX_CONFIG_PATH")
          fi

        else
          # Otherwise we ignore the rest of the ConfigMap
          echo "enableKeylessSigning is not set, using default empty values"

        fi
      else
        # Because we used --ignore-not-found this doesn't produce an error
        echo "ConfigMap not found, using default empty values"

      fi

    else
      # Some error other than "not found"
      # (Stderr from kubectl should be visible in the task log.)
      echo "Problem reading ConfigMap, using default empty values"

st3penta
st3penta previously approved these changes Mar 24, 2026
Copy link
Contributor

@st3penta st3penta left a comment

Choose a reason for hiding this comment

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

left a few minor comments, but overall LGTM, feel free to merge it

There are pushes happening in both Konflux and GitHub, and each of
those pushes to the new and old locations. I added the new task
everywhere, even the deprecated repo.

Ref: https://redhat.atlassian.net/browse/EC-1695
@simonbaird
Copy link
Member Author

Added some extra comments in the acceptance test, and changed cluster-config-0 to -1.

@simonbaird
Copy link
Member Author

I'll merge once CI is green.

@simonbaird simonbaird enabled auto-merge March 24, 2026 18:11
@simonbaird simonbaird merged commit ad4e392 into conforma:main Mar 24, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants