Skip to content

CNTRLPLANE-3237: kms: report preflight results via KMSEncryptionStatus - #2375

Merged
openshift-merge-bot[bot] merged 7 commits into
openshift:masterfrom
p0lyn0mial:kms-preflight-result-reporting
Jul 27, 2026
Merged

CNTRLPLANE-3237: kms: report preflight results via KMSEncryptionStatus#2375
openshift-merge-bot[bot] merged 7 commits into
openshift:masterfrom
p0lyn0mial:kms-preflight-result-reporting

Conversation

@p0lyn0mial

@p0lyn0mial p0lyn0mial commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • KMS preflight checks now persist the remote key ID in encryption status.
    • Completed preflight checks are reused when the KMS configuration hash hasn’t changed, reducing unnecessary pod activity.
  • Bug Fixes
    • Preflight results are now more authoritative across retries, failures, and configuration changes.
    • Preflight pods are cleaned up immediately after successful completion; stale or mismatched preflight states are handled more reliably.

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

openshift-ci-robot commented Jul 24, 2026

Copy link
Copy Markdown

@p0lyn0mial: This pull request references CNTRLPLANE-3237 which is a valid jira issue.

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

Details

In response to this:

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

@openshift-ci
openshift-ci Bot requested review from ardaguclu and bertinatto July 24, 2026 03:13
@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 63edc026-084a-4394-bc8b-26f45d5f6caf

📥 Commits

Reviewing files that changed from the base of the PR and between aba173d and ed70b63.

📒 Files selected for processing (3)
  • pkg/operator/encryption/controllers/kms_preflight_controller.go
  • pkg/operator/encryption/controllers/kms_preflight_controller_test.go
  • pkg/operator/encryption/kms/preflight/pod_status_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/operator/encryption/kms/preflight/pod_status_test.go
  • pkg/operator/encryption/controllers/kms_preflight_controller_test.go
  • pkg/operator/encryption/controllers/kms_preflight_controller.go

Walkthrough

The KMS preflight controller now coordinates with an encryption status provider using observed configuration hashes and persisted preflight results. It records remote key IDs, reuses stored outcomes, cleans up successful checks immediately, and updates pod assertions and controller tests.

Changes

KMS preflight coordination

Layer / File(s) Summary
Status provider contract and hash gating
pkg/operator/encryption/controllers/kms_preflight_controller.go
The controller accepts an encryption status provider and determines preflight requirements from ObservedConfigHash and the computed KMS configuration hash.
Persisted result lifecycle
pkg/operator/encryption/controllers/kms_preflight_controller.go
Stored results control pod handling; completed pod results persist remote key IDs and hashes, successful checks are cleaned up immediately, and failed checks retain errors.
Pod reporting and controller validation
pkg/operator/encryption/kms/preflight/*, pkg/operator/encryption/controllers/kms_preflight_controller_test.go, test/library/encryption/preflight_deploy.go
Pod and deployment assertions use the remote key ID condition, while scenarios validate provider writes, update counts, cleanup, and degraded outcomes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: bertinatto

🚥 Pre-merge checks | ✅ 14 | ❌ 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 (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: KMS preflight results are now reported via KMSEncryptionStatus.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Changed test titles are static and deterministic; no generated suffixes, timestamps, UUIDs, node/ns names, or IPs appear in names.
Test Structure And Quality ✅ Passed No Ginkgo blocks here; the changed tests use table-driven subtests, t.Cleanup, explicit timeouts, and contextual failure messages.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e test declarations were added; the changed files are unit tests/helper code and contain no MicroShift-unsupported APIs or skip guards to evaluate.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Touched code is unit tests and a shared e2e helper; no new Ginkgo test bodies or multi-node/HA assumptions were added, and no SNO skip was needed.
Topology-Aware Scheduling Compatibility ✅ Passed Changed files only update KMS preflight status handling and pod condition names; no node selectors, affinities, replicas, tolerations, or topology spread constraints were added.
Ote Binary Stdout Contract ✅ Passed Changed files have no main/init/TestMain/BeforeSuite hooks and no fmt.Print/log.SetOutput/klog stdout writes.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e specs were added; touched files are controller/unit tests and a helper, with no IPv4 assumptions or external connectivity.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB or secret/token comparisons were added; only FNV is used for deterministic config hashing.
Container-Privileges ✅ Passed No added privileged/host*/*Escalation/SYS_ADMIN/root settings appear in the PR diff; existing hostNetwork in a test fixture was unchanged.
No-Sensitive-Data-In-Logs ✅ Passed No new logs expose passwords/tokens/PII; the only added klog line logs config hashes, and remoteKeyID is stored in status, not logged.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
pkg/operator/encryption/kms/preflight/pod_status_test.go (1)

41-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Leftover "KEK" naming after the rename to KMSPreflightRemoteKeyIDPodCondition. Two local variables still use the old terminology even though the condition type/constant they reference was renamed.

  • pkg/operator/encryption/kms/preflight/pod_status_test.go#L41-L44: rename kekId to something like remoteKeyIDCond.
  • test/library/encryption/preflight_deploy.go#L116-L117: rename kekCond to something like remoteKeyIDCond.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/kms/preflight/pod_status_test.go` around lines 41 -
44, Rename the local variable kekId to remoteKeyIDCond in pod_status_test.go and
update its references; likewise rename kekCond to remoteKeyIDCond in
preflight_deploy.go and update all references there to use the current
KMSPreflightRemoteKeyIDPodCondition terminology.
pkg/operator/encryption/controllers/kms_preflight_controller.go (1)

466-514: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

No-op guard in ensurePreflightResult ignores Status, only compares ConfigHash.

If a config hash's result is ever re-evaluated for a different Status while ConfigHash stays the same (e.g. once retries for failed preflights are implemented per the TODO above), this write would silently be dropped since only ConfigHash equality is checked. Not an issue today since nothing currently re-runs a check for the same hash, but worth keeping in mind when retry support lands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/controllers/kms_preflight_controller.go` around lines
466 - 514, The no-op guard in ensurePreflightResult currently skips updates when
only Status changes; require both ConfigHash and Status to match before
returning early, so a result with the same hash but a new status is persisted.
pkg/operator/encryption/controllers/kms_preflight_controller_test.go (1)

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

updateErr field on fakeEncryptionStatusProvider is never exercised.

No scenario sets updateErr, so the write-failure branches in production code (ensurePreflightResult returning an error on scenario 3e, and the wrapped-error formatting on scenario 3f, in kms_preflight_controller.go) have no test coverage.

♻️ Suggested scenario to add
{
	name:                     "UpdateKMSEncryptionStatus fails when writing succeeded result",
	deployer: &fakeDeployer{podStatus: corev1.PodStatus{
		Conditions: []corev1.PodCondition{
			{Type: KMSPreflightConfigHashPodCondition, Message: wellKnownMatchingHashForBaseVaultConfig},
			{Type: KMSPreflightResultPodCondition, Status: corev1.ConditionTrue},
		},
	}},
	encryptionStatusProvider: &fakeEncryptionStatusProvider{
		observedConfigHash: wellKnownMatchingHashForBaseVaultConfig,
		updateErr:          fmt.Errorf("conflict"),
	},
	apiServerObjects: []runtime.Object{apiServerWithKMS},
	coreObjects:      []runtime.Object{&wellKnownBaseSecret, &wellKnownBaseConfigMap},
	preconditionsMet: true,
	expectedError:    "conflict",
	expectedEncryptionStatusProviderUpdateCalls: 1,
	expectedConditions: []operatorv1.OperatorCondition{
		{Type: "EncryptionKMSPreflightControllerDegraded", Status: "True", Reason: "Error", Message: "conflict"},
	},
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/controllers/kms_preflight_controller_test.go` around
lines 335 - 368, Add a controller test scenario that configures
fakeEncryptionStatusProvider.updateErr and exercises the successful
preflight-result write path in ensurePreflightResult. Assert the returned error
contains the underlying “conflict” message, exactly one status update is
attempted, and the expected degraded OperatorCondition is recorded, covering
both the direct write failure and wrapped-error behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/operator/encryption/controllers/kms_preflight_controller_test.go`:
- Around line 335-368: Add a controller test scenario that configures
fakeEncryptionStatusProvider.updateErr and exercises the successful
preflight-result write path in ensurePreflightResult. Assert the returned error
contains the underlying “conflict” message, exactly one status update is
attempted, and the expected degraded OperatorCondition is recorded, covering
both the direct write failure and wrapped-error behavior.

In `@pkg/operator/encryption/controllers/kms_preflight_controller.go`:
- Around line 466-514: The no-op guard in ensurePreflightResult currently skips
updates when only Status changes; require both ConfigHash and Status to match
before returning early, so a result with the same hash but a new status is
persisted.

In `@pkg/operator/encryption/kms/preflight/pod_status_test.go`:
- Around line 41-44: Rename the local variable kekId to remoteKeyIDCond in
pod_status_test.go and update its references; likewise rename kekCond to
remoteKeyIDCond in preflight_deploy.go and update all references there to use
the current KMSPreflightRemoteKeyIDPodCondition terminology.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c961b24c-ca2d-40ca-a25e-09e72aece819

📥 Commits

Reviewing files that changed from the base of the PR and between d8f45c2 and 2b394b9.

⛔ Files ignored due to path filters (14)
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/openshift/api/envtest-releases.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/features.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/features/features.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operator/v1/types_kmsencryption.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1/zz_generated.model_name.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/client-go/operator/applyconfigurations/internal/internal.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/client-go/operator/applyconfigurations/operator/v1/kmsencryptionstatus.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/client-go/operator/applyconfigurations/operator/v1/kmspreflightcheck.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/client-go/operator/applyconfigurations/operator/v1/kmspreflightresult.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/client-go/operator/applyconfigurations/utils.go is excluded by !**/vendor/**, !vendor/**
  • vendor/modules.txt is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (6)
  • go.mod
  • pkg/operator/encryption/controllers/kms_preflight_controller.go
  • pkg/operator/encryption/controllers/kms_preflight_controller_test.go
  • pkg/operator/encryption/kms/preflight/pod_status.go
  • pkg/operator/encryption/kms/preflight/pod_status_test.go
  • test/library/encryption/preflight_deploy.go

@ardaguclu ardaguclu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With respect to states, only a comment about the removal of cleanup retention. The rest is about code.

t.Fatalf("unexpected error: %v", err)
}

if !reflect.DeepEqual(scenario.encryptionStatusProvider.writtenStatus, scenario.expectedKMSPreflightResult) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can equality.semantics be used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure, we can.

}
requiredCondition := operatorv1helpers.FindOperatorCondition(operatorStatus.Conditions, "EncryptionKMSPreflightRequired")
if requiredCondition == nil || requiredCondition.Status != operatorv1.ConditionTrue {
requiredHash := encryptionStatus.Preflight.ObservedConfigHash

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can encryptionStatus.Preflight be nil?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

encryptionStatus.Preflight is a value type

return false, err
}
return false, c.cleanupAfterRetention(ctx, resultCondition.LastTransitionTime.Time)
return false, c.deployer.Cleanup(ctx)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm just thinking about the benefits of keeping this pod 1 hour, even in success cases. But probably there is no scenario cluster admin checks it. So this change makes sense to me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

must-gathers really, but for that we should maybe keep them around for 12h or so

})
}

func (c *kmsPreflightController) cleanupAfterRetention(ctx context.Context, completedAt time.Time) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we remove this. Don't we retain the pod 1 hour in failed cases?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The failed pod is retained forever this was about pods that passed. I think it didn't make sense. Thus I decided to simply the code. The failure case is more important for which the pod will be kept until the "next" preflight.

// existingResult is non-nil and its ConfigHash already matches result.ConfigHash,
// making it safe to call on every sync without overwriting a previously stored outcome.
func (c *kmsPreflightController) ensurePreflightResult(ctx context.Context, existingResult *operatorv1.KMSPreflightResult, result operatorv1.KMSPreflightResult) error {
if existingResult != nil && existingResult.ConfigHash == result.ConfigHash {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Idempotency is only for configHash. So status field can be updated with the given hash?. Should we document this or should we add status in the idempotency?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the logic here was that for a given hash status should not "flap"(on/off). does it make sense ?

  // Idempotency is keyed on ConfigHash only, not Status, because for a given hash
  // the result status is stable: a pod posts its result condition once and does not
  // change it. Success pods are cleaned up immediately; failure pods are retained
  // until the config changes (new hash), so the same hash cannot produce both
  // Succeeded and Failed.

observedConfigHash string
writtenStatus *operatorv1.KMSPreflightResult
updateCallCount int
updateErr error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We don't assert this, do we?

@@ -39,8 +39,8 @@ func TestPodCheckConditions(t *testing.T) {
}

kekId := conditions[2]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
kekId := conditions[2]
remoteKeyID := conditions[2]


return requiredHash, nil
if encryptionStatus.Preflight.Result.ConfigHash == requiredHash {
return requiredHash, &encryptionStatus.Preflight.Result, nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we have to return as reference type?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we don't have to. I can change it.

in general it was ok because the client returns a new struct on each invocation.

// Result already recorded as Failed and the pod is gone: surface the error
// without re-deploying. The admin must fix the config (new hash) before a
// new check can run.
if isPreflightResult(existingResult, operatorv1.KMSPreflightResultFailed) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you add two functions helpers where this is more explicit in the name PreflightResultSuceeded / PreflightResultFailed

@p0lyn0mial
p0lyn0mial force-pushed the kms-preflight-result-reporting branch from 2b394b9 to aba173d Compare July 27, 2026 10:53
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 27, 2026
@p0lyn0mial
p0lyn0mial force-pushed the kms-preflight-result-reporting branch from aba173d to ed70b63 Compare July 27, 2026 10:59
@ardaguclu

Copy link
Copy Markdown
Member

Thank you
/lgtm
/hold
for @tjungblu looks at

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 27, 2026
@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 27, 2026
@openshift-ci

openshift-ci Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ardaguclu, p0lyn0mial

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

The pull request process is described here

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

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

@tjungblu

Copy link
Copy Markdown
Contributor

/hold cancel

All good! Thanks @p0lyn0mial

@openshift-ci openshift-ci Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 27, 2026
@p0lyn0mial

Copy link
Copy Markdown
Contributor Author

/retest

@openshift-ci

openshift-ci Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

@p0lyn0mial: all tests passed!

Full PR test history. Your PR dashboard.

Details

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

@openshift-merge-bot
openshift-merge-bot Bot merged commit 4d897a5 into openshift:master Jul 27, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants