Skip to content

CNTRLPLANE-3701: simplify logging, ensure proper fields in reoncilers - #8802

Open
stevekuznetsov wants to merge 2 commits into
openshift:mainfrom
stevekuznetsov:skuznets/controller-logs
Open

CNTRLPLANE-3701: simplify logging, ensure proper fields in reoncilers#8802
stevekuznetsov wants to merge 2 commits into
openshift:mainfrom
stevekuznetsov:skuznets/controller-logs

Conversation

@stevekuznetsov

@stevekuznetsov stevekuznetsov commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

We want to ensure that contextual, structural logging is used everywhere. When it's not, crucial information like what reconciler is spitting out the log or what key is being reconciled is missing, leaving users with no easy way to write structured queries over our outputs. Removed the pkg/log export as it's always the wrong thing to use.

What this PR does / why we need it:

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • Refactor
    • Standardized command and controller logging across create/destroy flows and reconcilers.
    • Improved controller naming consistency for several components.
  • New Features
    • Added support for certificate rotation setup in hosted control plane reconciliation.
  • Bug Fixes
    • Updated hosted control plane and related observer behavior to use context-aware logging, improving diagnostics during reconciliation and shutdown.
  • Chores
    • Updated dependencies in the module files.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces cmd/util.NewLogger() backed by a zap logger with RFC3339 timestamps, removes the global cmd/log.Log variable, and migrates all CLI command files to use the new per-call logger. Separately, adds exported ControllerName constants with Named() registration across all controllers, migrates stored struct loggers in PrivateServiceObserver and ManagedCAObserver to context-derived loggers, removes the stored Log field from HostedControlPlaneReconciler with full context-logger migration, updates SetupWithManager to accept an explicit logger, and wires certificate rotation controllers into the HCP reconciler startup.

Changes

CLI Logger Migration

Layer / File(s) Summary
cmd/util/log.go: NewLogger() foundation
cmd/util/log.go
Adds NewLogger() exporting a zap-backed logr.Logger with zapcore.RFC3339TimeEncoder.
CLI commands: replace log.Log with util.NewLogger()
cmd/bastion/aws/..., cmd/cluster/..., cmd/consolelogs/aws/..., cmd/infra/aws/..., cmd/infra/azure/..., cmd/infra/powervs/..., cmd/nodepool/..., product-cli/cmd/cluster/...
Removes cmd/log imports and replaces all log.Log.* calls with locally created util.NewLogger() instances across bastion, cluster, infra, nodepool, consolelogs, and product-cli command files.

Controller Modernization and HCP Cert Rotation

Layer / File(s) Summary
ControllerName constants and Named() registration
hypershift-operator/controllers/hostedcluster/..., hypershift-operator/controllers/nodepool/..., hypershift-operator/controllers/platform/aws/..., hypershift-operator/controllers/supportedversion/..., hypershift-operator/controllers/uwmtelemetry/..., control-plane-operator/controllers/awsprivatelink/...
Adds exported ControllerName constants and .Named(ControllerName) to SetupWithManager across all listed controllers; adds ReconcilerControllerName to awsprivatelink.
PrivateServiceObserver and ManagedCAObserver: context logger migration
control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go, control-plane-operator/hostedclusterconfigoperator/controllers/cmca/...
PrivateServiceObserver replaces its stored log field with a clientset, sets up a namespaced shared informer for Services in SetupWithManager, and derives the logger from reconcile context. ManagedCAObserver removes its stored log field and derives logger from context; ControllerName constant added to cmca setup.
HCP reconciler: remove Log field, update SetupWithManager signature
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go, control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
Adds ControllerName constant, removes the stored Log logr.Logger field from HostedControlPlaneReconciler, updates SetupWithManager to accept an explicit logger logr.Logger parameter, and removes now-invalid Log: field assignments from test constructors.
HCP reconciler: context-derived logger throughout reconcile methods
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
Introduces log := ctrl.LoggerFrom(ctx) in Reconcile and all sub-methods (update, reconcile, reconcilePKI, reconcileManagedEtcd, reconcileUnmanagedEtcd, reconcileKubeAPIServer, reconcileKubeControllerManager, reconcileOpenShiftAPIServer, reconcileOAuthServer, reconcileCloudControllerManager, hostedControlPlaneInNamespace), replacing all r.Log usages.
HCP cert rotation wiring in reconciler and main.go
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go, control-plane-operator/main.go, go.mod
Adds defaultRotationDay and ensureCertRotationControllers to the HCP reconciler using informers, listers, and event recorder. Updates main.go to construct kubeClient, namespace-scoped informers, operator client, owner reference, events recorder, and rotationDay, passing them into the extended HostedControlPlaneReconciler; go.mod adds github.com/robfig/cron, sigs.k8s.io/kube-storage-version-migrator, bumps openshift/library-go, and advances Kubernetes dependencies to v0.27.4.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Important

Pre-merge checks failed

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

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
No-Weak-Crypto ❌ Error The patch adds a bytes.Equal comparison on service-account signing key bytes, which is a non-constant-time secret comparison. Replace bytes.Equal with subtle.ConstantTimeCompare (or compare non-secret metadata) when checking signing key bytes.
No-Sensitive-Data-In-Logs ❌ Error FAIL: route53.go logs private zone and record names (e.g. "Deleted records from private hosted zone" with "names"), which can expose internal hostnames. Drop zone/record names from these logs (or redact/hash them) and keep only non-sensitive IDs/status.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Single Node Openshift (Sno) Test Compatibility ⚠️ Warning New e2e tests nodepool_autoscaling and nodepool_rolling_upgrade require 2-3 nodes/multiple NodePools and have no SNO skip guard. Add [Skipped:SingleReplicaTopology] or a runtime SNO check to those tests (and any similar node-scaling cases).
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main logging refactor and reconciler wiring updates.
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 No Ginkgo-style test titles were added or changed; the touched test file uses static Test/t.Run names, with no dynamic titles introduced by this PR.
Test Structure And Quality ✅ Passed PASS: The only touched test file uses plain testing.T/Gomega, with no Ginkgo blocks, Eventually/Consistently waits, or resource cleanup issues introduced by this PR.
Microshift Test Compatibility ✅ Passed No new Ginkgo/e2e specs were added; the only changed test file is a standard testing unit-test file and uses no MicroShift-unsupported APIs.
Topology-Aware Scheduling Compatibility ✅ Passed Diff is logging/controller-name refactors only; inspected deployment builders show no new affinity, nodeSelector, spread, toleration, or PDB scheduling constraints.
Ote Binary Stdout Contract ✅ Passed Touched entrypoints only write to stderr; util.NewLogger() and klog default to stderr, and no new stdout writes appear in main/init/setup code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only test file is a standard Go unit test file with Test* functions and no IPv4/public-network assumptions.
Container-Privileges ✅ Passed The PR only refactors logging/controller wiring; no changed files introduce privileged pod settings or container securityContext changes.
✨ 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 requested review from clebs and devguyio June 22, 2026 19:17
@openshift-ci openshift-ci Bot added area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/aws PR/issue for AWS (AWSPlatform) platform area/platform/azure PR/issue for Azure (AzurePlatform) platform area/platform/gcp PR/issue for GCP (GCPPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure area/platform/openstack PR/issue for OpenStack (OpenStackPlatform) platform area/platform/powervs PR/issue for PowerVS (PowerVSPlatform) platform and removed do-not-merge/needs-area labels Jun 22, 2026
@stevekuznetsov
stevekuznetsov force-pushed the skuznets/controller-logs branch 2 times, most recently from 6b7e682 to 3992c0f Compare June 22, 2026 19:24

@coderabbitai coderabbitai Bot left a comment

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.

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)
control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go (1)

65-75: ⚠️ Potential issue | 🟡 Minor

Use the informer lister in Reconcile instead of direct API calls.

SetupWithManager creates a shared informer and watches it (lines 102-128), but Reconcile bypasses this by making direct API calls via r.clientset.CoreV1().Services(req.Namespace).Get(). This defeats the caching benefit of the informer.

Store the informer's lister during setup and use services.Lister().Services(req.Namespace).Get(req.Name) in Reconcile to read from the cache.

🤖 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
`@control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go`
around lines 65 - 75, The PrivateServiceObserver struct makes direct API calls
in the Reconcile method using r.clientset.CoreV1().Services(req.Namespace).Get()
instead of leveraging the informer lister that is set up in SetupWithManager. To
fix this, add a services lister field to the PrivateServiceObserver struct to
store the informer's lister, initialize this lister in SetupWithManager when the
shared informer is created, and then update the Reconcile method to use
services.Lister().Services(req.Namespace).Get() instead of the direct client API
call to benefit from caching.
🧹 Nitpick comments (10)
product-cli/cmd/cluster/aws/destroy.go (1)

32-41: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the pre-initialized logger from opts.Log instead of allocating a new one.

Line 39 creates a new logger instance for every error that occurs. The opts parameter carries a pre-initialized logr.Logger (set by the caller in cluster.go) that should be reused for consistency and efficiency. This follows the established pattern elsewhere (e.g., cmd/cluster/core/destroy.go).

♻️ Proposed fix
 		if err = hypershiftaws.DestroyCluster(cmd.Context(), opts); err != nil {
-			cmdutil.NewLogger().Error(err, "Failed to destroy cluster")
+			opts.Log.Error(err, "Failed to destroy cluster")
 			return err
 		}
🤖 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 `@product-cli/cmd/cluster/aws/destroy.go` around lines 32 - 41, The RunE
function in the destroy command is creating a new logger instance with
cmdutil.NewLogger() on each error instead of reusing the pre-initialized logger
available in opts.Log. Replace the cmdutil.NewLogger().Error() call with
opts.Log.Error() to use the existing logger that was set by the caller, ensuring
consistency and avoiding unnecessary allocations following the established
pattern in similar files like cmd/cluster/core/destroy.go.
product-cli/cmd/cluster/agent/destroy.go (1)

18-22: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the pre-initialized logger from opts.Log instead of allocating a new one.

Line 20 creates a new logger instance for every error that occurs. The opts parameter carries a pre-initialized logr.Logger (set by the caller in cluster.go) that should be reused for consistency and efficiency. This follows the established pattern elsewhere (e.g., cmd/cluster/core/destroy.go).

♻️ Proposed fix
 	cmd.RunE = func(cmd *cobra.Command, args []string) error {
 		if err := agent.DestroyCluster(cmd.Context(), opts); err != nil {
-			cmdutil.NewLogger().Error(err, "Failed to destroy cluster")
+			opts.Log.Error(err, "Failed to destroy cluster")
 			return err
 		}

 		return nil
 	}
🤖 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 `@product-cli/cmd/cluster/agent/destroy.go` around lines 18 - 22, In the RunE
function of the destroy command, replace the inefficient `cmdutil.NewLogger()`
call in the error handling block with the pre-initialized logger from
`opts.Log`. Instead of creating a new logger instance each time an error occurs
when `agent.DestroyCluster` fails, use the logger that was already initialized
and passed in through the options parameter. This maintains consistency with the
established pattern used elsewhere in the codebase and avoids unnecessary logger
allocations.
product-cli/cmd/cluster/openstack/destroy.go (1)

16-39: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the pre-initialized logger from opts.Log instead of allocating a new one.

Line 23 creates a new logger instance and stores it in a local variable. The opts parameter carries a pre-initialized logr.Logger (set by the caller in cluster.go) that should be reused for consistency and efficiency. This follows the established pattern elsewhere (e.g., cmd/cluster/core/destroy.go).

♻️ Proposed fix
 	cmd.Run = func(cmd *cobra.Command, args []string) {
 		ctx, cancel := context.WithCancel(context.Background())
 		defer cancel()

 		sigs := make(chan os.Signal, 1)
 		signal.Notify(sigs, syscall.SIGINT)
 		go func() {
 			<-sigs
 			cancel()
 		}()

 		if err := openstack.DestroyCluster(ctx, opts); err != nil {
-			logger.Error(err, "Failed to destroy cluster")
+			opts.Log.Error(err, "Failed to destroy cluster")
 			os.Exit(1)
 		}
 	}

-	logger := cmdutil.NewLogger()
🤖 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 `@product-cli/cmd/cluster/openstack/destroy.go` around lines 16 - 39, In the
NewDestroyCommand function, replace the local logger initialization (logger :=
cmdutil.NewLogger()) with direct use of the pre-initialized logger available in
the opts parameter. Update the logger.Error call within the Run function to use
opts.Log instead of the local logger variable. This ensures consistency with the
established pattern in the codebase and reuses the logger that was already
initialized by the caller.
product-cli/cmd/cluster/kubevirt/destroy.go (1)

18-24: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the pre-initialized logger from opts.Log instead of allocating a new one.

Line 20 creates a new logger instance for every error that occurs. The opts parameter carries a pre-initialized logr.Logger (set by the caller in cluster.go) that should be reused for consistency and efficiency. This follows the established pattern elsewhere (e.g., cmd/cluster/core/destroy.go).

♻️ Proposed fix
 	cmd.RunE = func(cmd *cobra.Command, args []string) error {
 		if err := none.DestroyCluster(cmd.Context(), opts); err != nil {
-			cmdutil.NewLogger().Error(err, "Failed to destroy cluster")
+			opts.Log.Error(err, "Failed to destroy cluster")
 			return err
 		}
 		return nil
 	}
🤖 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 `@product-cli/cmd/cluster/kubevirt/destroy.go` around lines 18 - 24, The RunE
function in the destroy command is creating a new logger instance with
cmdutil.NewLogger() for the error log, which is inefficient and inconsistent
with the pattern used elsewhere. Instead, replace the
cmdutil.NewLogger().Error() call with opts.Log.Error() to reuse the
pre-initialized logger that is already available in the opts parameter (which is
passed by the caller and carries the context-aware logger configuration). This
ensures consistency, efficiency, and proper log context propagation throughout
the cluster destruction operation.
cmd/cluster/azure/destroy.go (1)

49-49: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Redundant logger creation.

opts.Log is already initialized in cluster.go line 51. Creating a separate logger here for error reporting in cmd.Run means this command uses two different logger instances. Consider using opts.Log consistently:

-	logger := util.NewLogger()
 	cmd.Run = func(cmd *cobra.Command, args []string) {
 		ctx, cancel := context.WithCancel(context.Background())
 		defer cancel()
 
 		sigs := make(chan os.Signal, 1)
 		signal.Notify(sigs, syscall.SIGINT)
 		go func() {
 			<-sigs
 			cancel()
 		}()
 
 		if err := DestroyCluster(ctx, opts); err != nil {
-			logger.Error(err, "Failed to destroy cluster")
+			opts.Log.Error(err, "Failed to destroy cluster")
 			os.Exit(1)
 		}
 	}

This pattern appears across all destroy commands in this layer.

🤖 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 `@cmd/cluster/azure/destroy.go` at line 49, The code in the destroy command is
creating a redundant logger instance using util.NewLogger() when opts.Log is
already initialized and available. Replace the logger assignment on line 49 to
use opts.Log instead of creating a new logger, and update all references to the
logger variable throughout the cmd.Run function to consistently use opts.Log.
This ensures the destroy command uses a single, consistent logger instance
rather than maintaining two separate logger instances.
cmd/infra/azure/rbac.go (1)

219-219: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Reuse one logger instance inside assignRole.

Lines 219–275 repeatedly call cmdutil.NewLogger() in the same request path. Create one local logger once and reuse it across branches to avoid repeated setup and keep log context consistent.

Proposed fix
 func (r *RBACManager) assignRole(ctx context.Context, client roleAssignmentClient, infraID, component, assigneeID, role, scope string) error {
+	logger := cmdutil.NewLogger().WithValues("infraID", infraID, "component", component)
 	// Generate the role assignment name
 	roleAssignmentName := cmdutil.GenerateRoleAssignmentName(infraID, component, scope)
@@
-				cmdutil.NewLogger().Info("Skipping role assignment creation, matching assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
+				logger.Info("Skipping role assignment creation, matching assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
@@
-			cmdutil.NewLogger().Info("Skipping role assignment creation, role assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
+			logger.Info("Skipping role assignment creation, role assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
@@
-		cmdutil.NewLogger().Info("Deleting stale role assignment with mismatched principal",
+		logger.Info("Deleting stale role assignment with mismatched principal",
@@
-				cmdutil.NewLogger().Info("Get not permitted; will attempt create and rely on 409 for idempotency.", "role", role, "assigneeID", assigneeID, "scope", scope)
+				logger.Info("Get not permitted; will attempt create and rely on 409 for idempotency.", "role", role, "assigneeID", assigneeID, "scope", scope)
@@
-			cmdutil.NewLogger().Info("Failed role assignment creation, role assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
+			logger.Info("Failed role assignment creation, role assignment already exists.", "role", role, "assigneeID", assigneeID, "scope", scope)
@@
-	cmdutil.NewLogger().Info("successfully created role assignment", "role", role, "assigneeID", assigneeID, "scope", scope)
+	logger.Info("successfully created role assignment", "role", role, "assigneeID", assigneeID, "scope", scope)

Also applies to: 235-243, 256-257, 270-275

🤖 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 `@cmd/infra/azure/rbac.go` at line 219, The assignRole function repeatedly
calls cmdutil.NewLogger() across multiple branches (lines 219-275), which
creates unnecessary logger instances and can be inefficient. Create a single
logger instance once at the beginning of the assignRole function and reuse it
throughout all the branches and conditional blocks instead of calling
cmdutil.NewLogger() each time. Replace all occurrences of
cmdutil.NewLogger().Info() and similar calls with the reused local logger
instance to maintain consistent log context and avoid repeated setup overhead.
cmd/infra/powervs/create.go (1)

230-237: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Bind logger name after flag parsing.

On Line 230, opts.InfraID is still at its default when NewCreateCommand() runs, so .WithName(opts.InfraID) can end up empty even when --infra-id is provided. Initialize the named logger inside RunE after flags are applied.

Proposed fix
-	logger := cmdutil.NewLogger().WithName(opts.InfraID)
 	cmd.RunE = func(cmd *cobra.Command, args []string) error {
+		logger := cmdutil.NewLogger().WithName(opts.InfraID)
 		if err := opts.Run(cmd.Context(), logger); err != nil {
 			logger.Error(err, "Failed to create infrastructure")
 			return err
 		}
🤖 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 `@cmd/infra/powervs/create.go` around lines 230 - 237, The logger is being
initialized with opts.InfraID before the command flags are parsed, causing
opts.InfraID to be at its default value. Move the logger initialization line
`logger := cmdutil.NewLogger().WithName(opts.InfraID)` from outside the RunE
function into the beginning of the RunE anonymous function, after flag parsing
has occurred. This ensures opts.InfraID contains the actual value from the
--infra-id flag when the named logger is created.
hypershift-operator/controllers/nodepool/nodepool_controller.go (1)

156-156: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Extract hardcoded controller name to a constant for consistency.

The main controller uses ControllerName constant at Line 134, but the secretjanitor controller uses a hardcoded string. For consistency and maintainability, extract this to a package-level constant.

♻️ Proposed refactor
 const ControllerName = "nodepool"
+const SecretJanitorControllerName = "secretjanitor"

Then update the usage:

 	if err := ctrl.NewControllerManagedBy(mgr).
-		Named("secretjanitor").
+		Named(SecretJanitorControllerName).
 		For(&corev1.Secret{}, builder.WithPredicates(supportutil.PredicatesForHostedClusterAnnotationScoping(mgr.GetClient()))).
🤖 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 `@hypershift-operator/controllers/nodepool/nodepool_controller.go` at line 156,
The Named() method call uses a hardcoded string "secretjanitor" instead of a
constant, inconsistent with how the main controller uses the ControllerName
constant. Extract the hardcoded "secretjanitor" string to a package-level
constant (similar to ControllerName) and replace the hardcoded string in the
Named() call with this new constant to maintain consistency across the
controller definitions.
control-plane-operator/main.go (1)

536-536: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the exported controller-name constant here instead of a duplicated literal.

This avoids drift if the controller name changes in one place but not the other.

Suggested change
-		}).SetupWithManager(mgr, upsert.New(enableCIDebugOutput).CreateOrUpdate, hcp, mgr.GetLogger().WithName("hostedcontrolplane")); err != nil {
+		}).SetupWithManager(
+			mgr,
+			upsert.New(enableCIDebugOutput).CreateOrUpdate,
+			hcp,
+			mgr.GetLogger().WithName(hostedcontrolplane.ControllerName),
+		); err != nil {
🤖 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 `@control-plane-operator/main.go` at line 536, Replace the hardcoded string
literal "hostedcontrolplane" in the mgr.GetLogger().WithName() call on line 536
of the SetupWithManager method invocation with an exported controller-name
constant. This ensures that if the controller name needs to be updated in the
future, it only needs to be changed in one place, preventing drift between
different usages of the same controller name throughout the codebase.
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go (1)

605-605: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use structured fields for the release-image mismatch log.

Line 605 still builds one long message string, which makes filtering harder in structured log backends.

Suggested change
-		log.Info("releaseImage is " + util.HCPControlPlaneReleaseImage(hostedControlPlane) + ", but this operator is configured for " + r.OperateOnReleaseImage + ", skipping reconciliation")
+		log.Info(
+			"Skipping reconciliation due to release image mismatch",
+			"hcpReleaseImage", util.HCPControlPlaneReleaseImage(hostedControlPlane),
+			"operatorReleaseImage", r.OperateOnReleaseImage,
+		)
🤖 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
`@control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go`
at line 605, The log.Info statement at line 605 builds a single concatenated
string message using the "+" operator, which prevents structured log backends
from efficiently filtering and processing the log data. Replace the string
concatenation with structured logging fields by passing the release image values
and operator configuration as separate key-value pairs to the log.Info call
instead of concatenating them into a single message string. The values
util.HCPControlPlaneReleaseImage(hostedControlPlane) and r.OperateOnReleaseImage
should be passed as structured fields alongside a descriptive log message.
🤖 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 `@cmd/infra/powervs/destroy.go`:
- Line 120: The logger initialization with WithName(opts.InfraID) on line 120
occurs before Cobra parses the command-line flags, so opts.InfraID contains its
default value instead of the runtime --infra-id flag value. Move the logger
creation statement to after the Cobra flag parsing completes (typically after
opts.Complete() or similar flag binding method is called) so that opts.InfraID
contains the actual value provided by the user at runtime.

In
`@control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go`:
- Line 1559: The logr.Error function does not perform printf-style formatting on
the message parameter, so the `%w` verb in the message string at line 1559 will
be logged literally instead of substituting the actual error value. Remove the
`%w` printf verb from the message string in the log.Error call and any other
occurrences at lines 1766 and 1799 in the same file. The error value should be
passed as a separate parameter to logr.Error if needed for structured logging,
not embedded in the message string itself.

---

Outside diff comments:
In
`@control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go`:
- Around line 65-75: The PrivateServiceObserver struct makes direct API calls in
the Reconcile method using r.clientset.CoreV1().Services(req.Namespace).Get()
instead of leveraging the informer lister that is set up in SetupWithManager. To
fix this, add a services lister field to the PrivateServiceObserver struct to
store the informer's lister, initialize this lister in SetupWithManager when the
shared informer is created, and then update the Reconcile method to use
services.Lister().Services(req.Namespace).Get() instead of the direct client API
call to benefit from caching.

---

Nitpick comments:
In `@cmd/cluster/azure/destroy.go`:
- Line 49: The code in the destroy command is creating a redundant logger
instance using util.NewLogger() when opts.Log is already initialized and
available. Replace the logger assignment on line 49 to use opts.Log instead of
creating a new logger, and update all references to the logger variable
throughout the cmd.Run function to consistently use opts.Log. This ensures the
destroy command uses a single, consistent logger instance rather than
maintaining two separate logger instances.

In `@cmd/infra/azure/rbac.go`:
- Line 219: The assignRole function repeatedly calls cmdutil.NewLogger() across
multiple branches (lines 219-275), which creates unnecessary logger instances
and can be inefficient. Create a single logger instance once at the beginning of
the assignRole function and reuse it throughout all the branches and conditional
blocks instead of calling cmdutil.NewLogger() each time. Replace all occurrences
of cmdutil.NewLogger().Info() and similar calls with the reused local logger
instance to maintain consistent log context and avoid repeated setup overhead.

In `@cmd/infra/powervs/create.go`:
- Around line 230-237: The logger is being initialized with opts.InfraID before
the command flags are parsed, causing opts.InfraID to be at its default value.
Move the logger initialization line `logger :=
cmdutil.NewLogger().WithName(opts.InfraID)` from outside the RunE function into
the beginning of the RunE anonymous function, after flag parsing has occurred.
This ensures opts.InfraID contains the actual value from the --infra-id flag
when the named logger is created.

In
`@control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go`:
- Line 605: The log.Info statement at line 605 builds a single concatenated
string message using the "+" operator, which prevents structured log backends
from efficiently filtering and processing the log data. Replace the string
concatenation with structured logging fields by passing the release image values
and operator configuration as separate key-value pairs to the log.Info call
instead of concatenating them into a single message string. The values
util.HCPControlPlaneReleaseImage(hostedControlPlane) and r.OperateOnReleaseImage
should be passed as structured fields alongside a descriptive log message.

In `@control-plane-operator/main.go`:
- Line 536: Replace the hardcoded string literal "hostedcontrolplane" in the
mgr.GetLogger().WithName() call on line 536 of the SetupWithManager method
invocation with an exported controller-name constant. This ensures that if the
controller name needs to be updated in the future, it only needs to be changed
in one place, preventing drift between different usages of the same controller
name throughout the codebase.

In `@hypershift-operator/controllers/nodepool/nodepool_controller.go`:
- Line 156: The Named() method call uses a hardcoded string "secretjanitor"
instead of a constant, inconsistent with how the main controller uses the
ControllerName constant. Extract the hardcoded "secretjanitor" string to a
package-level constant (similar to ControllerName) and replace the hardcoded
string in the Named() call with this new constant to maintain consistency across
the controller definitions.

In `@product-cli/cmd/cluster/agent/destroy.go`:
- Around line 18-22: In the RunE function of the destroy command, replace the
inefficient `cmdutil.NewLogger()` call in the error handling block with the
pre-initialized logger from `opts.Log`. Instead of creating a new logger
instance each time an error occurs when `agent.DestroyCluster` fails, use the
logger that was already initialized and passed in through the options parameter.
This maintains consistency with the established pattern used elsewhere in the
codebase and avoids unnecessary logger allocations.

In `@product-cli/cmd/cluster/aws/destroy.go`:
- Around line 32-41: The RunE function in the destroy command is creating a new
logger instance with cmdutil.NewLogger() on each error instead of reusing the
pre-initialized logger available in opts.Log. Replace the
cmdutil.NewLogger().Error() call with opts.Log.Error() to use the existing
logger that was set by the caller, ensuring consistency and avoiding unnecessary
allocations following the established pattern in similar files like
cmd/cluster/core/destroy.go.

In `@product-cli/cmd/cluster/kubevirt/destroy.go`:
- Around line 18-24: The RunE function in the destroy command is creating a new
logger instance with cmdutil.NewLogger() for the error log, which is inefficient
and inconsistent with the pattern used elsewhere. Instead, replace the
cmdutil.NewLogger().Error() call with opts.Log.Error() to reuse the
pre-initialized logger that is already available in the opts parameter (which is
passed by the caller and carries the context-aware logger configuration). This
ensures consistency, efficiency, and proper log context propagation throughout
the cluster destruction operation.

In `@product-cli/cmd/cluster/openstack/destroy.go`:
- Around line 16-39: In the NewDestroyCommand function, replace the local logger
initialization (logger := cmdutil.NewLogger()) with direct use of the
pre-initialized logger available in the opts parameter. Update the logger.Error
call within the Run function to use opts.Log instead of the local logger
variable. This ensures consistency with the established pattern in the codebase
and reuses the logger that was already initialized by the caller.
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 3788e3e5-969f-465f-b6e9-9f38631bdf8a

📥 Commits

Reviewing files that changed from the base of the PR and between d3162ab and 28e9ae9.

📒 Files selected for processing (76)
  • cmd/bastion/aws/create.go
  • cmd/bastion/aws/destroy.go
  • cmd/cluster/agent/destroy.go
  • cmd/cluster/aws/destroy.go
  • cmd/cluster/azure/destroy.go
  • cmd/cluster/azure/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/core/create.go
  • cmd/cluster/core/destroy_test.go
  • cmd/cluster/core/dump.go
  • cmd/cluster/gcp/destroy.go
  • cmd/cluster/kubevirt/destroy.go
  • cmd/cluster/none/destroy.go
  • cmd/cluster/openstack/destroy.go
  • cmd/cluster/powervs/destroy.go
  • cmd/consolelogs/aws/getlogs.go
  • cmd/infra/aws/create.go
  • cmd/infra/aws/create_cli_role.go
  • cmd/infra/aws/create_iam.go
  • cmd/infra/aws/destroy.go
  • cmd/infra/aws/destroy_iam.go
  • cmd/infra/azure/create.go
  • cmd/infra/azure/create_iam.go
  • cmd/infra/azure/destroy.go
  • cmd/infra/azure/destroy_iam.go
  • cmd/infra/azure/rbac.go
  • cmd/infra/gcp/create_iam.go
  • cmd/infra/gcp/create_infra.go
  • cmd/infra/gcp/destroy_iam.go
  • cmd/infra/gcp/destroy_infra.go
  • cmd/infra/powervs/create.go
  • cmd/infra/powervs/destroy.go
  • cmd/log/log.go
  • cmd/nodepool/core/create.go
  • cmd/nodepool/powervs/create.go
  • cmd/oadp/backup.go
  • cmd/oadp/restore.go
  • cmd/oadp/schedule.go
  • cmd/oadp/schedule_test.go
  • cmd/util/azure_test.go
  • cmd/util/log.go
  • control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go
  • control-plane-operator/controllers/azureprivatelinkservice/controller.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/observer.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/configmap_observer.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/setup.go
  • control-plane-operator/main.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook_test.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook_test.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • hypershift-operator/controllers/platform/aws/controller.go
  • hypershift-operator/controllers/platform/azure/controller.go
  • hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller.go
  • hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go
  • hypershift-operator/controllers/sharedingress/sharedingress_controller.go
  • hypershift-operator/controllers/supportedversion/reconciler.go
  • hypershift-operator/controllers/uwmtelemetry/uwm_telemetry.go
  • hypershift-operator/main.go
  • product-cli/cmd/cluster/agent/destroy.go
  • product-cli/cmd/cluster/aws/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • product-cli/cmd/cluster/kubevirt/destroy.go
  • product-cli/cmd/cluster/openstack/destroy.go
  • product-cli/cmd/iam/azure/create.go
  • product-cli/cmd/iam/azure/destroy.go
  • product-cli/cmd/infra/azure/create.go
  • product-cli/cmd/infra/azure/destroy.go
  • product-cli/cmd/nodepool/destroy.go
💤 Files with no reviewable changes (4)
  • cmd/log/log.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go
  • hypershift-operator/main.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go

Comment thread cmd/infra/powervs/destroy.go Outdated
@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 28.93401% with 140 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.78%. Comparing base (d45a465) to head (140d64d).
⚠️ Report is 244 commits behind head on main.

Files with missing lines Patch % Lines
cmd/infra/azure/create.go 0.00% 21 Missing ⚠️
...ostedcontrolplane/hostedcontrolplane_controller.go 59.61% 20 Missing and 1 partial ⚠️
cmd/infra/azure/create_iam.go 0.00% 20 Missing ⚠️
cmd/infra/azure/destroy_iam.go 0.00% 15 Missing ⚠️
cmd/infra/azure/destroy.go 0.00% 7 Missing ⚠️
...ollers/awsprivatelink/awsprivatelink_controller.go 0.00% 5 Missing ⚠️
cmd/infra/aws/create_cli_role.go 0.00% 3 Missing ⚠️
cmd/infra/aws/create_iam.go 0.00% 3 Missing ⚠️
cmd/cluster/cluster.go 0.00% 2 Missing ⚠️
cmd/consolelogs/aws/getlogs.go 0.00% 2 Missing ⚠️
... and 38 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8802      +/-   ##
==========================================
+ Coverage   43.77%   43.78%   +0.01%     
==========================================
  Files         772      773       +1     
  Lines       95941    95918      -23     
==========================================
+ Hits        41994    42002       +8     
+ Misses      51035    51004      -31     
  Partials     2912     2912              
Files with missing lines Coverage Δ
cmd/cluster/core/create.go 61.74% <100.00%> (ø)
cmd/infra/azure/rbac.go 47.18% <100.00%> (ø)
cmd/infra/gcp/destroy_iam.go 43.85% <100.00%> (ø)
cmd/oadp/schedule.go 68.19% <100.00%> (ø)
cmd/util/log.go 100.00% <100.00%> (ø)
...ntrollers/auditlogpersistence/configmap_webhook.go 25.00% <ø> (+1.41%) ⬆️
...tor/controllers/auditlogpersistence/pod_webhook.go 54.22% <ø> (+3.58%) ⬆️
...rollers/auditlogpersistence/snapshot_controller.go 62.24% <ø> (+0.10%) ⬆️
...trollers/hostedcluster/hostedcluster_controller.go 54.72% <ø> (ø)
...erator/controllers/nodepool/nodepool_controller.go 43.34% <ø> (ø)
... and 56 more
Flag Coverage Δ
cmd-support 37.38% <15.25%> (+<0.01%) ⬆️
cpo-hostedcontrolplane 45.93% <59.61%> (+0.02%) ⬆️
cpo-other 45.12% <28.57%> (+<0.01%) ⬆️
other 32.08% <30.76%> (ø)

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

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

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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
`@hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller.go`:
- Line 77: The logger.Info call that logs "Initialized GCP platform information"
includes the raw projectID field, which is customer-identifying data that should
not be exposed in shared logs. Remove the projectID field from the logger.Info
call to avoid leaking tenant context, or alternatively redact the identifier
with a masked or hashed value instead of logging the raw project ID.
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 2cae6c22-dd7d-49c2-a615-11b65355bb5a

📥 Commits

Reviewing files that changed from the base of the PR and between 28e9ae9 and 3992c0f.

📒 Files selected for processing (76)
  • cmd/bastion/aws/create.go
  • cmd/bastion/aws/destroy.go
  • cmd/cluster/agent/destroy.go
  • cmd/cluster/aws/destroy.go
  • cmd/cluster/azure/destroy.go
  • cmd/cluster/azure/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/core/create.go
  • cmd/cluster/core/destroy_test.go
  • cmd/cluster/core/dump.go
  • cmd/cluster/gcp/destroy.go
  • cmd/cluster/kubevirt/destroy.go
  • cmd/cluster/none/destroy.go
  • cmd/cluster/openstack/destroy.go
  • cmd/cluster/powervs/destroy.go
  • cmd/consolelogs/aws/getlogs.go
  • cmd/infra/aws/create.go
  • cmd/infra/aws/create_cli_role.go
  • cmd/infra/aws/create_iam.go
  • cmd/infra/aws/destroy.go
  • cmd/infra/aws/destroy_iam.go
  • cmd/infra/azure/create.go
  • cmd/infra/azure/create_iam.go
  • cmd/infra/azure/destroy.go
  • cmd/infra/azure/destroy_iam.go
  • cmd/infra/azure/rbac.go
  • cmd/infra/gcp/create_iam.go
  • cmd/infra/gcp/create_infra.go
  • cmd/infra/gcp/destroy_iam.go
  • cmd/infra/gcp/destroy_infra.go
  • cmd/infra/powervs/create.go
  • cmd/infra/powervs/destroy.go
  • cmd/log/log.go
  • cmd/nodepool/core/create.go
  • cmd/nodepool/powervs/create.go
  • cmd/oadp/backup.go
  • cmd/oadp/restore.go
  • cmd/oadp/schedule.go
  • cmd/oadp/schedule_test.go
  • cmd/util/azure_test.go
  • cmd/util/log.go
  • control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go
  • control-plane-operator/controllers/azureprivatelinkservice/controller.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/observer.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/configmap_observer.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/setup.go
  • control-plane-operator/main.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook_test.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook_test.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • hypershift-operator/controllers/platform/aws/controller.go
  • hypershift-operator/controllers/platform/azure/controller.go
  • hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller.go
  • hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go
  • hypershift-operator/controllers/sharedingress/sharedingress_controller.go
  • hypershift-operator/controllers/supportedversion/reconciler.go
  • hypershift-operator/controllers/uwmtelemetry/uwm_telemetry.go
  • hypershift-operator/main.go
  • product-cli/cmd/cluster/agent/destroy.go
  • product-cli/cmd/cluster/aws/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • product-cli/cmd/cluster/kubevirt/destroy.go
  • product-cli/cmd/cluster/openstack/destroy.go
  • product-cli/cmd/iam/azure/create.go
  • product-cli/cmd/iam/azure/destroy.go
  • product-cli/cmd/infra/azure/create.go
  • product-cli/cmd/infra/azure/destroy.go
  • product-cli/cmd/nodepool/destroy.go
💤 Files with no reviewable changes (3)
  • cmd/log/log.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller_test.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
✅ Files skipped from review due to trivial changes (15)
  • product-cli/cmd/cluster/openstack/destroy.go
  • cmd/util/log.go
  • cmd/cluster/none/destroy.go
  • cmd/infra/gcp/create_iam.go
  • cmd/infra/gcp/destroy_iam.go
  • cmd/cluster/kubevirt/destroy.go
  • control-plane-operator/main.go
  • hypershift-operator/controllers/sharedingress/sharedingress_controller.go
  • cmd/cluster/agent/destroy.go
  • cmd/cluster/core/destroy_test.go
  • hypershift-operator/controllers/auditlogpersistence/snapshot_controller.go
  • cmd/infra/powervs/create.go
  • cmd/oadp/schedule_test.go
  • product-cli/cmd/nodepool/destroy.go
  • cmd/cluster/azure/destroy.go
🚧 Files skipped from review as they are similar to previous changes (56)
  • product-cli/cmd/infra/azure/create.go
  • product-cli/cmd/cluster/kubevirt/destroy.go
  • hypershift-operator/controllers/platform/aws/controller.go
  • product-cli/cmd/iam/azure/destroy.go
  • product-cli/cmd/infra/azure/destroy.go
  • product-cli/cmd/cluster/aws/destroy.go
  • cmd/bastion/aws/create.go
  • cmd/cluster/openstack/destroy.go
  • cmd/cluster/gcp/destroy.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/psc_endpoint_controller.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook_test.go
  • cmd/cluster/cluster.go
  • cmd/nodepool/powervs/create.go
  • cmd/infra/gcp/destroy_infra.go
  • cmd/infra/gcp/create_infra.go
  • hypershift-operator/controllers/supportedversion/reconciler.go
  • product-cli/cmd/iam/azure/create.go
  • cmd/oadp/schedule.go
  • control-plane-operator/controllers/azureprivatelinkservice/controller.go
  • hypershift-operator/controllers/uwmtelemetry/uwm_telemetry.go
  • cmd/infra/aws/destroy_iam.go
  • hypershift-operator/controllers/platform/azure/controller.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/setup.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook_test.go
  • cmd/infra/aws/create_cli_role.go
  • cmd/oadp/backup.go
  • hypershift-operator/controllers/platform/gcp/privateserviceconnect_controller_test.go
  • cmd/infra/azure/create_iam.go
  • cmd/util/azure_test.go
  • cmd/cluster/core/dump.go
  • cmd/bastion/aws/destroy.go
  • cmd/oadp/restore.go
  • hypershift-operator/main.go
  • cmd/cluster/azure/destroy_test.go
  • cmd/cluster/powervs/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • cmd/infra/azure/destroy_iam.go
  • cmd/cluster/core/create.go
  • cmd/infra/azure/destroy.go
  • cmd/consolelogs/aws/getlogs.go
  • cmd/infra/aws/create_iam.go
  • cmd/infra/aws/destroy.go
  • control-plane-operator/controllers/gcpprivateserviceconnect/observer.go
  • hypershift-operator/controllers/auditlogpersistence/configmap_webhook.go
  • cmd/cluster/aws/destroy.go
  • cmd/infra/powervs/destroy.go
  • cmd/nodepool/core/create.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/cmca/configmap_observer.go
  • hypershift-operator/controllers/auditlogpersistence/pod_webhook.go
  • cmd/infra/aws/create.go
  • cmd/infra/azure/create.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
  • cmd/infra/azure/rbac.go
  • control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go

@stevekuznetsov
stevekuznetsov force-pushed the skuznets/controller-logs branch from 3992c0f to 9c3df5c Compare June 22, 2026 19:57
@csrwng csrwng changed the title *: simplify logging, ensure proper fields in reoncilers CNTRLPLANE-3701: simplify logging, ensure proper fields in reoncilers Jun 24, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 24, 2026
@openshift-ci-robot

openshift-ci-robot commented Jun 24, 2026

Copy link
Copy Markdown

@stevekuznetsov: This pull request references CNTRLPLANE-3701 which is a valid jira issue.

Details

In response to this:

We want to ensure that contextual, structural logging is used everywhere. When it's not, crucial information like what reconciler is spitting out the log or what key is being reconciled is missing, leaving users with no easy way to write structured queries over our outputs. Removed the pkg/log export as it's always the wrong thing to use.

What this PR does / why we need it:

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • Refactor
  • Standardized CLI/infra/OADP command logging to use scoped NewLogger() instances (including consistent timestamp formatting).
  • Controllers and admission/webhook handlers now derive logging from request/reconcile context for more consistent, scoped diagnostics.
  • Added deterministic controller naming constants for stable controller-runtime log identifiers.
  • Updated shared command wiring for platform/infra helpers (including Azure credential setup) and removed the old global CLI logger export.
  • Tests
  • Updated unit tests to match the new logger wiring and constructor signatures.

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.

@csrwng

csrwng commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

/override codecov/patch

This is simply changing how logging is invoked, no new logic is introduced.

@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

@csrwng: Overrode contexts on behalf of csrwng: codecov/patch

Details

In response to this:

/override codecov/patch

This is simply changing how logging is invoked, no new logic is introduced.

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.

@csrwng csrwng left a comment

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.

LGTM. Clean and consistent migration to contextual structured logging.

Key wins:

  • Fixes the r.Log concurrency bug (shared mutable state across reconcile calls)
  • Deterministic ControllerName constants for stable log identifiers
  • Removes cmd/log.Log singleton that controllers shouldn't have been using
  • Incidentally fixes incorrect %w format verbs in logr.Error() calls

Mechanical changes are all consistent — verified a representative sample across controllers, webhooks, and CLI layer.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/hold

Revision 40dd9ea was retested 3 times: holding

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

csrwng commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

/rebase

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🤖 Rebasing PR onto main: workflow run

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

The rebase onto main succeeded locally (1 conflict in cmd/infra/aws/create_cli_role.go was resolved while preserving the PR's intent), but the force push failed with:

! [remote rejected] skuznets/controller-logs -> skuznets/controller-logs
(refusing to allow a GitHub App to create or update workflow
`.github/workflows/address-review-comments.yaml` without `workflows` permission)

The rebase picked up upstream commits that modified .github/workflows/ files, and the CI token lacks workflows permission to push those changes to the fork.

@stevekuznetsov — please rebase locally and push, or enable "Allow edits from maintainers" on this PR and retry /rebase.

@csrwng
csrwng force-pushed the skuznets/controller-logs branch from 40dd9ea to d113944 Compare July 6, 2026 19:04
@openshift-ci-robot openshift-ci-robot removed the verified Signifies that the PR passed pre-merge verification criteria label Jul 6, 2026
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Jul 6, 2026
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 6, 2026
@csrwng

csrwng commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

/lgtm
/verified later @stevekuznetsov

@openshift-ci-robot openshift-ci-robot added verified-later verified Signifies that the PR passed pre-merge verification criteria labels Jul 6, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@csrwng: This PR has been marked to be verified later by @stevekuznetsov.

Details

In response to this:

/lgtm
/verified later @stevekuznetsov

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 added the lgtm Indicates that a PR is ready to be merged. label Jul 6, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aks-4-22
/test e2e-aws-4-22
/test e2e-aks
/test e2e-aws
/test e2e-aws-upgrade-hypershift-operator
/test e2e-azure-v2-self-managed
/test e2e-kubevirt-aws-ovn-reduced
/test e2e-v2-aws
/test e2e-v2-gke

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

AI Test Failure Analysis

Job: pull-ci-openshift-hypershift-main-e2e-aks | Build: 2074208226450083840 | Cost: $4.765897500000001 | Failed step: hypershift-azure-run-e2e

View full analysis report


Generated by hypershift-analyze-e2e-failure post-step using Claude claude-opus-4-6

We want to ensure that contextual, structural logging is used
everywhere. When it's not, crucial information like what reconciler is
spitting out the log or what key is being reconciled is missing, leaving
users with no easy way to write structured queries over our outputs.
Removed the `pkg/log` export as it's always the wrong thing to use.

Signed-off-by: Steve Kuznetsov <stekuznetsov@microsoft.com>
Signed-off-by: Steve Kuznetsov <stekuznetsov@microsoft.com>
@stevekuznetsov
stevekuznetsov force-pushed the skuznets/controller-logs branch from d113944 to 140d64d Compare July 10, 2026 19:38
@openshift-ci-robot openshift-ci-robot removed the verified Signifies that the PR passed pre-merge verification criteria label Jul 10, 2026
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Jul 10, 2026
@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

New changes are detected. LGTM label has been removed.

@hypershift-jira-solve-ci

hypershift-jira-solve-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Now I have all the evidence I need. Let me compile the final analysis:

Test Failure Analysis Complete

Job Information

  • Prow Job: codecov/patch
  • Build ID: Check Run 86451004700
  • PR: #8802CNTRLPLANE-3701: simplify logging, ensure proper fields in reconcilers
  • Branch: skuznets/controller-logsmain

Test Failure Analysis

Error

Patch coverage is 28.93% with 140 lines in your changes missing coverage.
Target: 43.77% (project coverage baseline).
28.93% < 43.77% → FAIL

Summary

The codecov/patch check failed because the PR's patch coverage (28.93%) falls significantly below the project's baseline coverage target (43.77%). The PR modifies 77 files across a large-scale logging refactor — replacing the global cmd/log.Log logger with per-call util.NewLogger() instances and adding ControllerName constants with Named() registration across all controllers. Of the ~197 new/changed lines that Codecov tracks, 140 lines lack test coverage. The vast majority of uncovered lines are in CLI infrastructure code (cmd/infra/azure/*, cmd/infra/aws/*, cmd/cluster/*) that has no unit tests and historically has not been tested — these files are at 0% patch coverage. Only hostedcontrolplane_controller.go (59.61% patch) and cmd-support flag code (15.25% patch) have partial coverage.

Root Cause

The failure is not a test regression — no existing tests broke. It is a code coverage policy violation caused by the nature of the refactoring:

  1. Mechanical refactoring in untested CLI code: The PR replaces log.Log with util.NewLogger() across ~40+ CLI command files (cmd/infra/azure/*, cmd/infra/aws/*, cmd/cluster/*/destroy.go, cmd/consolelogs/*, etc.). These files contain infrastructure provisioning/teardown commands that have never had unit tests — they run against real cloud APIs (AWS, Azure, GCP, PowerVS). Every logger := util.NewLogger() call and subsequent logger.Info()/logger.Error() call in these files counts as an uncovered new line.

  2. Codecov default threshold behavior: The codecov.yml has no explicit status: section, so Codecov uses its default policy: the patch check requires new/changed lines to meet the project's overall coverage (43.77%). With 140 of ~197 trackable lines uncovered, the patch hits only 28.93%.

  3. Top uncovered files (all at 0% patch coverage, all in untested CLI infrastructure):

    • cmd/infra/azure/create.go — 21 missing lines
    • cmd/infra/azure/create_iam.go — 20 missing lines
    • cmd/infra/azure/destroy_iam.go — 15 missing lines
    • cmd/infra/azure/destroy.go — 7 missing lines
    • control-plane-operator/controllers/awsprivatelink/awsprivatelink_controller.go — 5 missing lines
    • cmd/infra/aws/create_cli_role.go — 3 missing lines
    • cmd/infra/aws/create_iam.go — 3 missing lines
    • Plus 38 more files with 1–2 missing lines each
  4. The cmd/log/log.go ignore rule is insufficient: The codecov.yml already ignores cmd/log/log.go (which this PR modifies), but does not ignore the many cmd/infra/*, cmd/cluster/*, or cmd/consolelogs/* files that also lack tests.

  5. Controller code has better coverage: The hostedcontrolplane_controller.go changes achieve 59.61% patch coverage (20 lines missing + 1 partial), meaning the controller-side logging changes are reasonably well covered by existing tests. The cmd-support flag is at only 15.25% because it aggregates the CLI code.

Recommendations
  1. Add cmd/util/log.go is already covered (100%) — no action needed for the new logger utility itself.

  2. Option A — Adjust codecov.yml ignore list (lowest effort, appropriate for mechanical refactoring):
    Add CLI infrastructure directories that have no unit tests and are tested only via e2e:

    ignore:
      # ... existing entries ...
      # CLI infrastructure commands (tested via e2e, not unit tests)
      - "cmd/infra/aws/*.go"
      - "cmd/infra/azure/*.go"  
      - "cmd/infra/gcp/*.go"
      - "cmd/infra/powervs/*.go"
      - "cmd/cluster/*/destroy.go"
      - "cmd/consolelogs/**"
      - "cmd/bastion/**"
  3. Option B — Add a status section to codecov.yml to lower the patch threshold or make patch informational:

    coverage:
      status:
        patch:
          default:
            target: auto
            threshold: 15%  # Allow patch coverage 15% below project average
  4. Option C — Add minimal unit tests for the logging calls in the top contributors (e.g., test that NewLogger() returns a non-nil logger, test error-path logging in Azure/AWS create/destroy). This would close the gap but is high effort for low-value tests on cloud CLI paths.

  5. Preferred approach: Option A or B. This is a pure mechanical refactoring (replacing one logger constructor with another). The changed lines are logger initialization and error-logging calls — testing them directly adds minimal value. The project coverage actually improved slightly (+0.01%) because the PR also deletes uncovered lines.

Evidence
Evidence Detail
Patch coverage 28.93% (140 of ~197 trackable lines uncovered)
Project target 43.77% (Codecov default: patch must meet project baseline)
Coverage gap 14.84 percentage points below threshold
Total files changed 77 files across CLI commands, controllers, and operator code
Files at 0% patch cmd/infra/azure/create.go (21 lines), cmd/infra/azure/create_iam.go (20), cmd/infra/azure/destroy_iam.go (15), cmd/infra/azure/destroy.go (7), plus 35+ more
Best-covered area cmd/util/log.go at 100%, hostedcontrolplane_controller.go at 59.61%
Project coverage delta +0.01% (43.77% → 43.78%) — overall coverage improved
Net code change -23 lines (95,941 → 95,918) — PR deletes more than it adds
codecov.yml config No status: section → Codecov uses default threshold (patch ≥ project avg)
Nature of changes Mechanical: log.Logutil.NewLogger() in CLI code; ControllerName constants + Named() in controllers
Check run conclusion failure — Codecov status check, not a test/build failure

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 30, 2026
@openshift-ci

openshift-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PR needs rebase.

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.

@openshift-ci

openshift-ci Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@stevekuznetsov: 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 d113944 link true /test e2e-aws
ci/prow/e2e-aws-upgrade-hypershift-operator d113944 link true /test e2e-aws-upgrade-hypershift-operator
ci/prow/e2e-aws-4-22 d113944 link true /test e2e-aws-4-22
ci/prow/e2e-aks d113944 link true /test e2e-aks
ci/prow/e2e-azure-v2-self-managed d113944 link true /test e2e-azure-v2-self-managed
ci/prow/e2e-v2-aws d113944 link true /test e2e-v2-aws
ci/prow/rosa-e2e-images 140d64d link true /test rosa-e2e-images

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

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/aws PR/issue for AWS (AWSPlatform) platform area/platform/azure PR/issue for Azure (AzurePlatform) platform area/platform/gcp PR/issue for GCP (GCPPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure area/platform/openstack PR/issue for OpenStack (OpenStackPlatform) platform area/platform/powervs PR/issue for PowerVS (PowerVSPlatform) platform do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants