Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/controllers/nodeclass/ami.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewAMIReconciler(provider amifamily.Provider) *AMI {
func (a *AMI) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (reconcile.Result, error) {
amis, err := a.amiProvider.List(ctx, nodeClass)
if err != nil {
if amifamily.IsAl2DeprecationError(err) || amifamily.IsWS2025UnsupportedVersionError(err) {
if amifamily.IsAl2DeprecationError(err) || amifamily.IsWS2025UnsupportedVersionError(err) || amifamily.IsAMINotFoundForAliasError(err) {
nodeClass.StatusConditions().SetFalse(v1.ConditionTypeAMIsReady, "UnsupportedAlias", err.Error())
return reconcile.Result{}, reconcile.TerminalError(fmt.Errorf("getting amis, %w", err))
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/controllers/nodeclass/ami_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,36 @@ var _ = Describe("NodeClass AMI Status Controller", func() {
nodeClass = ExpectExists(ctx, env.Client, nodeClass)
Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeAMIsReady)).To(BeFalse())
})
DescribeTable("should set UnsupportedAlias condition when no AMIs are found for alias", func(alias string) {
awsEnv.SSMAPI.Parameters = map[string]string{"not-real": "not-real"}

nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: alias}}
ExpectApplied(ctx, env.Client, nodeClass)
_ = ExpectObjectReconcileFailed(ctx, env.Client, controller, nodeClass)
nodeClass = ExpectExists(ctx, env.Client, nodeClass)

Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).IsFalse()).To(BeTrue())
Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).Reason).To(Equal("UnsupportedAlias"))
Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).Message).To(ContainSubstring("failed to discover any AMIs for alias"))
},
Entry("AL2023 with invalid version", "al2023@v20240101"),
Entry("Bottlerocket with invalid version", "bottlerocket@v20240101"),
Entry("Windows with invalid version", "windows2022@latest"),
)
DescribeTable("should retry when SSM returns transient errors", func(alias string) {
awsEnv.SSMAPI.WantErr = fmt.Errorf("RequestLimitExceeded: Rate exceeded")

nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: alias}}
ExpectApplied(ctx, env.Client, nodeClass)
_ = ExpectObjectReconcileFailed(ctx, env.Client, controller, nodeClass)
nodeClass = ExpectExists(ctx, env.Client, nodeClass)

Expect(nodeClass.StatusConditions().Get(v1.ConditionTypeAMIsReady).Reason).ToNot(Equal("UnsupportedAlias"))
},
Entry("AL2023 with invalid version", "al2023@v20240101"),
Entry("Bottlerocket with invalid version", "bottlerocket@v20240101"),
Entry("Windows with invalid version", "windows2022@latest"),
)
Context("NodeClass AMI Status", func() {
BeforeEach(func() {
// Set time using the injectable/fake clock to now
Expand Down
1 change: 1 addition & 0 deletions pkg/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var (
"QueueDoesNotExist",
"NoSuchEntity",
"ParameterNotFound",
"ParameterVersionNotFound",
)
alreadyExistsErrorCodes = sets.New(
"EntityAlreadyExists",
Expand Down
7 changes: 5 additions & 2 deletions pkg/fake/ssmapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import (
"fmt"

"github.com/Pallinder/go-randomdata"
"github.com/awslabs/operatorpkg/serrors"
"github.com/samber/lo"

"github.com/aws/aws-sdk-go-v2/service/ssm"
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
"github.com/aws/smithy-go"

sdk "github.com/aws/karpenter-provider-aws/pkg/aws"
)
Expand Down Expand Up @@ -54,7 +54,10 @@ func (a SSMAPI) GetParameter(_ context.Context, input *ssm.GetParameterInput, _
if len(a.Parameters) != 0 {
value, ok := a.Parameters[parameter]
if !ok {
return &ssm.GetParameterOutput{}, serrors.Wrap(fmt.Errorf("parameter not found"), "parameter", lo.FromPtr(input.Name))
return &ssm.GetParameterOutput{}, &smithy.GenericAPIError{
Code: "ParameterNotFound",
Message: fmt.Sprintf("parameter %s not found", parameter),
}
}
return &ssm.GetParameterOutput{
Parameter: &ssmtypes.Parameter{
Expand Down
8 changes: 8 additions & 0 deletions pkg/providers/amifamily/al2.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"sigs.k8s.io/karpenter/pkg/scheduling"

v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
"github.com/aws/karpenter-provider-aws/pkg/errors"

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

Expand All @@ -42,6 +43,7 @@ type AL2 struct {

func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (DescribeImageQuery, error) {
ids := map[string][]Variant{}
isAllNotFoundErrors := true
for path, variants := range map[string][]Variant{
fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/%s/image_id", k8sVersion, lo.Ternary(
amiVersion == v1.AliasVersionLatest,
Expand All @@ -64,12 +66,18 @@ func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k
IsMutable: amiVersion == v1.AliasVersionLatest,
})
if err != nil {
isAllNotFoundErrors = isAllNotFoundErrors && errors.IsNotFound(err)
continue
}
ids[imageID] = variants
}
// Failed to discover any AMIs, we should short circuit AMI discovery
if len(ids) == 0 {
if isAllNotFoundErrors {
return DescribeImageQuery{}, &AMINotFoundForAliasError{
error: serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2@%s", amiVersion)),
}
}
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2@%s", amiVersion))
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/providers/amifamily/al2023.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"

v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
"github.com/aws/karpenter-provider-aws/pkg/errors"
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap"
"github.com/aws/karpenter-provider-aws/pkg/providers/ssm"
)
Expand All @@ -38,6 +39,7 @@ type AL2023 struct {

func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (DescribeImageQuery, error) {
ids := map[string]Variant{}
isAllNotFoundErrors := true
for arch, variants := range map[string][]Variant{
"x86_64": {VariantStandard, VariantNvidia, VariantNeuron},
"arm64": {VariantStandard, VariantNvidia},
Expand All @@ -49,13 +51,19 @@ func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider
IsMutable: amiVersion == v1.AliasVersionLatest,
})
if err != nil {
isAllNotFoundErrors = isAllNotFoundErrors && errors.IsNotFound(err)
continue
}
ids[imageID] = variant
}
}
// Failed to discover any AMIs, we should short circuit AMI discovery
if len(ids) == 0 {
if isAllNotFoundErrors {
return DescribeImageQuery{}, &AMINotFoundForAliasError{
error: serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2023@%s", amiVersion)),
}
}
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("al2023@%s", amiVersion))
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/providers/amifamily/bottlerocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/samber/lo"

v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
"github.com/aws/karpenter-provider-aws/pkg/errors"
"github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap"
"github.com/aws/karpenter-provider-aws/pkg/providers/ssm"
"github.com/aws/karpenter-provider-aws/pkg/providers/version"
Expand All @@ -45,6 +46,7 @@ func (b Bottlerocket) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Pr
// Bottlerocket AMIs versions are prefixed with a v on GitHub, but not in the SSM path. We should accept both.
trimmedAMIVersion := strings.TrimLeft(amiVersion, "v")
ids := map[string][]Variant{}
isAllNotFoundErrors := true
for path, variants := range map[string][]Variant{
fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/x86_64/%s/image_id", k8sVersion, trimmedAMIVersion): {VariantStandard, VariantNeuron},
fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/arm64/%s/image_id", k8sVersion, trimmedAMIVersion): {VariantStandard},
Expand All @@ -56,12 +58,18 @@ func (b Bottlerocket) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Pr
IsMutable: amiVersion == v1.AliasVersionLatest,
})
if err != nil {
isAllNotFoundErrors = isAllNotFoundErrors && errors.IsNotFound(err)
continue
}
ids[imageID] = variants
}
// Failed to discover any AMIs, we should short circuit AMI discovery
if len(ids) == 0 {
if isAllNotFoundErrors {
return DescribeImageQuery{}, &AMINotFoundForAliasError{
error: serrors.Wrap(fmt.Errorf(`failed to discover any AMIs for alias`), "alias", fmt.Sprintf("bottlerocket@%s", amiVersion)),
}
}
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf(`failed to discover any AMIs for alias`), "alias", fmt.Sprintf("bottlerocket@%s", amiVersion))
}

Expand Down
12 changes: 12 additions & 0 deletions pkg/providers/amifamily/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,15 @@ func IsWS2025UnsupportedVersionError(err error) bool {
var ws2025Err *WS2025UnsupportedVersionError
return errors.As(err, &ws2025Err)
}

type AMINotFoundForAliasError struct {
error
}

func IsAMINotFoundForAliasError(err error) bool {
if err == nil {
return false
}
var notFoundErr *AMINotFoundForAliasError
return errors.As(err, &notFoundErr)
}
6 changes: 6 additions & 0 deletions pkg/providers/amifamily/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sigs.k8s.io/karpenter/pkg/scheduling"

v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1"
"github.com/aws/karpenter-provider-aws/pkg/errors"

"github.com/samber/lo"
"k8s.io/apimachinery/pkg/api/resource"
Expand Down Expand Up @@ -53,6 +54,11 @@ func (w Windows) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provide
IsMutable: true,
})
if err != nil {
if errors.IsNotFound(err) {
return DescribeImageQuery{}, &AMINotFoundForAliasError{
error: serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("windows%s@%s", w.Version, amiVersion)),
}
}
return DescribeImageQuery{}, serrors.Wrap(fmt.Errorf("failed to discover any AMIs for alias"), "alias", fmt.Sprintf("windows%s@%s", w.Version, amiVersion))
}
return DescribeImageQuery{
Expand Down