Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 37 additions & 21 deletions control-plane-operator/hostedclusterconfigoperator/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ import (
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/spotremediation"
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/operator"
hyperapi "github.com/openshift/hypershift/support/api"
"github.com/openshift/hypershift/support/capabilities"
"github.com/openshift/hypershift/support/labelenforcingclient"
"github.com/openshift/hypershift/support/releaseinfo"
"github.com/openshift/hypershift/support/supportedversion"
"github.com/openshift/hypershift/support/upsert"
"github.com/openshift/hypershift/support/util"

"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"

ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -222,6 +224,19 @@ func (o *HostedClusterConfigOperator) Run(ctx context.Context) error {
}
cfg := operator.CfgFromFile(o.TargetKubeconfig)
cpConfig := ctrl.GetConfigOrDie()

// Detect the management cluster capabilities once at startup so controllers can
// gate optional behavior (e.g. watching route.openshift.io Routes) without each
// having to build its own discovery client.
cpDiscoveryClient, err := discovery.NewDiscoveryClientForConfig(cpConfig)
if err != nil {
return fmt.Errorf("failed to create management cluster discovery client: %w", err)
}
mgmtClusterCaps, err := capabilities.DetectManagementClusterCapabilities(cpDiscoveryClient)
if err != nil {
return fmt.Errorf("failed to detect management cluster capabilities: %w", err)
}
Comment on lines +228 to +238

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add unit coverage for capability detection and config wiring.

Line 228 and Line 341 introduce startup-time capability detection and behavior-driving config injection, but this path has no accompanying tests in the PR. Please add unit tests for at least: discovery success, discovery failure, and capability propagation into controller setup inputs.

As per coding guidelines: **/*.go: Always include unit tests when creating new functions or modifying existing ones.

Also applies to: 320-342

🤖 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/hostedclusterconfigoperator/cmd.go` around lines 228 -
238, Add unit tests covering the startup capability-detection path: write tests
that exercise discovery.NewDiscoveryClientForConfig and
capabilities.DetectManagementClusterCapabilities wiring in cmd.go so you verify
(1) successful discovery returns expected mgmtClusterCaps and those capabilities
are injected into controller setup inputs, (2) discovery client creation failure
returns the expected error, and (3) capability detection failure returns the
expected error. Use a fake discovery client (or mock
NewDiscoveryClientForConfig) to simulate success and failure paths and assert
the returned error messages and that the mgmtClusterCaps value is passed into
the controllers' setup/config functions (the code that consumes mgmtClusterCaps
during controller initialization). Ensure tests reference the functions
NewDiscoveryClientForConfig, DetectManagementClusterCapabilities and the
controller setup entrypoints to validate propagation.


mgr := operator.Mgr(ctx, cfg, cpConfig, o.Namespace, o.HostedControlPlaneName)
mgr.GetLogger().Info("Starting hosted-cluster-config-operator", "version", supportedversion.String())
cpCluster, err := cluster.New(cpConfig, func(opt *cluster.Options) {
Expand Down Expand Up @@ -302,27 +317,28 @@ func (o *HostedClusterConfigOperator) Run(ctx context.Context) error {
APIClient: apiReadingClient,
},

Config: cpConfig,
TargetConfig: cfg,
KubevirtInfraConfig: kubevirtInfraConfig,
Manager: mgr,
Namespace: o.Namespace,
HCPName: o.HostedControlPlaneName,
InitialCA: string(o.initialCA),
ClusterSignerCA: string(o.clusterSignerCA),
ControllerFuncs: controllersToRun,
Versions: versions,
PlatformType: hyperv1.PlatformType(o.platformType),
CPCluster: cpCluster,
Logger: ctrl.Log.WithName("hypershift-operator"),
ReleaseProvider: releaseProvider,
KonnectivityAddress: o.KonnectivityAddress,
KonnectivityPort: o.KonnectivityPort,
OAuthAddress: o.OAuthAddress,
OAuthPort: o.OAuthPort,
OperateOnReleaseImage: os.Getenv("OPERATE_ON_RELEASE_IMAGE"),
EnableCIDebugOutput: o.enableCIDebugOutput,
ImageMetaDataProvider: imageMetaDataProvider,
Config: cpConfig,
TargetConfig: cfg,
KubevirtInfraConfig: kubevirtInfraConfig,
Manager: mgr,
Namespace: o.Namespace,
HCPName: o.HostedControlPlaneName,
InitialCA: string(o.initialCA),
ClusterSignerCA: string(o.clusterSignerCA),
ControllerFuncs: controllersToRun,
Versions: versions,
PlatformType: hyperv1.PlatformType(o.platformType),
CPCluster: cpCluster,
Logger: ctrl.Log.WithName("hypershift-operator"),
ReleaseProvider: releaseProvider,
KonnectivityAddress: o.KonnectivityAddress,
KonnectivityPort: o.KonnectivityPort,
OAuthAddress: o.OAuthAddress,
OAuthPort: o.OAuthPort,
OperateOnReleaseImage: os.Getenv("OPERATE_ON_RELEASE_IMAGE"),
EnableCIDebugOutput: o.enableCIDebugOutput,
ImageMetaDataProvider: imageMetaDataProvider,
ManagementClusterCapabilities: mgmtClusterCaps,
}
configmetrics.Register(mgr.GetCache())
return operatorConfig.Start(ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,12 @@ func Setup(ctx context.Context, opts *operator.HostedClusterConfigOperatorConfig
}

// Watch metrics-proxy Route on the control plane cluster for hostname changes.
if err := c.Watch(source.Kind[client.Object](opts.CPCluster.GetCache(), &routev1.Route{}, eventHandler())); err != nil {
return fmt.Errorf("failed to watch Route: %w", err)
// Skip when the management cluster does not expose the route.openshift.io API
// (non-OpenShift management cluster); otherwise the watch fails and HCCO does not start.
if opts.ManagementClusterCapabilities.Has(capabilities.CapabilityRoute) {
if err := c.Watch(source.Kind[client.Object](opts.CPCluster.GetCache(), &routev1.Route{}, eventHandler())); err != nil {
return fmt.Errorf("failed to watch Route: %w", err)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

// Watch HostedControlPlane namespace pull-secret on the control plane cluster so guest pull secrets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,30 @@ func cacheLabelSelector() labels.Selector {
type ControllerSetupFunc func(context.Context, *HostedClusterConfigOperatorConfig) error

type HostedClusterConfigOperatorConfig struct {
Manager ctrl.Manager
Config *rest.Config
TargetConfig *rest.Config
KubevirtInfraConfig *rest.Config
TargetCreateOrUpdateProvider upsert.CreateOrUpdateProvider
CPCluster cluster.Cluster
Logger logr.Logger
Versions map[string]string
HCPName string
Namespace string
InitialCA string
ClusterSignerCA string
Controllers []string
PlatformType hyperv1.PlatformType
ControllerFuncs map[string]ControllerSetupFunc
ReleaseProvider releaseinfo.Provider
KonnectivityAddress string
KonnectivityPort int32
OAuthAddress string
OAuthPort int32
OperateOnReleaseImage string
EnableCIDebugOutput bool
ImageMetaDataProvider util.ImageMetadataProvider
Manager ctrl.Manager
Config *rest.Config
TargetConfig *rest.Config
KubevirtInfraConfig *rest.Config
TargetCreateOrUpdateProvider upsert.CreateOrUpdateProvider
CPCluster cluster.Cluster
Logger logr.Logger
Versions map[string]string
HCPName string
Namespace string
InitialCA string
ClusterSignerCA string
Controllers []string
PlatformType hyperv1.PlatformType
ControllerFuncs map[string]ControllerSetupFunc
ReleaseProvider releaseinfo.Provider
KonnectivityAddress string
KonnectivityPort int32
OAuthAddress string
OAuthPort int32
OperateOnReleaseImage string
EnableCIDebugOutput bool
ImageMetaDataProvider util.ImageMetadataProvider
ManagementClusterCapabilities capabilities.CapabiltyChecker

kubeClient kubeclient.Interface
}
Expand Down
Loading