diff --git a/controlplane/rosa/controllers/rosacontrolplane_controller.go b/controlplane/rosa/controllers/rosacontrolplane_controller.go index 5a5b07a718..fd44760061 100644 --- a/controlplane/rosa/controllers/rosacontrolplane_controller.go +++ b/controlplane/rosa/controllers/rosacontrolplane_controller.go @@ -31,6 +31,7 @@ import ( stsv2 "github.com/aws/aws-sdk-go-v2/service/sts" sts "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/google/go-cmp/cmp" idputils "github.com/openshift-online/ocm-common/pkg/idp/utils" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" @@ -40,6 +41,7 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apiserver/pkg/storage/names" @@ -58,6 +60,7 @@ import ( rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2" expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/annotations" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa" @@ -89,6 +92,8 @@ type ROSAControlPlaneReconciler struct { WatchFilterValue string WaitInfraPeriod time.Duration Endpoints []scope.ServiceEndpoint + NewStsClient func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsiface.STSAPI + NewOCMClient func(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (rosa.OCMClient, error) } // SetupWithManager is used to setup the controller. @@ -173,6 +178,7 @@ func (r *ROSAControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Req ControllerName: strings.ToLower(rosaControlPlaneKind), Endpoints: r.Endpoints, Logger: log, + NewStsClient: r.NewStsClient, }) if err != nil { return ctrl.Result{}, fmt.Errorf("failed to create scope: %w", err) @@ -203,7 +209,7 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc } } - ocmClient, err := rosa.NewOCMClient(ctx, rosaScope) + ocmClient, err := r.NewOCMClient(ctx, rosaScope) if err != nil { // TODO: need to expose in status, as likely the credentials are invalid return ctrl.Result{}, fmt.Errorf("failed to create OCM client: %w", err) @@ -406,7 +412,7 @@ func (r *ROSAControlPlaneReconciler) deleteMachinePools(ctx context.Context, ros return len(machinePools) == 0, nil } -func (r *ROSAControlPlaneReconciler) reconcileClusterVersion(rosaScope *scope.ROSAControlPlaneScope, ocmClient *ocm.Client, cluster *cmv1.Cluster) error { +func (r *ROSAControlPlaneReconciler) reconcileClusterVersion(rosaScope *scope.ROSAControlPlaneScope, ocmClient rosa.OCMClient, cluster *cmv1.Cluster) error { version := rosaScope.ControlPlane.Spec.Version if version == rosa.RawVersionID(cluster.Version()) { conditions.MarkFalse(rosaScope.ControlPlane, rosacontrolplanev1.ROSAControlPlaneUpgradingCondition, "upgraded", clusterv1.ConditionSeverityInfo, "") @@ -461,7 +467,7 @@ func (r *ROSAControlPlaneReconciler) reconcileClusterVersion(rosaScope *scope.RO return nil } -func (r *ROSAControlPlaneReconciler) updateOCMCluster(rosaScope *scope.ROSAControlPlaneScope, ocmClient *ocm.Client, cluster *cmv1.Cluster, creator *rosaaws.Creator) error { +func (r *ROSAControlPlaneReconciler) updateOCMCluster(rosaScope *scope.ROSAControlPlaneScope, ocmClient rosa.OCMClient, cluster *cmv1.Cluster, creator *rosaaws.Creator) error { ocmClusterSpec, updated := r.updateOCMClusterSpec(rosaScope.ControlPlane, cluster) if updated { @@ -758,7 +764,7 @@ func (r *ROSAControlPlaneReconciler) reconcileExternalAuthBootstrapKubeconfig(ct return nil } -func (r *ROSAControlPlaneReconciler) reconcileKubeconfig(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope, ocmClient *ocm.Client, cluster *cmv1.Cluster) error { +func (r *ROSAControlPlaneReconciler) reconcileKubeconfig(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope, ocmClient rosa.OCMClient, cluster *cmv1.Cluster) error { rosaScope.Debug("Reconciling ROSA kubeconfig for cluster", "cluster-name", rosaScope.RosaClusterName()) clusterRef := client.ObjectKeyFromObject(rosaScope.Cluster) @@ -870,7 +876,7 @@ func (r *ROSAControlPlaneReconciler) reconcileClusterAdminPassword(ctx context.C return password, nil } -func validateControlPlaneSpec(ocmClient *ocm.Client, rosaScope *scope.ROSAControlPlaneScope) (string, error) { +func validateControlPlaneSpec(ocmClient rosa.OCMClient, rosaScope *scope.ROSAControlPlaneScope) (string, error) { version := rosaScope.ControlPlane.Spec.Version valid, err := ocmClient.ValidateHypershiftVersion(version, ocm.DefaultChannelGroup) if err != nil { diff --git a/exp/controllers/rosamachinepool_controller.go b/exp/controllers/rosamachinepool_controller.go index 41a8f15848..7783696e5f 100644 --- a/exp/controllers/rosamachinepool_controller.go +++ b/exp/controllers/rosamachinepool_controller.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/blang/semver" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -16,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/tools/record" @@ -31,6 +33,7 @@ import ( rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2" expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa" @@ -48,6 +51,8 @@ type ROSAMachinePoolReconciler struct { Recorder record.EventRecorder WatchFilterValue string Endpoints []scope.ServiceEndpoint + NewStsClient func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsiface.STSAPI + NewOCMClient func(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (rosa.OCMClient, error) } // SetupWithManager is used to setup the controller. @@ -148,6 +153,7 @@ func (r *ROSAMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ ControlPlane: controlPlane, ControllerName: "rosaControlPlane", Endpoints: r.Endpoints, + NewStsClient: r.NewStsClient, }) if err != nil { return ctrl.Result{}, errors.Wrap(err, "failed to create rosaControlPlane scope") @@ -186,7 +192,7 @@ func (r *ROSAMachinePoolReconciler) reconcileNormal(ctx context.Context, } } - ocmClient, err := rosa.NewOCMClient(ctx, rosaControlPlaneScope) + ocmClient, err := r.NewOCMClient(ctx, rosaControlPlaneScope) if err != nil { // TODO: need to expose in status, as likely the credentials are invalid return ctrl.Result{}, fmt.Errorf("failed to create OCM client: %w", err) @@ -298,7 +304,7 @@ func (r *ROSAMachinePoolReconciler) reconcileDelete( ) error { machinePoolScope.Info("Reconciling deletion of RosaMachinePool") - ocmClient, err := rosa.NewOCMClient(ctx, rosaControlPlaneScope) + ocmClient, err := r.NewOCMClient(ctx, rosaControlPlaneScope) if err != nil { // TODO: need to expose in status, as likely the credentials are invalid return fmt.Errorf("failed to create OCM client: %w", err) @@ -320,7 +326,7 @@ func (r *ROSAMachinePoolReconciler) reconcileDelete( return nil } -func (r *ROSAMachinePoolReconciler) reconcileMachinePoolVersion(machinePoolScope *scope.RosaMachinePoolScope, ocmClient *ocm.Client, nodePool *cmv1.NodePool) error { +func (r *ROSAMachinePoolReconciler) reconcileMachinePoolVersion(machinePoolScope *scope.RosaMachinePoolScope, ocmClient rosa.OCMClient, nodePool *cmv1.NodePool) error { version := machinePoolScope.RosaMachinePool.Spec.Version if version == "" || version == rosa.RawVersionID(nodePool.Version()) { conditions.MarkFalse(machinePoolScope.RosaMachinePool, expinfrav1.RosaMachinePoolUpgradingCondition, "upgraded", clusterv1.ConditionSeverityInfo, "") @@ -356,7 +362,7 @@ func (r *ROSAMachinePoolReconciler) reconcileMachinePoolVersion(machinePoolScope return nil } -func (r *ROSAMachinePoolReconciler) updateNodePool(machinePoolScope *scope.RosaMachinePoolScope, ocmClient *ocm.Client, nodePool *cmv1.NodePool) (*cmv1.NodePool, error) { +func (r *ROSAMachinePoolReconciler) updateNodePool(machinePoolScope *scope.RosaMachinePoolScope, ocmClient rosa.OCMClient, nodePool *cmv1.NodePool) (*cmv1.NodePool, error) { machinePool := machinePoolScope.RosaMachinePool.DeepCopy() // default all fields before comparing, so that nil/unset fields don't cause an unnecessary update call. machinePool.Default() diff --git a/exp/controllers/rosamachinepool_controller_test.go b/exp/controllers/rosamachinepool_controller_test.go index 0ff8ae0c83..223bbe90ff 100644 --- a/exp/controllers/rosamachinepool_controller_test.go +++ b/exp/controllers/rosamachinepool_controller_test.go @@ -1,17 +1,36 @@ package controllers import ( + "context" "testing" "time" + "github.com/aws/aws-sdk-go/service/sts/stsiface" + "github.com/golang/mock/gomock" . "github.com/onsi/gomega" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" + rosacontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/rosa/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-aws/v2/test/mocks" + infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/sts/mock_stsiface" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" ) func TestNodePoolToRosaMachinePoolSpec(t *testing.T) { @@ -60,3 +79,246 @@ func TestNodePoolToRosaMachinePoolSpec(t *testing.T) { g.Expect(computeSpecDiff(rosaMachinePoolSpec, nodePoolSpec)).To(BeEmpty()) } + +func TestRosaMachinePoolReconcile(t *testing.T) { + g := NewWithT(t) + var ( + recorder *record.FakeRecorder + mockCtrl *gomock.Controller + ctx context.Context + scheme *runtime.Scheme + ns *corev1.Namespace + secret *corev1.Secret + rosaControlPlane *rosacontrolplanev1.ROSAControlPlane + ownerCluster *clusterv1.Cluster + ownerMachinePool *expclusterv1.MachinePool + rosaMachinePool *expinfrav1.ROSAMachinePool + ocmMock *mocks.MockOCMClient + objects []client.Object + err error + ) + + setup := func(t *testing.T) { + t.Helper() + mockCtrl = gomock.NewController(t) + recorder = record.NewFakeRecorder(10) + ctx = context.TODO() + scheme = runtime.NewScheme() + ns, err = testEnv.CreateNamespace(ctx, "test-namespace") + g.Expect(err).To(BeNil()) + + g.Expect(expinfrav1.AddToScheme(scheme)).To(Succeed()) + g.Expect(infrav1.AddToScheme(scheme)).To(Succeed()) + g.Expect(clusterv1.AddToScheme(scheme)).To(Succeed()) + g.Expect(expclusterv1.AddToScheme(scheme)).To(Succeed()) + g.Expect(rosacontrolplanev1.AddToScheme(scheme)).To(Succeed()) + g.Expect(corev1.AddToScheme(scheme)).To(Succeed()) + + secret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rosa-secret", + Namespace: ns.Name, + }, + Data: map[string][]byte{ + "ocmToken": []byte("secret-ocm-token-string"), + }, + } + + rosaControlPlane = &rosacontrolplanev1.ROSAControlPlane{ + ObjectMeta: metav1.ObjectMeta{Name: "rosa-control-plane", Namespace: ns.Name}, + TypeMeta: metav1.TypeMeta{ + Kind: "ROSAControlPlane", + APIVersion: rosacontrolplanev1.GroupVersion.String(), + }, + Spec: rosacontrolplanev1.RosaControlPlaneSpec{ + RosaClusterName: "rosa-control-plane", + Subnets: []string{"subnet-0ac99a6230b408813", "subnet-1ac99a6230b408811"}, + AvailabilityZones: []string{"az-1", "az-2"}, + Network: &rosacontrolplanev1.NetworkSpec{ + MachineCIDR: "10.0.0.0/16", + PodCIDR: "10.128.0.0/14", + ServiceCIDR: "172.30.0.0/16", + }, + Region: "us-east-1", + Version: "4.15.20", + RolesRef: rosacontrolplanev1.AWSRolesRef{}, + OIDCID: "iodcid1", + InstallerRoleARN: "arn1", + WorkerRoleARN: "arn2", + SupportRoleARN: "arn3", + CredentialsSecretRef: &corev1.LocalObjectReference{ + Name: secret.Name, + }, + }, + Status: rosacontrolplanev1.RosaControlPlaneStatus{ + Ready: true, + ID: "rosa-control-plane1", + }, + } + + ownerCluster = &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "capi-test-6", + Namespace: ns.Name, + }, + Spec: clusterv1.ClusterSpec{ + ControlPlaneRef: &corev1.ObjectReference{ + Name: rosaControlPlane.Name, + Kind: "ROSAControlPlane", + APIVersion: rosacontrolplanev1.GroupVersion.String(), + }, + }, + } + + ownerMachinePool = &expclusterv1.MachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "machinepool-1", + Namespace: ns.Name, + Labels: map[string]string{clusterv1.ClusterNameLabel: ownerCluster.Name}, + UID: "owner-mp-uid-1", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "MachinePool", + APIVersion: expclusterv1.GroupVersion.String(), + }, + Spec: expclusterv1.MachinePoolSpec{ + ClusterName: ownerCluster.Name, + Template: clusterv1.MachineTemplateSpec{ + Spec: clusterv1.MachineSpec{ + ClusterName: ownerCluster.Name, + }, + }, + }, + } + + rosaMachinePool = &expinfrav1.ROSAMachinePool{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rosa-machinepool", + Namespace: ns.Name, + OwnerReferences: []metav1.OwnerReference{ + { + Name: ownerMachinePool.Name, + UID: ownerMachinePool.UID, + Kind: "MachinePool", + APIVersion: clusterv1.GroupVersion.String(), + }, + }, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "ROSAMachinePool", + APIVersion: expinfrav1.GroupVersion.String(), + }, + Spec: expinfrav1.RosaMachinePoolSpec{}, + } + + objects = []client.Object{secret, ownerCluster, ownerMachinePool} + + for _, obj := range objects { + createObject(g, obj, ns.Name) + } + } + + teardown := func() { + mockCtrl.Finish() + for _, obj := range objects { + cleanupObject(g, obj) + } + } + + t.Run("Reconcile create node pool", func(t *testing.T) { + setup(t) + defer teardown() + ocmMock = mocks.NewMockOCMClient(mockCtrl) + expect := func(m *mocks.MockOCMClientMockRecorder) { + m.GetNodePool(gomock.Any(), gomock.Any()).DoAndReturn(func(clusterId string, nodePoolId string) (*cmv1.NodePool, bool, error) { + return nil, false, nil + }).Times(1) + m.CreateNodePool(gomock.Any(), gomock.Any()).DoAndReturn(func(clusterId string, nodePool *cmv1.NodePool) (*cmv1.NodePool, error) { + return nodePool, nil + }).Times(1) + } + expect(ocmMock.EXPECT()) + + c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(rosaMachinePool, ownerCluster, ownerMachinePool, rosaControlPlane, secret).Build() + stsMock := mock_stsiface.NewMockSTSAPI(mockCtrl) + stsMock.EXPECT().GetCallerIdentity(gomock.Any()).Times(1) + + r := ROSAMachinePoolReconciler{ + Recorder: recorder, + WatchFilterValue: "", + Endpoints: []scope.ServiceEndpoint{}, + Client: c, + NewStsClient: func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsiface.STSAPI { return stsMock }, + NewOCMClient: func(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (rosa.OCMClient, error) { + return ocmMock, nil + }, + } + + req := ctrl.Request{} + req.NamespacedName = types.NamespacedName{Name: "rosa-machinepool", Namespace: ns.Name} + + result, err := r.Reconcile(ctx, req) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(result).To(Equal(ctrl.Result{})) + + }) + + t.Run("Reconcile delete", func(t *testing.T) { + setup(t) + defer teardown() + + deleteTime := metav1.NewTime(time.Now().Add(5 * time.Second)) + rosaMachinePool.ObjectMeta.Finalizers = []string{"finalizer-rosa"} + rosaMachinePool.ObjectMeta.DeletionTimestamp = &deleteTime + + ocmMock := mocks.NewMockOCMClient(mockCtrl) + expect := func(m *mocks.MockOCMClientMockRecorder) { + m.GetNodePool(gomock.Any(), gomock.Any()).DoAndReturn(func(clusterId string, nodePoolId string) (*cmv1.NodePool, bool, error) { + nodePoolBuilder := nodePoolBuilder(rosaMachinePool.Spec, ownerMachinePool.Spec) + nodePool, err := nodePoolBuilder.ID("node-pool-1").Build() + g.Expect(err).To(BeNil()) + return nodePool, true, nil + }).Times(1) + m.DeleteNodePool("rosa-control-plane1", "node-pool-1").DoAndReturn(func(clusterId string, nodePoolId string) error { + return nil + }).Times(1) + } + expect(ocmMock.EXPECT()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(rosaMachinePool, ownerCluster, ownerMachinePool, rosaControlPlane, secret).Build() + stsMock := mock_stsiface.NewMockSTSAPI(mockCtrl) + stsMock.EXPECT().GetCallerIdentity(gomock.Any()).Times(1) + + r := ROSAMachinePoolReconciler{ + Recorder: recorder, + WatchFilterValue: "", + Endpoints: []scope.ServiceEndpoint{}, + Client: client, + NewStsClient: func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsiface.STSAPI { return stsMock }, + NewOCMClient: func(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (rosa.OCMClient, error) { + return ocmMock, nil + }, + } + + req := ctrl.Request{} + req.NamespacedName = types.NamespacedName{Name: "rosa-machinepool", Namespace: ns.Name} + + result, err := r.Reconcile(ctx, req) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(result).To(Equal(ctrl.Result{})) + }) +} + +func createObject(g *WithT, obj client.Object, namespace string) { + if obj.DeepCopyObject() != nil { + obj.SetNamespace(namespace) + g.Expect(testEnv.Create(ctx, obj)).To(Succeed()) + } +} + +func cleanupObject(g *WithT, obj client.Object) { + if obj.DeepCopyObject() != nil { + g.Expect(testEnv.Cleanup(ctx, obj)).To(Succeed()) + } +} diff --git a/main.go b/main.go index c4bb50df4c..79dae27b58 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,7 @@ import ( "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger" "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record" + "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa" "sigs.k8s.io/cluster-api-provider-aws/v2/version" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" @@ -238,6 +239,8 @@ func main() { WatchFilterValue: watchFilterValue, WaitInfraPeriod: waitInfraPeriod, Endpoints: awsServiceEndpoints, + NewOCMClient: rosa.NewOCMClient, + NewStsClient: scope.NewSTSClient, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: ptr.To[bool](true)}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ROSAControlPlane") os.Exit(1) @@ -260,6 +263,8 @@ func main() { Recorder: mgr.GetEventRecorderFor("rosamachinepool-controller"), WatchFilterValue: watchFilterValue, Endpoints: awsServiceEndpoints, + NewOCMClient: rosa.NewOCMClient, + NewStsClient: scope.NewSTSClient, }).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: awsClusterConcurrency, RecoverPanic: ptr.To[bool](true)}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ROSAMachinePool") os.Exit(1) diff --git a/pkg/cloud/scope/rosacontrolplane.go b/pkg/cloud/scope/rosacontrolplane.go index 71cc24ed61..166620710b 100644 --- a/pkg/cloud/scope/rosacontrolplane.go +++ b/pkg/cloud/scope/rosacontrolplane.go @@ -22,9 +22,11 @@ import ( awsclient "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -45,6 +47,7 @@ type ROSAControlPlaneScopeParams struct { ControlPlane *rosacontrolplanev1.ROSAControlPlane ControllerName string Endpoints []ServiceEndpoint + NewStsClient func(cloud.ScopeUsage, cloud.Session, logger.Wrapper, runtime.Object) stsiface.STSAPI } // NewROSAControlPlaneScope creates a new ROSAControlPlaneScope from the supplied parameters. @@ -83,7 +86,7 @@ func NewROSAControlPlaneScope(params ROSAControlPlaneScopeParams) (*ROSAControlP managedScope.session = session managedScope.serviceLimiters = serviceLimiters - stsClient := NewSTSClient(managedScope, managedScope, managedScope, managedScope.ControlPlane) + stsClient := params.NewStsClient(managedScope, managedScope, managedScope, managedScope.ControlPlane) identity, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{}) if err != nil { return nil, fmt.Errorf("failed to identify the AWS caller: %w", err) diff --git a/pkg/rosa/client.go b/pkg/rosa/client.go index 36c9ae333b..6add3518c7 100644 --- a/pkg/rosa/client.go +++ b/pkg/rosa/client.go @@ -7,6 +7,9 @@ import ( "os" sdk "github.com/openshift-online/ocm-sdk-go" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + v1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + "github.com/openshift/rosa/pkg/aws" ocmcfg "github.com/openshift/rosa/pkg/config" "github.com/openshift/rosa/pkg/ocm" "github.com/sirupsen/logrus" @@ -20,16 +23,144 @@ const ( ocmAPIURLKey = "ocmApiUrl" ) +type ocmclient struct { + ocmClient *ocm.Client +} + +type OCMClient interface { + AckVersionGate(clusterID string, gateID string) error + AddHTPasswdUser(username string, password string, clusterID string, idpID string) error + CreateNodePool(clusterID string, nodePool *v1.NodePool) (*v1.NodePool, error) + CreateIdentityProvider(clusterID string, idp *v1.IdentityProvider) (*v1.IdentityProvider, error) + CreateCluster(config ocm.Spec) (*v1.Cluster, error) + CreateUser(clusterID string, group string, user *v1.User) (*v1.User, error) + DeleteCluster(clusterKey string, bestEffort bool, creator *aws.Creator) (*v1.Cluster, error) + DeleteNodePool(clusterID string, nodePoolID string) error + DeleteUser(clusterID string, group string, username string) error + GetCluster(clusterKey string, creator *aws.Creator) (*v1.Cluster, error) + GetControlPlaneUpgradePolicies(clusterID string) (controlPlaneUpgradePolicies []*v1.ControlPlaneUpgradePolicy, err error) + GetHTPasswdUserList(clusterID string, htpasswdIDPId string) (*v1.HTPasswdUserList, error) + GetIdentityProviders(clusterID string) ([]*v1.IdentityProvider, error) + GetMissingGateAgreementsHypershift(clusterID string, upgradePolicy *v1.ControlPlaneUpgradePolicy) ([]*v1.VersionGate, error) + GetNodePool(clusterID string, nodePoolID string) (*cmv1.NodePool, bool, error) + GetHypershiftNodePoolUpgrade(clusterID string, clusterKey string, nodePoolID string) (*v1.NodePool, *v1.NodePoolUpgradePolicy, error) + GetUser(clusterID string, group string, username string) (*v1.User, error) + ScheduleHypershiftControlPlaneUpgrade(clusterID string, upgradePolicy *v1.ControlPlaneUpgradePolicy) (*v1.ControlPlaneUpgradePolicy, error) + ScheduleNodePoolUpgrade(clusterID string, nodePoolId string, upgradePolicy *v1.NodePoolUpgradePolicy) (*v1.NodePoolUpgradePolicy, error) + UpdateNodePool(clusterID string, nodePool *v1.NodePool) (*v1.NodePool, error) + UpdateCluster(clusterKey string, creator *aws.Creator, config ocm.Spec) error + ValidateHypershiftVersion(versionRawID string, channelGroup string) (bool, error) +} + +func (c ocmclient) AckVersionGate(clusterID string, gateID string) error { + return c.ocmClient.AckVersionGate(clusterID, gateID) +} + +func (c ocmclient) AddHTPasswdUser(username string, password string, clusterID string, idpID string) error { + return c.ocmClient.AddHTPasswdUser(username, password, clusterID, idpID) +} +func (c ocmclient) CreateIdentityProvider(clusterID string, idp *v1.IdentityProvider) (*v1.IdentityProvider, error) { + return c.ocmClient.CreateIdentityProvider(clusterID, idp) +} +func (c ocmclient) CreateNodePool(clusterID string, nodePool *v1.NodePool) (*v1.NodePool, error) { + return c.ocmClient.CreateNodePool(clusterID, nodePool) +} + +func (c ocmclient) CreateCluster(config ocm.Spec) (*v1.Cluster, error) { + return c.ocmClient.CreateCluster(config) +} +func (c ocmclient) CreateUser(clusterID string, group string, user *v1.User) (*v1.User, error) { + return c.ocmClient.CreateUser(clusterID, group, user) +} + +func (c ocmclient) DeleteUser(clusterID string, group string, username string) error { + return c.ocmClient.DeleteUser(clusterID, group, username) +} + +func (c ocmclient) DeleteNodePool(clusterID string, nodePoolID string) error { + return c.ocmClient.DeleteNodePool(clusterID, nodePoolID) +} + +func (c ocmclient) DeleteCluster(clusterKey string, bestEffort bool, creator *aws.Creator) (*v1.Cluster, error) { + return c.ocmClient.DeleteCluster(clusterKey, bestEffort, creator) +} + +func (c ocmclient) GetIdentityProviders(clusterID string) ([]*v1.IdentityProvider, error) { + return c.ocmClient.GetIdentityProviders(clusterID) +} + +func (c ocmclient) GetControlPlaneUpgradePolicies(clusterID string) (controlPlaneUpgradePolicies []*v1.ControlPlaneUpgradePolicy, err error) { + return c.ocmClient.GetControlPlaneUpgradePolicies(clusterID) +} + +func (c ocmclient) GetHTPasswdUserList(clusterID string, htpasswdIDPId string) (*v1.HTPasswdUserList, error) { + return c.ocmClient.GetHTPasswdUserList(clusterID, htpasswdIDPId) +} + +func (c ocmclient) GetMissingGateAgreementsHypershift(clusterID string, upgradePolicy *v1.ControlPlaneUpgradePolicy) ([]*v1.VersionGate, error) { + return c.ocmClient.GetMissingGateAgreementsHypershift(clusterID, upgradePolicy) +} + +func (c ocmclient) GetNodePool(clusterID string, nodePoolID string) (*cmv1.NodePool, bool, error) { + return c.ocmClient.GetNodePool(clusterID, nodePoolID) +} + +func (c ocmclient) GetHypershiftNodePoolUpgrade(clusterID string, clusterKey string, nodePoolID string) (*v1.NodePool, *v1.NodePoolUpgradePolicy, error) { + return c.ocmClient.GetHypershiftNodePoolUpgrade(clusterID, clusterKey, nodePoolID) +} + +func (c ocmclient) GetCluster(clusterKey string, creator *aws.Creator) (*v1.Cluster, error) { + return c.ocmClient.GetCluster(clusterKey, creator) +} + +func (c ocmclient) GetUser(clusterID string, group string, username string) (*v1.User, error) { + return c.ocmClient.GetUser(clusterID, group, username) +} + +func (c ocmclient) ScheduleNodePoolUpgrade(clusterID string, nodePoolID string, upgradePolicy *v1.NodePoolUpgradePolicy) (*v1.NodePoolUpgradePolicy, error) { + return c.ocmClient.ScheduleNodePoolUpgrade(clusterID, nodePoolID, upgradePolicy) +} + +func (c ocmclient) ScheduleHypershiftControlPlaneUpgrade(clusterID string, upgradePolicy *v1.ControlPlaneUpgradePolicy) (*v1.ControlPlaneUpgradePolicy, error) { + return c.ocmClient.ScheduleHypershiftControlPlaneUpgrade(clusterID, upgradePolicy) +} + +func (c ocmclient) UpdateCluster(clusterKey string, creator *aws.Creator, config ocm.Spec) error { + return c.ocmClient.UpdateCluster(clusterKey, creator, config) +} + +func (c ocmclient) UpdateNodePool(clusterID string, nodePool *v1.NodePool) (*v1.NodePool, error) { + return c.ocmClient.UpdateNodePool(clusterID, nodePool) +} + +func (c ocmclient) ValidateHypershiftVersion(versionRawID string, channelGroup string) (bool, error) { + return c.ocmClient.ValidateHypershiftVersion(versionRawID, channelGroup) +} + // NewOCMClient creates a new OCM client. -func NewOCMClient(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (*ocm.Client, error) { +func NewOCMClient(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (OCMClient, error) { token, url, err := ocmCredentials(ctx, rosaScope) if err != nil { - return nil, err + return ocmclient{}, err } - return ocm.NewClient().Logger(logrus.New()).Config(&ocmcfg.Config{ + ocmClient, err := ocm.NewClient().Logger(logrus.New()).Config(&ocmcfg.Config{ AccessToken: token, URL: url, }).Build() + + c := ocmclient{ + ocmClient: ocmClient, + } + return c, err +} + +func NewMockOCMClient(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (OCMClient, error) { + ocmClient := ocm.Client{} + + c := ocmclient{ + ocmClient: &ocmClient, + } + return c, nil } func newOCMRawConnection(ctx context.Context, rosaScope *scope.ROSAControlPlaneScope) (*sdk.Connection, error) { diff --git a/pkg/rosa/idps.go b/pkg/rosa/idps.go index bfa9fce65e..0d80bd7d56 100644 --- a/pkg/rosa/idps.go +++ b/pkg/rosa/idps.go @@ -4,7 +4,6 @@ import ( "fmt" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - "github.com/openshift/rosa/pkg/ocm" ) const ( @@ -14,7 +13,7 @@ const ( // CreateAdminUserIfNotExist creates a new admin user withe username/password in the cluster if username doesn't already exist. // the user is granted admin privileges by being added to a special IDP called `cluster-admin` which will be created if it doesn't already exist. -func CreateAdminUserIfNotExist(client *ocm.Client, clusterID, username, password string) error { +func CreateAdminUserIfNotExist(client OCMClient, clusterID, username, password string) error { existingClusterAdminIDP, userList, err := findExistingClusterAdminIDP(client, clusterID) if err != nil { return fmt.Errorf("failed to find existing cluster admin IDP: %w", err) @@ -75,7 +74,7 @@ func CreateAdminUserIfNotExist(client *ocm.Client, clusterID, username, password } // CreateUserIfNotExist creates a new user with `username` and adds it to the group if it doesn't already exist. -func CreateUserIfNotExist(client *ocm.Client, clusterID string, group, username string) (*cmv1.User, error) { +func CreateUserIfNotExist(client OCMClient, clusterID string, group, username string) (*cmv1.User, error) { user, err := client.GetUser(clusterID, group, username) if user != nil || err != nil { return user, err @@ -88,7 +87,7 @@ func CreateUserIfNotExist(client *ocm.Client, clusterID string, group, username return client.CreateUser(clusterID, group, userCfg) } -func findExistingClusterAdminIDP(client *ocm.Client, clusterID string) ( +func findExistingClusterAdminIDP(client OCMClient, clusterID string) ( htpasswdIDP *cmv1.IdentityProvider, userList *cmv1.HTPasswdUserList, reterr error) { idps, err := client.GetIdentityProviders(clusterID) if err != nil { diff --git a/pkg/rosa/versions.go b/pkg/rosa/versions.go index 27136c8772..18706567d0 100644 --- a/pkg/rosa/versions.go +++ b/pkg/rosa/versions.go @@ -13,7 +13,7 @@ import ( var MinSupportedVersion = semver.MustParse("4.14.0") // CheckExistingScheduledUpgrade checks and returns the current upgrade schedule if any. -func CheckExistingScheduledUpgrade(client *ocm.Client, cluster *cmv1.Cluster) (*cmv1.ControlPlaneUpgradePolicy, error) { +func CheckExistingScheduledUpgrade(client OCMClient, cluster *cmv1.Cluster) (*cmv1.ControlPlaneUpgradePolicy, error) { upgradePolicies, err := client.GetControlPlaneUpgradePolicies(cluster.ID()) if err != nil { return nil, err @@ -27,7 +27,7 @@ func CheckExistingScheduledUpgrade(client *ocm.Client, cluster *cmv1.Cluster) (* } // ScheduleControlPlaneUpgrade schedules a new control plane upgrade to the specified version at the specified time. -func ScheduleControlPlaneUpgrade(client *ocm.Client, cluster *cmv1.Cluster, version string, nextRun time.Time, ack bool) (*cmv1.ControlPlaneUpgradePolicy, error) { +func ScheduleControlPlaneUpgrade(client OCMClient, cluster *cmv1.Cluster, version string, nextRun time.Time, ack bool) (*cmv1.ControlPlaneUpgradePolicy, error) { // earliestNextRun is set to at least 5 min from now by the OCM API. // Set our next run request to something slightly longer than 5min to make sure we account for the latency between when we send this // request and when the server processes it. @@ -71,7 +71,7 @@ func ScheduleControlPlaneUpgrade(client *ocm.Client, cluster *cmv1.Cluster, vers } // ScheduleNodePoolUpgrade schedules a new nodePool upgrade to the specified version at the specified time. -func ScheduleNodePoolUpgrade(client *ocm.Client, clusterID string, nodePool *cmv1.NodePool, version string, nextRun time.Time) (*cmv1.NodePoolUpgradePolicy, error) { +func ScheduleNodePoolUpgrade(client OCMClient, clusterID string, nodePool *cmv1.NodePool, version string, nextRun time.Time) (*cmv1.NodePoolUpgradePolicy, error) { // earliestNextRun is set to at least 5 min from now by the OCM API. // Set our next run request to something slightly longer than 5min to make sure we account for the latency between when we send this // request and when the server processes it. diff --git a/test/mocks/generate_capa.go b/test/mocks/generate_capa.go index a1a46e7c4b..8ae9b171fe 100644 --- a/test/mocks/generate_capa.go +++ b/test/mocks/generate_capa.go @@ -16,5 +16,6 @@ limitations under the License. //go:generate ../../hack/tools/bin/mockgen -destination capa_clusterscoper_mock.go -package mocks sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud ClusterScoper //go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt capa_clusterscoper_mock.go > _capa_clusterscoper_mock.go && mv _capa_clusterscoper_mock.go capa_clusterscoper_mock.go" - +//go:generate ../../hack/tools/bin/mockgen -destination ocm_client_mock.go -package mocks sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa OCMClient +//go:generate /usr/bin/env bash -c "cat ../../hack/boilerplate/boilerplate.generatego.txt ocm_client_mock.go > _ocm_client_mock.go && mv _ocm_client_mock.go ocm_client_mock.go" package mocks diff --git a/test/mocks/ocm_client_mock.go b/test/mocks/ocm_client_mock.go new file mode 100644 index 0000000000..4e948cf639 --- /dev/null +++ b/test/mocks/ocm_client_mock.go @@ -0,0 +1,380 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by MockGen. DO NOT EDIT. +// Source: sigs.k8s.io/cluster-api-provider-aws/v2/pkg/rosa (interfaces: OCMClient) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + aws "github.com/openshift/rosa/pkg/aws" + ocm "github.com/openshift/rosa/pkg/ocm" +) + +// MockOCMClient is a mock of OCMClient interface. +type MockOCMClient struct { + ctrl *gomock.Controller + recorder *MockOCMClientMockRecorder +} + +// MockOCMClientMockRecorder is the mock recorder for MockOCMClient. +type MockOCMClientMockRecorder struct { + mock *MockOCMClient +} + +// NewMockOCMClient creates a new mock instance. +func NewMockOCMClient(ctrl *gomock.Controller) *MockOCMClient { + mock := &MockOCMClient{ctrl: ctrl} + mock.recorder = &MockOCMClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOCMClient) EXPECT() *MockOCMClientMockRecorder { + return m.recorder +} + +// AckVersionGate mocks base method. +func (m *MockOCMClient) AckVersionGate(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AckVersionGate", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// AckVersionGate indicates an expected call of AckVersionGate. +func (mr *MockOCMClientMockRecorder) AckVersionGate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AckVersionGate", reflect.TypeOf((*MockOCMClient)(nil).AckVersionGate), arg0, arg1) +} + +// AddHTPasswdUser mocks base method. +func (m *MockOCMClient) AddHTPasswdUser(arg0, arg1, arg2, arg3 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddHTPasswdUser", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddHTPasswdUser indicates an expected call of AddHTPasswdUser. +func (mr *MockOCMClientMockRecorder) AddHTPasswdUser(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHTPasswdUser", reflect.TypeOf((*MockOCMClient)(nil).AddHTPasswdUser), arg0, arg1, arg2, arg3) +} + +// CreateCluster mocks base method. +func (m *MockOCMClient) CreateCluster(arg0 ocm.Spec) (*v1.Cluster, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCluster", arg0) + ret0, _ := ret[0].(*v1.Cluster) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCluster indicates an expected call of CreateCluster. +func (mr *MockOCMClientMockRecorder) CreateCluster(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCluster", reflect.TypeOf((*MockOCMClient)(nil).CreateCluster), arg0) +} + +// CreateIdentityProvider mocks base method. +func (m *MockOCMClient) CreateIdentityProvider(arg0 string, arg1 *v1.IdentityProvider) (*v1.IdentityProvider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateIdentityProvider", arg0, arg1) + ret0, _ := ret[0].(*v1.IdentityProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateIdentityProvider indicates an expected call of CreateIdentityProvider. +func (mr *MockOCMClientMockRecorder) CreateIdentityProvider(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateIdentityProvider", reflect.TypeOf((*MockOCMClient)(nil).CreateIdentityProvider), arg0, arg1) +} + +// CreateNodePool mocks base method. +func (m *MockOCMClient) CreateNodePool(arg0 string, arg1 *v1.NodePool) (*v1.NodePool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateNodePool", arg0, arg1) + ret0, _ := ret[0].(*v1.NodePool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateNodePool indicates an expected call of CreateNodePool. +func (mr *MockOCMClientMockRecorder) CreateNodePool(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNodePool", reflect.TypeOf((*MockOCMClient)(nil).CreateNodePool), arg0, arg1) +} + +// CreateUser mocks base method. +func (m *MockOCMClient) CreateUser(arg0, arg1 string, arg2 *v1.User) (*v1.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUser", arg0, arg1, arg2) + ret0, _ := ret[0].(*v1.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateUser indicates an expected call of CreateUser. +func (mr *MockOCMClientMockRecorder) CreateUser(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockOCMClient)(nil).CreateUser), arg0, arg1, arg2) +} + +// DeleteCluster mocks base method. +func (m *MockOCMClient) DeleteCluster(arg0 string, arg1 bool, arg2 *aws.Creator) (*v1.Cluster, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCluster", arg0, arg1, arg2) + ret0, _ := ret[0].(*v1.Cluster) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteCluster indicates an expected call of DeleteCluster. +func (mr *MockOCMClientMockRecorder) DeleteCluster(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCluster", reflect.TypeOf((*MockOCMClient)(nil).DeleteCluster), arg0, arg1, arg2) +} + +// DeleteNodePool mocks base method. +func (m *MockOCMClient) DeleteNodePool(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteNodePool", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteNodePool indicates an expected call of DeleteNodePool. +func (mr *MockOCMClientMockRecorder) DeleteNodePool(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNodePool", reflect.TypeOf((*MockOCMClient)(nil).DeleteNodePool), arg0, arg1) +} + +// DeleteUser mocks base method. +func (m *MockOCMClient) DeleteUser(arg0, arg1, arg2 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteUser", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteUser indicates an expected call of DeleteUser. +func (mr *MockOCMClientMockRecorder) DeleteUser(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockOCMClient)(nil).DeleteUser), arg0, arg1, arg2) +} + +// GetCluster mocks base method. +func (m *MockOCMClient) GetCluster(arg0 string, arg1 *aws.Creator) (*v1.Cluster, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCluster", arg0, arg1) + ret0, _ := ret[0].(*v1.Cluster) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCluster indicates an expected call of GetCluster. +func (mr *MockOCMClientMockRecorder) GetCluster(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCluster", reflect.TypeOf((*MockOCMClient)(nil).GetCluster), arg0, arg1) +} + +// GetControlPlaneUpgradePolicies mocks base method. +func (m *MockOCMClient) GetControlPlaneUpgradePolicies(arg0 string) ([]*v1.ControlPlaneUpgradePolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetControlPlaneUpgradePolicies", arg0) + ret0, _ := ret[0].([]*v1.ControlPlaneUpgradePolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetControlPlaneUpgradePolicies indicates an expected call of GetControlPlaneUpgradePolicies. +func (mr *MockOCMClientMockRecorder) GetControlPlaneUpgradePolicies(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetControlPlaneUpgradePolicies", reflect.TypeOf((*MockOCMClient)(nil).GetControlPlaneUpgradePolicies), arg0) +} + +// GetHTPasswdUserList mocks base method. +func (m *MockOCMClient) GetHTPasswdUserList(arg0, arg1 string) (*v1.HTPasswdUserList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHTPasswdUserList", arg0, arg1) + ret0, _ := ret[0].(*v1.HTPasswdUserList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetHTPasswdUserList indicates an expected call of GetHTPasswdUserList. +func (mr *MockOCMClientMockRecorder) GetHTPasswdUserList(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHTPasswdUserList", reflect.TypeOf((*MockOCMClient)(nil).GetHTPasswdUserList), arg0, arg1) +} + +// GetHypershiftNodePoolUpgrade mocks base method. +func (m *MockOCMClient) GetHypershiftNodePoolUpgrade(arg0, arg1, arg2 string) (*v1.NodePool, *v1.NodePoolUpgradePolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHypershiftNodePoolUpgrade", arg0, arg1, arg2) + ret0, _ := ret[0].(*v1.NodePool) + ret1, _ := ret[1].(*v1.NodePoolUpgradePolicy) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetHypershiftNodePoolUpgrade indicates an expected call of GetHypershiftNodePoolUpgrade. +func (mr *MockOCMClientMockRecorder) GetHypershiftNodePoolUpgrade(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHypershiftNodePoolUpgrade", reflect.TypeOf((*MockOCMClient)(nil).GetHypershiftNodePoolUpgrade), arg0, arg1, arg2) +} + +// GetIdentityProviders mocks base method. +func (m *MockOCMClient) GetIdentityProviders(arg0 string) ([]*v1.IdentityProvider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIdentityProviders", arg0) + ret0, _ := ret[0].([]*v1.IdentityProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetIdentityProviders indicates an expected call of GetIdentityProviders. +func (mr *MockOCMClientMockRecorder) GetIdentityProviders(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIdentityProviders", reflect.TypeOf((*MockOCMClient)(nil).GetIdentityProviders), arg0) +} + +// GetMissingGateAgreementsHypershift mocks base method. +func (m *MockOCMClient) GetMissingGateAgreementsHypershift(arg0 string, arg1 *v1.ControlPlaneUpgradePolicy) ([]*v1.VersionGate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMissingGateAgreementsHypershift", arg0, arg1) + ret0, _ := ret[0].([]*v1.VersionGate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMissingGateAgreementsHypershift indicates an expected call of GetMissingGateAgreementsHypershift. +func (mr *MockOCMClientMockRecorder) GetMissingGateAgreementsHypershift(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMissingGateAgreementsHypershift", reflect.TypeOf((*MockOCMClient)(nil).GetMissingGateAgreementsHypershift), arg0, arg1) +} + +// GetNodePool mocks base method. +func (m *MockOCMClient) GetNodePool(arg0, arg1 string) (*v1.NodePool, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNodePool", arg0, arg1) + ret0, _ := ret[0].(*v1.NodePool) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetNodePool indicates an expected call of GetNodePool. +func (mr *MockOCMClientMockRecorder) GetNodePool(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNodePool", reflect.TypeOf((*MockOCMClient)(nil).GetNodePool), arg0, arg1) +} + +// GetUser mocks base method. +func (m *MockOCMClient) GetUser(arg0, arg1, arg2 string) (*v1.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUser", arg0, arg1, arg2) + ret0, _ := ret[0].(*v1.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUser indicates an expected call of GetUser. +func (mr *MockOCMClientMockRecorder) GetUser(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockOCMClient)(nil).GetUser), arg0, arg1, arg2) +} + +// ScheduleHypershiftControlPlaneUpgrade mocks base method. +func (m *MockOCMClient) ScheduleHypershiftControlPlaneUpgrade(arg0 string, arg1 *v1.ControlPlaneUpgradePolicy) (*v1.ControlPlaneUpgradePolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ScheduleHypershiftControlPlaneUpgrade", arg0, arg1) + ret0, _ := ret[0].(*v1.ControlPlaneUpgradePolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ScheduleHypershiftControlPlaneUpgrade indicates an expected call of ScheduleHypershiftControlPlaneUpgrade. +func (mr *MockOCMClientMockRecorder) ScheduleHypershiftControlPlaneUpgrade(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleHypershiftControlPlaneUpgrade", reflect.TypeOf((*MockOCMClient)(nil).ScheduleHypershiftControlPlaneUpgrade), arg0, arg1) +} + +// ScheduleNodePoolUpgrade mocks base method. +func (m *MockOCMClient) ScheduleNodePoolUpgrade(arg0, arg1 string, arg2 *v1.NodePoolUpgradePolicy) (*v1.NodePoolUpgradePolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ScheduleNodePoolUpgrade", arg0, arg1, arg2) + ret0, _ := ret[0].(*v1.NodePoolUpgradePolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ScheduleNodePoolUpgrade indicates an expected call of ScheduleNodePoolUpgrade. +func (mr *MockOCMClientMockRecorder) ScheduleNodePoolUpgrade(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleNodePoolUpgrade", reflect.TypeOf((*MockOCMClient)(nil).ScheduleNodePoolUpgrade), arg0, arg1, arg2) +} + +// UpdateCluster mocks base method. +func (m *MockOCMClient) UpdateCluster(arg0 string, arg1 *aws.Creator, arg2 ocm.Spec) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateCluster", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateCluster indicates an expected call of UpdateCluster. +func (mr *MockOCMClientMockRecorder) UpdateCluster(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCluster", reflect.TypeOf((*MockOCMClient)(nil).UpdateCluster), arg0, arg1, arg2) +} + +// UpdateNodePool mocks base method. +func (m *MockOCMClient) UpdateNodePool(arg0 string, arg1 *v1.NodePool) (*v1.NodePool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateNodePool", arg0, arg1) + ret0, _ := ret[0].(*v1.NodePool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateNodePool indicates an expected call of UpdateNodePool. +func (mr *MockOCMClientMockRecorder) UpdateNodePool(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateNodePool", reflect.TypeOf((*MockOCMClient)(nil).UpdateNodePool), arg0, arg1) +} + +// ValidateHypershiftVersion mocks base method. +func (m *MockOCMClient) ValidateHypershiftVersion(arg0, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateHypershiftVersion", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateHypershiftVersion indicates an expected call of ValidateHypershiftVersion. +func (mr *MockOCMClientMockRecorder) ValidateHypershiftVersion(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateHypershiftVersion", reflect.TypeOf((*MockOCMClient)(nil).ValidateHypershiftVersion), arg0, arg1) +}