Skip to content

OCPBUGS-57437: Always validate EgressIPs - #309

Open
mdbooth wants to merge 5 commits into
openshift:mainfrom
mdbooth:OCPBUGS-57437
Open

OCPBUGS-57437: Always validate EgressIPs#309
mdbooth wants to merge 5 commits into
openshift:mainfrom
mdbooth:OCPBUGS-57437

Conversation

@mdbooth

@mdbooth mdbooth commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

EgressIPs manifest as additional IP addresses on a Node. They are dynamic, so can be added and removed. Even though they cannot be used to serve incoming traffic, CCMs cannot currently distinguish EgressIPs and they will typically be added to a Node's Addresses. Consequently kubelet will typically request a CSR containing EgressIPs when they are present on a Node.

We had some existing code to make the approver aware of OpenShiftSDN egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:

  • Extend support for EgressIP matching to OVN Kubernetes
  • Ensure that a Node cannot continue to request an EgressIP which it no longer has

For the first, we also fetch OVNKubernetes EgressIP based on the infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know the complete set of permitted address in a UPI cluster. To enable this for IPI clusters, we deterministically split the authorisation path chosen based on the presence of a Machine object for a Node. We no longer permit fall-through authentication. A side benefit of this is that the error messages reported on authentication failures become shorter. However, note from the test changes that this doesn't result in any additional failure modes.

Summary by CodeRabbit

  • New Features
    • CSR approval now supports OVN-Kubernetes EgressIP resources when validating serving certificates.
    • Serving-certificate SAN validation accepts assigned node egress IPs alongside existing certificate addresses.
  • Bug Fixes
    • Improved CSR authorization error handling by distinguishing validation failures from retryable issues.
    • Unauthorized certificate requests are now ignored without unnecessary retries.
  • Chores
    • Updated supporting dependencies and access permissions for EgressIP resources.

@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 3, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@mdbooth: This pull request references Jira Issue OCPBUGS-57437, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

EgressIPs manifest as additional IP addresses on a Node. They are dynamic, so can be added and removed. Even though they cannot be used to serve incoming traffic, CCMs cannot currently distinguish EgressIPs and they will typically be added to a Node's Addresses. Consequently kubelet will typically request a CSR containing EgressIPs when they are present on a Node.

We had some existing code to make the approver aware of OpenShiftSDN egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:

  • Extend support for EgressIP matching to OVN Kubernetes
  • Ensure that a Node cannot continue to request an EgressIP which it no longer has

For the first, we also fetch OVNKubernetes EgressIP based on the infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know the complete set of permitted address in a UPI cluster. To enable this for IPI clusters, we deterministically split the authorisation path chosen based on the presence of a Machine object for a Node. We no longer permit fall-through authentication. A side benefit of this is that the error messages reported on authentication failures become shorter. However, note from the test changes that this doesn't result in any additional failure modes.

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

@openshift-ci-robot openshift-ci-robot added the jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. label Jul 3, 2026
@mdbooth

mdbooth commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

/jira refresh

@openshift-ci

openshift-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

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

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

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jul 3, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@mdbooth: This pull request references Jira Issue OCPBUGS-57437, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @sunzhaohua2

Details

In response to this:

/jira refresh

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.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Walkthrough

This PR registers the OVN EgressIP CRD, adds egress-aware CSR authorization and serving-certificate validation, updates retry handling for validation errors, and refreshes tests and indirect dependencies.

Changes

OVN EgressIP CSR authorization

Layer / File(s) Summary
EgressIP registration and permissions
go.mod, main.go, manifests/01-rbac.yaml
Adds the OVN EgressIP dependency and scheme registration. Updates client caching and RBAC permissions for EgressIP resources.
Context-aware CSR validation
pkg/controller/csr_check.go, pkg/controller/controller.go
Discovers node EgressIPs, validates machine and renewal CSRs, uses context-aware kubelet certificate access, and marks validation failures as non-retryable.
Authorization test coverage
pkg/controller/csr_check_test.go, status_test.go
Adds IPI and UPI EgressIP cases, updates test fixtures and expectations, and aligns unchanged channel comments.

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

Suggested reviewers: theobarberbany, radekmanak


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error New reconcileCSR logging emits CSRValidationError details; machine validation errors include requested DNS/IP SANs and all machine addresses, which can expose internal hostnames and network data. Do not log validation-error contents. Log only the CSR name and a generic rejection reason, or redact DNS names and IP addresses before logging.
Test Structure And Quality ⚠️ Warning Ginkgo tests contain bare error assertions at status_test.go:50, 55, 90, and 120, which violate the required meaningful assertion-message rule. Add diagnostic messages to each HaveOccurred assertion, such as identifying the failed client creation, cleanup, or ClusterOperator creation.
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: validating EgressIPs during CSR authorization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All Ginkgo titles are static descriptive literals; no title contains node or namespace names, IPs, timestamps, UUIDs, generated suffixes, or runtime formatting.
Microshift Test Compatibility ✅ Passed The PR adds no new Ginkgo e2e tests. Changes use standard Go tests and modify existing Ginkgo formatting only, so the MicroShift API check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The PR adds no Ginkgo e2e tests. It modifies Go unit-test tables and only adjusts spacing in the existing Ginkgo status test; no SNO-sensitive assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed The PR adds no scheduling constraints. Deployment manifests are unchanged, and the only manifest change replaces HostSubnet RBAC with OVN EgressIP get/list/watch permissions.
Ote Binary Stdout Contract ✅ Passed The repository builds a controller manager, not an OTE binary; no OTE references or stdout writes exist. klog defaults to logtostderr=true and writes to os.Stderr.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added. status_test.go only changes spacing, and csr_check_test.go uses testing.T unit tests.
No-Weak-Crypto ✅ Passed The PR adds only standard TLS/x509 certificate verification and no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret/token comparisons.
Container-Privileges ✅ Passed No first-party manifest sets privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation; deployments require restricted-v2 SCC.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@openshift-ci

openshift-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@openshift-ci-robot: GitHub didn't allow me to request PR reviews from the following users: sunzhaohua2.

Note that only openshift members and repo collaborators can review this PR, and authors cannot review their own PRs.

Details

In response to this:

@mdbooth: This pull request references Jira Issue OCPBUGS-57437, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @sunzhaohua2

In response to this:

/jira refresh

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.

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.

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@mdbooth: This pull request references Jira Issue OCPBUGS-57437, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @sunzhaohua2

Details

In response to this:

EgressIPs manifest as additional IP addresses on a Node. They are dynamic, so can be added and removed. Even though they cannot be used to serve incoming traffic, CCMs cannot currently distinguish EgressIPs and they will typically be added to a Node's Addresses. Consequently kubelet will typically request a CSR containing EgressIPs when they are present on a Node.

We had some existing code to make the approver aware of OpenShiftSDN egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:

  • Extend support for EgressIP matching to OVN Kubernetes
  • Ensure that a Node cannot continue to request an EgressIP which it no longer has

For the first, we also fetch OVNKubernetes EgressIP based on the infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know the complete set of permitted address in a UPI cluster. To enable this for IPI clusters, we deterministically split the authorisation path chosen based on the presence of a Machine object for a Node. We no longer permit fall-through authentication. A side benefit of this is that the error messages reported on authentication failures become shorter. However, note from the test changes that this doesn't result in any additional failure modes.

Summary by CodeRabbit

  • New Features

  • CSR approval now accounts for node egress IPs and CIDRs when validating serving certificate requests.

  • Support was added for additional network-related resources used during certificate approval.

  • Bug Fixes

  • Improved handling of certificate signing requests so validation failures are reported more clearly.

  • Serving certificate renewal checks now allow valid IP ranges instead of requiring exact IP matches, reducing false rejections.

  • Chores

  • Updated several dependencies to newer versions.

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 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@openshift-ci-robot: GitHub didn't allow me to request PR reviews from the following users: sunzhaohua2.

Note that only openshift members and repo collaborators can review this PR, and authors cannot review their own PRs.

Details

In response to this:

@mdbooth: This pull request references Jira Issue OCPBUGS-57437, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @sunzhaohua2

In response to this:

EgressIPs manifest as additional IP addresses on a Node. They are dynamic, so can be added and removed. Even though they cannot be used to serve incoming traffic, CCMs cannot currently distinguish EgressIPs and they will typically be added to a Node's Addresses. Consequently kubelet will typically request a CSR containing EgressIPs when they are present on a Node.

We had some existing code to make the approver aware of OpenShiftSDN egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:

  • Extend support for EgressIP matching to OVN Kubernetes
  • Ensure that a Node cannot continue to request an EgressIP which it no longer has

For the first, we also fetch OVNKubernetes EgressIP based on the infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know the complete set of permitted address in a UPI cluster. To enable this for IPI clusters, we deterministically split the authorisation path chosen based on the presence of a Machine object for a Node. We no longer permit fall-through authentication. A side benefit of this is that the error messages reported on authentication failures become shorter. However, note from the test changes that this doesn't result in any additional failure modes.

Summary by CodeRabbit

  • New Features

  • CSR approval now accounts for node egress IPs and CIDRs when validating serving certificate requests.

  • Support was added for additional network-related resources used during certificate approval.

  • Bug Fixes

  • Improved handling of certificate signing requests so validation failures are reported more clearly.

  • Serving certificate renewal checks now allow valid IP ranges instead of requiring exact IP matches, reducing false rejections.

  • Chores

  • Updated several dependencies to newer versions.

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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

Inline comments:
In `@go.mod`:
- Line 48: The dependency on go.opentelemetry.io/otel/sdk is still pinned to
v1.39.0, which needs to be raised to a safe version. Update the OpenTelemetry
module set in go.mod to v1.40.0 or later, and make sure any related go.sum
entries are refreshed so the dependency graph stays consistent.

In `@pkg/controller/csr_check_test.go`:
- Line 82: The init path in csr_check_test.go is ignoring the error from
egressipv1.AddToScheme(scheme.Scheme), which is inconsistent with the existing
panic-on-failure pattern in init(). Update the init() setup around AddToScheme
to handle its returned error explicitly, using a panic or equivalent hard-fail
consistent with generateCertKeyPair, so golangci-lint errcheck is satisfied. Use
the AddToScheme call and init() as the key symbols to locate the change.

In `@pkg/controller/csr_check.go`:
- Around line 94-164: getNodeEgressIPs and getKubeletCA are still making API
requests with context.Background(), so they can hang without a deadline. Replace
the internal background context usage in getNodeEgressIPs (for the network,
EgressIP list, and HostSubnet reads) and in getKubeletCA with a bounded context
created locally with a timeout, and ensure it is canceled after each call. Keep
the fix localized to these two functions and preserve their existing error
handling.
- Around line 381-400: In authorizeServingRenewal, stop wrapping every
verifyCertificateCommonName failure as CSRValidationError so that cert-chain and
expiry verification failures from currentCert.Verify(options) can propagate back
to reconcileCSR as retryable errors. Keep the SAN/CN comparison checks in
authorizeServingRenewal as validation errors, but only convert the specific
mismatch cases to newCSRValidationError and let non-validation errors from
verifyCertificateCommonName pass through unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: be9227c1-af7b-4382-934e-d0a7667dc34b

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae3f15 and fc72252.

⛔ Files ignored due to path filters (249)
  • go.sum is excluded by !**/*.sum
  • vendor/cel.dev/expr/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/MODULE.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/checked.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/eval.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/explain.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/syntax.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/cel.dev/expr/value.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/pointer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/json.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yaml.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/bytestostr.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/lexer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jwriter/writer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/spf13/afero/.editorconfig is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/.golangci.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/copyOnWriteFs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/iofs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/lstater.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/mem/file.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/unionFile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/id.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/number.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/status.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/traces.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/internal/telemetry/value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/auto/sdk/tracer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/.clomonitor.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/.codespellignore is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/.lycheeignore is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CHANGELOG.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CODEOWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CONTRIBUTING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/RELEASING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/SECURITY-INSIGHTS.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/VERSIONING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/encoder.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/filter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/hash.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/internal/xxhash/xxhash.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/iterator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/key.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/kv.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/set.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/type_string.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/baggage/baggage.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/codes/codes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/dependencies.Dockerfile is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/errorhandler/errorhandler.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/handler.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/instruments.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/meter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/state.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/internal/global/trace.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/asyncint64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/meter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/noop/noop.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/syncfloat64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/syncint64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/baggage.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/propagation.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/trace_context.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/container.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/process.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/resource.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/provider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/MIGRATION.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/error_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/exception.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/schema.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/MIGRATION.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/error_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/exception.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/schema.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/auto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/hex.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/noop.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/noop/noop.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/trace.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/tracestate.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/versions.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/oauth2/deviceauth.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/oauth2/oauth2.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/oauth2/pkce.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/oauth2/token.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/oauth2/transport.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/time/rate/rate.go is excluded by !**/vendor/**, !vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/jsonpatch.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/CONTRIBUTING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/MAINTAINERS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/balancer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer/subconn.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/balancer_wrapper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/clientconn.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/credentials/credentials.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/credentials/insecure/insecure.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/credentials/tls.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/dialoptions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/encoding/encoding.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/encoding/gzip/gzip.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/encoding/internal/internal.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/encoding/proto/proto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/experimental/stats/metricregistry.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/experimental/stats/metrics.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/interceptor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/balancer/weight/weight.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/buffer/unbounded.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/channelz/trace.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/credentials/credentials.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/envconfig/envconfig.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/envconfig/xds.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/experimental.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/grpcsync/event.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/idle/idle.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/internal.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/resolver/delegatingresolver/delegatingresolver.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/stats/stats.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/status/status.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/client_stream.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/controlbuf.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/flowcontrol.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/handler_server.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/http2_client.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/http2_server.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/http_util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/server_stream.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/internal/transport/transport.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/mem/buffer_pool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/mem/buffer_slice.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/mem/buffers.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/picker_wrapper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/preloader.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/resolver/resolver.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/resolver_wrapper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/rpc_util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/server.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/stats/handlers.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/stats/stats.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/stream.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/grpc/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/editionssupport/editions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/encoding/text/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/filedesc/desc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/filedesc/editions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/impl/codec_map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/impl/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/impl/validate.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/internal/version/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/proto/decode.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/reflect/protodesc/desc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/reflect/protodesc/editions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/reflect/protodesc/proto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go is excluded by !**/vendor/**, !vendor/**
  • vendor/modules.txt is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (5)
  • go.mod
  • main.go
  • pkg/controller/controller.go
  • pkg/controller/csr_check.go
  • pkg/controller/csr_check_test.go

Comment thread go.mod Outdated
Comment thread pkg/controller/csr_check_test.go Outdated
Comment thread pkg/controller/csr_check.go Outdated
Comment thread pkg/controller/csr_check.go Outdated
Comment thread pkg/controller/controller.go
Comment thread pkg/controller/csr_check.go Outdated
// getNodeEgressIPs returns the egress IPs assigned to the given node.
// It determines the network type and queries the appropriate CRD:
// - OVNKubernetes: queries EgressIP objects from k8s.ovn.org/v1
// - OpenShiftSDN: queries HostSubnet objects

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.

SDN hasn't been supported since ~4.17 IIRC. We should verify this and remove any SDN references now

@mdbooth mdbooth Jul 6, 2026

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.

If you go back to the Jira that was my original plan. But @sdodson had asked for a 4.16 backport where it is required, and the code was already there so I left it in. I'm inclined to follow up with another patch removing SDN support back to 4.17.

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.

That comment is a year old, do we still intend to backport this all the way to 4.16? I would expect us to only backport this as far as 4.18 which is the oldest, currently in maintenance release.

Sustaining may wish to backport to 4.16 to support the EUS term 2 customers that are using 4.16 there, perhaps we should get their impression on whether this woud be something they want to backport?

// No error, the renewal is authorized.
return true, nil
// Use machine API authorization if the node has a machine.
machine, machineErr := machinehandlerpkg.FindMatchingMachineFromNodeRef(machines, nodeAsking)

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.

Why are we reversing this order of machine vs renewing against existing SANs? Seems like an unrelated change we don't necessarily need to include here?

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're not reversing it. We're eliminating it. There is no fallback any more. Either we have a Machine, in which case we do the Machine validation flow, or we don't, in which case we do the existing SAN validation flow.

This is relevant to EgressIPs because the IPI flow can reject a CSR with an EgressIP which was formerly present but is no longer present, but the existing SAN validation can't. If we allow a fallback back we remove that ability.

It's also interesting to note that if you look carefully at the tests, we haven't altered any existing test cases with this change, except by removing unrelated information from their error messages.

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 discussed this. @JoelSpeed additionally raised the question of whether this could impact platforms which use keepalived to manage a VIP, e.g. metal, vSphere, OpenStack.

To verify this, I checked the state of all e2e jobs running against the installer. I noted that, as expected, all UPI jobs implement an approve_csrs method to 'manually' approve CSRs. In contrast, only 1 IPI job currently does this: openshift-e2e-vsphere-hybrid-env. This job adds 2 BM nodes to a vSphere cluster. As the additional nodes will not be auto-approved, it approves the additional nodes.

We know that SAN-reapproval is not relevant to joining nodes, so this means that all IPI platforms under test are approved by CMA using the Machine-backed flow.

As for keepalived specifically, I checked what actually happens here. I created a vsphere cluster and found the control plane node which currently owned the external api VIP. I confirmed that the API VIP was added to br-ex as an additional IP address. I deleted kubelet's serving cert and restarted it, which caused it to request a new one. The new CSR:

  • Did not include the api server VIP
  • Was automatically approved

The reason was that the CCM had not added the VIP to the Node. The reason for this is that the CCM is only aware of IPs which have been added via infrastructure API calls. Keepalived is moving the VIP through L2 network shenanigans, and therefore isn't visible to the CCM.

By contrast, EgressIPs are added via infrastructure API calls, and are visible to the CCM. Therefore they must either be picked up by (ideally) the {Machine,Cluster}API provider, or CMA must be explicitly aware of them.

Comment thread pkg/controller/csr_check.go
Comment thread go.mod Outdated
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1
github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e
github.com/openshift/library-go v0.0.0-20260318142011-72bf34f474bc
github.com/ovn-kubernetes/ovn-kubernetes/go-controller v0.0.0-20260703130629-a6c3ed490571

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.

This is an unfortunate dependency, is it necessary? Is it worth looking at unstructured instead?

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.

Hah, that's exactly what the bot said. I said no. I think the ovn-kubernetes api package was fairly well isolated, no? IIRC this doesn't pull in anything other than apimachinery, which we've already got.

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.

I think Unstructured was viable here, I just prefer the relative safety of structured types whenever possible. I can look at this again, but IIRC it was just apimachinery, which I felt was safe enough.

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 discussed this. The resolution: on my recognizance 😅 I'm going to double check my assertion that the api-only import from ovn-kubernetes is as clean as I'm claiming, but if so then I'm ok with it.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/controller/csr_check.go (1)

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

Update the stale requeue comment.

This now returns CSRValidationError, which downstream reconciliation handles without retry, so the comment says the opposite of the current behavior.

🧹 Proposed comment fix
-			// return error so we requeue, in case machine network is out of date
-			// for some reason
+			// Return a validation error so invalid CSRs are not retried automatically.

Based on learnings, keep documentation aligned with actual behavior.

🤖 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/controller/csr_check.go` around lines 435 - 439, The requeue comment in
the CSR validation path is stale and contradicts the current behavior in the CSR
check logic. Update the comment near the code that logs the DNS name mismatch
and returns newCSRValidationError so it accurately states that this path returns
CSRValidationError and is handled by downstream reconciliation without retry,
rather than implying it will requeue; keep the wording aligned with the behavior
of csr_check.go and the validation flow.

Source: Learnings

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

Inline comments:
In `@pkg/controller/csr_check.go`:
- Line 603: The deferred conn.Close() in the CSR check flow is ignoring a
returned error, which violates the never-ignore-errors rule and the errcheck
hint. Update the cleanup in the function that opens conn so the close error is
handled explicitly, either by logging it or by propagating it through the
surrounding error path, while keeping the connection lifecycle in this same
function.
- Line 596: The log in csr_check.go currently exposes the kubelet endpoint via
the klog.Infof call in the serving-cert retrieval path. Update the logging
around the retrieval flow (near the existing nodeName/kubelet message) to omit
the kubelet value and keep only non-sensitive context such as the node name.
Make sure any related log statements in this code path follow the same rule and
do not print internal hostnames, addresses, or ports.
- Around line 310-315: Propagate the serving-cert lookup failure from the CSR
check path instead of swallowing it: in the getServingCert call site inside
csr_check logic, and any related reconcileCSR handling, return the actual error
when Node GET or internal-IP lookup fails so the reconciliation loop retries.
Keep only explicit validation/approval failures as non-retryable, and preserve
the existing logging around the getServingCert error path.

---

Nitpick comments:
In `@pkg/controller/csr_check.go`:
- Around line 435-439: The requeue comment in the CSR validation path is stale
and contradicts the current behavior in the CSR check logic. Update the comment
near the code that logs the DNS name mismatch and returns newCSRValidationError
so it accurately states that this path returns CSRValidationError and is handled
by downstream reconciliation without retry, rather than implying it will
requeue; keep the wording aligned with the behavior of csr_check.go and the
validation flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: ed680079-9f81-4e5a-9061-3a0a3a1e5554

📥 Commits

Reviewing files that changed from the base of the PR and between fc72252 and a870966.

⛔ Files ignored due to path filters (154)
  • go.sum is excluded by !**/*.sum
  • vendor/go.opentelemetry.io/otel/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/AGENTS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CHANGELOG.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CLAUDE.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CONTRIBUTING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/RELEASING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/encoder.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/hash.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/key.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/kv.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/set.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/type_string.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/attribute/value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/baggage/baggage.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/dependencies.Dockerfile is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/asyncint64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/instrument.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/syncfloat64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/metric/syncint64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/baggage.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/trace_context.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/requirements.txt is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/container.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_exec.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/host_id_readfile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/process.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/resource/resource.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/provider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/sdk/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.26.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/MIGRATION.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/error_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/exception.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/otelconv/metric.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.41.0/schema.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/auto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/config.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/trace.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/tracestate.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/versions.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/plan9/syscall_plan9.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/affinity_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ioctl_signed.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ioctl_unsigned.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/mkall.sh is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/mkerrors.sh is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/readv_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_darwin.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_linux_arm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_linux_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_linux_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_openbsd.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_solaris.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/syscall_unix.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_386.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_arm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_mips.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_386.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_386.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_arm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_mips.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/aliases.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/dll_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/registry/key.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/security_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/syscall_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/types_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/zsyscall_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/modules.txt is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (5)
  • go.mod
  • main.go
  • pkg/controller/controller.go
  • pkg/controller/csr_check.go
  • pkg/controller/csr_check_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • main.go
  • pkg/controller/controller.go
  • go.mod
  • pkg/controller/csr_check_test.go

Comment thread pkg/controller/csr_check.go Outdated
Comment thread pkg/controller/csr_check.go
Comment thread pkg/controller/csr_check.go
@mdbooth

mdbooth commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

/retest-required

@openshift-ci

openshift-ci Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

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

Test name Commit Details Required Rerun command
ci/prow/e2e-aws-operator 0db2470 link true /test e2e-aws-operator
ci/prow/e2e-aws-techpreview-post-install 0db2470 link true /test e2e-aws-techpreview-post-install

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.

Comment on lines +104 to +107
egressIPList := &egressipv1.EgressIPList{}
if err := c.List(ctx, egressIPList); err != nil {
return result, fmt.Errorf("could not list EgressIP objects: %v", err)
}

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.

Do you have RBAC for egressIP?

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.

Nope 😅

klog.Errorf("%v: IP address '%s' not in machine addresses: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return fmt.Errorf("IP address '%s' not in machine addresses: %s", san, strings.Join(attemptedAddresses, " "))
klog.Errorf("%v: IP address '%s' not in machine addresses or egress IPs: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return newCSRValidationError("IP address '%s' not in machine addresses or egress IPs: %s", san, strings.Join(attemptedAddresses, " "))

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.

Are the IPs and DNS already available when the CSR is first reconciled? CSRValidationError is being handled as permanent failure. Previously this returned error and requeued.

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.

This is ok because:

  • For the initial serving cert there are no egress IPs and we know Machine addresses are up to date
  • For rotations, kubelet will retry after 15 minutes by creating a new CSR

EgressIPs manifest as additional IP addresses on a Node. They are
dynamic, so can be added and removed. Even though they cannot be used to
serve incoming traffic, CCMs cannot currently distinguish EgressIPs and
they will typically be added to a Node's Addresses. Consequently kubelet
will typically request a CSR containing EgressIPs when they are present
on a Node.

We had some existing code to make the approver aware of OpenShiftSDN
egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:
* Extend support for EgressIP matching to OVN Kubernetes
* Ensure that a Node cannot continue to request an EgressIP which it no
  longer has

For the first, we also fetch OVNKubernetes EgressIP based on the
infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know
the complete set of permitted address in a UPI cluster. To enable this
for IPI clusters, we deterministically split the authorisation path
chosen based on the presence of a Machine object for a Node. We no
longer permit fall-through authentication. A side benefit of this is
that the error messages reported on authentication failures become
shorter. However, note from the test changes that this doesn't result in
any additional failure modes.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@mdbooth: An error was encountered querying GitHub for users with public email (zhsun@redhat.com) for bug OCPBUGS-57437 on the Jira server at https://redhat.atlassian.net. No known errors were detected, please see the full error message for details.

Full error message. Post "http://ghproxy/graphql": dial tcp 172.30.229.2:80: connect: connection refused

Please contact an administrator to resolve this issue, then request a bug refresh with /jira refresh.

Details

In response to this:

EgressIPs manifest as additional IP addresses on a Node. They are dynamic, so can be added and removed. Even though they cannot be used to serve incoming traffic, CCMs cannot currently distinguish EgressIPs and they will typically be added to a Node's Addresses. Consequently kubelet will typically request a CSR containing EgressIPs when they are present on a Node.

We had some existing code to make the approver aware of OpenShiftSDN egress IPs, but no equivalent for OVN Kubernetes.

This change fixes 2 aspects of validating a Node with EgressIPs:

  • Extend support for EgressIP matching to OVN Kubernetes
  • Ensure that a Node cannot continue to request an EgressIP which it no longer has

For the first, we also fetch OVNKubernetes EgressIP based on the infrastructure's networkType.

For the second, we can only do this for IPI clusters as we don't know the complete set of permitted address in a UPI cluster. To enable this for IPI clusters, we deterministically split the authorisation path chosen based on the presence of a Machine object for a Node. We no longer permit fall-through authentication. A side benefit of this is that the error messages reported on authentication failures become shorter. However, note from the test changes that this doesn't result in any additional failure modes.

Summary by CodeRabbit

  • New Features
  • CSR approval now supports OVN-Kubernetes EgressIP resources when validating serving certificates.
  • Serving-certificate SAN validation accepts assigned node egress IPs alongside existing certificate addresses.
  • Bug Fixes
  • Improved CSR authorization error handling by distinguishing validation failures from retryable issues.
  • Unauthorized certificate requests are now ignored without unnecessary retries.
  • Chores
  • Updated supporting dependencies and access permissions for EgressIP resources.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
pkg/controller/csr_check.go (1)

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

Reuse makeIPSet in subsetIPAddresses.

subsetIPAddresses builds the same string set that makeIPSet builds. Both are added in this change. Call the helper to remove the duplicate loop.

♻️ Proposed refactor
 func subsetIPAddresses(super, sub []net.IP) bool {
-	superSet := make(map[string]struct{})
-	for _, ipAddr := range super {
-		superSet[ipAddr.String()] = struct{}{}
-	}
-
+	superSet := makeIPSet(super)
 	for _, ipAddr := range sub {
 		if !ipInSet(superSet, ipAddr) {
 			return false
 		}
 	}
 
 	return true
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/csr_check.go` around lines 656 - 677, Update subsetIPAddresses
to initialize superSet by calling makeIPSet(super) instead of duplicating the
loop that converts IP addresses to strings, while preserving the existing
sub-address validation through ipInSet.
pkg/controller/csr_check_test.go (2)

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

Add a UPI case for a removed egress IP.

The IPI cases cover removal at lines 1598-1621. The UPI cases cover only "assigned to node" and "assigned elsewhere". The PR states that the UPI path cannot enforce removal, because the current certificate IPs remain in the allowed set. A test that asserts this documented asymmetry prevents a later refactor from silently changing it.

Add a case with node: withName("test", defaultNode()), csr: goodCSR, networkType: "OVNKubernetes", no egressIPObjs, and ca: []*x509.Certificate{parseCert(t, rootCertGood)}. The CSR IPs are still present in the current serving certificate, so authorize is true.

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

In `@pkg/controller/csr_check_test.go` around lines 1652 - 1726, Add a UPI test
case alongside the existing OVNKubernetes cases using node withName("test",
defaultNode()), goodCSR, no egressIPObjs, and the rootCertGood CA. Assert
authorization succeeds (authorize: true) with no error, documenting that removed
egress IPs remain allowed through the current serving certificate.

2433-2472: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a case for the IPv4-mapped IPv6 form.

subsetIPAddresses, makeIPSet, and ipInSet all key on net.IP.String(). That normalizes a 16-byte ::ffff:10.0.0.1 and a 4-byte 10.0.0.1 to the same key. The feature depends on this, because net.ParseIP on an EgressIP string can return a 16-byte value while x509 stores IPv4 SANs as 4 bytes. No case covers this.

💚 Proposed test case
 		{
 			name:     "sub is a subset with duplicates",
 			super:    []net.IP{tenDotOne, tenDotTwo},
 			sub:      []net.IP{tenDotOne, tenDotOne},
 			expected: true,
 		},
+		{
+			name:     "IPv4-mapped IPv6 matches dotted-quad",
+			super:    []net.IP{net.ParseIP("10.0.0.1")},
+			sub:      []net.IP{net.IPv4(10, 0, 0, 1).To4()},
+			expected: true,
+		},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/csr_check_test.go` around lines 2433 - 2472, Add a
table-driven test case to TestSubsetIPAddresses covering an IPv4-mapped IPv6
address in one input and the equivalent 4-byte IPv4 address in the other,
asserting subsetIPAddresses returns true. Use the existing tenDotOne value and
an explicitly constructed ::ffff:10.0.0.1 representation so the test verifies
normalization through makeIPSet and ipInSet.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/controller/csr_check_test.go`:
- Line 1952: Update the error assertions in the getServingCert test cases so an
empty tt.wantErr explicitly requires err to be nil before any substring check;
retain strings.Contains for non-empty expected-error prefixes and keep the
existing error-message validation for those cases.

In `@pkg/controller/csr_check.go`:
- Around line 285-290: Update the getServingCert error handling in reconcileCSR
so kubelet TLS verification failures return a CSRValidationError, making
first-boot UPI serving CSRs non-retryable, while preserving plain errors for
infrastructure failures such as Node lookup or missing internal IP. Reuse the
existing CSRValidationError classification and queue behavior symbols rather
than changing unrelated retry logic.
- Around line 436-444: Update the denial log and newCSRValidationError message
in the CSR validation path to include the node’s egress IPs alongside
attemptedAddresses, while preserving the existing machine-address details and
wording. Use the egressIPs value already checked by ipInSet, and update affected
expected strings in the related CSR validation tests.

---

Nitpick comments:
In `@pkg/controller/csr_check_test.go`:
- Around line 1652-1726: Add a UPI test case alongside the existing
OVNKubernetes cases using node withName("test", defaultNode()), goodCSR, no
egressIPObjs, and the rootCertGood CA. Assert authorization succeeds (authorize:
true) with no error, documenting that removed egress IPs remain allowed through
the current serving certificate.
- Around line 2433-2472: Add a table-driven test case to TestSubsetIPAddresses
covering an IPv4-mapped IPv6 address in one input and the equivalent 4-byte IPv4
address in the other, asserting subsetIPAddresses returns true. Use the existing
tenDotOne value and an explicitly constructed ::ffff:10.0.0.1 representation so
the test verifies normalization through makeIPSet and ipInSet.

In `@pkg/controller/csr_check.go`:
- Around line 656-677: Update subsetIPAddresses to initialize superSet by
calling makeIPSet(super) instead of duplicating the loop that converts IP
addresses to strings, while preserving the existing sub-address validation
through ipInSet.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: abcb9044-baed-43cc-8906-a1974352630a

📥 Commits

Reviewing files that changed from the base of the PR and between cdf2735 and e794af4.

⛔ Files ignored due to path filters (28)
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/go-openapi/jsonpointer/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/pointer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/crd/egressip/v1/zz_generated.model_name.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/spf13/afero/.editorconfig is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/.golangci.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/copyOnWriteFs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/iofs.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/lstater.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/mem/file.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/unionFile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/spf13/afero/util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/gomodules.xyz/jsonpatch/v2/jsonpatch.go is excluded by !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/rpc/errdetails/error_details.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/modules.txt is excluded by !**/vendor/**, !vendor/**
  • vendor/sigs.k8s.io/structured-merge-diff/v6/fieldpath/element.go is excluded by !**/vendor/**, !vendor/**
  • vendor/sigs.k8s.io/structured-merge-diff/v6/fieldpath/pathelementmap.go is excluded by !**/vendor/**, !vendor/**
  • vendor/sigs.k8s.io/structured-merge-diff/v6/fieldpath/set.go is excluded by !**/vendor/**, !vendor/**
  • vendor/sigs.k8s.io/structured-merge-diff/v6/value/allocator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/sigs.k8s.io/structured-merge-diff/v6/value/jsontagutil.go is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (7)
  • go.mod
  • main.go
  • manifests/01-rbac.yaml
  • pkg/controller/controller.go
  • pkg/controller/csr_check.go
  • pkg/controller/csr_check_test.go
  • status_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/controller/controller.go

node: wrongAddr,
rootCerts: []*x509.Certificate{parseCert(t, rootCertGood)},
wantErr: "dial tcp 127.0.0.1:25544: connect: connection refused",
wantErr: "dial tcp 127.0.0.1:25544:",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

strings.Contains makes the success cases unable to fail.

tt.wantErr is the empty string for the "all good" case. strings.Contains(anything, "") returns true, so line 1990 passes for any error. Line 1993 then guards on err == nil, so it also skips. A regression that makes getServingCert return an error for a valid node would pass this test.

Keep strings.Contains for the prefix cases, but assert err == nil explicitly when tt.wantErr is empty.

💚 Proposed fix
 			go respond(server)
 			serverCert, err := getServingCert(t.Context(), cl, tt.nodeName, certPool)
-			if !strings.Contains(errString(err), tt.wantErr) {
+			if tt.wantErr == "" {
+				if err != nil {
+					t.Fatalf("got: %v, want: no error", err)
+				}
+			} else if !strings.Contains(errString(err), tt.wantErr) {
 				t.Fatalf("got: %v, want: %s", err, tt.wantErr)
 			}
 			if err == nil && !serverCert.Equal(parseCert(t, serverCertGood)) {
 				t.Fatal("Expected server certificate match on success")
 			}

Also applies to: 1989-1995

🤖 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/controller/csr_check_test.go` at line 1952, Update the error assertions
in the getServingCert test cases so an empty tt.wantErr explicitly requires err
to be nil before any substring check; retain strings.Contains for non-empty
expected-error prefixes and keep the existing error-message validation for those
cases.

Comment on lines +285 to 290
servingCert, err := getServingCert(ctx, c, nodeAsking, ca)
if err != nil {
klog.Infof("Could not determine if egress enabled: %v", err)
return false, fmt.Errorf("could not determine if egress enabled: %v", err)
}

if servingCert != nil && egressEnabled {
klog.Infof("Falling back to serving cert renewal with Egress IP checks")
if err := authorizeServingRenewalWithEgressIPs(c, nodeAsking, csr, servingCert, x509VerificationOpts); err != nil {
approvalErrors = append(approvalErrors, err)
klog.Infof("Could not use current serving cert and egress IPs for renewal: %v", err)
} else {
// No error means the machine was able to authorize the cert
return true, nil
}
// Could not reach kubelet or verify cert. Not auto-approvable on
// this attempt; kubelet will resubmit the CSR.
return false, fmt.Errorf("failed to retrieve current serving cert: %w", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

getServingCert failures are now retryable for every UPI serving CSR.

getServingCert returns an error for two very different conditions:

  1. Infrastructure failures, for example the Node GET fails or the node has no internal IP.
  2. Kubelet TLS failures, for example the kubelet presents a self-signed or otherwise untrusted certificate.

Both are returned as plain errors here, so reconcileCSR classifies both as retryable and requeues forever with backoff. Condition 2 is expected on a UPI node that has no signed serving certificate yet. Those CSRs are never approvable through this path, so the item stays in the workqueue permanently.

Confirm that this is the intended queue behavior for UPI first-boot serving CSRs. If it is not, return a CSRValidationError for TLS verification failures and keep the plain error for the Node lookup failures.

🤖 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/controller/csr_check.go` around lines 285 - 290, Update the
getServingCert error handling in reconcileCSR so kubelet TLS verification
failures return a CSRValidationError, making first-boot UPI serving CSRs
non-retryable, while preserving plain errors for infrastructure failures such as
Node lookup or missing internal IP. Reuse the existing CSRValidationError
classification and queue behavior symbols rather than changing unrelated retry
logic.

Comment on lines +436 to +444
// The CSR requested an IP not in machine addresses.
// Check if it's an egress IP assigned to this node.
if ipInSet(makeIPSet(egressIPs), san) {
continue
}

//TODO: set annotation/emit event here.
// return error so we requeue, in case machine network is out of date
// for some reason
klog.Errorf("%v: IP address '%s' not in machine addresses: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return fmt.Errorf("IP address '%s' not in machine addresses: %s", san, strings.Join(attemptedAddresses, " "))
klog.Errorf("%v: IP address '%s' not in machine addresses or egress IPs: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return newCSRValidationError("IP address '%s' not in machine addresses or egress IPs: %s", san, strings.Join(attemptedAddresses, " "))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

The denial message lists only machine addresses but claims to list egress IPs.

attemptedAddresses collects machine addresses only. The message text is "not in machine addresses or egress IPs: ". A reader who debugs an EgressIP denial cannot tell whether the node had zero assigned egress IPs or a different set. Include the egress IPs in the message.

🛠️ Proposed fix
 			//TODO: set annotation/emit event here.
-			klog.Errorf("%v: IP address '%s' not in machine addresses or egress IPs: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
-			return newCSRValidationError("IP address '%s' not in machine addresses or egress IPs: %s", san, strings.Join(attemptedAddresses, " "))
+			egressStrings := make([]string, 0, len(egressIPs))
+			for _, ip := range egressIPs {
+				egressStrings = append(egressStrings, ip.String())
+			}
+			klog.Errorf("%v: IP address '%s' not in machine addresses [%s] or egress IPs [%s]", req.Name, san, strings.Join(attemptedAddresses, " "), strings.Join(egressStrings, " "))
+			return newCSRValidationError("IP address '%s' not in machine addresses [%s] or egress IPs [%s]", san, strings.Join(attemptedAddresses, " "), strings.Join(egressStrings, " "))
 		}

This changes the expected strings in pkg/controller/csr_check_test.go at lines 744, 774, 1595, 1619, and 1704.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// The CSR requested an IP not in machine addresses.
// Check if it's an egress IP assigned to this node.
if ipInSet(makeIPSet(egressIPs), san) {
continue
}
//TODO: set annotation/emit event here.
// return error so we requeue, in case machine network is out of date
// for some reason
klog.Errorf("%v: IP address '%s' not in machine addresses: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return fmt.Errorf("IP address '%s' not in machine addresses: %s", san, strings.Join(attemptedAddresses, " "))
klog.Errorf("%v: IP address '%s' not in machine addresses or egress IPs: %s", req.Name, san, strings.Join(attemptedAddresses, " "))
return newCSRValidationError("IP address '%s' not in machine addresses or egress IPs: %s", san, strings.Join(attemptedAddresses, " "))
// The CSR requested an IP not in machine addresses.
// Check if it's an egress IP assigned to this node.
if ipInSet(makeIPSet(egressIPs), san) {
continue
}
//TODO: set annotation/emit event here.
egressStrings := make([]string, 0, len(egressIPs))
for _, ip := range egressIPs {
egressStrings = append(egressStrings, ip.String())
}
klog.Errorf("%v: IP address '%s' not in machine addresses [%s] or egress IPs [%s]", req.Name, san, strings.Join(attemptedAddresses, " "), strings.Join(egressStrings, " "))
return newCSRValidationError("IP address '%s' not in machine addresses [%s] or egress IPs [%s]", san, strings.Join(attemptedAddresses, " "), strings.Join(egressStrings, " "))
🤖 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/controller/csr_check.go` around lines 436 - 444, Update the denial log
and newCSRValidationError message in the CSR validation path to include the
node’s egress IPs alongside attemptedAddresses, while preserving the existing
machine-address details and wording. Use the egressIPs value already checked by
ipInSet, and update affected expected strings in the related CSR validation
tests.

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

Labels

jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants