Skip to content
Open
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
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250804142706-7b3ab438a292
github.com/openshift/api v0.0.0-20260408092441-8b086e6b9eb9
github.com/openshift/api v0.0.0-20260429122012-1180c0f5c3e9
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1
github.com/openshift/library-go v0.0.0-20260318142011-72bf34f474bc
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a
github.com/openshift/library-go v0.0.0-20260429171401-d45f1f2500c3
github.com/spf13/cobra v1.10.0
github.com/spf13/pflag v1.0.9
github.com/stretchr/testify v1.11.1
gopkg.in/gcfg.v1 v1.2.0
gopkg.in/gcfg.v1 v1.2.3
k8s.io/api v0.35.1
k8s.io/apimachinery v0.35.1
k8s.io/client-go v0.35.1
Expand Down Expand Up @@ -106,7 +106,7 @@ require (
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.35.1 // indirect
k8s.io/apiserver v0.35.1 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250804142706-7b3ab438a292 h1:3athg6KQ+TaNfW4BWZDlGFt1ImSZEJWgzXtPC1VPITI=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20250804142706-7b3ab438a292/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M=
github.com/openshift/api v0.0.0-20260408092441-8b086e6b9eb9 h1:+4hiHKWaTxwZMhyGkOtEWcPaVBbfIup2w91Ik005eHs=
github.com/openshift/api v0.0.0-20260408092441-8b086e6b9eb9/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo=
github.com/openshift/api v0.0.0-20260429122012-1180c0f5c3e9 h1:lZw6pYY7El1giNk1lYvkp6hLungiqwIOqLlH+Hm7w9g=
github.com/openshift/api v0.0.0-20260429122012-1180c0f5c3e9/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo=
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee h1:+Sp5GGnjHDhT/a/nQ1xdp43UscBMr7G5wxsYotyhzJ4=
github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE=
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1 h1:Hr/R38eg5ZJXfbiaHumjJIN1buDZwhsm4ys4npVCXH0=
github.com/openshift/client-go v0.0.0-20260317180604-743f664b82d1/go.mod h1:Za51LlH76ALiQ/aKGBYJXmyJNkA//IDJ+I///30CA2M=
github.com/openshift/library-go v0.0.0-20260318142011-72bf34f474bc h1:a+rVRzEdFIwgDQLTbhiG3MEVuBXjLb/6HJRikTob+nY=
github.com/openshift/library-go v0.0.0-20260318142011-72bf34f474bc/go.mod h1:3bi4pLpYRdVd1aEhsHfRTJkwxwPLfRZ+ZePn3RmJd2k=
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a h1:4GR6seHvlfv0rADe+LCQx63FqSExx6gaSo8uNiyWq+c=
github.com/openshift/client-go v0.0.0-20260429123927-c81f86abfa6a/go.mod h1:Lm7X7aYbAaKhGsNhgYaowP7hiLKwfN/w0r+Q6VlQoI8=
github.com/openshift/library-go v0.0.0-20260429171401-d45f1f2500c3 h1:JJt8PwBi2mll/hgBzmJomTI/gkQyavM10K13/CnfPSc=
github.com/openshift/library-go v0.0.0-20260429171401-d45f1f2500c3/go.mod h1:k1tefCr+PAZ7kY8TJjpE6rW6t6Yu4iOmBwO+1+3qD2s=
github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12 h1:AKx/w1qpS8We43bsRgf8Nll3CGlDHpr/WAXvuedTNZI=
github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
Expand Down Expand Up @@ -313,14 +313,14 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/gcfg.v1 v1.2.0 h1:0HIbH907iBTAntm+88IJV2qmJALDAh8sPekI9Vc1fm0=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/warnings.v0 v0.1.1 h1:XM28wIgFzaBmeZ5dNHIpWLQpt/9DGKxk+rCg/22nnYE=
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
78 changes: 72 additions & 6 deletions pkg/operator/kube_cloud_config/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ import (
"time"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/api/features"
configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
"github.com/openshift/cluster-config-operator/pkg/operator/operatorclient"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
operatorv1helpers "github.com/openshift/library-go/pkg/operator/v1helpers"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
)

const (
Expand All @@ -34,9 +37,10 @@ type cloudConfigTransformer func(input *corev1.ConfigMap, key string, obj *confi
// user specifications from `infrastructure.spec.platformSpec` to stitch together a new ConfigMap for kube cloud config.
// The stitched ConfigMap is stored at `openshift-config-managed/kube-cloud-confg`.
type KubeCloudConfigController struct {
infraClient configv1client.InfrastructureInterface
infraLister configv1listers.InfrastructureLister
configMapClient corev1client.ConfigMapsGetter
infraClient configv1client.InfrastructureInterface
infraLister configv1listers.InfrastructureLister
configMapClient corev1client.ConfigMapsGetter
featureGateAccess featuregates.FeatureGateAccess

Comment thread
coderabbitai[bot] marked this conversation as resolved.
// transformers stores per platform tranformer
cloudConfigTransformers map[configv1.PlatformType]cloudConfigTransformer
Expand All @@ -47,11 +51,13 @@ func NewController(operatorClient operatorv1helpers.OperatorClient,
infraClient configv1client.InfrastructuresGetter, infraLister configv1listers.InfrastructureLister, infraInformer cache.SharedIndexInformer,
configMapClient corev1client.ConfigMapsGetter,
openshiftConfigConfigMapInformer cache.SharedIndexInformer, openshiftConfigManagedConfigMapInformer cache.SharedIndexInformer,
featureGateAccess featuregates.FeatureGateAccess,
recorder events.Recorder) factory.Controller {
c := &KubeCloudConfigController{
infraClient: infraClient.Infrastructures(),
infraLister: infraLister,
configMapClient: configMapClient,
featureGateAccess: featureGateAccess,
cloudConfigTransformers: cloudConfigTransformers(),
}
return factory.New().
Expand All @@ -69,7 +75,7 @@ func NewController(operatorClient operatorv1helpers.OperatorClient,

func (c KubeCloudConfigController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
obj, err := c.infraLister.Get("cluster")
if errors.IsNotFound(err) {
if apierrors.IsNotFound(err) {
syncCtx.Recorder().Warningf("KubeCloudConfigController", "Required infrastructures.%s/cluster not found", configv1.GroupName)
return nil
}
Expand All @@ -87,6 +93,16 @@ func (c KubeCloudConfigController) sync(ctx context.Context, syncCtx factory.Syn
platformName = currentInfra.Status.Platform
}

// Check if this controller should manage the kube-cloud-config for this platform
shouldManage, err := c.shouldManageCloudConfig(platformName)
if err != nil {
return err
}
if !shouldManage {
syncCtx.Recorder().Eventf("KubeCloudConfigController", "Skipping kube-cloud-config management for platform %s", platformName)
return nil
}

sourceCloudConfigMap := currentInfra.Spec.CloudConfig.Name
sourceCloudConfigKey := currentInfra.Spec.CloudConfig.Key

Expand All @@ -113,7 +129,7 @@ func (c KubeCloudConfigController) sync(ctx context.Context, syncCtx factory.Syn
targetCloudConfigMap := targetConfigName
if len(target.Data) == 0 && len(target.BinaryData) == 0 { // delete if exists
err := c.configMapClient.ConfigMaps(operatorclient.GlobalMachineSpecifiedConfigNamespace).Delete(ctx, targetCloudConfigMap, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if err == nil {
Expand Down Expand Up @@ -160,3 +176,53 @@ func cloudConfigTransformers() map[configv1.PlatformType]cloudConfigTransformer
}
return cloudConfigTransformers
}

// shouldManageCloudConfig determines whether this controller should manage the kube-cloud-config
// ConfigMap for the given platform type. This allows for platform-specific logic to determine
// when ownership should transfer to another operator.
func (c KubeCloudConfigController) shouldManageCloudConfig(platformType configv1.PlatformType) (bool, error) {
switch platformType {
case configv1.VSpherePlatformType:
// For vSphere, check if VSphereMultiVCenterDay2 feature gate is enabled
// When enabled, ownership transfers to cluster-cloud-controller-manager-operator
enabled, err := c.isFeatureGateEnabled(features.FeatureGateVSphereMultiVCenterDay2)
if err != nil {
return false, err
}
// Return false (do not manage) if enabled, true (manage) if not enabled
return !enabled, nil

case configv1.AWSPlatformType,
configv1.AzurePlatformType,
configv1.GCPPlatformType,
configv1.OpenStackPlatformType,
configv1.OvirtPlatformType,
configv1.KubevirtPlatformType,
configv1.IBMCloudPlatformType,
configv1.PowerVSPlatformType,
configv1.AlibabaCloudPlatformType,
configv1.NutanixPlatformType,
configv1.ExternalPlatformType,
configv1.NonePlatformType,
configv1.EquinixMetalPlatformType,
configv1.BareMetalPlatformType:
// For all other platforms, this controller manages the cloud config
return true, nil

default:
// Unknown platform type, default to managing
return true, nil
}
}

// isFeatureGateEnabled checks if the specified feature gate is enabled in the cluster.
// It returns an error only if there's an issue retrieving the feature gate (other than NotFound).
func (c KubeCloudConfigController) isFeatureGateEnabled(gateName configv1.FeatureGateName) (bool, error) {
currentFeatures, err := c.featureGateAccess.CurrentFeatureGates()
if err != nil {
klog.Errorf("unable to get feature gates: %v", err)
return false, err
}
Comment on lines +221 to +225
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This shouldn't ever change result, so you can do this in the init of c and then you don't need any error for this path


return currentFeatures.Enabled(gateName), nil
Comment on lines +218 to +227
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the file to see the context around lines 218-227
cat -n pkg/operator/kube_cloud_config/controller.go | sed -n '210,235p'

Repository: openshift/cluster-config-operator

Length of output: 818


🏁 Script executed:

# Find the type definition for featureGateAccess to understand what methods are available
fd -t f "controller.go" pkg/operator/kube_cloud_config/ -x cat -n {} | head -100

Repository: openshift/cluster-config-operator

Length of output: 5181


🏁 Script executed:

# Search for the featureGateAccess field definition in the controller struct
rg "featureGateAccess" pkg/operator/kube_cloud_config/controller.go -A 2 -B 2

Repository: openshift/cluster-config-operator

Length of output: 1130


🏁 Script executed:

# Search for AreInitialFeatureGatesObserved method in the entire codebase
rg "AreInitialFeatureGatesObserved" --type go

Repository: openshift/cluster-config-operator

Length of output: 1419


🏁 Script executed:

# Search for the interface definition that featureGateAccess might implement
rg "FeatureGateAccess|featureGateAccess" pkg/operator/kube_cloud_config/ -A 3 -B 3

Repository: openshift/cluster-config-operator

Length of output: 6228


🏁 Script executed:

# Find the FeatureGateAccess interface definition
rg "type FeatureGateAccess interface" --type go -A 20

Repository: openshift/cluster-config-operator

Length of output: 3755


🏁 Script executed:

# Look for how CurrentFeatureGates() is used in the library-go vendored code
rg "CurrentFeatureGates\(\)" vendor/github.com/openshift/library-go/pkg/operator/configobserver/featuregates/ -B 3 -A 3

Repository: openshift/cluster-config-operator

Length of output: 5032


🏁 Script executed:

# Check if there are any error checks for uninitialized feature gates
rg "AreInitialFeatureGatesObserved" vendor/github.com/openshift/library-go/pkg/operator/configobserver/featuregates/ -B 5 -A 5

Repository: openshift/cluster-config-operator

Length of output: 10680


🏁 Script executed:

# Look at how isFeatureGateEnabled is called in the codebase to understand the impact
rg "isFeatureGateEnabled" pkg/operator/kube_cloud_config/controller.go -B 2 -A 2

Repository: openshift/cluster-config-operator

Length of output: 745


🏁 Script executed:

# Check if there's any context where this would fail at startup
rg "sync.*defer" pkg/operator/kube_cloud_config/controller.go -A 10 | head -40

Repository: openshift/cluster-config-operator

Length of output: 59


Handle uninitialized feature gates gracefully during startup.

Currently, any error from CurrentFeatureGates() is returned, including "featureGates not yet observed" errors that occur during startup. This hard-fails the sync when feature gates haven't been initialized yet, degrading controller state on vSphere. Check AreInitialFeatureGatesObserved() before calling CurrentFeatureGates() and return (false, nil) as a safe fallback for the not-yet-observed case.

Suggested fix
 func (c KubeCloudConfigController) isFeatureGateEnabled(gateName configv1.FeatureGateName) (bool, error) {
+	if c.featureGateAccess == nil {
+		return false, nil
+	}
+	if !c.featureGateAccess.AreInitialFeatureGatesObserved() {
+		return false, nil
+	}
+
 	currentFeatures, err := c.featureGateAccess.CurrentFeatureGates()
 	if err != nil {
 		klog.Errorf("unable to get feature gates: %v", err)
-		return false, err
+		return false, err
 	}
 
 	return currentFeatures.Enabled(gateName), nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/operator/kube_cloud_config/controller.go` around lines 218 - 227, In
isFeatureGateEnabled, avoid hard-failing when feature gates aren't initialized
by first calling c.featureGateAccess.AreInitialFeatureGatesObserved(); if it
returns false, return (false, nil) as a safe fallback; otherwise proceed to call
c.featureGateAccess.CurrentFeatureGates() and continue using
currentFeatures.Enabled(gateName) and propagate real errors from
CurrentFeatureGates() as before.

}
132 changes: 132 additions & 0 deletions pkg/operator/kube_cloud_config/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"time"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/api/features"
configfakeclient "github.com/openshift/client-go/config/clientset/versioned/fake"
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
"github.com/openshift/cluster-config-operator/pkg/operator/operatorclient"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -215,10 +217,14 @@ SubnetID = subnet-test
}
fakeConfig := configfakeclient.NewSimpleClientset(test.inputinfra)

// Create a feature gate accessor with no enabled feature gates
featureGateAccessor := featuregates.NewHardcodedFeatureGateAccess(nil, nil)

ctrl := KubeCloudConfigController{
infraClient: fakeConfig.ConfigV1().Infrastructures(),
infraLister: configv1listers.NewInfrastructureLister(indexerInfra),
configMapClient: fake.CoreV1(),
featureGateAccess: featureGateAccessor,
cloudConfigTransformers: cloudConfigTransformers(),
}

Expand Down Expand Up @@ -252,3 +258,129 @@ SubnetID = subnet-test
})
}
}

func Test_sync_withVSphereMultiVCenterDay2FeatureGate(t *testing.T) {
testCases := []struct {
name string
platformType configv1.PlatformType
inputData string
featureGateEnabled bool
expectedActions int
description string
}{
{
name: "vSphere platform with feature gate enabled",
platformType: configv1.VSpherePlatformType,
inputData: `[Global]\ntest = value`,
featureGateEnabled: true,
expectedActions: 0,
description: "Should skip ConfigMap update when VSphereMultiVCenterDay2 is enabled on vSphere",
},
{
name: "vSphere platform with feature gate disabled",
platformType: configv1.VSpherePlatformType,
inputData: `[Global]\ntest = value`,
featureGateEnabled: false,
expectedActions: 3, // Get source config, Get target config, Update target config
description: "Should update ConfigMap when VSphereMultiVCenterDay2 is disabled on vSphere",
},
{
name: "AWS platform with feature gate enabled",
platformType: configv1.AWSPlatformType,
inputData: `[Global]\nVPC = vpc-test`,
featureGateEnabled: true,
expectedActions: 3, // Get source config, Get target config, Update target config
description: "Should update ConfigMap on AWS even if VSphereMultiVCenterDay2 is enabled",
},
{
name: "Azure platform with feature gate enabled",
platformType: configv1.AzurePlatformType,
inputData: `{"resourceGroup":"test-rg"}`,
featureGateEnabled: true,
expectedActions: 3, // Get source config, Get target config, Update target config
description: "Should update ConfigMap on Azure even if VSphereMultiVCenterDay2 is enabled",
},
{
name: "GCP platform with feature gate enabled",
platformType: configv1.GCPPlatformType,
inputData: `[global]\nsomekey = somevalue`,
featureGateEnabled: true,
expectedActions: 3, // Get source config, Get target config, Update target config
description: "Should update ConfigMap on GCP even if VSphereMultiVCenterDay2 is enabled",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
inputInfra := &configv1.Infrastructure{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
Spec: configv1.InfrastructureSpec{
CloudConfig: configv1.ConfigMapFileReference{
Name: "cluster-config-v1",
Key: "config",
},
},
Status: configv1.InfrastructureStatus{
PlatformStatus: &configv1.PlatformStatus{
Type: tc.platformType,
},
},
}

// Add Azure-specific platform status
if tc.platformType == configv1.AzurePlatformType {
inputInfra.Status.PlatformStatus.Azure = &configv1.AzurePlatformStatus{}
}

// Create a FeatureGateAccess based on the test case
var featureGateAccessor featuregates.FeatureGateAccess
if tc.featureGateEnabled {
// Create accessor with VSphereMultiVCenterDay2 enabled
featureGateAccessor = featuregates.NewHardcodedFeatureGateAccess(
[]configv1.FeatureGateName{features.FeatureGateVSphereMultiVCenterDay2},
nil,
)
} else {
// Create accessor with VSphereMultiVCenterDay2 disabled
featureGateAccessor = featuregates.NewHardcodedFeatureGateAccess(
nil,
[]configv1.FeatureGateName{features.FeatureGateVSphereMultiVCenterDay2},
)
}

fake := fake.NewClientset()
if err := fake.Tracker().Add(&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "cluster-config-v1", Namespace: "openshift-config"},
Data: map[string]string{"config": tc.inputData},
}); err != nil {
t.Fatalf("failed to add cluster-config-v1 ConfigMap to fake tracker: %v", err)
}
if err := fake.Tracker().Add(&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "kube-cloud-config", Namespace: "openshift-config-managed"},
}); err != nil {
t.Fatalf("failed to add kube-cloud-config ConfigMap to fake tracker: %v", err)
}

indexerInfra := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
if err := indexerInfra.Add(inputInfra); err != nil {
t.Fatal(err.Error())
}

fakeConfig := configfakeclient.NewSimpleClientset(inputInfra)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify constructor availability/deprecation in vendored openshift config fake client.
rg -n "func NewClientset|func NewSimpleClientset|deprecated" \
  vendor/github.com/openshift/client-go/config/clientset/versioned/fake -C2

# Verify current usage in this test file.
rg -n "configfakeclient.NewSimpleClientset|configfakeclient.NewClientset" \
  pkg/operator/kube_cloud_config/controller_test.go -C2

Repository: openshift/cluster-config-operator

Length of output: 1930


🏁 Script executed:

# Check the full function definitions and any deprecation markers
cat -n vendor/github.com/openshift/client-go/config/clientset/versioned/fake/clientset_generated.go | sed -n '25,100p'

Repository: openshift/cluster-config-operator

Length of output: 3205


🏁 Script executed:

# Find all usages of NewSimpleClientset in the repository
rg -n "NewSimpleClientset|NewClientset" pkg/operator/kube_cloud_config/controller_test.go

Repository: openshift/cluster-config-operator

Length of output: 296


🏁 Script executed:

# Check the context around line 369 in controller_test.go
sed -n '365,375p' pkg/operator/kube_cloud_config/controller_test.go

Repository: openshift/cluster-config-operator

Length of output: 516


🏁 Script executed:

# Check context around line 218
sed -n '214,224p' pkg/operator/kube_cloud_config/controller_test.go

Repository: openshift/cluster-config-operator

Length of output: 576


Replace deprecated configfakeclient.NewSimpleClientset to avoid SA1019 lint failure.

The NewSimpleClientset constructor is explicitly deprecated in the vendored openshift/client-go fake config client. Use NewClientset(inputInfra) instead, which provides improved support for field management and server-side apply testing. Both functions have compatible signatures.

Note: Line 218 in the same file also uses the deprecated constructor and should be updated for consistency.

Suggested change
-			fakeConfig := configfakeclient.NewSimpleClientset(inputInfra)
+			fakeConfig := configfakeclient.NewClientset(inputInfra)
🧰 Tools
🪛 golangci-lint (2.11.4)

[error] 369-369: SA1019: configfakeclient.NewSimpleClientset is deprecated: NewClientset replaces this with support for field management, which significantly improves server side apply testing. NewClientset is only available when apply configurations are generated (e.g. via --with-applyconfig).

(staticcheck)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/operator/kube_cloud_config/controller_test.go` at line 369, Replace uses
of the deprecated constructor configfakeclient.NewSimpleClientset with
configfakeclient.NewClientset to avoid SA1019; update both occurrences (the
fakeConfig assignment creating fakeConfig and the earlier usage around line 218)
so they call NewClientset(inputInfra) with the same arguments, keeping the
fakeConfig variable and test setup unchanged except for the constructor name.


ctrl := KubeCloudConfigController{
infraClient: fakeConfig.ConfigV1().Infrastructures(),
infraLister: configv1listers.NewInfrastructureLister(indexerInfra),
configMapClient: fake.CoreV1(),
featureGateAccess: featureGateAccessor,
cloudConfigTransformers: cloudConfigTransformers(),
}

err := ctrl.sync(context.TODO(),
factory.NewSyncContext("KubeCloudConfigController", events.NewInMemoryRecorder("KubeCloudConfigController", clocktesting.NewFakePassiveClock(time.Now()))))

assert.NoError(t, err)
assert.Equal(t, tc.expectedActions, len(fake.Actions()), tc.description)
})
}
}
Loading