Skip to content

CM-902: Fix golangci-lint errors#368

Open
anandkuma77 wants to merge 14 commits intoopenshift:masterfrom
anandkuma77:cm-902-golangci-lint-part2
Open

CM-902: Fix golangci-lint errors#368
anandkuma77 wants to merge 14 commits intoopenshift:masterfrom
anandkuma77:cm-902-golangci-lint-part2

Conversation

@anandkuma77
Copy link
Contributor

@anandkuma77 anandkuma77 commented Jan 29, 2026

Enable Additional golangci-lint Linters and Fix Reported Issues

What

This PR enables additional golangci-lint linters (err113, mnd, cyclop, maintidx, nestif, unparam, and dupl) and fixes all the code quality issues they report across the codebase. The changes include:

  • err113: Fixed dynamic error issues by replacing dynamic errors with static error types
  • mnd (magic number detector): Addressed magic number violations by extracting constants or adding appropriate comments
  • cyclop: Reduced cyclomatic complexity in functions by refactoring complex logic
  • maintidx: Improved maintainability index by simplifying code structure
  • nestif: Reduced nested if statements by extracting functions or using early returns
  • unparam: Removed unused function parameters
  • dupl: Eliminated code duplication by extracting common logic
  • Fixed issues with generateName resource updates in RBAC reconciliation logic

Why

Enabling these additional linters improves code quality, maintainability, and consistency across the codebase. These linters help:

  • err113: Ensures consistent error handling patterns and makes errors easier to test and handle
  • mnd: Improves code readability by making magic numbers explicit and self-documenting
  • cyclop: Reduces complexity, making code easier to understand, test, and maintain
  • maintidx: Helps identify and improve code that may be difficult to maintain over time
  • nestif: Reduces cognitive load by flattening nested conditionals
  • unparam: Removes dead code and unused parameters, keeping the codebase clean
  • dupl: Prevents code duplication which can lead to maintenance issues and bugs

This is part of an ongoing effort (CM-902) to improve code quality standards and ensure the codebase adheres to best practices.

- Refactor TestCreateOrApplyDeployments: extract complex updateIstioCSR functions into helper functions
- Refactor TestCreateOrApplyRBACResource: extract helper functions for status updates
- Refactor TestProcessReconcileRequest: already had helper functions, verified complexity is reduced
- Enable gocyclo linter in .golangci.yaml
- All test cases and coverage preserved, no functionality changed
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jan 29, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Jan 29, 2026

@anandkuma77: This pull request references CM-902 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target either version "4.22." or "openshift-4.22.", but it targets "cert-manager-1.19" instead.

Details

In response to this:

  • Refactor TestCreateOrApplyDeployments: extract complex updateIstioCSR functions into helper functions
  • Refactor TestCreateOrApplyRBACResource: extract helper functions for status updates
  • Refactor TestProcessReconcileRequest: already had helper functions, verified complexity is reduced
  • Enable gocyclo linter in .golangci.yaml
  • All test cases and coverage preserved, no functionality changed

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-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 29, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Walkthrough

Large refactor: adds many internal helpers, sentinel errors, logging verbosity constants, and centralized reconciliation flows across IstioCSR, RBAC, deployments, certificates, CA handling, operator startup; introduces cloud-credentials mounting helpers with unit tests; enables additional linters and removes many lint exclusions.

Changes

Cohort / File(s) Summary
Linting configuration
\.golangci\.yaml
Enables multiple linters (dupl, dupword, gocognit, gocyclo, revive, maintidx, err113, mnd, cyclop) and removes numerous per-path exclusions and specialized ignores.
Deployment — credentials & tests
pkg/controller/deployment/credentials_request.go, pkg/controller/deployment/credentials_request_test.go
Adds cloud-secret verification, platform dispatch (createCloudCredentialsResources, AWS/GCP creators), applyCloudCredentialsToDeployment, sentinel errors, and comprehensive unit tests.
Deployment — overrides, helpers, tests
pkg/controller/deployment/...
pkg/controller/deployment/cert_manager_networkpolicy.go, deployment_helper.go, deployment_helper_test.go, deployment_overrides.go, deployment_overrides_validation.go, deployment_unsupported_overrides.go, default_cert_manager_controller.go
Introduces package-level sentinel errors, defaultVolumeMode constant, wraps errors with %w, extracts validation helpers, adds test helpers for scheduling, and tightens error messages.
IstioCSR — controller & predicates
pkg/controller/istiocsr/controller.go
Extracts reconcile map and predicates, adds label parsing, interest checks, multi-instance handling, and centralized reconcile error/success handlers with new helper functions.
IstioCSR — certificates & constants
pkg/controller/istiocsr/certificates.go, pkg/controller/istiocsr/constants.go, pkg/controller/istiocsr/certificates_test.go
Splits certificate updates into focused helpers (CN, DNS, URIs, duration, renewBefore, private-key defaults/validation, issuer ref), adds errCertificateParamsNotCompliant and logging verbosity constants, and updates related tests' expected messages.
IstioCSR — deployments & CA handling
pkg/controller/istiocsr/deployments.go, install_istiocsr.go
Centralizes error variables and defaultVolumeMode; refactors deployment create/update into helpers; adds user-provided vs issuer-derived CA flows, PEM validation, buildResourceLabels, reconcileAllResources, and updateProcessedAnnotation.
IstioCSR — RBAC reconciliation
pkg/controller/istiocsr/rbacs.go, pkg/controller/istiocsr/rbacs_test.go
Replaces ad-hoc RBAC logic with a generic reconcileRBACResource flow and many find/create/update helpers for ClusterRole/ClusterRoleBinding/Role/RoleBinding; adds sentinel errors and standardizes status/error wrapping; updates many RBAC tests.
IstioCSR — supporting resources & tests
pkg/controller/istiocsr/networkpolicies.go, serviceaccounts.go, services.go, test_utils.go, services_test.go, install_istiocsr_test.go
Replaces hard-coded V(4) logging with logVerbosityLevelDebug, wraps some errors with context, parameterizes test constants, and updates tests to expect new error prefixes.
IstioCSR — utils, comparisons, multi-instance logic
pkg/controller/istiocsr/utils.go, pkg/controller/istiocsr/test_utils.go
Adds many internal sentinel errors, refactors decode helpers to return typed objects, breaks object/spec comparison into per-type checks, and enhances multi-instance/processing-ignore handling.
IstioCSR — client & misc small changes
pkg/controller/istiocsr/client.go, pkg/controller/istiocsr/controller_test.go, deployments_test.go, other *_test.go
Minor error-wrapping changes in UpdateWithRetry and adjusted test expectations for wrapped error messages.
Operator — status apply & startup refactor
pkg/operator/operatorclient/operatorclient.go, pkg/operator/starter.go, pkg/operator/optionalinformer/optional_informer_test.go
Adds Clock field to OperatorClient, refactors ApplyOperatorStatus into new/existing instance flows with helpers (status extraction, canonicalization, transition times), modularizes operator initialization/startup, and swaps one test fmt->errors.
Misc small fixes
pkg/controller/deployment/default_cert_manager_controller.go, pkg/controller/deployment/deployment_unsupported_overrides.go, various tests
Small error-wrapping additions, logging tweaks, and updated test expectation strings.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@openshift-ci openshift-ci bot requested review from mytreya-rh and swghosh January 29, 2026 05:28
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 29, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: anandkuma77
Once this PR has been reviewed and has the lgtm label, please assign trilokgeer 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

Anand Kumar added 6 commits January 29, 2026 13:45
- Enable dupl linter in .golangci.yaml
- Refactor duplicate code in rbacs.go by extracting common reconciliation logic
  - Created reconcileRBACResource() generic helper function
  - Created reconcileRole() and reconcileRoleBinding() wrapper functions
  - Removed duplication between createOrApplyRoles/createOrApplyRoleForLeases
  - Removed duplication between createOrApplyRoleBindings/createOrApplyRoleBindingForLeases
- All tests passing after refactoring
- Enable maintidx linter in golangci-lint configuration
- Fix TestMergePodScheduling maintainability by extracting helper functions
  for creating tolerations, node selectors, and scheduling objects
- Fix TestCreateOrApplyDeployments maintainability by extracting
  inline functions into named wrapper functions
- All test cases and code coverage preserved
- Enable err113 linter in golangci-lint configuration
- Replace all dynamic fmt.Errorf() calls with wrapped static errors
- Add static error variables at package level for better error handling
- Fix 40 err113 errors across 12 files:
  - deployment: cert_manager_networkpolicy, credentials_request,
    deployment_helper, deployment_overrides_validation
  - istiocsr: certificates, controller, deployments, install_instiocsr_test,
    rbacs, utils
  - operator: operatorclient, optional_informer_test
- Improve error handling consistency and enable better error checking
  with errors.Is() and errors.As()
- Enable 'mnd' linter in .golangci.yaml
- Replace magic numbers with named constants:
  - Add defaultVolumeMode constant (420) for file permission mode
  - Add log verbosity level constants (logVerbosityLevelDebug=4, logVerbosityLevelInfo=2)
  - Add expectedLabelParts constant (2) for label format validation
  - Add test constants (testFakeRecorderBufferSize, testCertificateDuration, testRenewBeforeDuration)
  - Use existing DefaultRSAPrivateKeySize constant for RSA key size
- Fix all 13 magic number issues across multiple files
- Enable 'cyclop' linter in .golangci.yaml
- Refactor high complexity functions by extracting helper functions:
  - Deployment controller: withCloudCredentials, validation hooks
  - IstioCSR certificates: updateCertificateParams split into focused helpers
  - IstioCSR controller: SetupWithManager and processReconcileRequest refactored
  - IstioCSR deployments: createOrApplyDeployments simplified
  - IstioCSR install: reconcileIstioCSRDeployment extracted resource loop
  - IstioCSR RBAC: createOrApplyClusterRoles/Bindings extracted find/reconcile logic
  - IstioCSR utils: hasObjectChanged, deploymentSpecModified, disallowMultipleIstioCSRInstances refactored
  - Operator client: ApplyOperatorStatus split into focused helpers
  - Operator starter: RunOperator extracted initialization and setup logic
- Fix error message format in deployments.go to match test expectations
- Fix multiple instance check logic to properly stop processing when detected
- All unit tests passing after refactoring
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pkg/operator/optionalinformer/optional_informer_test.go (1)

53-60: ⚠️ Potential issue | 🟡 Minor

Static analysis still flags this as a dynamic error (err113).

The change from fmt.Errorf to errors.New doesn't satisfy the err113 linter. To fully address the lint error, define a package-level sentinel error.

Proposed fix
 type alwaysErrorFakeDiscovery struct {
 	fakediscovery.FakeDiscovery
 }
+
+var errExpectedFoo = errors.New("expected foo error")

 // ServerResourcesForGroupVersion is the only func that OptionalInformer's discovery client calls.
 func (f *alwaysErrorFakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
-	return nil, errors.New("expected foo error")
+	return nil, errExpectedFoo
 }
pkg/controller/deployment/deployment_overrides_validation.go (1)

120-127: ⚠️ Potential issue | 🟡 Minor

Wrong error sentinel used for environment variable validation.

The validateEnv function uses errUnsupportedArg when reporting unsupported environment variables. This should use a dedicated error variable (e.g., errUnsupportedEnv) for semantic correctness and clearer error messages.

Proposed fix

Add a new error variable and update the validation:

 var (
 	errUnsupportedArg           = errors.New("validation failed due to unsupported arg")
+	errUnsupportedEnv           = errors.New("validation failed due to unsupported env")
 	errUnsupportedLabel         = errors.New("validation failed due to unsupported label")
 	errUnsupportedResourceLimit = errors.New("validation failed due to unsupported resource limits")
 	errUnsupportedResourceReq   = errors.New("validation failed due to unsupported resource requests")
 )
 	validateEnv := func(argMap map[string]corev1.EnvVar, supportedEnv []string) error {
 		for k, v := range argMap {
 			if !slices.Contains(supportedEnv, k) {
-				return fmt.Errorf("%w: %q=%q", errUnsupportedArg, k, v)
+				return fmt.Errorf("%w: %q=%q", errUnsupportedEnv, k, v)
 			}
 		}
 		return nil
 	}
🤖 Fix all issues with AI agents
In `@pkg/controller/deployment/credentials_request.go`:
- Around line 134-150: The function applyCloudCredentialsToDeployment currently
indexes deployment.Spec.Template.Spec.Containers[0] without guarding against an
empty slice; update it to first check
len(deployment.Spec.Template.Spec.Containers) == 0 and if so append a new
corev1.Container{} (or return an error/log and skip mutation) before accessing
Containers[0], then proceed to append the VolumeMount and Env (respecting the
existing envVar nil check); ensure you reference
deployment.Spec.Template.Spec.Containers, VolumeMounts and Env when making the
defensive check and modification.

In `@pkg/controller/istiocsr/controller.go`:
- Around line 134-189: The linter wants unexported helpers placed after the
exported Reconcile method; move the methods createReconcileMapFunc,
extractNamespaceFromLabels, and isObjectOfInterest so they are defined below the
Reconcile(...) method in the same file, keeping their signatures and logic
unchanged and updating any references if necessary so Reconcile can call them as
before.
- Around line 181-185: The r.log.Error call in controller.go is using a
printf-style message with positional args; update the r.log.Error invocation in
the block that checks key/expectedLabelParts (variables: key,
expectedLabelParts) to pass a short message and then structured key/value pairs
(e.g. "invalid label format", "error", errInvalidLabelFormat, "label",
IstiocsrResourceWatchLabelName, "value", value, "resource", obj.GetName()) so
the logr.Logger receives even-numbered key/value pairs after the message; ensure
you remove the formatted string and positional args and replace them with the
structured pairs in the r.log.Error call.

In `@pkg/controller/istiocsr/deployments.go`:
- Around line 29-33: Remove the unused sentinel error variable
errKeyNotFoundInConfigMap from the var block (alongside errIstioCSRImageNotSet,
errFailedToFetchCACertificate, errFailedToFindCACertificate) so golangci-lint
unused check passes; simply delete the declaration of errKeyNotFoundInConfigMap
and run `go vet`/`golangci-lint` to verify no other references exist.

In `@pkg/controller/istiocsr/utils.go`:
- Around line 505-515: In checkForMultipleInstances replace the dynamic error
construction using errors.New(statusMessage) with fmt.Errorf to satisfy err113;
specifically, pass fmt.Errorf(statusMessage) (or fmt.Errorf("%s",
statusMessage)) into NewMultipleInstanceError so the call becomes
NewMultipleInstanceError(fmt.Errorf(statusMessage)), keeping the existing
statusMessage and NewMultipleInstanceError usage intact.
- Around line 27-34: The declared sentinel
errFailedToCreateIstioCSRWithFinalizersAdded is unused; update the addFinalizer
function to wrap the actual creation error with this sentinel instead of
constructing a new fmt.Errorf string directly (use fmt.Errorf with %w to wrap,
e.g. wrap the error returned from the client Create call with
errFailedToCreateIstioCSRWithFinalizersAdded), or if you prefer the other option
remove the errFailedToCreateIstioCSRWithFinalizersAdded declaration to avoid the
unused variable; reference symbols: errFailedToCreateIstioCSRWithFinalizersAdded
and addFinalizer.
- Around line 489-493: The code dynamically creates an error with
errors.New(statusMessage) which violates err113; define a package-level sentinel
error (e.g., var ErrInstanceFailed = errors.New("instance failed")) and replace
the dynamic creation by wrapping that sentinel with the statusMessage using
fmt.Errorf with %w (so the original message is preserved but the error is a
static sentinel), then pass the wrapped error into utilerrors.NewAggregate
alongside updateErr in the NewMultipleInstanceError call; update references
around NewMultipleInstanceError, utilerrors.NewAggregate, statusMessage, and
updateErr accordingly.

In `@pkg/operator/operatorclient/operatorclient.go`:
- Around line 62-69: The linter flagged ordering: move the unexported helper
methods (applyStatusForNewInstance, applyStatusForExistingInstance,
extractOperatorStatus, setConditionTransitionTimes, canonicalizeStatuses,
isStatusUnchanged, applyStatus) so they appear after the last exported method
RemoveFinalizer and before saveFinalizers; reorder the file accordingly to place
all those unexported functions below RemoveFinalizer to satisfy funcorder rules
without changing their signatures or behavior.
- Around line 143-148: Remove the unused operatorStatus parameter from the
prepareStatusForApply signature and all call sites; update func
prepareStatusForApply(desired *applyconfig.CertManagerApplyConfiguration,
desiredConfiguration *applyoperatorv1.OperatorStatusApplyConfiguration) to drop
operatorStatus, and modify any callers (e.g., the call that currently passes
operatorStatus around) to call prepareStatusForApply with only the desired and
desiredConfiguration arguments; ensure you update imports/types if necessary to
match the new parameter list.

In `@pkg/operator/starter.go`:
- Around line 209-222: The startIstioCSRController currently calls
ctrl.SetupSignalHandler() directly and blocks; change its signature to accept a
parent context (e.g., ctx context.Context) so it uses the operator's root
cancellation instead of creating a new signal handler; after creating the
manager via NewControllerManager(), start the manager with manager.Start(ctx)
but do so in a goroutine (since Start blocks) and surface startup errors via
logging or a channel (or return immediately and log errors) so callers like
RunOperator can pass in the root ctx and control shutdown consistently; remove
any direct calls to ctrl.SetupSignalHandler() from startIstioCSRController.
- Around line 69-71: The call to startIstioCSRController creates its own signal
handler (ctrl.SetupSignalHandler) causing inconsistent shutdown; change the call
site to pass the parent ctx into startIstioCSRController(ctx) and modify the
function signature startIstioCSRController to accept a context.Context
parameter, then replace ctrl.SetupSignalHandler() usage inside
startIstioCSRController with that passed ctx (use ctx.Done() or context-aware
helpers for controller manager lifecycle and informer stops) so the IstioCSR
controller honors cancellation of the parent context.
🧹 Nitpick comments (2)
pkg/operator/starter.go (1)

86-104: Wrap client initialization errors for better diagnostics.

Static analysis flags that errors from external packages should be wrapped. This provides better context when troubleshooting initialization failures.

♻️ Proposed fix
 	kubeClient, err := kubernetes.NewForConfig(cc.ProtoKubeConfig)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to create kubernetes client: %w", err)
 	}

 	certManagerOperatorClient, err := certmanoperatorclient.NewForConfig(cc.KubeConfig)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to create cert-manager operator client: %w", err)
 	}

 	apiExtensionsClient, err := apiextensionsclient.NewForConfig(cc.KubeConfig)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to create api extensions client: %w", err)
 	}

 	configClient, err := configv1client.NewForConfig(cc.KubeConfig)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("failed to create config client: %w", err)
 	}
pkg/controller/deployment/deployment_helper.go (1)

243-243: Consider applying the sentinel error pattern consistently.

The functions getOverrideReplicasFor, getOverrideResourcesFor, and getOverrideSchedulingFor still use inline error strings (fmt.Errorf("unsupported deployment name %q provided", ...)) while getOverrideArgsFor, getOverrideEnvFor, and getOverridePodLabelsFor were updated to use the new errUnsupportedDeploymentName sentinel.

For consistency and to fully satisfy err113, consider updating these three functions as well.

♻️ Proposed fix for consistency
 	default:
-		return nil, fmt.Errorf("unsupported deployment name %q provided", deploymentName)
+		return nil, fmt.Errorf("%w: %q", errUnsupportedDeploymentName, deploymentName)
 	}

Apply similar changes to lines 270 and 297.

Also applies to: 270-270, 297-297

Comment on lines +62 to +69
func (c OperatorClient) applyStatusForNewInstance(ctx context.Context, fieldManager string, desired *applyconfig.CertManagerApplyConfiguration, desiredConfiguration *applyoperatorv1.OperatorStatusApplyConfiguration) error {
v1helpers.SetApplyConditionsLastTransitionTime(c.Clock, &desiredConfiguration.Conditions, nil)
desiredStatus := &applyconfig.CertManagerStatusApplyConfiguration{
OperatorStatusApplyConfiguration: *desiredConfiguration,
}
desired.WithStatus(desiredStatus)
return c.applyStatus(ctx, fieldManager, desired)
}
Copy link

@coderabbitai coderabbitai bot Jan 30, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Move unexported helper methods after all exported methods.

The funcorder linter flagged all these new unexported methods (applyStatusForNewInstance, applyStatusForExistingInstance, extractOperatorStatus, setConditionTransitionTimes, canonicalizeStatuses, isStatusUnchanged, applyStatus) for being placed before exported methods. Per Go conventions enforced by the linter, they should be placed after RemoveFinalizer (the last exported method) and before saveFinalizers.

Since this PR specifically aims to fix golangci-lint errors, these funcorder violations should be addressed.

Also applies to: 71-91, 93-107, 109-118, 120-123, 125-141, 150-159

🧰 Tools
🪛 golangci-lint (2.5.0)

[error] 62-62: unexported method "applyStatusForNewInstance" for struct "OperatorClient" should be placed after the exported method "RemoveFinalizer"

(funcorder)

🤖 Prompt for AI Agents
In `@pkg/operator/operatorclient/operatorclient.go` around lines 62 - 69, The
linter flagged ordering: move the unexported helper methods
(applyStatusForNewInstance, applyStatusForExistingInstance,
extractOperatorStatus, setConditionTransitionTimes, canonicalizeStatuses,
isStatusUnchanged, applyStatus) so they appear after the last exported method
RemoveFinalizer and before saveFinalizers; reorder the file accordingly to place
all those unexported functions below RemoveFinalizer to satisfy funcorder rules
without changing their signatures or behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 30, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
.golangci.yaml (1)

112-114: ⚠️ Potential issue | 🔴 Critical

Fix typo in linter exclusion path: change pkg/operators to pkg/operator.

The exclusion rule targets pkg/operators (plural), but the actual directory is pkg/operator (singular). This causes the rule to be ineffective. If the intent is to exclude linters from the operator package (which contains auto-generated clientset, informers, and lister code alongside hand-written setup/starter code), correct the path to pkg/operator.

pkg/controller/istiocsr/rbacs.go (3)

166-177: ⚠️ Potential issue | 🟡 Minor

Silent failure: empty name is logged but not returned as an error.

When both desired.GetName() and existing.GetName() are empty, the function logs an error (line 172) but continues to update the status with an empty ClusterRole name and returns nil (assuming updateStatus succeeds). This silent failure could mask issues and cause confusion in subsequent reconciliation cycles.

Consider returning an error when the name cannot be determined:

🔧 Suggested fix
 func (r *Reconciler) updateClusterRoleNameInStatus(istiocsr *v1alpha1.IstioCSR, desired, existing *rbacv1.ClusterRole) (string, error) {
 	name := desired.GetName()
 	if name == "" {
 		if existing != nil && existing.GetName() != "" {
 			name = existing.GetName()
 		} else {
-			r.log.Error(errErrorUpdatingClusterRoleName, "istiocsr", istiocsr.GetNamespace())
+			return "", errErrorUpdatingClusterRoleName
 		}
 	}
 	istiocsr.Status.ClusterRole = name
 	return name, r.updateStatus(r.ctx, istiocsr)
 }

278-289: ⚠️ Potential issue | 🟡 Minor

Same silent failure pattern as updateClusterRoleNameInStatus.

This function has the same issue: when the name cannot be determined, an error is logged (line 284) but the status is updated with an empty name and no error is returned. Apply the same fix to return the error instead of just logging it.


1-13: ⚠️ Potential issue | 🔴 Critical

Define missing testError variable in test files.

testError is referenced in 40+ locations across multiple test files (rbacs_test.go, services_test.go, deployments_test.go, controller_test.go, certificates_test.go, install_instiocsr_test.go) but is never defined anywhere in the codebase. This will cause a compilation failure. Define testError as a package-level variable (e.g., in a test helper file or at the top of the test files).

🤖 Fix all issues with AI agents
In `@pkg/controller/istiocsr/controller.go`:
- Around line 331-346: The Ready condition in
Reconciler.handleIrrecoverableError is being set with v1alpha1.ReasonReady which
is inconsistent for an irrecoverable failure; change the call that sets Ready
(the second SetCondition on istiocsr.Status) to use v1alpha1.ReasonFailed (same
reason as Degraded) so Ready=False has a failure reason consistent with the
Degraded condition.

In `@pkg/controller/istiocsr/rbacs.go`:
- Around line 335-349: The current logic logs "resource already exists and is in
expected state" when exist==false because the else matches the combined
condition; change the control flow to first check exist and then handle
update/unchanged, otherwise handle creation so logs match reality: use if exist
{ if hasObjectChanged(desired,fetched) { call r.UpdateWithRetry(...);
r.eventRecorder.Eventf(...) } else { r.log.V(...).Info(...) } } else { call
r.Create(r.ctx, desired); r.eventRecorder.Eventf(...) } and keep the same error
handling via FromClientError for UpdateWithRetry and Create.
🧹 Nitpick comments (4)
.golangci.yaml (1)

32-32: Consider whether both gocyclo and cyclop are needed.

Both gocyclo (line 32) and cyclop (line 66) measure cyclomatic complexity. Having both enabled may produce duplicate or overlapping warnings for the same functions. If this is intentional (e.g., different thresholds or reporting preferences), feel free to ignore.

Also applies to: 66-66

pkg/controller/istiocsr/deployments.go (1)

88-95: Inconsistent logging verbosity constant usage.

Line 89 uses V(1) directly while other debug-level logs in this file (lines 57, 82, 612) use logVerbosityLevelDebug. Consider using the constant for consistency.

♻️ Suggested fix
 func (r *Reconciler) updateDeploymentResource(istiocsr *v1alpha1.IstioCSR, desired *appsv1.Deployment, deploymentName string) error {
-	r.log.V(1).Info("deployment has been modified, updating to desired state", "name", deploymentName)
+	r.log.V(logVerbosityLevelDebug).Info("deployment has been modified, updating to desired state", "name", deploymentName)
 	if err := r.UpdateWithRetry(r.ctx, desired); err != nil {
pkg/controller/istiocsr/rbacs.go (1)

91-103: ClusterRole is cluster-scoped; namespace references are misleading.

ClusterRole is a cluster-scoped resource, so desired.GetNamespace() always returns an empty string. The roleName format namespace/name and the Namespace field in ObjectKey are misleading. While this doesn't break functionality (the namespace is ignored for cluster-scoped lookups), it reduces code clarity.

Consider simplifying to just use the name:

♻️ Suggested improvement
 func (r *Reconciler) findClusterRoleByStatus(istiocsr *v1alpha1.IstioCSR, desired *rbacv1.ClusterRole) (bool, *rbacv1.ClusterRole, string, error) {
-	roleName := fmt.Sprintf("%s/%s", desired.GetNamespace(), istiocsr.Status.ClusterRole)
+	roleName := istiocsr.Status.ClusterRole
 	fetched := &rbacv1.ClusterRole{}
 	key := client.ObjectKey{
-		Name:      istiocsr.Status.ClusterRole,
-		Namespace: desired.GetNamespace(),
+		Name: istiocsr.Status.ClusterRole,
 	}
pkg/operator/operatorclient/operatorclient.go (1)

62-69: Add nil-safe wrapper for Clock to prevent panics.

The Clock field in OperatorClient can theoretically be nil if instantiated without initialization, and it's dereferenced in applyStatusForNewInstance (line 63) and setConditionTransitionTimes (lines 114, 116) when passed to SetApplyConditionsLastTransitionTime, which calls clock.Now(). While current production code paths properly initialize Clock via initializeOperatorComponents, consider adding defensive safeguards to prevent panics from future direct struct instantiations or misconfigurations.

Optional safeguard
+func (c OperatorClient) effectiveClock() clock.PassiveClock {
+	if c.Clock == nil {
+		return clock.RealClock{}
+	}
+	return c.Clock
+}
+
 func (c OperatorClient) applyStatusForNewInstance(ctx context.Context, fieldManager string, desired *applyconfig.CertManagerApplyConfiguration, desiredConfiguration *applyoperatorv1.OperatorStatusApplyConfiguration) error {
-	v1helpers.SetApplyConditionsLastTransitionTime(c.Clock, &desiredConfiguration.Conditions, nil)
+	v1helpers.SetApplyConditionsLastTransitionTime(c.effectiveClock(), &desiredConfiguration.Conditions, nil)
 	desiredStatus := &applyconfig.CertManagerStatusApplyConfiguration{
 		OperatorStatusApplyConfiguration: *desiredConfiguration,
 	}
 	desired.WithStatus(desiredStatus)
 	return c.applyStatus(ctx, fieldManager, desired)
 }
@@
 func (c OperatorClient) setConditionTransitionTimes(desiredConfiguration, operatorStatus *applyoperatorv1.OperatorStatusApplyConfiguration) {
 	if desiredConfiguration.Conditions == nil {
 		return
 	}
 	if operatorStatus != nil {
-		v1helpers.SetApplyConditionsLastTransitionTime(c.Clock, &desiredConfiguration.Conditions, operatorStatus.Conditions)
+		v1helpers.SetApplyConditionsLastTransitionTime(c.effectiveClock(), &desiredConfiguration.Conditions, operatorStatus.Conditions)
 	} else {
-		v1helpers.SetApplyConditionsLastTransitionTime(c.Clock, &desiredConfiguration.Conditions, nil)
+		v1helpers.SetApplyConditionsLastTransitionTime(c.effectiveClock(), &desiredConfiguration.Conditions, nil)
 	}
 }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controller/istiocsr/utils.go (1)

312-396: ⚠️ Potential issue | 🟠 Major

Add nil-pointer and empty-container guards to deployment/container checks.

Several helpers dereference pointers or index into slices without guards. Replicas is an optional *int32 in Kubernetes specs and can be nil; ReadinessProbe and HTTPGet are optional pointers and can be nil; SecurityContext is optional and can be nil. The containers slice can also be empty, causing Containers[0] to panic even though checkDeploymentTemplate only guards against differing lengths, not zero-length slices. Add nil and length checks before dereferencing.

🛡️ Suggested nil-safe guards
 func checkDeploymentBasicSpec(desired, fetched *appsv1.Deployment) bool {
-	return *desired.Spec.Replicas != *fetched.Spec.Replicas ||
-		!reflect.DeepEqual(desired.Spec.Selector.MatchLabels, fetched.Spec.Selector.MatchLabels)
+	if desired.Spec.Replicas == nil || fetched.Spec.Replicas == nil {
+		if desired.Spec.Replicas != fetched.Spec.Replicas {
+			return true
+		}
+	} else if *desired.Spec.Replicas != *fetched.Spec.Replicas {
+		return true
+	}
+	return !reflect.DeepEqual(desired.Spec.Selector.MatchLabels, fetched.Spec.Selector.MatchLabels)
 }
 
 func checkDeploymentContainer(desired, fetched *appsv1.Deployment) bool {
+	if len(desired.Spec.Template.Spec.Containers) == 0 || len(fetched.Spec.Template.Spec.Containers) == 0 {
+		return len(desired.Spec.Template.Spec.Containers) != len(fetched.Spec.Template.Spec.Containers)
+	}
 	desiredContainer := desired.Spec.Template.Spec.Containers[0]
 	fetchedContainer := fetched.Spec.Template.Spec.Containers[0]
 
 	if checkContainerBasicFields(desiredContainer, fetchedContainer) {
 		return true
 	}
 	if checkContainerPorts(desiredContainer, fetchedContainer) {
 		return true
 	}
 	if checkContainerProbe(desiredContainer, fetchedContainer) {
 		return true
 	}
 	return checkContainerResources(desiredContainer, fetchedContainer)
 }
 
 func checkContainerProbe(desired, fetched corev1.Container) bool {
+	if desired.ReadinessProbe == nil || fetched.ReadinessProbe == nil {
+		return desired.ReadinessProbe != fetched.ReadinessProbe
+	}
+	if desired.ReadinessProbe.HTTPGet == nil || fetched.ReadinessProbe.HTTPGet == nil {
+		return desired.ReadinessProbe.HTTPGet != fetched.ReadinessProbe.HTTPGet
+	}
 	return desired.ReadinessProbe.HTTPGet.Path != fetched.ReadinessProbe.HTTPGet.Path ||
 		desired.ReadinessProbe.InitialDelaySeconds != fetched.ReadinessProbe.InitialDelaySeconds ||
 		desired.ReadinessProbe.PeriodSeconds != fetched.ReadinessProbe.PeriodSeconds
 }
 
 func checkContainerResources(desired, fetched corev1.Container) bool {
 	return !reflect.DeepEqual(desired.Resources, fetched.Resources) ||
-		!reflect.DeepEqual(*desired.SecurityContext, *fetched.SecurityContext) ||
+		!reflect.DeepEqual(desired.SecurityContext, fetched.SecurityContext) ||
 		!reflect.DeepEqual(desired.VolumeMounts, fetched.VolumeMounts)
 }
🤖 Fix all issues with AI agents
In `@pkg/controller/istiocsr/utils.go`:
- Around line 267-279: In checkRBACObjectSpecModified, avoid forced type
assertions on fetched; replace fetched.(*rbacv1.ClusterRoleBinding) and
fetched.(*rbacv1.RoleBinding) with ok-assert checks (e.g., v, ok :=
fetched.(*rbacv1.ClusterRoleBinding); if !ok { /* handle: return nil or panic
with clear message */ }) before calling checkClusterRoleBindingModified and
checkRoleBindingModified, or explicitly panic with a clear message if you
prefer; ensure you return a pointer to the bool result as the function currently
does.

- Fix forced type assertions in utils.go with ok-assert pattern
- Fix misleading log messages when resources don't exist (certificates, services, networkpolicies, rbacs, deployments)
- Fix logr.Logger.Error to use structured key/value pairs
- Fix funcorder: move unexported methods below Reconcile
- Fix Ready condition reason for irrecoverable errors (use ReasonFailed)
- Add safety check for empty containers in credentials_request.go
- Remove unused error variable errKeyNotFoundInConfigMap
- Use sentinel errors instead of dynamic error creation
- Remove unused operatorStatus parameter
- Pass context to startIstioCSRController for consistent shutdown behavior
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controller/istiocsr/rbacs.go (1)

166-176: ⚠️ Potential issue | 🟡 Minor

Fix logr.Logger.Error calls with odd number of key/value arguments and non-descriptive messages.

Lines 172 and 284 pass invalid arguments to logr.Logger.Error. The calls use a single key-value pair as arguments after the error, but treat the key as the message, violating the API signature: Error(err error, msg string, keysAndValues ...interface{}). The keysAndValues args must be even-length key-value pairs.

Proposed fix
-			r.log.Error(errErrorUpdatingClusterRoleName, "istiocsr", istiocsr.GetNamespace())
+			r.log.Error(errErrorUpdatingClusterRoleName, "failed to resolve clusterrole name for istiocsr",
+				"namespace", istiocsr.GetNamespace(),
+				"name", istiocsr.GetName())
...
-			r.log.Error(errErrorUpdatingClusterRoleBindingName, "istiocsr", istiocsr.GetNamespace())
+			r.log.Error(errErrorUpdatingClusterRoleBindingName, "failed to resolve clusterrolebinding name for istiocsr",
+				"namespace", istiocsr.GetNamespace(),
+				"name", istiocsr.GetName())
🤖 Fix all issues with AI agents
In `@pkg/controller/deployment/credentials_request.go`:
- Around line 136-139: Define a package-level sentinel error (e.g. var
ErrNoContainers = errors.New("no containers")) and replace the dynamic
fmt.Errorf return in applyCloudCredentialsToDeployment with a wrapped sentinel,
e.g. return fmt.Errorf("deployment %s/%s has no containers: %w",
deployment.GetNamespace(), deployment.GetName(), ErrNoContainers); add the
"errors" import if needed so golangci-lint err113 is fixed.

In `@pkg/controller/istiocsr/rbacs.go`:
- Around line 105-121: The two functions findClusterRoleByLabels and
findClusterRoleBindingByLabels share almost identical logic; either refactor to
a single parameterized helper (e.g., findResourceByLabels[T client.Object] or
findByLabels(ctx, listObj client.ObjectList, desired client.Object, ...) that
accepts the list/result types and constructs the fetched object) and centralize:
call r.List with MatchingLabels, handle zero/one/multiple items, DeepCopyInto
the fetched object, emit the same eventRecorder.Eventf for duplicates, and
return the same tuple shape; or if refactoring is infeasible, add a
//nolint:dupl comment above both functions with a brief explanation why the
duplication is type-specific and cannot be shared. Ensure the helper preserves
existing behaviors (error wrapping via FromClientError, NewIrrecoverableError
with errMultipleClusterRolesExist, and roleName formatting) and update callers
to use the new helper.

In `@pkg/controller/istiocsr/utils.go`:
- Around line 389-393: The checkContainerProbe function can panic if
ReadinessProbe or ReadinessProbe.HTTPGet is nil; update checkContainerProbe to
perform nil-safe comparisons: first handle cases where one ReadinessProbe is nil
and the other is not (consider that a difference), then if both are non-nil
compare InitialDelaySeconds and PeriodSeconds, and separately handle HTTPGet
being nil on either side (consider nil vs non-nil a difference) before comparing
HTTPGet.Path; use the container parameter names desired and fetched to locate
the logic and ensure all dereferences of ReadinessProbe and
ReadinessProbe.HTTPGet are guarded.
- Around line 395-399: The checkContainerResources function can panic by
dereferencing SecurityContext without nil checks; update it to compare
SecurityContext safely (e.g., use reflect.DeepEqual on the pointers themselves
or check for nil before dereferencing) instead of *desired.SecurityContext and
*fetched.SecurityContext, keeping the existing comparisons for Resources and
VolumeMounts; ensure you reference the checkContainerResources function and the
desired.SecurityContext / fetched.SecurityContext symbols so the fix safely
handles cases where either pointer is nil.
🧹 Nitpick comments (2)
pkg/operator/operatorclient/operatorclient.go (1)

93-107: JSON round-trip for status extraction adds overhead.

The extractOperatorStatus function uses JSON marshal/unmarshal to copy the operator status. While this works, it's less efficient than a direct field copy or using DeepCopy if available on the type.

pkg/controller/istiocsr/certificates.go (1)

23-57: Cyclomatic complexity slightly exceeds limit.

The createOrApplyCertificates function has cyclomatic complexity of 11 (limit is 10). Consider extracting the update-or-create logic into a helper similar to deployments.go.

♻️ Proposed refactor to reduce complexity
 func (r *Reconciler) createOrApplyCertificates(istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string, istioCSRCreateRecon bool) error {
 	desired, err := r.getCertificateObject(istiocsr, resourceLabels)
 	if err != nil {
 		return fmt.Errorf("failed to generate certificate resource for creation in %s: %w", istiocsr.GetNamespace(), err)
 	}

 	certificateName := fmt.Sprintf("%s/%s", desired.GetNamespace(), desired.GetName())
 	r.log.V(logVerbosityLevelDebug).Info("reconciling certificate resource", "name", certificateName)
 	fetched := &certmanagerv1.Certificate{}
 	exist, err := r.Exists(r.ctx, client.ObjectKeyFromObject(desired), fetched)
 	if err != nil {
 		return FromClientError(err, "failed to check %s certificate resource already exists", certificateName)
 	}

+	return r.reconcileCertificateResource(istiocsr, desired, fetched, certificateName, exist, istioCSRCreateRecon)
+}
+
+func (r *Reconciler) reconcileCertificateResource(istiocsr *v1alpha1.IstioCSR, desired, fetched *certmanagerv1.Certificate, certificateName string, exist, istioCSRCreateRecon bool) error {
 	if exist && istioCSRCreateRecon {
 		r.eventRecorder.Eventf(istiocsr, corev1.EventTypeWarning, "ResourceAlreadyExists", "%s certificate resource already exists, maybe from previous installation", certificateName)
 	}
 	if exist && hasObjectChanged(desired, fetched) {
 		r.log.V(1).Info("certificate has been modified, updating to desired state", "name", certificateName)
 		if err := r.UpdateWithRetry(r.ctx, desired); err != nil {
 			return FromClientError(err, "failed to update %s certificate resource", certificateName)
 		}
 		r.eventRecorder.Eventf(istiocsr, corev1.EventTypeNormal, "Reconciled", "certificate resource %s reconciled back to desired state", certificateName)
-	} else if exist {
+		return nil
+	}
+	if exist {
 		r.log.V(logVerbosityLevelDebug).Info("certificate resource already exists and is in expected state", "name", certificateName)
+		return nil
 	}
-	if !exist {
-		if err := r.Create(r.ctx, desired); err != nil {
-			return FromClientError(err, "failed to create %s certificate resource", certificateName)
-		}
-		r.eventRecorder.Eventf(istiocsr, corev1.EventTypeNormal, "Reconciled", "certificate resource %s created", certificateName)
+	if err := r.Create(r.ctx, desired); err != nil {
+		return FromClientError(err, "failed to create %s certificate resource", certificateName)
 	}
-
+	r.eventRecorder.Eventf(istiocsr, corev1.EventTypeNormal, "Reconciled", "certificate resource %s created", certificateName)
 	return nil
 }

- Fix err113: Add sentinel errors (errCloudSecretNotFound, errDeploymentHasNoContainers)
  and wrap them with context in credentials_request.go
- Fix dupl: Extract generic helper function findRBACResourceByLabels to eliminate
  duplication between findClusterRoleByLabels and findClusterRoleBindingByLabels
- Fix nil pointer dereferences: Add nil checks for ReadinessProbe, HTTPGet, and
  SecurityContext in checkContainerProbe and checkContainerResources functions
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controller/istiocsr/utils.go (1)

370-387: ⚠️ Potential issue | 🟡 Minor

Compare full port specification, not just container port number.

The current comparison only checks ContainerPort; changes to Name, Protocol, or HostPort won't be detected. While the managed deployments don't currently set these fields, use reflect.DeepEqual to handle potential future changes and align with similar reconciliation patterns elsewhere in the codebase.

Proposed fix
 func checkContainerPorts(desired, fetched corev1.Container) bool {
 	if len(desired.Ports) != len(fetched.Ports) {
 		return true
 	}
 	for _, fetchedPort := range fetched.Ports {
 		matched := false
 		for _, desiredPort := range desired.Ports {
-			if fetchedPort.ContainerPort == desiredPort.ContainerPort {
+			if reflect.DeepEqual(fetchedPort, desiredPort) {
 				matched = true
 				break
 			}
 		}
 		if !matched {
 			return true
 		}
 	}
 	return false
 }
🤖 Fix all issues with AI agents
In `@pkg/controller/deployment/credentials_request_test.go`:
- Around line 182-271: The test's nested closures increase cognitive complexity
and incorrectly access the embedded VolumeSource secret field; extract the
inline checkVolume and checkEnvVar closures into package-level helper functions
(e.g., verifyCloudVolume(t *testing.T, vol *corev1.Volume, expectedSecret
string) and verifyCloudEnvVar(t *testing.T, env *corev1.EnvVar, expectEnv bool,
expectedName string)) and update selectors to use the embedded field access
(replace vol.VolumeSource.Secret.SecretName with vol.Secret.SecretName). Update
the table-driven tests to call these helpers (for AWS/GCP cases) and keep the
existing assertions around createCloudCredentialsResources,
cloudCredentialsVolumeName and errUnsupportedCloudProvider unchanged.
- Around line 77-180: The test TestApplyCloudCredentialsToDeployment is nested
too deeply; refactor the subtest body to use early returns so the success
verification path isn't inside the else block. Specifically, after calling
applyCloudCredentialsToDeployment in the t.Run closure, handle the
expected-error case immediately (if tt.wantErr != nil: assert error matches and
return), then continue with the success assertions (volume, volumeMount, env
var) without an else branch; for the env var check, guard with an early return
or a simple if that doesn't add extra nesting. This targets the t.Run anonymous
function inside TestApplyCloudCredentialsToDeployment and preserves all
assertions while reducing nesting to meet the lint rule.

In `@pkg/controller/deployment/credentials_request.go`:
- Around line 85-96: The GCP helper createGCPCredentialsResources always returns
nil for the EnvVar, so remove that unused third return from its signature and
implementation (update createGCPCredentialsResources to return only
(*corev1.Volume, *corev1.VolumeMount)), then update its call site in
createCloudCredentialsResources to accept only two return values and set the
envVar to nil explicitly (i.e., volume, volumeMount :=
createGCPCredentialsResources(secretName); return volume, volumeMount, nil,
nil). Also update any other callers and imports impacted by the signature change
to match the new two-value return.

In `@pkg/controller/istiocsr/rbacs.go`:
- Around line 164-193: The reconcile path must copy the Name and ResourceVersion
from the fetched object to the desired object for resources created with
generateName before performing an update: in reconcileClusterRoleResource,
before calling updateClusterRole, set desired.Name = fetched.Name and
desired.ResourceVersion = fetched.ResourceVersion so client.ObjectKeyFromObject
and UpdateWithRetry succeed; apply the same fix where reconcile calls
updateClusterRoleBinding (copy fetched.Name and fetched.ResourceVersion into
desired before calling updateClusterRoleBinding) to handle generateName-created
ClusterRole/ClusterRoleBinding objects.

- Fix unparam: Remove unused EnvVar return from createGCPCredentialsResources
  GCP helper always returns nil for EnvVar, so removed it from signature
  and set to nil at call site instead

- Fix nestif: Flatten nested conditionals in TestApplyCloudCredentialsResources
  using early returns to reduce nesting from 6 to 5 levels

- Fix cognitive complexity: Extract checkVolume and checkEnvVar closures to
  package-level helpers and fix embedded field access (vol.Secret.SecretName
  instead of vol.VolumeSource.Secret.SecretName)

- Fix generateName update: Copy Name and ResourceVersion from fetched to
  desired before update for ClusterRole/ClusterRoleBinding resources
  created with generateName to prevent UpdateWithRetry failures
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controller/istiocsr/rbacs.go (1)

212-223: ⚠️ Potential issue | 🟡 Minor

Fix logr.Error key/value pairs to avoid malformed log entries.

Both Error calls at lines 218 and 343 pass an odd number of keysAndValues (a single value with no corresponding key). The logr.Error signature is Error(err error, msg string, keysAndValues ...any), which requires key/value pairs. Provide explicit key/value pairs for proper structured logging:

Suggested fix
-			r.log.Error(errErrorUpdatingClusterRoleName, "istiocsr", istiocsr.GetNamespace())
+			r.log.Error(errErrorUpdatingClusterRoleName, "failed to resolve clusterrole name for istiocsr", "namespace", istiocsr.GetNamespace())
@@
-			r.log.Error(errErrorUpdatingClusterRoleBindingName, "istiocsr", istiocsr.GetNamespace())
+			r.log.Error(errErrorUpdatingClusterRoleBindingName, "failed to resolve clusterrolebinding name for istiocsr", "namespace", istiocsr.GetNamespace())
🤖 Fix all issues with AI agents
In `@pkg/controller/deployment/credentials_request_test.go`:
- Around line 19-21: Update the comment above the helper function
checkVolumeSecretName to satisfy the godot linter by adding a trailing period to
the sentence "Uses embedded field access: vol.Secret.SecretName instead of
vol.VolumeSource.Secret.SecretName" so the comment ends with a period.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 3, 2026

@anandkuma77: This pull request references CM-902 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target either version "4.22." or "openshift-4.22.", but it targets "cert-manager-1.19" instead.

Details

In response to this:

Enable Additional golangci-lint Linters and Fix Reported Issues

What

This PR enables additional golangci-lint linters (err113, mnd, cyclop, maintidx, nestif, unparam, and dupl) and fixes all the code quality issues they report across the codebase. The changes include:

  • err113: Fixed dynamic error issues by replacing dynamic errors with static error types
  • mnd (magic number detector): Addressed magic number violations by extracting constants or adding appropriate comments
  • cyclop: Reduced cyclomatic complexity in functions by refactoring complex logic
  • maintidx: Improved maintainability index by simplifying code structure
  • nestif: Reduced nested if statements by extracting functions or using early returns
  • unparam: Removed unused function parameters
  • dupl: Eliminated code duplication by extracting common logic
  • Fixed issues with generateName resource updates in RBAC reconciliation logic

Why

Enabling these additional linters improves code quality, maintainability, and consistency across the codebase. These linters help:

  • err113: Ensures consistent error handling patterns and makes errors easier to test and handle
  • mnd: Improves code readability by making magic numbers explicit and self-documenting
  • cyclop: Reduces complexity, making code easier to understand, test, and maintain
  • maintidx: Helps identify and improve code that may be difficult to maintain over time
  • nestif: Reduces cognitive load by flattening nested conditionals
  • unparam: Removes dead code and unused parameters, keeping the codebase clean
  • dupl: Prevents code duplication which can lead to maintenance issues and bugs

This is part of an ongoing effort (CM-902) to improve code quality standards and ensure the codebase adheres to best practices.

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.

1 similar comment
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 3, 2026

@anandkuma77: This pull request references CM-902 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target either version "4.22." or "openshift-4.22.", but it targets "cert-manager-1.19" instead.

Details

In response to this:

Enable Additional golangci-lint Linters and Fix Reported Issues

What

This PR enables additional golangci-lint linters (err113, mnd, cyclop, maintidx, nestif, unparam, and dupl) and fixes all the code quality issues they report across the codebase. The changes include:

  • err113: Fixed dynamic error issues by replacing dynamic errors with static error types
  • mnd (magic number detector): Addressed magic number violations by extracting constants or adding appropriate comments
  • cyclop: Reduced cyclomatic complexity in functions by refactoring complex logic
  • maintidx: Improved maintainability index by simplifying code structure
  • nestif: Reduced nested if statements by extracting functions or using early returns
  • unparam: Removed unused function parameters
  • dupl: Eliminated code duplication by extracting common logic
  • Fixed issues with generateName resource updates in RBAC reconciliation logic

Why

Enabling these additional linters improves code quality, maintainability, and consistency across the codebase. These linters help:

  • err113: Ensures consistent error handling patterns and makes errors easier to test and handle
  • mnd: Improves code readability by making magic numbers explicit and self-documenting
  • cyclop: Reduces complexity, making code easier to understand, test, and maintain
  • maintidx: Helps identify and improve code that may be difficult to maintain over time
  • nestif: Reduces cognitive load by flattening nested conditionals
  • unparam: Removes dead code and unused parameters, keeping the codebase clean
  • dupl: Prevents code duplication which can lead to maintenance issues and bugs

This is part of an ongoing effort (CM-902) to improve code quality standards and ensure the codebase adheres to best practices.

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.

disable:
# depguard has configuration issues in golangci-lint v2 - see https://github.com/golangci/golangci-lint/issues/3906
- depguard
# Below linters will be enabled once the issues reported by enabled linters are fixed.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: we can remove the comment as well I think.

func verifyCloudSecretExists(secretsInformer coreinformersv1.SecretInformer, secretName string) error {
_, err := secretsInformer.Lister().Secrets(operatorclient.TargetNamespace).Get(secretName)
if err != nil && apierrors.IsNotFound(err) {
return fmt.Errorf("(Retrying) cloud secret %q doesn't exist due to %w: %w", secretName, errCloudSecretNotFound, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: This was present earlier, true. But retrying the error is misleading I think. Neither here, not in the library methods where this hook is invoked, retry logic doesn't exist. So should we remove it?

}
default:
return nil, fmt.Errorf("unsupported deployment name %q provided", deploymentName)
return nil, fmt.Errorf("%w: %q", errUnsupportedDeploymentName, deploymentName)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: wrapped errors at the end of message are more common and conventional, I think.

Suggested change
return nil, fmt.Errorf("%w: %q", errUnsupportedDeploymentName, deploymentName)
return nil, fmt.Errorf("%q: %w", deploymentName, errUnsupportedDeploymentName)

- linters:
- unparam
path: pkg/controller/deployment/
settings:
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we need the linter settings anymore?

for k, v := range argMap {
if !slices.Contains(supportedArgs, k) {
return fmt.Errorf("validation failed due to unsupported arg %q=%q", k, v)
return fmt.Errorf("%w: %q=%q", errUnsupportedArg, k, v)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Here too, wrapped errors at the end of message are more common and conventional, I think.

DefaultECDSA384PrivateKeySize = 384

// Log verbosity levels
logVerbosityLevelDebug = 4 // Debug level logging
Copy link
Contributor

Choose a reason for hiding this comment

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

very nit: should this be ordered :)

"github.com/openshift/cert-manager-operator/pkg/operator/assets"
)

var (
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also simplify createOrApplyRBACResource(), same as reconcileIstioCSRDeployment()?

panic(err)
}
deployment, ok := obj.(*appsv1.Deployment)
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this was added previously, as it was suggested by linter correct?

}

func (c OperatorClient) applyStatusForNewInstance(ctx context.Context, fieldManager string, desired *applyconfig.CertManagerApplyConfiguration, desiredConfiguration *applyoperatorv1.OperatorStatusApplyConfiguration) error {
v1helpers.SetApplyConditionsLastTransitionTime(c.Clock, &desiredConfiguration.Conditions, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would Clock ever be nil? I think we need to handle it.

func initializeClients(cc *controllercmd.ControllerContext) (*operatorClients, error) {
kubeClient, err := kubernetes.NewForConfig(cc.ProtoKubeConfig)
if err != nil {
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would benefit to add a context as to which client creation failed.

Suggested change
return nil, err
return nil, fmt.Errorf("failed to create kubernetes client: %w", err)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controller/deployment/deployment_overrides_validation.go (1)

120-127: ⚠️ Potential issue | 🟡 Minor

Wrong sentinel error used for environment variable validation.

Line 123 uses errUnsupportedArg for env validation, but it should use a distinct sentinel (e.g., errUnsupportedEnv) for clarity. Currently, an unsupported env variable would report "validation failed due to unsupported arg" which is misleading.

🐛 Suggested fix

Add a new sentinel:

 var (
 	errUnsupportedArg           = errors.New("validation failed due to unsupported arg")
+	errUnsupportedEnv           = errors.New("validation failed due to unsupported env")
 	errUnsupportedLabel         = errors.New("validation failed due to unsupported label")

Then update the validation:

-			return fmt.Errorf("%w: %q=%q", errUnsupportedArg, k, v)
+			return fmt.Errorf("%w: %q=%q", errUnsupportedEnv, k, v)
🤖 Fix all issues with AI agents
In `@pkg/controller/istiocsr/utils.go`:
- Around line 347-361: The function checkDeploymentContainer currently indexes
Containers[0] without verifying the slice length, risking a panic; update
checkDeploymentContainer to first validate that both
desired.Spec.Template.Spec.Containers and fetched.Spec.Template.Spec.Containers
are non-empty (and optionally that their lengths match if intended) and return
false (or handle appropriately) when either is empty before calling
checkContainerBasicFields, checkContainerPorts, checkContainerProbe, and
checkContainerResources; reference the existing symbols
checkContainerBasicFields, checkContainerPorts, checkContainerProbe, and
checkContainerResources when adding the guard logic so the function safely
accesses Containers[0].
- Around line 337-340: checkDeploymentBasicSpec dereferences
desired.Spec.Replicas and fetched.Spec.Replicas without nil checks which can
panic; update the function to first handle nil pointers: if both Replicas are
nil treat them as equal, if one is nil and the other not return true
(difference), otherwise compare *desired.Spec.Replicas !=
*fetched.Spec.Replicas; keep the existing comparison of Selector.MatchLabels.
Reference: function checkDeploymentBasicSpec and the fields
desired.Spec.Replicas and fetched.Spec.Replicas.
🧹 Nitpick comments (3)
pkg/controller/deployment/deployment_helper.go (1)

243-243: Inconsistent error handling across similar functions.

getOverrideArgsFor, getOverrideEnvFor, and getOverridePodLabelsFor now use the errUnsupportedDeploymentName sentinel, but getOverrideReplicasFor (line 243), getOverrideResourcesFor (line 270), and getOverrideSchedulingFor (line 297) still use inline error strings. Consider updating these for consistency.

♻️ Suggested fix for consistency
 	default:
-		return nil, fmt.Errorf("unsupported deployment name %q provided", deploymentName)
+		return nil, fmt.Errorf("%q: %w", deploymentName, errUnsupportedDeploymentName)

Apply similar changes to lines 270 and 297.

Also applies to: 270-270, 297-297

pkg/controller/istiocsr/certificates.go (2)

41-41: Inconsistent log verbosity constant usage.

Line 41 uses hardcoded V(1) while lines 30 and 47 correctly use logVerbosityLevelDebug. Consider using the constant here for consistency.

♻️ Suggested fix
-		r.log.V(1).Info("certificate has been modified, updating to desired state", "name", certificateName)
+		r.log.V(logVerbosityLevelDebug).Info("certificate has been modified, updating to desired state", "name", certificateName)

154-161: Consider using else if or switch for mutually exclusive conditions.

The algorithm can only match one condition, so else if or switch would be slightly cleaner and make the mutual exclusivity explicit.

♻️ Suggested refactor
 func setDefaultPrivateKeySize(certificate *certmanagerv1.Certificate) {
-	if certificate.Spec.PrivateKey.Algorithm == certmanagerv1.RSAKeyAlgorithm {
+	switch certificate.Spec.PrivateKey.Algorithm {
+	case certmanagerv1.RSAKeyAlgorithm:
 		certificate.Spec.PrivateKey.Size = DefaultRSAPrivateKeySize
-	}
-	if certificate.Spec.PrivateKey.Algorithm == certmanagerv1.ECDSAKeyAlgorithm {
+	case certmanagerv1.ECDSAKeyAlgorithm:
 		certificate.Spec.PrivateKey.Size = DefaultECDSA384PrivateKeySize
 	}
 }

Comment on lines 347 to 361
func checkDeploymentContainer(desired, fetched *appsv1.Deployment) bool {
desiredContainer := desired.Spec.Template.Spec.Containers[0]
fetchedContainer := fetched.Spec.Template.Spec.Containers[0]
if !reflect.DeepEqual(desiredContainer.Args, fetchedContainer.Args) ||
desiredContainer.Name != fetchedContainer.Name || desiredContainer.Image != fetchedContainer.Image ||
desiredContainer.ImagePullPolicy != fetchedContainer.ImagePullPolicy {

if checkContainerBasicFields(desiredContainer, fetchedContainer) {
return true
}
if checkContainerPorts(desiredContainer, fetchedContainer) {
return true
}
if checkContainerProbe(desiredContainer, fetchedContainer) {
return true
}
return checkContainerResources(desiredContainer, fetchedContainer)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential index out of bounds panic.

Accessing Containers[0] without verifying the slice is non-empty will panic if either deployment has zero containers. The length comparison in checkDeploymentTemplate doesn't guarantee non-empty slices.

🛡️ Proposed fix
 func checkDeploymentContainer(desired, fetched *appsv1.Deployment) bool {
+	if len(desired.Spec.Template.Spec.Containers) == 0 || len(fetched.Spec.Template.Spec.Containers) == 0 {
+		return len(desired.Spec.Template.Spec.Containers) != len(fetched.Spec.Template.Spec.Containers)
+	}
 	desiredContainer := desired.Spec.Template.Spec.Containers[0]
 	fetchedContainer := fetched.Spec.Template.Spec.Containers[0]
📝 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
func checkDeploymentContainer(desired, fetched *appsv1.Deployment) bool {
desiredContainer := desired.Spec.Template.Spec.Containers[0]
fetchedContainer := fetched.Spec.Template.Spec.Containers[0]
if !reflect.DeepEqual(desiredContainer.Args, fetchedContainer.Args) ||
desiredContainer.Name != fetchedContainer.Name || desiredContainer.Image != fetchedContainer.Image ||
desiredContainer.ImagePullPolicy != fetchedContainer.ImagePullPolicy {
if checkContainerBasicFields(desiredContainer, fetchedContainer) {
return true
}
if checkContainerPorts(desiredContainer, fetchedContainer) {
return true
}
if checkContainerProbe(desiredContainer, fetchedContainer) {
return true
}
return checkContainerResources(desiredContainer, fetchedContainer)
}
func checkDeploymentContainer(desired, fetched *appsv1.Deployment) bool {
if len(desired.Spec.Template.Spec.Containers) == 0 || len(fetched.Spec.Template.Spec.Containers) == 0 {
return len(desired.Spec.Template.Spec.Containers) != len(fetched.Spec.Template.Spec.Containers)
}
desiredContainer := desired.Spec.Template.Spec.Containers[0]
fetchedContainer := fetched.Spec.Template.Spec.Containers[0]
if checkContainerBasicFields(desiredContainer, fetchedContainer) {
return true
}
if checkContainerPorts(desiredContainer, fetchedContainer) {
return true
}
if checkContainerProbe(desiredContainer, fetchedContainer) {
return true
}
return checkContainerResources(desiredContainer, fetchedContainer)
}
🤖 Prompt for AI Agents
In `@pkg/controller/istiocsr/utils.go` around lines 347 - 361, The function
checkDeploymentContainer currently indexes Containers[0] without verifying the
slice length, risking a panic; update checkDeploymentContainer to first validate
that both desired.Spec.Template.Spec.Containers and
fetched.Spec.Template.Spec.Containers are non-empty (and optionally that their
lengths match if intended) and return false (or handle appropriately) when
either is empty before calling checkContainerBasicFields, checkContainerPorts,
checkContainerProbe, and checkContainerResources; reference the existing symbols
checkContainerBasicFields, checkContainerPorts, checkContainerProbe, and
checkContainerResources when adding the guard logic so the function safely
accesses Containers[0].

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/controller/istiocsr/constants.go`:
- Around line 126-129: The top-level comment above the log verbosity constants
(currently "// Log verbosity levels") is missing a trailing period; update that
comment to end with a period (e.g., "// Log verbosity levels.") near the
constants logVerbosityLevelInfo and logVerbosityLevelDebug so it satisfies the
godot linter rule.

Comment on lines +126 to +129
// Log verbosity levels

logVerbosityLevelInfo = 0 // Info level logging
logVerbosityLevelDebug = 4 // Debug level logging
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and read the relevant lines
if [ -f "pkg/controller/istiocsr/constants.go" ]; then
  echo "=== File exists. Reading lines 120-135 for context ==="
  sed -n '120,135p' pkg/controller/istiocsr/constants.go | cat -n
else
  echo "File not found"
  find . -name "constants.go" -path "*/istiocsr/*" 2>/dev/null
fi

Repository: openshift/cert-manager-operator

Length of output: 889


🏁 Script executed:

# Check if godot is used in this project (golangci-lint config)
find . -name ".golangci*" -o -name "golangci.yml" -o -name "golangci.yaml" 2>/dev/null | head -5

Repository: openshift/cert-manager-operator

Length of output: 304


🏁 Script executed:

# Check the golangci configuration
cat -n .golangci.yaml | head -100

Repository: openshift/cert-manager-operator

Length of output: 3253


Add trailing period to comment on line 126.

The comment // Log verbosity levels violates the godot linter (enabled in this project) and needs a trailing period.

Proposed fix
-	// Log verbosity levels
+	// Log verbosity levels.
📝 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
// Log verbosity levels
logVerbosityLevelInfo = 0 // Info level logging
logVerbosityLevelDebug = 4 // Debug level logging
// Log verbosity levels.
logVerbosityLevelInfo = 0 // Info level logging
logVerbosityLevelDebug = 4 // Debug level logging
🧰 Tools
🪛 golangci-lint (2.5.0)

[error] 126-126: Comment should end in a period

(godot)

🤖 Prompt for AI Agents
In `@pkg/controller/istiocsr/constants.go` around lines 126 - 129, The top-level
comment above the log verbosity constants (currently "// Log verbosity levels")
is missing a trailing period; update that comment to end with a period (e.g.,
"// Log verbosity levels.") near the constants logVerbosityLevelInfo and
logVerbosityLevelDebug so it satisfies the godot linter rule.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 6, 2026

@anandkuma77: all tests passed!

Full PR test history. Your PR dashboard.

Details

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

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants