Skip to content
Draft
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
12 changes: 7 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-20260717133910-57eb58a15422
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-20260720094807-fbc45213df28
github.com/openshift/library-go v0.0.0-20260909144031-8fc95c63c590
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 All @@ -122,3 +122,5 @@ require (

// This replace is required for we use the OCP fork of Ginkgo.
replace github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12

replace github.com/openshift/api => github.com/vr4manta/api v0.0.0-20260804142043-4f1fc2ae3902
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,12 @@ 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/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-20260720094807-fbc45213df28 h1:7umi+uA/Bm1PAEXduwzfQRsMZ8WR0/LHh9YXezJnxaA=
github.com/openshift/client-go v0.0.0-20260720094807-fbc45213df28/go.mod h1:dOrynOHvh9q01db+7+Eu8O0do1FTrAUQSnI9tzJhO8Y=
github.com/openshift/library-go v0.0.0-20260909144031-8fc95c63c590 h1:PwCbVsfAqoxCPmgIzrHiyDJNXIy9JC7MQU3YvjajruA=
github.com/openshift/library-go v0.0.0-20260909144031-8fc95c63c590/go.mod h1:jnC7fncyNlp9wu20ElexbQvZBnXXZ7NbejCOvU7UqWU=
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 @@ -197,6 +195,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/vr4manta/api v0.0.0-20260804142043-4f1fc2ae3902 h1:scCwlHbxUtfT6LHP57JhF94Tj1IcyAeXKZJcjZQHNbM=
github.com/vr4manta/api v0.0.0-20260804142043-4f1fc2ae3902/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
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: 74 additions & 4 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 @@ -38,6 +41,8 @@ type KubeCloudConfigController struct {
infraLister configv1listers.InfrastructureLister
configMapClient corev1client.ConfigMapsGetter

featureGateAccessor featuregates.FeatureGateAccess

// transformers stores per platform tranformer
cloudConfigTransformers map[configv1.PlatformType]cloudConfigTransformer
}
Expand All @@ -47,13 +52,16 @@ 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,
cloudConfigTransformers: cloudConfigTransformers(),
featureGateAccessor: featureGateAccess,
}

return factory.New().
WithInformers(
operatorClient.Informer(),
Expand All @@ -67,9 +75,9 @@ func NewController(operatorClient operatorv1helpers.OperatorClient,
ToController("KubeCloudConfigController", recorder)
}

func (c KubeCloudConfigController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
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 +95,17 @@ 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 {
// Set log level to 4 instead of using an event recorder due to this logging / happening every minute.
klog.V(4).Infof("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 +132,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 +179,54 @@ 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 := c.isFeatureGateEnabled(features.FeatureGateVSphereMultiVCenterDay2)
// 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 uses the feature gates that were retrieved during controller initialization.
// If feature gates weren't available at initialization, it returns false as a safe fallback.
func (c *KubeCloudConfigController) isFeatureGateEnabled(gateName configv1.FeatureGateName) bool {
if !c.featureGateAccessor.AreInitialFeatureGatesObserved() {
// Feature gates weren't initialized, return safe fallback
klog.Warningf("unable to check featuregate %v due to currentFeatureGates == nil", gateName)
return false
}

// We can ignore error since only time error happens if initial feature gates were not observed and we checked above
currentFeatureGates, _ := c.featureGateAccessor.CurrentFeatureGates()
klog.V(4).Infof("is featuregate %v enabled? %v", gateName, currentFeatureGates.Enabled(gateName))
return currentFeatureGates.Enabled(gateName)
}
134 changes: 133 additions & 1 deletion 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 @@ -213,12 +215,16 @@ SubnetID = subnet-test
if err := indexerInfra.Add(test.inputinfra); err != nil {
t.Fatal(err.Error())
}
fakeConfig := configfakeclient.NewSimpleClientset(test.inputinfra)
fakeConfig := configfakeclient.NewClientset(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(),
featureGateAccessor: 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.NewClientset(inputInfra)

ctrl := KubeCloudConfigController{
infraClient: fakeConfig.ConfigV1().Infrastructures(),
infraLister: configv1listers.NewInfrastructureLister(indexerInfra),
configMapClient: fake.CoreV1(),
featureGateAccessor: 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