Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authorization Controller for NodeClass #7571

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
2 changes: 2 additions & 0 deletions pkg/apis/v1/ec2nodeclass_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
ConditionTypeSecurityGroupsReady = "SecurityGroupsReady"
ConditionTypeAMIsReady = "AMIsReady"
ConditionTypeInstanceProfileReady = "InstanceProfileReady"
ConditionTypeAuthorization = "AuthorizationReady"
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
ConditionTypeValidationSucceeded = "ValidationSucceeded"
)

Expand Down Expand Up @@ -94,6 +95,7 @@ func (in *EC2NodeClass) StatusConditions() status.ConditionSet {
ConditionTypeSubnetsReady,
ConditionTypeSecurityGroupsReady,
ConditionTypeInstanceProfileReady,
ConditionTypeAuthorization,
ConditionTypeValidationSucceeded,
).For(in)
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/cloudprovider/cloudprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (

"github.com/aws/karpenter-provider-aws/pkg/apis"
v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
awserrors "github.com/aws/karpenter-provider-aws/pkg/errors"
"github.com/aws/karpenter-provider-aws/pkg/operator/options"
"github.com/aws/karpenter-provider-aws/pkg/utils"

Expand Down Expand Up @@ -93,7 +94,7 @@ func (c *CloudProvider) Create(ctx context.Context, nodeClaim *karpv1.NodeClaim)
if nodeClassReady.IsFalse() {
return nil, cloudprovider.NewNodeClassNotReadyError(stderrors.New(nodeClassReady.Message))
}
if nodeClassReady.IsUnknown() {
if nodeClassReady.IsUnknown() && ctx.Value("DryRun") == nil {
Copy link
Contributor

@engedaam engedaam Jan 7, 2025

Choose a reason for hiding this comment

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

Why are we checking dry-run here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because during the initial reconciliation nodeclass readiness is unknown so it will fail without a check

Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we care about the dry-run value in that case? If the nodeclass has not been checked yet

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, this check feels odd to me too -- it's leaking details into the implementation and I'm wondering if there are ways to inject details into the providers themselves or into the API layer so that we don't have to leak this detail here

return nil, cloudprovider.NewCreateError(fmt.Errorf("resolving NodeClass readiness, NodeClass is in Ready=Unknown, %s", nodeClassReady.Message), "NodeClass is in Ready=Unknown")
}
instanceTypes, err := c.resolveInstanceTypes(ctx, nodeClaim, nodeClass)
Expand All @@ -109,6 +110,9 @@ func (c *CloudProvider) Create(ctx context.Context, nodeClaim *karpv1.NodeClaim)
}
instance, err := c.instanceProvider.Create(ctx, nodeClass, nodeClaim, tags, instanceTypes)
if err != nil {
if awserrors.IsUnauthorizedError(err) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just checking: Is this here to handle the case where we get unauthorized and that causes us to fail the launch -- is the idea here that eventually the controller will reconcile and should block further launches (similar to what we do with other validation checks, where we expect the reconciliation to eventually catch-up and mark the NodeClass as NotReady)?

return nil, cloudprovider.NewNodeClassNotReadyError(err)
}
conditionMessage := "Error creating instance"
var createError *cloudprovider.CreateError
if stderrors.As(err, &createError) {
Expand Down
6 changes: 3 additions & 3 deletions pkg/cloudprovider/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ var _ = Describe("CloudProvider", func() {
{SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(100),
Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}},
}})
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider)
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider, *cloudProvider, awsEnv.InstanceProvider)
ExpectApplied(ctx, env.Client, nodePool, nodeClass)
ExpectObjectReconciled(ctx, env.Client, controller, nodeClass)
pod := coretest.UnschedulablePod(coretest.PodOptions{NodeSelector: map[string]string{corev1.LabelTopologyZone: "test-zone-1a"}})
Expand All @@ -1173,7 +1173,7 @@ var _ = Describe("CloudProvider", func() {
{SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(11),
Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}},
}})
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider)
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider, *cloudProvider, awsEnv.InstanceProvider)
nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{
MaxPods: aws.Int32(1),
}
Expand Down Expand Up @@ -1214,7 +1214,7 @@ var _ = Describe("CloudProvider", func() {
}})
nodeClass.Spec.SubnetSelectorTerms = []v1.SubnetSelectorTerm{{Tags: map[string]string{"Name": "test-subnet-1"}}}
ExpectApplied(ctx, env.Client, nodePool, nodeClass)
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider)
controller := status.NewController(env.Client, awsEnv.SubnetProvider, awsEnv.SecurityGroupProvider, awsEnv.AMIProvider, awsEnv.InstanceProfileProvider, awsEnv.LaunchTemplateProvider, *cloudProvider, awsEnv.InstanceProvider)
ExpectObjectReconciled(ctx, env.Client, controller, nodeClass)
podSubnet1 := coretest.UnschedulablePod()
ExpectProvisioned(ctx, env.Client, cluster, cloudProvider, prov, podSubnet1)
Expand Down
5 changes: 4 additions & 1 deletion pkg/controllers/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ import (
servicesqs "github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/samber/lo"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
"k8s.io/utils/clock"
"sigs.k8s.io/controller-runtime/pkg/client"

"sigs.k8s.io/karpenter/pkg/events"

awscache "github.com/aws/karpenter-provider-aws/pkg/cache"
provcloudprovider "github.com/aws/karpenter-provider-aws/pkg/cloudprovider"
"github.com/aws/karpenter-provider-aws/pkg/controllers/interruption"
nodeclaimgarbagecollection "github.com/aws/karpenter-provider-aws/pkg/controllers/nodeclaim/garbagecollection"
nodeclaimtagging "github.com/aws/karpenter-provider-aws/pkg/controllers/nodeclaim/tagging"
Expand Down Expand Up @@ -82,7 +84,8 @@ func NewControllers(
instanceTypeProvider *instancetype.DefaultProvider) []controller.Controller {
controllers := []controller.Controller{
nodeclasshash.NewController(kubeClient),
nodeclassstatus.NewController(kubeClient, subnetProvider, securityGroupProvider, amiProvider, instanceProfileProvider, launchTemplateProvider),
nodeclassstatus.NewController(kubeClient, subnetProvider, securityGroupProvider, amiProvider, instanceProfileProvider, launchTemplateProvider, *provcloudprovider.New(instanceTypeProvider, instanceProvider, events.NewRecorder(&record.FakeRecorder{}),
kubeClient, amiProvider, securityGroupProvider), instanceProvider),
nodeclasstermination.NewController(kubeClient, recorder, instanceProfileProvider, launchTemplateProvider),
nodeclaimgarbagecollection.NewController(kubeClient, cloudProvider),
nodeclaimtagging.NewController(kubeClient, cloudProvider, instanceProvider),
Expand Down
61 changes: 61 additions & 0 deletions pkg/controllers/nodeclass/status/authorization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
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) {
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
//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)
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
if err == nil {
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
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))
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
if corecloudprovider.IsNodeClassNotReadyError(err) {
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
nodeClass.StatusConditions().SetFalse(v1.ConditionTypeAuthorization, "NodeClassNotReady", "Unauthorized Operation")
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
return reconcile.Result{}, fmt.Errorf("unauthorized operation %w", err)
}
nodeClass.StatusConditions().SetTrue(v1.ConditionTypeAuthorization)
return reconcile.Result{}, nil
}
70 changes: 70 additions & 0 deletions pkg/controllers/nodeclass/status/authorization_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add an integration test for permission related failures?

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())
})
})
7 changes: 6 additions & 1 deletion pkg/controllers/nodeclass/status/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import (
"github.com/awslabs/operatorpkg/reasonable"

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/amifamily"
"github.com/aws/karpenter-provider-aws/pkg/providers/instance"
"github.com/aws/karpenter-provider-aws/pkg/providers/instanceprofile"
"github.com/aws/karpenter-provider-aws/pkg/providers/launchtemplate"
"github.com/aws/karpenter-provider-aws/pkg/providers/securitygroup"
Expand All @@ -52,11 +54,12 @@ type Controller struct {
subnet *Subnet
securitygroup *SecurityGroup
validation *Validation
authorization *Authorization
readiness *Readiness //TODO : Remove this when we have sub status conditions
}

func NewController(kubeClient client.Client, subnetProvider subnet.Provider, securityGroupProvider securitygroup.Provider,
amiProvider amifamily.Provider, instanceProfileProvider instanceprofile.Provider, launchTemplateProvider launchtemplate.Provider) *Controller {
amiProvider amifamily.Provider, instanceProfileProvider instanceprofile.Provider, launchTemplateProvider launchtemplate.Provider, cloudProvider cloudprovider.CloudProvider, instanceProvider instance.Provider) *Controller {
return &Controller{
kubeClient: kubeClient,

Expand All @@ -65,6 +68,7 @@ func NewController(kubeClient client.Client, subnetProvider subnet.Provider, sec
securitygroup: &SecurityGroup{securityGroupProvider: securityGroupProvider},
instanceprofile: &InstanceProfile{instanceProfileProvider: instanceProfileProvider},
validation: &Validation{},
authorization: &Authorization{cloudProvider: cloudProvider, instanceProvider: instanceProvider},
readiness: &Readiness{launchTemplateProvider: launchTemplateProvider},
}
}
Expand Down Expand Up @@ -96,6 +100,7 @@ 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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/controllers/nodeclass/status/readiness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(6))
Expect(nodeClass.Status.Conditions).To(HaveLen(7))
Expect(nodeClass.StatusConditions().Get(status.ConditionReady).IsTrue()).To(BeTrue())
})
It("should update status condition as Not Ready", func() {
Expand Down
33 changes: 33 additions & 0 deletions pkg/controllers/nodeclass/status/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import (
coreoptions "sigs.k8s.io/karpenter/pkg/operator/options"
coretest "sigs.k8s.io/karpenter/pkg/test"

"github.com/samber/lo"

"github.com/aws/karpenter-provider-aws/pkg/apis"
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/controllers/nodeclass/status"
"github.com/aws/karpenter-provider-aws/pkg/operator/options"
"github.com/aws/karpenter-provider-aws/pkg/test"
Expand All @@ -40,6 +43,7 @@ var env *coretest.Environment
var awsEnv *test.Environment
var nodeClass *v1.EC2NodeClass
var statusController *status.Controller
var cloudProvider cloudprovider.CloudProvider

func TestAPIs(t *testing.T) {
ctx = TestContextWithLogger(t)
Expand All @@ -53,13 +57,23 @@ var _ = BeforeSuite(func() {
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(),
edibble21 marked this conversation as resolved.
Show resolved Hide resolved
env.Client, awsEnv.AMIProvider, awsEnv.SecurityGroupProvider))

statusController = status.NewController(
env.Client,
awsEnv.SubnetProvider,
awsEnv.SecurityGroupProvider,
awsEnv.AMIProvider,
awsEnv.InstanceProfileProvider,
awsEnv.LaunchTemplateProvider,
cloudProvider,
awsEnv.InstanceProvider,
)
})

Expand All @@ -71,6 +85,25 @@ var _ = BeforeEach(func() {
ctx = coreoptions.ToContext(ctx, coretest.Options())
nodeClass = test.EC2NodeClass()
awsEnv.Reset()

instanceTypesProvider := awsEnv.InstanceTypesProvider
edibble21 marked this conversation as resolved.
Show resolved Hide resolved

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 _ = AfterEach(func() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/controllers/nodeclass/status/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var _ = Describe("NodeClass Validation Status Controller", func() {
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.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"))
Expand Down
14 changes: 14 additions & 0 deletions pkg/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package errors

import (
"errors"
"strings"

ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/smithy-go"
Expand Down Expand Up @@ -106,3 +107,16 @@ func IsLaunchTemplateNotFound(err error) bool {
}
return false
}

func IsUnauthorizedError(err error) bool {
if err == nil {
return false
}
var apiErr smithy.APIError
if errors.As(err, &apiErr) {
return apiErr.ErrorCode() == "UnauthorizedOperation"
}

return strings.Contains(err.Error(), "UnauthorizedOperation")

}
4 changes: 3 additions & 1 deletion pkg/fake/ec2api.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ func (e *EC2API) CreateLaunchTemplate(_ context.Context, input *ec2.CreateLaunch
defer e.NextError.Reset()
return nil, e.NextError.Get()
}
e.CalledWithCreateLaunchTemplateInput.Add(input)
if !*input.DryRun {
e.CalledWithCreateLaunchTemplateInput.Add(input)
}
launchTemplate := ec2types.LaunchTemplate{LaunchTemplateName: input.LaunchTemplateName}
e.LaunchTemplates.Store(input.LaunchTemplateName, launchTemplate)
return &ec2.CreateLaunchTemplateOutput{LaunchTemplate: lo.ToPtr(launchTemplate)}, nil
Expand Down
Loading
Loading