From 47c04880782f8a9dc7fe1fbe71abd7e831d08f36 Mon Sep 17 00:00:00 2001 From: edibble21 <85638465+edibble21@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:18:23 -0800 Subject: [PATCH] Feedback Addressed --- pkg/apis/v1/ec2nodeclass_status.go | 2 - .../nodeclass/status/authorization.go | 61 -------- .../nodeclass/status/authorization_test.go | 70 --------- .../nodeclass/status/controller.go | 5 +- .../nodeclass/status/readiness_test.go | 2 +- .../nodeclass/status/suite_test.go | 27 +--- .../nodeclass/status/validation.go | 37 ++++- .../nodeclass/status/validation_test.go | 134 ++++++++++++------ 8 files changed, 132 insertions(+), 206 deletions(-) delete mode 100644 pkg/controllers/nodeclass/status/authorization.go delete mode 100644 pkg/controllers/nodeclass/status/authorization_test.go diff --git a/pkg/apis/v1/ec2nodeclass_status.go b/pkg/apis/v1/ec2nodeclass_status.go index 7ce2b12a946e..4c210ef81789 100644 --- a/pkg/apis/v1/ec2nodeclass_status.go +++ b/pkg/apis/v1/ec2nodeclass_status.go @@ -24,7 +24,6 @@ const ( ConditionTypeSecurityGroupsReady = "SecurityGroupsReady" ConditionTypeAMIsReady = "AMIsReady" ConditionTypeInstanceProfileReady = "InstanceProfileReady" - ConditionTypeAuthorization = "AuthorizationReady" ConditionTypeValidationSucceeded = "ValidationSucceeded" ) @@ -95,7 +94,6 @@ func (in *EC2NodeClass) StatusConditions() status.ConditionSet { ConditionTypeSubnetsReady, ConditionTypeSecurityGroupsReady, ConditionTypeInstanceProfileReady, - ConditionTypeAuthorization, ConditionTypeValidationSucceeded, ).For(in) } diff --git a/pkg/controllers/nodeclass/status/authorization.go b/pkg/controllers/nodeclass/status/authorization.go deleted file mode 100644 index a85bb744d02a..000000000000 --- a/pkg/controllers/nodeclass/status/authorization.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -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. -*/ - -package status - -import ( - "context" - "fmt" - - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - corecloudprovider "sigs.k8s.io/karpenter/pkg/cloudprovider" - coretest "sigs.k8s.io/karpenter/pkg/test" - - "github.com/samber/lo" - - v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" - "github.com/aws/karpenter-provider-aws/pkg/cloudprovider" - "github.com/aws/karpenter-provider-aws/pkg/providers/instance" -) - -type Authorization struct { - cloudProvider cloudprovider.CloudProvider - instanceProvider instance.Provider -} - -func (a Authorization) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (reconcile.Result, error) { - //nolint:ineffassign, staticcheck - ctx = context.WithValue(ctx, "DryRun", lo.ToPtr(true)) - if nodeClass.StatusConditions().Get(v1.ConditionTypeSubnetsReady).IsFalse() || nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).IsFalse() { - return reconcile.Result{}, nil - } - nodeClaim := coretest.NodeClaim() - nodeClaim.Spec.NodeClassRef.Name = nodeClass.Name - _, err := a.cloudProvider.Create(ctx, nodeClaim) - if err == nil { - err = a.instanceProvider.Delete(ctx, "mock-id") - if err == nil { - err = a.instanceProvider.CreateTags(ctx, "mock-id", map[string]string{"mock-tag": "mock-tag-value"}) - } - } - //nolint:ineffassign, staticcheck - ctx = context.WithValue(ctx, "DryRun", lo.ToPtr(false)) - if corecloudprovider.IsNodeClassNotReadyError(err) { - nodeClass.StatusConditions().SetFalse(v1.ConditionTypeAuthorization, "NodeClassNotReady", "Unauthorized Operation") - return reconcile.Result{}, fmt.Errorf("unauthorized operation %w", err) - } - nodeClass.StatusConditions().SetTrue(v1.ConditionTypeAuthorization) - return reconcile.Result{}, nil -} diff --git a/pkg/controllers/nodeclass/status/authorization_test.go b/pkg/controllers/nodeclass/status/authorization_test.go deleted file mode 100644 index 840ad8565bf6..000000000000 --- a/pkg/controllers/nodeclass/status/authorization_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -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. -*/ - -package status_test - -import ( - "github.com/aws/smithy-go" - - v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" - "github.com/aws/karpenter-provider-aws/pkg/fake" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - . "sigs.k8s.io/karpenter/pkg/test/expectations" -) - -var _ = Describe("NodeClass Authorized Status Controller", func() { - It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to createfleet", func() { - ExpectApplied(ctx, env.Client, nodeClass) - awsEnv.EC2API.CreateFleetBehavior.Error.Set(&smithy.GenericAPIError{ - Code: "UnauthorizedOperation", - }, fake.MaxCalls(1)) - err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) - Expect(err).To(HaveOccurred()) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.Status.Conditions).To(HaveLen(7)) - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAuthorization).IsFalse()).To(BeTrue()) - }) - It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to terminateinstances", func() { - ExpectApplied(ctx, env.Client, nodeClass) - awsEnv.EC2API.TerminateInstancesBehavior.Error.Set(&smithy.GenericAPIError{ - Code: "UnauthorizedOperation", - }, fake.MaxCalls(2)) - err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) - Expect(err).To(HaveOccurred()) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.Status.Conditions).To(HaveLen(7)) - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAuthorization).IsFalse()).To(BeTrue()) - }) - It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to CreateTags", func() { - ExpectApplied(ctx, env.Client, nodeClass) - awsEnv.EC2API.CreateTagsBehavior.Error.Set(&smithy.GenericAPIError{ - Code: "UnauthorizedOperation", - }, fake.MaxCalls(1)) - err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) - Expect(err).To(HaveOccurred()) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.Status.Conditions).To(HaveLen(7)) - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAuthorization).IsFalse()).To(BeTrue()) - }) - It("should update status condition as Ready", func() { - nodeClass.Spec.Tags = map[string]string{} - ExpectApplied(ctx, env.Client, nodeClass) - ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) - - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAuthorization).IsTrue()).To(BeTrue()) - }) -}) diff --git a/pkg/controllers/nodeclass/status/controller.go b/pkg/controllers/nodeclass/status/controller.go index c99ca51adfa0..8e419056a141 100644 --- a/pkg/controllers/nodeclass/status/controller.go +++ b/pkg/controllers/nodeclass/status/controller.go @@ -54,7 +54,6 @@ type Controller struct { subnet *Subnet securitygroup *SecurityGroup validation *Validation - authorization *Authorization readiness *Readiness //TODO : Remove this when we have sub status conditions } @@ -67,8 +66,7 @@ func NewController(kubeClient client.Client, subnetProvider subnet.Provider, sec subnet: &Subnet{subnetProvider: subnetProvider}, securitygroup: &SecurityGroup{securityGroupProvider: securityGroupProvider}, instanceprofile: &InstanceProfile{instanceProfileProvider: instanceProfileProvider}, - validation: &Validation{}, - authorization: &Authorization{cloudProvider: cloudProvider, instanceProvider: instanceProvider}, + validation: &Validation{cloudProvider: cloudProvider, instanceProvider: instanceProvider}, readiness: &Readiness{launchTemplateProvider: launchTemplateProvider}, } } @@ -100,7 +98,6 @@ func (c *Controller) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) c.securitygroup, c.instanceprofile, c.validation, - c.authorization, c.readiness, } { res, err := reconciler.Reconcile(ctx, nodeClass) diff --git a/pkg/controllers/nodeclass/status/readiness_test.go b/pkg/controllers/nodeclass/status/readiness_test.go index 6b7348e23b3f..ed6bd9d5ea91 100644 --- a/pkg/controllers/nodeclass/status/readiness_test.go +++ b/pkg/controllers/nodeclass/status/readiness_test.go @@ -53,7 +53,7 @@ var _ = Describe("NodeClass Status Condition Controller", func() { ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.Status.Conditions).To(HaveLen(7)) + Expect(nodeClass.Status.Conditions).To(HaveLen(6)) Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsTrue()).To(BeTrue()) }) It("should update status condition as Not Ready", func() { diff --git a/pkg/controllers/nodeclass/status/suite_test.go b/pkg/controllers/nodeclass/status/suite_test.go index 2181f210ac1d..ccf8d05dbbb7 100644 --- a/pkg/controllers/nodeclass/status/suite_test.go +++ b/pkg/controllers/nodeclass/status/suite_test.go @@ -56,25 +56,6 @@ var _ = BeforeSuite(func() { ctx = coreoptions.ToContext(ctx, coretest.Options()) ctx = options.ToContext(ctx, test.Options()) awsEnv = test.NewEnvironment(ctx, env) - - instanceTypesProvider := awsEnv.InstanceTypesProvider - - Expect(instanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) - Expect(instanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) - - cloudProvider = lo.FromPtr(cloudprovider.New(instanceTypesProvider, awsEnv.InstanceProvider, coretest.NewEventRecorder(), - env.Client, awsEnv.AMIProvider, awsEnv.SecurityGroupProvider)) - - statusController = status.NewController( - env.Client, - awsEnv.SubnetProvider, - awsEnv.SecurityGroupProvider, - awsEnv.AMIProvider, - awsEnv.InstanceProfileProvider, - awsEnv.LaunchTemplateProvider, - cloudProvider, - awsEnv.InstanceProvider, - ) }) var _ = AfterSuite(func() { @@ -86,12 +67,10 @@ var _ = BeforeEach(func() { nodeClass = test.EC2NodeClass() awsEnv.Reset() - instanceTypesProvider := awsEnv.InstanceTypesProvider - - Expect(instanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) - Expect(instanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) + Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) + Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) - cloudProvider = lo.FromPtr(cloudprovider.New(instanceTypesProvider, awsEnv.InstanceProvider, coretest.NewEventRecorder(), + cloudProvider = lo.FromPtr(cloudprovider.New(awsEnv.InstanceTypesProvider, awsEnv.InstanceProvider, coretest.NewEventRecorder(), env.Client, awsEnv.AMIProvider, awsEnv.SecurityGroupProvider)) statusController = status.NewController( diff --git a/pkg/controllers/nodeclass/status/validation.go b/pkg/controllers/nodeclass/status/validation.go index 2e0b1eccedc4..0236e71e0f8f 100644 --- a/pkg/controllers/nodeclass/status/validation.go +++ b/pkg/controllers/nodeclass/status/validation.go @@ -16,18 +16,27 @@ package status import ( "context" + "errors" "fmt" "github.com/samber/lo" "sigs.k8s.io/controller-runtime/pkg/reconcile" + corecloudprovider "sigs.k8s.io/karpenter/pkg/cloudprovider" + coretest "sigs.k8s.io/karpenter/pkg/test" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + "github.com/aws/karpenter-provider-aws/pkg/cloudprovider" + "github.com/aws/karpenter-provider-aws/pkg/providers/instance" ) -type Validation struct{} +type Validation struct { + cloudProvider cloudprovider.CloudProvider + instanceProvider instance.Provider +} func (n Validation) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (reconcile.Result, error) { + //Tag Validation if offendingTag, found := lo.FindKeyBy(nodeClass.Spec.Tags, func(k string, v string) bool { for _, exp := range v1.RestrictedTagPatterns { if exp.MatchString(k) { @@ -40,6 +49,32 @@ func (n Validation) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) ( fmt.Sprintf("%q tag does not pass tag validation requirements", offendingTag)) return reconcile.Result{}, reconcile.TerminalError(fmt.Errorf("%q tag does not pass tag validation requirements", offendingTag)) } + //Auth Validation + //validates createfleet, describelaunchtemplate, createtags, and terminateinstances + //nolint:ineffassign, staticcheck + ctx = context.WithValue(ctx, "DryRun", lo.ToPtr(true)) + if nodeClass.StatusConditions().Get(v1.ConditionTypeSubnetsReady).IsFalse() || nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).IsFalse() { + return reconcile.Result{}, nil + } + nodeClaim := coretest.NodeClaim() + nodeClaim.Spec.NodeClassRef.Name = nodeClass.Name + var errs []error + //create checks createfleet, and describelaunchtemplate + if _, err := n.cloudProvider.Create(ctx, nodeClaim); err != nil { + errs = append(errs, fmt.Errorf("create: %w", err)) + } + + if err := n.instanceProvider.Delete(ctx, "mock-id"); err != nil { + errs = append(errs, fmt.Errorf("delete: %w", err)) + } + + if err := n.instanceProvider.CreateTags(ctx, "mock-id", map[string]string{"mock-tag": "mock-tag-value"}); err != nil { + errs = append(errs, fmt.Errorf("create tags: %w", err)) + } + if corecloudprovider.IsNodeClassNotReadyError(errors.Join(errs...)) { + nodeClass.StatusConditions().SetFalse(v1.ConditionTypeValidationSucceeded, "NodeClassNotReady", fmt.Sprintf("unauthorized operation %v", errors.Join(errs...))) + return reconcile.Result{}, fmt.Errorf("unauthorized operation %w", errors.Join(errs...)) + } nodeClass.StatusConditions().SetTrue(v1.ConditionTypeValidationSucceeded) return reconcile.Result{}, nil } diff --git a/pkg/controllers/nodeclass/status/validation_test.go b/pkg/controllers/nodeclass/status/validation_test.go index 3d46344c2685..b406c14d8436 100644 --- a/pkg/controllers/nodeclass/status/validation_test.go +++ b/pkg/controllers/nodeclass/status/validation_test.go @@ -18,7 +18,10 @@ import ( status "github.com/awslabs/operatorpkg/status" "github.com/samber/lo" + "github.com/aws/smithy-go" + v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + "github.com/aws/karpenter-provider-aws/pkg/fake" "github.com/aws/karpenter-provider-aws/pkg/test" . "github.com/onsi/ginkgo/v2" @@ -27,55 +30,100 @@ import ( ) var _ = Describe("NodeClass Validation Status Controller", func() { - BeforeEach(func() { - nodeClass = test.EC2NodeClass(v1.EC2NodeClass{ - Spec: v1.EC2NodeClassSpec{ - SubnetSelectorTerms: []v1.SubnetSelectorTerm{ - { - Tags: map[string]string{"*": "*"}, + Context("Tag Validation", func() { + BeforeEach(func() { + nodeClass = test.EC2NodeClass(v1.EC2NodeClass{ + Spec: v1.EC2NodeClassSpec{ + SubnetSelectorTerms: []v1.SubnetSelectorTerm{ + { + Tags: map[string]string{"*": "*"}, + }, }, - }, - SecurityGroupSelectorTerms: []v1.SecurityGroupSelectorTerm{ - { - Tags: map[string]string{"*": "*"}, + SecurityGroupSelectorTerms: []v1.SecurityGroupSelectorTerm{ + { + Tags: map[string]string{"*": "*"}, + }, }, - }, - AMIFamily: lo.ToPtr(v1.AMIFamilyCustom), - AMISelectorTerms: []v1.AMISelectorTerm{ - { - Tags: map[string]string{"*": "*"}, + AMIFamily: lo.ToPtr(v1.AMIFamilyCustom), + AMISelectorTerms: []v1.AMISelectorTerm{ + { + Tags: map[string]string{"*": "*"}, + }, + }, + Tags: map[string]string{ + "kubernetes.io/cluster/anothercluster": "owned", }, }, - Tags: map[string]string{ - "kubernetes.io/cluster/anothercluster": "owned", - }, - }, + }) + }) + DescribeTable("should update status condition on nodeClass as NotReady when tag validation fails", func(illegalTag map[string]string) { + nodeClass.Spec.Tags = illegalTag + ExpectApplied(ctx, env.Client, nodeClass) + err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) + Expect(err).To(HaveOccurred()) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) + Expect(nodeClass.Status.Conditions).To(HaveLen(6)) + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsFalse()).To(BeTrue()) + Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsFalse()).To(BeTrue()) + Expect(nodeClass.StatusConditions().Get(status.ConditionReady).Message).To(Equal("ValidationSucceeded=False")) + }, + Entry("kubernetes.io/cluster*", map[string]string{"kubernetes.io/cluster/acluster": "owned"}), + Entry(v1.NodePoolTagKey, map[string]string{v1.NodePoolTagKey: "testnodepool"}), + Entry(v1.EKSClusterNameTagKey, map[string]string{v1.EKSClusterNameTagKey: "acluster"}), + Entry(v1.NodeClassTagKey, map[string]string{v1.NodeClassTagKey: "testnodeclass"}), + Entry(v1.NodeClaimTagKey, map[string]string{v1.NodeClaimTagKey: "testnodeclaim"}), + ) + It("should update status condition as Ready when tags are valid", func() { + nodeClass.Spec.Tags = map[string]string{} + ExpectApplied(ctx, env.Client, nodeClass) + ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) + + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsTrue()).To(BeTrue()) + Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsTrue()).To(BeTrue()) }) }) - DescribeTable("should update status condition on nodeClass as NotReady when tag validation fails", func(illegalTag map[string]string) { - nodeClass.Spec.Tags = illegalTag - ExpectApplied(ctx, env.Client, nodeClass) - err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) - Expect(err).To(HaveOccurred()) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.Status.Conditions).To(HaveLen(7)) - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsFalse()).To(BeTrue()) - Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsFalse()).To(BeTrue()) - Expect(nodeClass.StatusConditions().Get(status.ConditionReady).Message).To(Equal("ValidationSucceeded=False")) - }, - Entry("kubernetes.io/cluster*", map[string]string{"kubernetes.io/cluster/acluster": "owned"}), - Entry(v1.NodePoolTagKey, map[string]string{v1.NodePoolTagKey: "testnodepool"}), - Entry(v1.EKSClusterNameTagKey, map[string]string{v1.EKSClusterNameTagKey: "acluster"}), - Entry(v1.NodeClassTagKey, map[string]string{v1.NodeClassTagKey: "testnodeclass"}), - Entry(v1.NodeClaimTagKey, map[string]string{v1.NodeClaimTagKey: "testnodeclaim"}), - ) - It("should update status condition as Ready when tags are valid", func() { - nodeClass.Spec.Tags = map[string]string{} - ExpectApplied(ctx, env.Client, nodeClass) - ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) - nodeClass = ExpectExists(ctx, env.Client, nodeClass) + Context("Authorization Validation", func() { + It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to createfleet", func() { + ExpectApplied(ctx, env.Client, nodeClass) + awsEnv.EC2API.CreateFleetBehavior.Error.Set(&smithy.GenericAPIError{ + Code: "UnauthorizedOperation", + }, fake.MaxCalls(1)) + err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) + Expect(err).To(HaveOccurred()) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) + Expect(nodeClass.Status.Conditions).To(HaveLen(6)) + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsFalse()).To(BeTrue()) + }) + It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to terminateinstances", func() { + ExpectApplied(ctx, env.Client, nodeClass) + awsEnv.EC2API.TerminateInstancesBehavior.Error.Set(&smithy.GenericAPIError{ + Code: "UnauthorizedOperation", + }, fake.MaxCalls(2)) + err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) + Expect(err).To(HaveOccurred()) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) + Expect(nodeClass.Status.Conditions).To(HaveLen(6)) + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsFalse()).To(BeTrue()) + }) + It("should update status condition on nodeClass as NotReady when nodeclass has authorization failure due to CreateTags", func() { + ExpectApplied(ctx, env.Client, nodeClass) + awsEnv.EC2API.CreateTagsBehavior.Error.Set(&smithy.GenericAPIError{ + Code: "UnauthorizedOperation", + }, fake.MaxCalls(1)) + err := ExpectObjectReconcileFailed(ctx, env.Client, statusController, nodeClass) + Expect(err).To(HaveOccurred()) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) + Expect(nodeClass.Status.Conditions).To(HaveLen(6)) + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsFalse()).To(BeTrue()) + }) + It("should update status condition as Ready", func() { + nodeClass.Spec.Tags = map[string]string{} + ExpectApplied(ctx, env.Client, nodeClass) + ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) + nodeClass = ExpectExists(ctx, env.Client, nodeClass) - Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsTrue()).To(BeTrue()) - Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsTrue()).To(BeTrue()) + Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeValidationSucceeded).IsTrue()).To(BeTrue()) + }) }) })