From 655d4733d635e2b633333373c969b1327deb7a81 Mon Sep 17 00:00:00 2001 From: edibble21 <85638465+edibble21@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:36:07 -0800 Subject: [PATCH] Chore: EC2 and EKS migration v2 (#7198) Signed-off-by: dependabot[bot] Co-authored-by: Amanuel Engeda <74629455+engedaam@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- cmd/controller/main.go | 2 +- go.mod | 31 +- go.sum | 59 +- hack/code/bandwidth_gen/main.go | 41 +- hack/code/instancetype_testdata_gen/main.go | 135 +-- hack/code/prices_gen/main.go | 27 +- hack/docs/instancetypes_gen/main.go | 21 +- hack/tools/allocatable_diff/main.go | 2 +- hack/tools/launchtemplate_counter/main.go | 18 +- pkg/apis/v1/ec2nodeclass_hash_test.go | 3 +- .../v1/ec2nodeclass_validation_cel_test.go | 3 +- pkg/aws/sdk.go | 37 +- pkg/batcher/createfleet.go | 29 +- pkg/batcher/createfleet_test.go | 141 ++- pkg/batcher/describeinstances.go | 46 +- pkg/batcher/describeinstances_test.go | 45 +- pkg/batcher/ec2api.go | 4 +- pkg/batcher/terminateinstances.go | 25 +- pkg/batcher/terminateinstances_test.go | 23 +- pkg/cache/unavailableofferings.go | 22 +- pkg/cloudprovider/cloudprovider.go | 8 +- pkg/cloudprovider/suite_test.go | 150 +-- pkg/controllers/controllers.go | 11 +- pkg/controllers/interruption/controller.go | 3 +- .../interruption_benchmark_test.go | 17 +- .../nodeclaim/garbagecollection/suite_test.go | 68 +- .../nodeclaim/tagging/suite_test.go | 36 +- pkg/controllers/nodeclass/hash/suite_test.go | 3 +- pkg/controllers/nodeclass/status/ami_test.go | 31 +- .../nodeclass/status/launchtemplate_test.go | 7 +- .../nodeclass/status/securitygroup.go | 4 +- pkg/controllers/nodeclass/status/subnet.go | 4 +- .../nodeclass/status/subnet_test.go | 13 +- .../nodeclass/termination/suite_test.go | 11 +- .../instancetype/capacity/suite_test.go | 2 +- .../providers/instancetype/suite_test.go | 9 +- .../providers/pricing/suite_test.go | 64 +- .../providers/ssm/invalidation/suite_test.go | 9 +- pkg/errors/errors.go | 54 +- pkg/fake/ec2api.go | 309 +++--- pkg/fake/eksapi.go | 15 +- pkg/fake/iamapi.go | 14 +- pkg/fake/pricingapi.go | 29 +- pkg/fake/utils.go | 85 +- .../zz_generated.describe_instance_types.go | 965 +++++++++--------- pkg/operator/operator.go | 106 +- pkg/operator/suite_test.go | 5 +- pkg/providers/amifamily/al2.go | 8 +- pkg/providers/amifamily/al2023.go | 10 +- pkg/providers/amifamily/ami.go | 41 +- .../amifamily/bootstrap/bottlerocket.go | 2 +- pkg/providers/amifamily/bootstrap/custom.go | 4 +- pkg/providers/amifamily/bottlerocket.go | 8 +- pkg/providers/amifamily/resolver.go | 14 +- pkg/providers/amifamily/suite_test.go | 101 +- pkg/providers/amifamily/types.go | 11 +- pkg/providers/amifamily/windows.go | 10 +- pkg/providers/instance/instance.go | 144 +-- pkg/providers/instance/suite_test.go | 30 +- pkg/providers/instance/types.go | 50 +- .../instanceprofile/instanceprofile.go | 6 +- pkg/providers/instancetype/instancetype.go | 78 +- pkg/providers/instancetype/suite_test.go | 193 ++-- pkg/providers/instancetype/types.go | 136 +-- .../launchtemplate/launchtemplate.go | 207 ++-- pkg/providers/launchtemplate/suite_test.go | 172 ++-- pkg/providers/pricing/pricing.go | 241 +++-- .../pricing/zz_generated.pricing_aws.go | 6 +- .../pricing/zz_generated.pricing_aws_cn.go | 5 +- .../zz_generated.pricing_aws_us_gov.go | 6 +- pkg/providers/securitygroup/securitygroup.go | 49 +- pkg/providers/securitygroup/suite_test.go | 57 +- pkg/providers/subnet/subnet.go | 76 +- pkg/providers/subnet/suite_test.go | 66 +- pkg/utils/utils.go | 11 +- test/hack/soak/get_clusters.go | 3 +- test/hack/soak/go.mod | 3 +- test/hack/soak/go.sum | 2 - test/pkg/environment/aws/environment.go | 55 +- test/pkg/environment/aws/expectations.go | 160 +-- test/pkg/environment/aws/metrics.go | 23 +- test/suites/ami/suite_test.go | 17 +- test/suites/consolidation/suite_test.go | 2 +- test/suites/drift/suite_test.go | 27 +- test/suites/integration/aws_metadata_test.go | 18 +- .../integration/block_device_mappings_test.go | 12 +- test/suites/integration/cni_test.go | 16 +- .../integration/instance_profile_test.go | 2 +- .../integration/launch_template_test.go | 11 +- .../suites/integration/security_group_test.go | 10 +- test/suites/integration/subnet_test.go | 19 +- test/suites/integration/tags_test.go | 55 +- .../nodeclaim/garbage_collection_test.go | 61 +- test/suites/nodeclaim/nodeclaim_test.go | 11 +- test/suites/scheduling/suite_test.go | 4 +- test/suites/storage/suite_test.go | 2 +- test/suites/termination/termination_test.go | 4 +- 97 files changed, 2543 insertions(+), 2462 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 4d2d464b59e1..7ae7ae9d8034 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -48,7 +48,7 @@ func main() { WithControllers(ctx, controllers.NewControllers( ctx, op.Manager, - op.Session, + op.Config, op.Clock, op.GetClient(), op.EventRecorder, diff --git a/go.mod b/go.mod index 1023cbe711b5..f8fd1c4a9f50 100644 --- a/go.mod +++ b/go.mod @@ -6,20 +6,24 @@ require ( github.com/Pallinder/go-randomdata v1.2.0 github.com/PuerkitoBio/goquery v1.10.0 github.com/avast/retry-go v3.0.0+incompatible - github.com/aws/aws-sdk-go v1.55.5 github.com/aws/aws-sdk-go-v2 v1.32.3 github.com/aws/aws-sdk-go-v2/config v1.28.1 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 + github.com/aws/aws-sdk-go-v2/service/eks v1.50.2 + github.com/aws/aws-sdk-go-v2/service/fis v1.30.3 github.com/aws/aws-sdk-go-v2/service/iam v1.37.3 + github.com/aws/aws-sdk-go-v2/service/pricing v1.32.2 + github.com/aws/aws-sdk-go-v2/service/sqs v1.36.3 + github.com/aws/aws-sdk-go-v2/service/ssm v1.55.3 github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 + github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.29.2 github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881 github.com/aws/smithy-go v1.22.0 github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647 github.com/awslabs/operatorpkg v0.0.0-20240920182301-771460b3160b github.com/go-logr/zapr v1.3.0 github.com/imdario/mergo v0.3.16 - github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.1-0.20240804232425-54c8227e0bab github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 @@ -48,6 +52,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect @@ -56,9 +61,6 @@ require ( require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/aws/aws-sdk-go-v2/service/fis v1.30.3 - github.com/aws/aws-sdk-go-v2/service/sqs v1.36.3 - github.com/aws/aws-sdk-go-v2/service/ssm v1.55.3 github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -82,6 +84,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.0 github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -121,3 +124,21 @@ require ( sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) + +//Here until we can drop dependency on github.com/jonathan-innis/aws-sdk-go-prometheus +replace ( + github.com/aws/aws-sdk-go-v2 => github.com/aws/aws-sdk-go-v2 v1.30.5 + github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.27.34 + github.com/aws/aws-sdk-go-v2/service/ec2 => github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0 + github.com/aws/aws-sdk-go-v2/service/eks => github.com/aws/aws-sdk-go-v2/service/eks v1.48.0 + github.com/aws/aws-sdk-go-v2/service/fis => github.com/aws/aws-sdk-go-v2/service/fis v1.27.0 + github.com/aws/aws-sdk-go-v2/service/iam => github.com/aws/aws-sdk-go-v2/service/iam v1.35.0 + github.com/aws/aws-sdk-go-v2/service/pricing => github.com/aws/aws-sdk-go-v2/service/pricing v1.28.0 + github.com/aws/aws-sdk-go-v2/service/sqs => github.com/aws/aws-sdk-go-v2/service/sqs v1.34.6 + github.com/aws/aws-sdk-go-v2/service/ssm => github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 + github.com/aws/aws-sdk-go-v2/service/sso => github.com/aws/aws-sdk-go-v2/service/sso v1.15.0 + github.com/aws/aws-sdk-go-v2/service/ssoadmin => github.com/aws/aws-sdk-go-v2/service/ssoadmin v1.22.0 + github.com/aws/aws-sdk-go-v2/service/ssooidc => github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.0 + github.com/aws/aws-sdk-go-v2/service/sts => github.com/aws/aws-sdk-go-v2/service/sts v1.28.2 + github.com/aws/aws-sdk-go-v2/service/timestreamwrite => github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.23.2 +) diff --git a/go.sum b/go.sum index 3f2450ea4db3..b995c9676f5c 100644 --- a/go.sum +++ b/go.sum @@ -8,44 +8,54 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= -github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= -github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw= -github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI= +github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g= +github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2/config v1.27.34 h1:5sLceuETg/215nLtY/QIVB2O6cosS0iC/Tx5oyqUhbw= +github.com/aws/aws-sdk-go-v2/config v1.27.34/go.mod h1:kEqdYzRb8dd8Sy2pOdEbExTTF5v7ozEXX0McgPE7xks= github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 h1:cA4hWo269CN5RY7Arqt8BfzXF0KIN8DSNo/KcqHKkWk= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0/go.mod h1:ossaD9Z1ugYb6sq9QIqQLEOorCGcqUoxlhud9M9yE70= -github.com/aws/aws-sdk-go-v2/service/fis v1.30.3 h1:ePY232tW0O4pq0oeXFNqsjixQqX+g8+vTgu72og4RtM= -github.com/aws/aws-sdk-go-v2/service/fis v1.30.3/go.mod h1:Xwo+AAp9l2Li0t9mFQRdtMqRtNb4AL0s6c6fHr0iF9E= -github.com/aws/aws-sdk-go-v2/service/iam v1.37.3 h1:uuoXyOwX2ReYgHJW0W84cKDUrvQNQA2l9KhkXUgT+R4= -github.com/aws/aws-sdk-go-v2/service/iam v1.37.3/go.mod h1:RCrjvkN/ZpVAzW3ZmIlyflv7MUM45YlWx3v+6MaVX2w= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0 h1:LAdDRIj5BEZM9fLDTUWUyPzWvv5A++nCEps/RGmZNOo= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.0/go.mod h1:ISODge3zgdwOEa4Ou6WM9PKbxJWJ15DYKnr2bfmCAIA= +github.com/aws/aws-sdk-go-v2/service/eks v1.48.0 h1:KuCjjkUNMUn8RV5ncnJ5rUlkkzKqS5qod67jgU4k/aA= +github.com/aws/aws-sdk-go-v2/service/eks v1.48.0/go.mod h1:awleuSoavuUt32hemzWdSrI47zq7slFtIj8St07EXpE= +github.com/aws/aws-sdk-go-v2/service/fis v1.27.0 h1:UfzjSUfxR7Suy/t1OfkExG0s1vxPgIP1//gIYutlZ9I= +github.com/aws/aws-sdk-go-v2/service/fis v1.27.0/go.mod h1:QmdVf0N/vrhckZLHK4x+f+u9EUuMhetsRgu1rjU1eL0= +github.com/aws/aws-sdk-go-v2/service/iam v1.35.0 h1:xIjTizH74aMNQBjp9D5cvjRZmOYtnrpjOGU3xkVqrjk= +github.com/aws/aws-sdk-go-v2/service/iam v1.35.0/go.mod h1:IdHqqRLKgxYR4IY7Omd7SuV4SJzJ8seF+U5PW+mvtP4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.2 h1:1G7TTQNPNv5fhCyIQGYk8FOggLgkzKq6c4Y1nOGzAOE= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.2/go.mod h1:+ybYGLXoF7bcD7wIcMcklxyABZQmuBf1cHUhvY6FGIo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= -github.com/aws/aws-sdk-go-v2/service/sqs v1.36.3 h1:H1bCg79Q4PDtxQH8Fn5kASQlbVv2WGP5o5IEFEBNOAs= -github.com/aws/aws-sdk-go-v2/service/sqs v1.36.3/go.mod h1:W6Uy6OWgxF9RZuHoikthB6f+A0oYXqnfWmFl5m7E2G4= -github.com/aws/aws-sdk-go-v2/service/ssm v1.55.3 h1:nbFGlCxyyFe2cgg8WNQQtzDRVczO4+1dL4hd3TDU6MM= -github.com/aws/aws-sdk-go-v2/service/ssm v1.55.3/go.mod h1:nzUlOBAMlQx9zKwtI10FOzJa2phU6bmFbXhD6LLbr/A= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs= -github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE= +github.com/aws/aws-sdk-go-v2/service/pricing v1.28.0 h1:fvHH3/l0qhZs4bEEkNJx/ljs9vpXtfJacUhNAQTS9bE= +github.com/aws/aws-sdk-go-v2/service/pricing v1.28.0/go.mod h1:oB3Na0szArXW5rngmmBdNdJN4jsMvRTFpWZ6sGaqDDk= +github.com/aws/aws-sdk-go-v2/service/sqs v1.34.6 h1:DbjODDHumQBdJ3T+EO7AXVoFUeUhAsJYOdjStH5Ws4A= +github.com/aws/aws-sdk-go-v2/service/sqs v1.34.6/go.mod h1:7idt3XszF6sE9WPS1GqZRiDJOxw4oPtlRBXodWnCGjU= +github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7M5lu8PqdvOmbRHw= +github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.0 h1:vuGK1vHNP9zx0PfOrtPumbwR2af0ATQ1Z2H6p75AgRQ= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.0/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.0 h1:/XiEU7VIFcVWRDQLabyrSjBoKIm8UkYgsvWDuFW8Img= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.0/go.mod h1:dWqm5G767qwKPuayKfzm4rjzFmVjiBFbOJrpSPnAMDs= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.2 h1:0YjXuWdYHvsm0HnT4vO8XpwG1D+i2roxSCBoN6deJ7M= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.2/go.mod h1:jI+FWmYkSMn+4APWmZiZTgt0oM0TrvymD51FMqCnWgA= +github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.23.2 h1:iQLzzfvdcTNhzQsLit7zAYfepsfBMOtKRW6kFBcOQrY= +github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.23.2/go.mod h1:Pb7gC2/opbYq9ynlFP0VC5moP16oT6rWPCW6dsY3Cyw= github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881 h1:m9rhsGhdepdQV96tZgfy68oU75AWAjOH8u65OefTjwA= github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881/go.mod h1:+Mk5k0b6HpKobxNq+B56DOhZ+I/NiPhd5MIBhQMSTSs= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647 h1:8yRBVsjGmI7qQsPWtIrbWP+XfwHO9Wq7gdLVzjqiZFs= @@ -94,6 +104,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -112,8 +123,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.1-0.20240804232425-54c8227e0bab h1:3eIYcxyYhgZEmXLn6Es74ztsfW+2SFd9WR2HwfEGumk= -github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.1-0.20240804232425-54c8227e0bab/go.mod h1:DDom1Ae898wsni+arqgipv+JgtDtVDmbJB5YLOQz25s= +github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.0 h1:6eJFFxJ+2hbSEshwyLCLGUAf4/tXq8vpajf5QxcL8ew= +github.com/jonathan-innis/aws-sdk-go-prometheus v0.1.0/go.mod h1:DDom1Ae898wsni+arqgipv+JgtDtVDmbJB5YLOQz25s= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/hack/code/bandwidth_gen/main.go b/hack/code/bandwidth_gen/main.go index 7ec947dbdd8a..d1e0539505ec 100644 --- a/hack/code/bandwidth_gen/main.go +++ b/hack/code/bandwidth_gen/main.go @@ -15,6 +15,7 @@ limitations under the License. package main import ( + "context" "flag" "fmt" "go/format" @@ -26,8 +27,9 @@ import ( "strings" "github.com/PuerkitoBio/goquery" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" ) @@ -62,8 +64,8 @@ func main() { log.Fatalf("Usage: `bandwidth_gen.go pkg/providers/instancetype/zz_generated.bandwidth.go`") } - bandwidth := map[string]int64{} - vagueBandwidth := map[string]string{} + bandwidth := map[ec2types.InstanceType]int64{} + vagueBandwidth := map[ec2types.InstanceType]string{} for uri, selector := range uriSelectors { func() { @@ -76,24 +78,24 @@ func main() { // description for bandwidth such as "Very Low", "Low", "Low to Moderate", etc. These instance types // will be ignored since we don't know the exact bandwidth for these instance types for _, row := range doc.Find(selector).NextAllFiltered(".table-container").Eq(0).Find("tbody").Find("tr").Nodes { - instanceTypeData := strings.TrimSpace(row.FirstChild.NextSibling.FirstChild.Data) - if !strings.ContainsAny(instanceTypeData, ".") { + instanceTypeName := strings.TrimSpace(row.FirstChild.NextSibling.FirstChild.Data) + if !strings.ContainsAny(instanceTypeName, ".") { continue } bandwidthData := row.FirstChild.NextSibling.NextSibling.NextSibling.FirstChild.Data // exclude all rows that contain any of the following strings if containsAny(bandwidthData, "Low", "Moderate", "High", "Up to") { - vagueBandwidth[instanceTypeData] = bandwidthData + vagueBandwidth[ec2types.InstanceType(instanceTypeName)] = bandwidthData continue } bandwidthSlice := strings.Split(bandwidthData, " ") // if the first value contains a multiplier i.e. (4x 100 Gigabit) if strings.HasSuffix(bandwidthSlice[0], "x") { multiplier := lo.Must(strconv.ParseFloat(bandwidthSlice[0][:len(bandwidthSlice[0])-1], 64)) - bandwidth[instanceTypeData] = int64(lo.Must(strconv.ParseFloat(bandwidthSlice[1], 64)) * 1000 * multiplier) + bandwidth[ec2types.InstanceType(instanceTypeName)] = int64(lo.Must(strconv.ParseFloat(bandwidthSlice[1], 64)) * 1000 * multiplier) // Check row for instancetype for described network performance value i.e (2 Gigabit) } else { - bandwidth[instanceTypeData] = int64(lo.Must(strconv.ParseFloat(bandwidthSlice[0], 64)) * 1000) + bandwidth[ec2types.InstanceType(instanceTypeName)] = int64(lo.Must(strconv.ParseFloat(bandwidthSlice[0], 64)) * 1000) } } }() @@ -101,8 +103,12 @@ func main() { allInstanceTypes := getAllInstanceTypes() instanceTypes := lo.Keys(bandwidth) // 2d sort for readability - sort.Strings(allInstanceTypes) - sort.Strings(instanceTypes) + sort.SliceStable(allInstanceTypes, func(i, j int) bool { + return allInstanceTypes[i] < allInstanceTypes[j] + }) + sort.SliceStable(instanceTypes, func(i, j int) bool { + return instanceTypes[i] < instanceTypes[j] + }) sort.SliceStable(instanceTypes, func(i, j int) bool { return bandwidth[instanceTypes[i]] < bandwidth[instanceTypes[j]] }) @@ -138,22 +144,23 @@ func containsAny(value string, excludedSubstrings ...string) bool { return false } -func getAllInstanceTypes() []string { +func getAllInstanceTypes() []ec2types.InstanceType { if err := os.Setenv("AWS_SDK_LOAD_CONFIG", "true"); err != nil { log.Fatalf("setting AWS_SDK_LOAD_CONFIG, %s", err) } if err := os.Setenv("AWS_REGION", "us-east-1"); err != nil { log.Fatalf("setting AWS_REGION, %s", err) } - sess := session.Must(session.NewSession()) - ec2api := ec2.New(sess) - var allInstanceTypes []string + ctx := context.Background() + cfg := lo.Must(config.LoadDefaultConfig(ctx)) + ec2api := ec2.NewFromConfig(cfg) + var allInstanceTypes []ec2types.InstanceType params := &ec2.DescribeInstanceTypesInput{} // Retrieve the instance types in a loop using NextToken for { - result := lo.Must(ec2api.DescribeInstanceTypes(params)) - allInstanceTypes = append(allInstanceTypes, lo.Map(result.InstanceTypes, func(info *ec2.InstanceTypeInfo, _ int) string { return *info.InstanceType })...) + result := lo.Must(ec2api.DescribeInstanceTypes(ctx, params)) + allInstanceTypes = append(allInstanceTypes, lo.Map(result.InstanceTypes, func(info ec2types.InstanceTypeInfo, _ int) ec2types.InstanceType { return info.InstanceType })...) // Check if they are any instances left if result.NextToken != nil { params.NextToken = result.NextToken diff --git a/hack/code/instancetype_testdata_gen/main.go b/hack/code/instancetype_testdata_gen/main.go index 0debaccd22c6..0aefceee2e3a 100644 --- a/hack/code/instancetype_testdata_gen/main.go +++ b/hack/code/instancetype_testdata_gen/main.go @@ -25,10 +25,10 @@ import ( "sort" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/samber/lo" ) @@ -36,8 +36,9 @@ const packageHeader = ` package fake import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) // GENERATED FILE. DO NOT EDIT DIRECTLY. @@ -63,8 +64,8 @@ func main() { log.Fatalf("setting AWS_REGION, %s", err) } ctx := context.Background() - sess := session.Must(session.NewSession()) - ec2Client := ec2.New(sess) + cfg := lo.Must(config.LoadDefaultConfig(ctx)) + ec2api := ec2.NewFromConfig(cfg) instanceTypes := strings.Split(instanceTypesStr, ",") src := &bytes.Buffer{} @@ -72,7 +73,7 @@ func main() { license := lo.Must(os.ReadFile("hack/boilerplate.go.txt")) fmt.Fprintln(src, string(license)) fmt.Fprint(src, packageHeader) - fmt.Fprintln(src, getDescribeInstanceTypesOutput(ctx, ec2Client, instanceTypes)) + fmt.Fprintln(src, getDescribeInstanceTypesOutput(ctx, ec2api, instanceTypes)) // Format and print to the file formatted, err := format.Source(src.Bytes()) @@ -84,21 +85,26 @@ func main() { } } -func getDescribeInstanceTypesOutput(ctx context.Context, ec2Client ec2iface.EC2API, instanceTypes []string) string { - out, err := ec2Client.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{ - InstanceTypes: aws.StringSlice(instanceTypes), +func getDescribeInstanceTypesOutput(ctx context.Context, ec2api sdk.EC2API, instanceTypes []string) string { + instanceTypeValues := lo.Map(instanceTypes, func(it string, _ int) ec2types.InstanceType { + return ec2types.InstanceType(it) }) + + out, err := ec2api.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{ + InstanceTypes: instanceTypeValues, + }) + if err != nil { log.Fatalf("describing instance types, %s", err) } // Sort them by name so that we get a consistent ordering sort.SliceStable(out.InstanceTypes, func(i, j int) bool { - return aws.StringValue(out.InstanceTypes[i].InstanceType) < aws.StringValue(out.InstanceTypes[j].InstanceType) + return out.InstanceTypes[i].InstanceType < out.InstanceTypes[j].InstanceType }) src := &bytes.Buffer{} fmt.Fprintln(src, "var defaultDescribeInstanceTypesOutput = &ec2.DescribeInstanceTypesOutput{") - fmt.Fprintln(src, "InstanceTypes: []*ec2.InstanceTypeInfo{") + fmt.Fprintln(src, "InstanceTypes: []ec2types.InstanceTypeInfo{") for _, elem := range out.InstanceTypes { fmt.Fprintln(src, "{") data := getInstanceTypeInfo(elem) @@ -110,46 +116,47 @@ func getDescribeInstanceTypesOutput(ctx context.Context, ec2Client ec2iface.EC2A return src.String() } -func getInstanceTypeInfo(info *ec2.InstanceTypeInfo) string { +func getInstanceTypeInfo(info ec2types.InstanceTypeInfo) string { src := &bytes.Buffer{} - fmt.Fprintf(src, "InstanceType: aws.String(\"%s\"),\n", lo.FromPtr(info.InstanceType)) - fmt.Fprintf(src, "SupportedUsageClasses: aws.StringSlice([]string{%s}),\n", getStringSliceData(info.SupportedUsageClasses)) - fmt.Fprintf(src, "SupportedVirtualizationTypes: aws.StringSlice([]string{%s}),\n", getStringSliceData(info.SupportedVirtualizationTypes)) + + fmt.Fprintf(src, "InstanceType: \"%s\",\n", info.InstanceType) + fmt.Fprintf(src, "SupportedUsageClasses:[]ec2types.UsageClassType{%s},\n", getStringSliceData(info.SupportedUsageClasses)) + fmt.Fprintf(src, "SupportedVirtualizationTypes: []ec2types.VirtualizationType{%s},\n", getStringSliceData(info.SupportedVirtualizationTypes)) fmt.Fprintf(src, "BurstablePerformanceSupported: aws.Bool(%t),\n", lo.FromPtr(info.BurstablePerformanceSupported)) fmt.Fprintf(src, "BareMetal: aws.Bool(%t),\n", lo.FromPtr(info.BareMetal)) - fmt.Fprintf(src, "Hypervisor: aws.String(\"%s\"),\n", lo.FromPtr(info.Hypervisor)) - fmt.Fprintf(src, "ProcessorInfo: &ec2.ProcessorInfo{\n") + fmt.Fprintf(src, "Hypervisor: \"%s\",\n", info.Hypervisor) + + fmt.Fprintf(src, "ProcessorInfo: &ec2types.ProcessorInfo{\n") fmt.Fprintf(src, "Manufacturer: aws.String(\"%s\"),\n", lo.FromPtr(info.ProcessorInfo.Manufacturer)) - fmt.Fprintf(src, "SupportedArchitectures: aws.StringSlice([]string{%s}),\n", getStringSliceData(info.ProcessorInfo.SupportedArchitectures)) + fmt.Fprintf(src, "SupportedArchitectures: []ec2types.ArchitectureType{%s},\n", getStringSliceData(info.ProcessorInfo.SupportedArchitectures)) fmt.Fprintf(src, "},\n") - fmt.Fprintf(src, "VCpuInfo: &ec2.VCpuInfo{\n") - fmt.Fprintf(src, "DefaultCores: aws.Int64(%d),\n", lo.FromPtr(info.VCpuInfo.DefaultCores)) - fmt.Fprintf(src, "DefaultVCpus: aws.Int64(%d),\n", lo.FromPtr(info.VCpuInfo.DefaultVCpus)) + fmt.Fprintf(src, "VCpuInfo: &ec2types.VCpuInfo{\n") + fmt.Fprintf(src, "DefaultCores: aws.Int32(%d),\n", lo.FromPtr(info.VCpuInfo.DefaultCores)) + fmt.Fprintf(src, "DefaultVCpus: aws.Int32(%d),\n", lo.FromPtr(info.VCpuInfo.DefaultVCpus)) fmt.Fprintf(src, "},\n") - fmt.Fprintf(src, "MemoryInfo: &ec2.MemoryInfo{\n") + fmt.Fprintf(src, "MemoryInfo: &ec2types.MemoryInfo{\n") fmt.Fprintf(src, "SizeInMiB: aws.Int64(%d),\n", lo.FromPtr(info.MemoryInfo.SizeInMiB)) fmt.Fprintf(src, "},\n") - if info.EbsInfo != nil { - fmt.Fprintf(src, "EbsInfo: &ec2.EbsInfo{\n") + fmt.Fprintf(src, "EbsInfo: &ec2types.EbsInfo{\n") if info.EbsInfo.EbsOptimizedInfo != nil { - fmt.Fprintf(src, "EbsOptimizedInfo: &ec2.EbsOptimizedInfo{\n") - fmt.Fprintf(src, "BaselineBandwidthInMbps: aws.Int64(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.BaselineBandwidthInMbps)) - fmt.Fprintf(src, "BaselineIops: aws.Int64(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.BaselineIops)) + fmt.Fprintf(src, "EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{\n") + fmt.Fprintf(src, "BaselineBandwidthInMbps: aws.Int32(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.BaselineBandwidthInMbps)) + fmt.Fprintf(src, "BaselineIops: aws.Int32(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.BaselineIops)) fmt.Fprintf(src, "BaselineThroughputInMBps: aws.Float64(%.2f),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.BaselineThroughputInMBps)) - fmt.Fprintf(src, "MaximumBandwidthInMbps: aws.Int64(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumBandwidthInMbps)) - fmt.Fprintf(src, "MaximumIops: aws.Int64(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumIops)) + fmt.Fprintf(src, "MaximumBandwidthInMbps: aws.Int32(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumBandwidthInMbps)) + fmt.Fprintf(src, "MaximumIops: aws.Int32(%d),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumIops)) fmt.Fprintf(src, "MaximumThroughputInMBps: aws.Float64(%.2f),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumThroughputInMBps)) fmt.Fprintf(src, "},\n") } - fmt.Fprintf(src, "EbsOptimizedSupport: aws.String(\"%s\"),\n", lo.FromPtr(info.EbsInfo.EbsOptimizedSupport)) - fmt.Fprintf(src, "EncryptionSupport: aws.String(\"%s\"),\n", lo.FromPtr(info.EbsInfo.EncryptionSupport)) - fmt.Fprintf(src, "NvmeSupport: aws.String(\"%s\"),\n", lo.FromPtr(info.EbsInfo.NvmeSupport)) + fmt.Fprintf(src, "EbsOptimizedSupport: \"%s\",\n", info.EbsInfo.EbsOptimizedSupport) + fmt.Fprintf(src, "EncryptionSupport: \"%s\",\n", info.EbsInfo.EncryptionSupport) + fmt.Fprintf(src, "NvmeSupport: \"%s\",\n", info.EbsInfo.NvmeSupport) fmt.Fprintf(src, "},\n") } if info.NeuronInfo != nil { - fmt.Fprintf(src, "NeuronInfo: &ec2.NeuronInfo{\n") - fmt.Fprintf(src, "NeuronDevices: []*ec2.NeuronDeviceInfo{\n") + fmt.Fprintf(src, "NeuronInfo: &ec2types.NeuronInfo{\n") + fmt.Fprintf(src, "NeuronDevices: []ec2types.NeuronDeviceInfo{\n") for _, elem := range info.NeuronInfo.NeuronDevices { fmt.Fprintf(src, getNeuronDeviceInfo(elem)) } @@ -157,8 +164,8 @@ func getInstanceTypeInfo(info *ec2.InstanceTypeInfo) string { fmt.Fprintf(src, "},\n") } if info.GpuInfo != nil { - fmt.Fprintf(src, "GpuInfo: &ec2.GpuInfo{\n") - fmt.Fprintf(src, "Gpus: []*ec2.GpuDeviceInfo{\n") + fmt.Fprintf(src, "GpuInfo: &ec2types.GpuInfo{\n") + fmt.Fprintf(src, "Gpus: []ec2types.GpuDeviceInfo{\n") for _, elem := range info.GpuInfo.Gpus { fmt.Fprintf(src, getGPUDeviceInfo(elem)) } @@ -166,22 +173,22 @@ func getInstanceTypeInfo(info *ec2.InstanceTypeInfo) string { fmt.Fprintf(src, "},\n") } if info.InstanceStorageInfo != nil { - fmt.Fprintf(src, "InstanceStorageInfo: &ec2.InstanceStorageInfo{") - fmt.Fprintf(src, "NvmeSupport: aws.String(\"%s\"),\n", lo.FromPtr(info.InstanceStorageInfo.NvmeSupport)) + fmt.Fprintf(src, "InstanceStorageInfo: &ec2types.InstanceStorageInfo{") + fmt.Fprintf(src, "NvmeSupport: \"%s\",\n", string(info.InstanceStorageInfo.NvmeSupport)) fmt.Fprintf(src, "TotalSizeInGB: aws.Int64(%d),\n", lo.FromPtr(info.InstanceStorageInfo.TotalSizeInGB)) fmt.Fprintf(src, "},\n") } - fmt.Fprintf(src, "NetworkInfo: &ec2.NetworkInfo{\n") + fmt.Fprintf(src, "NetworkInfo: &ec2types.NetworkInfo{\n") if info.NetworkInfo.EfaInfo != nil { - fmt.Fprintf(src, "EfaInfo: &ec2.EfaInfo{\n") - fmt.Fprintf(src, "MaximumEfaInterfaces: aws.Int64(%d),\n", lo.FromPtr(info.NetworkInfo.EfaInfo.MaximumEfaInterfaces)) + fmt.Fprintf(src, "EfaInfo: &ec2types.EfaInfo{\n") + fmt.Fprintf(src, "MaximumEfaInterfaces: aws.Int32(%d),\n", lo.FromPtr(info.NetworkInfo.EfaInfo.MaximumEfaInterfaces)) fmt.Fprintf(src, "},\n") } - fmt.Fprintf(src, "MaximumNetworkInterfaces: aws.Int64(%d),\n", lo.FromPtr(info.NetworkInfo.MaximumNetworkInterfaces)) - fmt.Fprintf(src, "Ipv4AddressesPerInterface: aws.Int64(%d),\n", lo.FromPtr(info.NetworkInfo.Ipv4AddressesPerInterface)) + fmt.Fprintf(src, "MaximumNetworkInterfaces: aws.Int32(%d),\n", lo.FromPtr(info.NetworkInfo.MaximumNetworkInterfaces)) + fmt.Fprintf(src, "Ipv4AddressesPerInterface: aws.Int32(%d),\n", lo.FromPtr(info.NetworkInfo.Ipv4AddressesPerInterface)) fmt.Fprintf(src, "EncryptionInTransitSupported: aws.Bool(%t),\n", lo.FromPtr(info.NetworkInfo.EncryptionInTransitSupported)) - fmt.Fprintf(src, "DefaultNetworkCardIndex: aws.Int64(%d),\n", lo.FromPtr(info.NetworkInfo.DefaultNetworkCardIndex)) - fmt.Fprintf(src, "NetworkCards: []*ec2.NetworkCardInfo{\n") + fmt.Fprintf(src, "DefaultNetworkCardIndex: aws.Int32(%d),\n", lo.FromPtr(info.NetworkInfo.DefaultNetworkCardIndex)) + fmt.Fprintf(src, "NetworkCards: []ec2types.NetworkCardInfo{\n") for _, networkCard := range info.NetworkInfo.NetworkCards { fmt.Fprintf(src, getNetworkCardInfo(networkCard)) } @@ -190,45 +197,45 @@ func getInstanceTypeInfo(info *ec2.InstanceTypeInfo) string { return src.String() } -func getNetworkCardInfo(info *ec2.NetworkCardInfo) string { +func getNetworkCardInfo(info ec2types.NetworkCardInfo) string { src := &bytes.Buffer{} fmt.Fprintf(src, "{\n") - fmt.Fprintf(src, "NetworkCardIndex: aws.Int64(%d),\n", lo.FromPtr(info.NetworkCardIndex)) - fmt.Fprintf(src, "MaximumNetworkInterfaces: aws.Int64(%d),\n", lo.FromPtr(info.MaximumNetworkInterfaces)) + fmt.Fprintf(src, "NetworkCardIndex: aws.Int32(%d),\n", lo.FromPtr(info.NetworkCardIndex)) + fmt.Fprintf(src, "MaximumNetworkInterfaces: aws.Int32(%d),\n", lo.FromPtr(info.MaximumNetworkInterfaces)) fmt.Fprintf(src, "},\n") return src.String() } -func getNeuronDeviceInfo(info *ec2.NeuronDeviceInfo) string { +func getNeuronDeviceInfo(info ec2types.NeuronDeviceInfo) string { src := &bytes.Buffer{} fmt.Fprintf(src, "{\n") - fmt.Fprintf(src, "Count: aws.Int64(%d),\n", lo.FromPtr(info.Count)) + fmt.Fprintf(src, "Count: aws.Int32(%d),\n", lo.FromPtr(info.Count)) fmt.Fprintf(src, "Name: aws.String(\"%s\"),\n", lo.FromPtr(info.Name)) - fmt.Fprintf(src, "CoreInfo: &ec2.NeuronDeviceCoreInfo{\n") - fmt.Fprintf(src, "Count: aws.Int64(%d),\n", lo.FromPtr(info.CoreInfo.Count)) - fmt.Fprintf(src, "Version: aws.Int64(%d),\n", lo.FromPtr(info.CoreInfo.Version)) + fmt.Fprintf(src, "CoreInfo: &ec2types.NeuronDeviceCoreInfo{\n") + fmt.Fprintf(src, "Count: aws.Int32(%d),\n", lo.FromPtr(info.CoreInfo.Count)) + fmt.Fprintf(src, "Version: aws.Int32(%d),\n", lo.FromPtr(info.CoreInfo.Version)) fmt.Fprintf(src, "},\n") - fmt.Fprintf(src, "MemoryInfo: &ec2.NeuronDeviceMemoryInfo{\n") - fmt.Fprintf(src, "SizeInMiB: aws.Int64(%d),\n", lo.FromPtr(info.MemoryInfo.SizeInMiB)) + fmt.Fprintf(src, "MemoryInfo: &ec2types.NeuronDeviceMemoryInfo{\n") + fmt.Fprintf(src, "SizeInMiB: aws.Int32(%d),\n", lo.FromPtr(info.MemoryInfo.SizeInMiB)) fmt.Fprintf(src, "},\n") fmt.Fprintf(src, "},\n") return src.String() } -func getGPUDeviceInfo(info *ec2.GpuDeviceInfo) string { +func getGPUDeviceInfo(info ec2types.GpuDeviceInfo) string { src := &bytes.Buffer{} fmt.Fprintf(src, "{\n") fmt.Fprintf(src, "Name: aws.String(\"%s\"),\n", lo.FromPtr(info.Name)) fmt.Fprintf(src, "Manufacturer: aws.String(\"%s\"),\n", lo.FromPtr(info.Manufacturer)) - fmt.Fprintf(src, "Count: aws.Int64(%d),\n", lo.FromPtr(info.Count)) - fmt.Fprintf(src, "MemoryInfo: &ec2.GpuDeviceMemoryInfo{\n") - fmt.Fprintf(src, "SizeInMiB: aws.Int64(%d),\n", lo.FromPtr(info.MemoryInfo.SizeInMiB)) + fmt.Fprintf(src, "Count: aws.Int32(%d),\n", lo.FromPtr(info.Count)) + fmt.Fprintf(src, "MemoryInfo: &ec2types.GpuDeviceMemoryInfo{\n") + fmt.Fprintf(src, "SizeInMiB: aws.Int32(%d),\n", lo.FromPtr(info.MemoryInfo.SizeInMiB)) fmt.Fprintf(src, "},\n") fmt.Fprintf(src, "},\n") return src.String() } -func getStringSliceData(slice []*string) string { - return strings.Join(lo.Map(slice, func(s *string, _ int) string { return fmt.Sprintf(`"%s"`, lo.FromPtr(s)) }), ",") +func getStringSliceData[T ec2types.UsageClassType | ec2types.VirtualizationType | ec2types.ArchitectureType](slice []T) string { + return strings.Join(lo.Map(slice, func(s T, _ int) string { return fmt.Sprintf(`"%s"`, s) }), ",") } diff --git a/hack/code/prices_gen/main.go b/hack/code/prices_gen/main.go index 837eeaedb360..2d0b2f4f852f 100644 --- a/hack/code/prices_gen/main.go +++ b/hack/code/prices_gen/main.go @@ -28,8 +28,10 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws/session" - ec22 "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/config" + + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" controllerspricing "github.com/aws/karpenter-provider-aws/pkg/controllers/providers/pricing" @@ -93,8 +95,8 @@ func main() { os.Setenv("AWS_REGION", region) ctx := context.Background() ctx = options.ToContext(ctx, test.Options()) - sess := session.Must(session.NewSession()) - ec2 := ec22.New(sess) + cfg := lo.Must(config.LoadDefaultConfig(ctx, config.WithRegion(region))) + ec2api := ec2.NewFromConfig(cfg) src := &bytes.Buffer{} fmt.Fprintln(src, "//go:build !ignore_autogenerated") license := lo.Must(os.ReadFile("hack/boilerplate.go.txt")) @@ -102,18 +104,21 @@ func main() { fmt.Fprintln(src, "package pricing") now := time.Now().UTC().Format(time.RFC3339) fmt.Fprintf(src, "// generated at %s for %s\n\n\n", now, region) - fmt.Fprintf(src, "var InitialOnDemandPrices%s = map[string]map[string]float64{\n", getPartitionSuffix(opts.partition)) + fmt.Fprintln(src, "import ec2types \"github.com/aws/aws-sdk-go-v2/service/ec2/types\"") + fmt.Fprintf(src, "var InitialOnDemandPrices%s = map[string]map[ec2types.InstanceType]float64{\n", getPartitionSuffix(opts.partition)) // record prices for each region we are interested in for _, region := range getAWSRegions(opts.partition) { log.Println("fetching for", region) - pricingProvider := pricing.NewDefaultProvider(ctx, pricing.NewAPI(sess, region), ec2, region) + pricingProvider := pricing.NewDefaultProvider(ctx, pricing.NewAPI(cfg), ec2api, region) controller := controllerspricing.NewController(pricingProvider) _, err := controller.Reconcile(ctx) if err != nil { log.Fatalf("failed to initialize pricing provider %s", err) } instanceTypes := pricingProvider.InstanceTypes() - sort.Strings(instanceTypes) + sort.SliceStable(instanceTypes, func(i, j int) bool { + return instanceTypes[i] < instanceTypes[j] + }) writePricing(src, instanceTypes, region, pricingProvider.OnDemandPrice) } @@ -135,14 +140,16 @@ func main() { } } -func writePricing(src *bytes.Buffer, instanceNames []string, region string, getPrice func(instanceType string) (float64, bool)) { +func writePricing(src *bytes.Buffer, instanceNames []ec2types.InstanceType, region string, getPrice func(instanceType ec2types.InstanceType) (float64, bool)) { fmt.Fprintf(src, "// %s\n", region) fmt.Fprintf(src, "%q: {\n", region) lineLen := 0 - sort.Strings(instanceNames) + sort.SliceStable(instanceNames, func(i, j int) bool { + return instanceNames[i] < instanceNames[j] + }) previousFamily := "" for _, instanceName := range instanceNames { - segs := strings.Split(instanceName, ".") + segs := strings.Split(string(instanceName), ".") if len(segs) != 2 { log.Fatalf("parsing instance family %s, got %v", instanceName, segs) } diff --git a/hack/docs/instancetypes_gen/main.go b/hack/docs/instancetypes_gen/main.go index 64cea3dd335e..e22064fb43aa 100644 --- a/hack/docs/instancetypes_gen/main.go +++ b/hack/docs/instancetypes_gen/main.go @@ -23,9 +23,10 @@ import ( "sort" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/patrickmn/go-cache" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -125,8 +126,8 @@ below are the resources available with some assumptions and after the instance o // Iterate through regions and take the union of instance types we discover across both for _, region := range []string{"us-east-1", "us-east-2", "us-west-2"} { - sess := session.Must(session.NewSession(&aws.Config{Region: lo.ToPtr(region)})) - ec2api := ec2.New(sess) + cfg := lo.Must(config.LoadDefaultConfig(ctx, config.WithRegion(region))) + ec2api := ec2.NewFromConfig(cfg) subnetProvider := subnet.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AvailableIPAddressTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AssociatePublicIPAddressTTL, awscache.DefaultCleanupInterval)) instanceTypeProvider := instancetype.NewDefaultProvider( cache.New(awscache.InstanceTypesAndZonesTTL, awscache.DefaultCleanupInterval), @@ -137,9 +138,9 @@ below are the resources available with some assumptions and after the instance o region, pricing.NewDefaultProvider( ctx, - pricing.NewAPI(sess, *sess.Config.Region), + pricing.NewAPI(cfg), ec2api, - *sess.Config.Region, + cfg.Region, ), awscache.NewUnavailableOfferings(), ), @@ -169,7 +170,7 @@ below are the resources available with some assumptions and after the instance o if err != nil { log.Fatalf("listing subnets, %s", err) } - nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet *ec2.Subnet, _ int) v1.Subnet { + nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet ec2types.Subnet, _ int) v1.Subnet { return v1.Subnet{ ID: *ec2subnet.SubnetId, Zone: *ec2subnet.AvailabilityZone, @@ -180,11 +181,11 @@ below are the resources available with some assumptions and after the instance o log.Fatalf("listing instance types, %s", err) } for _, it := range instanceTypes { - familyName := strings.Split(it.Name, ".")[0] + familyName := strings.Split(string(it.Name), ".")[0] if _, ok := families[familyName]; !ok { families[familyName] = map[string]*cloudprovider.InstanceType{} } - families[familyName][it.Name] = it + families[familyName][string(it.Name)] = it for labelName := range it.Requirements { labelNameMap.Insert(labelName) } diff --git a/hack/tools/allocatable_diff/main.go b/hack/tools/allocatable_diff/main.go index 10990107aef7..d686d9330740 100644 --- a/hack/tools/allocatable_diff/main.go +++ b/hack/tools/allocatable_diff/main.go @@ -102,7 +102,7 @@ func main() { // Write the details of the expected instance and the actual instance into a CSV line format lo.Must0(w.Write([]string{ - instanceType.Name, + string(instanceType.Name), fmt.Sprintf("%d", instanceType.Capacity.Memory().Value()/1024/1024), fmt.Sprintf("%d", instanceType.Capacity.Cpu().MilliValue()), fmt.Sprintf("%d", instanceType.Capacity.StorageEphemeral().Value()/1024/1024), diff --git a/hack/tools/launchtemplate_counter/main.go b/hack/tools/launchtemplate_counter/main.go index e75be08c82d2..2ea169ef2dd4 100644 --- a/hack/tools/launchtemplate_counter/main.go +++ b/hack/tools/launchtemplate_counter/main.go @@ -20,9 +20,9 @@ import ( "log" "os" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/patrickmn/go-cache" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -53,8 +53,8 @@ func main() { })) region := "us-west-2" - sess := session.Must(session.NewSession(&aws.Config{Region: lo.ToPtr(region)})) - ec2api := ec2.New(sess) + cfg := lo.Must(config.LoadDefaultConfig(ctx, config.WithRegion(region))) + ec2api := ec2.NewFromConfig(cfg) subnetProvider := subnet.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AvailableIPAddressTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AssociatePublicIPAddressTTL, awscache.DefaultCleanupInterval)) instanceTypeProvider := instancetype.NewDefaultProvider( cache.New(awscache.InstanceTypesAndZonesTTL, awscache.DefaultCleanupInterval), @@ -65,9 +65,9 @@ func main() { region, pricing.NewDefaultProvider( ctx, - pricing.NewAPI(sess, *sess.Config.Region), + pricing.NewAPI(cfg), ec2api, - *sess.Config.Region, + cfg.Region, ), awscache.NewUnavailableOfferings(), ), @@ -97,7 +97,7 @@ func main() { if err != nil { log.Fatalf("listing subnets, %s", err) } - nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet *ec2.Subnet, _ int) v1.Subnet { + nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet ec2types.Subnet, _ int) v1.Subnet { return v1.Subnet{ ID: *ec2subnet.SubnetId, Zone: *ec2subnet.AvailabilityZone, @@ -127,7 +127,7 @@ func main() { }, }, } - instanceTypes, err := instanceTypeProvider.List(ctx, nodeClass) + instanceTypes := lo.Must(instanceTypeProvider.List(ctx, nodeClass)) // See how many launch templates we get by constraining our instance types to just be "c", "m", and "r" reqs := scheduling.NewRequirements(scheduling.NewRequirement(v1.LabelInstanceCategory, corev1.NodeSelectorOpIn, "c", "m", "r")) diff --git a/pkg/apis/v1/ec2nodeclass_hash_test.go b/pkg/apis/v1/ec2nodeclass_hash_test.go index baf0a0c7f115..d88d561c2b34 100644 --- a/pkg/apis/v1/ec2nodeclass_hash_test.go +++ b/pkg/apis/v1/ec2nodeclass_hash_test.go @@ -15,13 +15,14 @@ limitations under the License. package v1_test import ( - "github.com/aws/aws-sdk-go/aws" "github.com/imdario/mergo" "github.com/samber/lo" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/karpenter/pkg/test" + "github.com/aws/aws-sdk-go-v2/aws" + v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" . "github.com/onsi/ginkgo/v2" diff --git a/pkg/apis/v1/ec2nodeclass_validation_cel_test.go b/pkg/apis/v1/ec2nodeclass_validation_cel_test.go index 41f3cd36c00f..7685f1546118 100644 --- a/pkg/apis/v1/ec2nodeclass_validation_cel_test.go +++ b/pkg/apis/v1/ec2nodeclass_validation_cel_test.go @@ -19,7 +19,6 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/aws" "github.com/imdario/mergo" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -28,6 +27,8 @@ import ( karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" "sigs.k8s.io/karpenter/pkg/test" + "github.com/aws/aws-sdk-go-v2/aws" + v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" . "github.com/onsi/ginkgo/v2" diff --git a/pkg/aws/sdk.go b/pkg/aws/sdk.go index 63a6b9e69398..e00d3d2cd509 100644 --- a/pkg/aws/sdk.go +++ b/pkg/aws/sdk.go @@ -17,32 +17,57 @@ package sdk import ( "context" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/aws/aws-sdk-go-v2/service/pricing" "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/timestreamwrite" ) +type EC2API interface { + DescribeImages(context.Context, *ec2.DescribeImagesInput, ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error) + DescribeLaunchTemplates(context.Context, *ec2.DescribeLaunchTemplatesInput, ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplatesOutput, error) + DescribeSubnets(context.Context, *ec2.DescribeSubnetsInput, ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) + DescribeSecurityGroups(context.Context, *ec2.DescribeSecurityGroupsInput, ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) + DescribeInstanceTypes(context.Context, *ec2.DescribeInstanceTypesInput, ...func(*ec2.Options)) (*ec2.DescribeInstanceTypesOutput, error) + DescribeInstanceTypeOfferings(context.Context, *ec2.DescribeInstanceTypeOfferingsInput, ...func(*ec2.Options)) (*ec2.DescribeInstanceTypeOfferingsOutput, error) + DescribeSpotPriceHistory(context.Context, *ec2.DescribeSpotPriceHistoryInput, ...func(*ec2.Options)) (*ec2.DescribeSpotPriceHistoryOutput, error) + CreateFleet(context.Context, *ec2.CreateFleetInput, ...func(*ec2.Options)) (*ec2.CreateFleetOutput, error) + TerminateInstances(context.Context, *ec2.TerminateInstancesInput, ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) + DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) + CreateTags(context.Context, *ec2.CreateTagsInput, ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error) + CreateLaunchTemplate(context.Context, *ec2.CreateLaunchTemplateInput, ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateOutput, error) + DeleteLaunchTemplate(context.Context, *ec2.DeleteLaunchTemplateInput, ...func(*ec2.Options)) (*ec2.DeleteLaunchTemplateOutput, error) +} + type IAMAPI interface { - // IAM Methods GetInstanceProfile(context.Context, *iam.GetInstanceProfileInput, ...func(*iam.Options)) (*iam.GetInstanceProfileOutput, error) CreateInstanceProfile(context.Context, *iam.CreateInstanceProfileInput, ...func(*iam.Options)) (*iam.CreateInstanceProfileOutput, error) DeleteInstanceProfile(context.Context, *iam.DeleteInstanceProfileInput, ...func(*iam.Options)) (*iam.DeleteInstanceProfileOutput, error) AddRoleToInstanceProfile(context.Context, *iam.AddRoleToInstanceProfileInput, ...func(*iam.Options)) (*iam.AddRoleToInstanceProfileOutput, error) TagInstanceProfile(context.Context, *iam.TagInstanceProfileInput, ...func(*iam.Options)) (*iam.TagInstanceProfileOutput, error) RemoveRoleFromInstanceProfile(context.Context, *iam.RemoveRoleFromInstanceProfileInput, ...func(*iam.Options)) (*iam.RemoveRoleFromInstanceProfileOutput, error) - UntagInstanceProfile(context.Context, *iam.UntagInstanceProfileInput, ...func(*iam.Options)) (*iam.UntagInstanceProfileOutput, error) - GetRole(context.Context, *iam.GetRoleInput, ...func(*iam.Options)) (*iam.GetRoleOutput, error) +} +type EKSAPI interface { + DescribeCluster(context.Context, *eks.DescribeClusterInput, ...func(*eks.Options)) (*eks.DescribeClusterOutput, error) +} + +type PricingAPI interface { + GetProducts(context.Context, *pricing.GetProductsInput, ...func(*pricing.Options)) (*pricing.GetProductsOutput, error) } type SSMAPI interface { - // SSM Methods GetParameter(context.Context, *ssm.GetParameterInput, ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) } type SQSAPI interface { - // SQS Methods - GetQueueUrl(context.Context, *sqs.GetQueueUrlInput, ...func(*sqs.Options)) (*sqs.GetQueueUrlOutput, error) ReceiveMessage(context.Context, *sqs.ReceiveMessageInput, ...func(*sqs.Options)) (*sqs.ReceiveMessageOutput, error) DeleteMessage(context.Context, *sqs.DeleteMessageInput, ...func(*sqs.Options)) (*sqs.DeleteMessageOutput, error) SendMessage(context.Context, *sqs.SendMessageInput, ...func(*sqs.Options)) (*sqs.SendMessageOutput, error) } + +type TimestreamWriteAPI interface { + WriteRecords(ctx context.Context, params *timestreamwrite.WriteRecordsInput, optFns ...func(*timestreamwrite.Options)) (*timestreamwrite.WriteRecordsOutput, error) +} diff --git a/pkg/batcher/createfleet.go b/pkg/batcher/createfleet.go index 6f9ab1221d7b..ef4908adf16c 100644 --- a/pkg/batcher/createfleet.go +++ b/pkg/batcher/createfleet.go @@ -19,9 +19,12 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" + "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -29,7 +32,7 @@ type CreateFleetBatcher struct { batcher *Batcher[ec2.CreateFleetInput, ec2.CreateFleetOutput] } -func NewCreateFleetBatcher(ctx context.Context, ec2api ec2iface.EC2API) *CreateFleetBatcher { +func NewCreateFleetBatcher(ctx context.Context, ec2api sdk.EC2API) *CreateFleetBatcher { options := Options[ec2.CreateFleetInput, ec2.CreateFleetOutput]{ Name: "create_fleet", IdleTimeout: 35 * time.Millisecond, @@ -40,7 +43,6 @@ func NewCreateFleetBatcher(ctx context.Context, ec2api ec2iface.EC2API) *CreateF } return &CreateFleetBatcher{batcher: NewBatcher(ctx, options)} } - func (b *CreateFleetBatcher) CreateFleet(ctx context.Context, createFleetInput *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) { if createFleetInput.TargetCapacitySpecification != nil && *createFleetInput.TargetCapacitySpecification.TotalTargetCapacity != 1 { return nil, fmt.Errorf("expected to receive a single instance only, found %d", *createFleetInput.TargetCapacitySpecification.TotalTargetCapacity) @@ -49,19 +51,19 @@ func (b *CreateFleetBatcher) CreateFleet(ctx context.Context, createFleetInput * return result.Output, result.Err } -func execCreateFleetBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.CreateFleetInput, ec2.CreateFleetOutput] { +func execCreateFleetBatch(ec2api sdk.EC2API) BatchExecutor[ec2.CreateFleetInput, ec2.CreateFleetOutput] { return func(ctx context.Context, inputs []*ec2.CreateFleetInput) []Result[ec2.CreateFleetOutput] { results := make([]Result[ec2.CreateFleetOutput], 0, len(inputs)) firstInput := inputs[0] - firstInput.TargetCapacitySpecification.TotalTargetCapacity = aws.Int64(int64(len(inputs))) - output, err := ec2api.CreateFleetWithContext(ctx, firstInput) + //nolint:gosec + firstInput.TargetCapacitySpecification.TotalTargetCapacity = aws.Int32(int32(len(inputs))) + output, err := ec2api.CreateFleet(ctx, firstInput) if err != nil { for range inputs { results = append(results, Result[ec2.CreateFleetOutput]{Err: err}) } return results } - // we can get partial fulfillment of a CreateFleet request, so we: // 1) split out the single instance IDs and deliver to each requestor // 2) deliver errors to any remaining requestors for which we don't have an instance @@ -70,16 +72,16 @@ func execCreateFleetBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.CreateFleetI for _, instanceID := range reservation.InstanceIds { requestIdx++ if requestIdx >= len(inputs) { - log.FromContext(ctx).Error(fmt.Errorf("received more instances than requested, ignoring instance %s", aws.StringValue(instanceID)), "received error while batching") + log.FromContext(ctx).Error(fmt.Errorf("received more instances than requested, ignoring instance %s", instanceID), "received error while batching") continue } results = append(results, Result[ec2.CreateFleetOutput]{ Output: &ec2.CreateFleetOutput{ FleetId: output.FleetId, Errors: output.Errors, - Instances: []*ec2.CreateFleetInstance{ + Instances: []ec2types.CreateFleetInstance{ { - InstanceIds: []*string{instanceID}, + InstanceIds: []string{instanceID}, InstanceType: reservation.InstanceType, LaunchTemplateAndOverrides: reservation.LaunchTemplateAndOverrides, Lifecycle: reservation.Lifecycle, @@ -90,11 +92,10 @@ func execCreateFleetBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.CreateFleetI }) } } - if requestIdx != len(inputs) { // we should receive some sort of error, but just in case if len(output.Errors) == 0 { - output.Errors = append(output.Errors, &ec2.CreateFleetError{ + output.Errors = append(output.Errors, ec2types.CreateFleetError{ ErrorCode: aws.String("too few instances returned"), ErrorMessage: aws.String("too few instances returned"), }) diff --git a/pkg/batcher/createfleet_test.go b/pkg/batcher/createfleet_test.go index 65bf44b4b5aa..a3e4a5906069 100644 --- a/pkg/batcher/createfleet_test.go +++ b/pkg/batcher/createfleet_test.go @@ -19,8 +19,10 @@ import ( "sync/atomic" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/samber/lo" "github.com/aws/karpenter-provider-aws/pkg/batcher" @@ -38,20 +40,20 @@ var _ = Describe("CreateFleet Batching", func() { It("should batch the same inputs into a single call", func() { input := &ec2.CreateFleetInput{ - LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ + LaunchTemplateConfigs: []ec2types.FleetLaunchTemplateConfigRequest{ { - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{ + Overrides: []ec2types.FleetLaunchTemplateOverridesRequest{ { AvailabilityZone: aws.String("us-east-1"), }, }, }, }, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + TotalTargetCapacity: aws.Int32(1), }, } var wg sync.WaitGroup @@ -63,13 +65,9 @@ var _ = Describe("CreateFleet Batching", func() { defer wg.Done() rsp, err := cfb.CreateFleet(ctx, input) Expect(err).To(BeNil()) - - var instanceIds []string - for _, rsv := range rsp.Instances { - for _, id := range rsv.InstanceIds { - instanceIds = append(instanceIds, *id) - } - } + instanceIds := lo.Flatten(lo.Map(rsp.Instances, func(rsv ec2types.CreateFleetInstance, _ int) []string { + return rsv.InstanceIds + })) atomic.AddInt64(&receivedInstance, 1) Expect(instanceIds).To(HaveLen(1)) }() @@ -83,37 +81,37 @@ var _ = Describe("CreateFleet Batching", func() { }) It("should batch different inputs into multiple calls", func() { east1input := &ec2.CreateFleetInput{ - LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ + LaunchTemplateConfigs: []ec2types.FleetLaunchTemplateConfigRequest{ { - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{ + Overrides: []ec2types.FleetLaunchTemplateOverridesRequest{ { AvailabilityZone: aws.String("us-east-1"), }, }, }, }, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + TotalTargetCapacity: aws.Int32(1), }, } east2input := &ec2.CreateFleetInput{ - LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ + LaunchTemplateConfigs: []ec2types.FleetLaunchTemplateConfigRequest{ { - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{ + Overrides: []ec2types.FleetLaunchTemplateOverridesRequest{ { AvailabilityZone: aws.String("us-east-2"), }, }, }, }, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + TotalTargetCapacity: aws.Int32(1), }, } var wg sync.WaitGroup @@ -131,12 +129,9 @@ var _ = Describe("CreateFleet Batching", func() { rsp, err := cfb.CreateFleet(ctx, input) Expect(err).To(BeNil()) - var instanceIds []string - for _, rsv := range rsp.Instances { - for _, id := range rsv.InstanceIds { - instanceIds = append(instanceIds, *id) - } - } + instanceIds := lo.Flatten(lo.Map(rsp.Instances, func(rsv ec2types.CreateFleetInstance, _ int) []string { + return rsv.InstanceIds + })) atomic.AddInt64(&receivedInstance, 1) Expect(instanceIds).To(HaveLen(1)) time.Sleep(100 * time.Millisecond) @@ -158,33 +153,33 @@ var _ = Describe("CreateFleet Batching", func() { }) It("should return any errors to callers", func() { input := &ec2.CreateFleetInput{ - LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ + LaunchTemplateConfigs: []ec2types.FleetLaunchTemplateConfigRequest{ { - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{ + Overrides: []ec2types.FleetLaunchTemplateOverridesRequest{ { AvailabilityZone: aws.String("us-east-1"), }, }, }, }, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + TotalTargetCapacity: aws.Int32(1), }, } fakeEC2API.CreateFleetBehavior.Output.Set(&ec2.CreateFleetOutput{ - Errors: []*ec2.CreateFleetError{ + Errors: []ec2types.CreateFleetError{ { ErrorCode: aws.String("some-error"), ErrorMessage: aws.String("some-error"), - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecification{ + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecification{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: &ec2.FleetLaunchTemplateOverrides{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ AvailabilityZone: aws.String("us-east-1"), }, }, @@ -192,24 +187,24 @@ var _ = Describe("CreateFleet Batching", func() { { ErrorCode: aws.String("some-other-error"), ErrorMessage: aws.String("some-other-error"), - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecification{ + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecification{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: &ec2.FleetLaunchTemplateOverrides{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ AvailabilityZone: aws.String("us-east-1"), }, }, }, }, FleetId: aws.String("some-id"), - Instances: []*ec2.CreateFleetInstance{ + Instances: []ec2types.CreateFleetInstance{ { - InstanceIds: []*string{aws.String("id-1"), aws.String("id-2"), aws.String("id-3"), aws.String("id-4"), aws.String("id-5")}, - InstanceType: nil, + InstanceIds: []string{"id-1", "id-2", "id-3", "id-4", "id-5"}, + InstanceType: "", LaunchTemplateAndOverrides: nil, - Lifecycle: nil, - Platform: nil, + Lifecycle: "", + Platform: "", }, }, }) @@ -229,12 +224,9 @@ var _ = Describe("CreateFleet Batching", func() { atomic.AddInt64(&numErrors, 1) } - var instanceIds []string - for _, rsv := range rsp.Instances { - for _, id := range rsv.InstanceIds { - instanceIds = append(instanceIds, *id) - } - } + instanceIds := lo.Flatten(lo.Map(rsp.Instances, func(rsv ec2types.CreateFleetInstance, _ int) []string { + return rsv.InstanceIds + })) atomic.AddInt64(&receivedInstance, 1) Expect(instanceIds).To(HaveLen(1)) }() @@ -251,33 +243,33 @@ var _ = Describe("CreateFleet Batching", func() { }) It("should handle partial fulfillment", func() { input := &ec2.CreateFleetInput{ - LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ + LaunchTemplateConfigs: []ec2types.FleetLaunchTemplateConfigRequest{ { - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{ + Overrides: []ec2types.FleetLaunchTemplateOverridesRequest{ { AvailabilityZone: aws.String("us-east-1"), }, }, }, }, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + TotalTargetCapacity: aws.Int32(1), }, } fakeEC2API.CreateFleetBehavior.Output.Set(&ec2.CreateFleetOutput{ - Errors: []*ec2.CreateFleetError{ + Errors: []ec2types.CreateFleetError{ { ErrorCode: aws.String("some-error"), ErrorMessage: aws.String("some-error"), - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecification{ + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecification{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: &ec2.FleetLaunchTemplateOverrides{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ AvailabilityZone: aws.String("us-east-1"), }, }, @@ -285,24 +277,24 @@ var _ = Describe("CreateFleet Batching", func() { { ErrorCode: aws.String("some-other-error"), ErrorMessage: aws.String("some-other-error"), - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecification{ + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecification{ LaunchTemplateName: aws.String("my-template"), }, - Overrides: &ec2.FleetLaunchTemplateOverrides{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ AvailabilityZone: aws.String("us-east-1"), }, }, }, }, FleetId: aws.String("some-id"), - Instances: []*ec2.CreateFleetInstance{ + Instances: []ec2types.CreateFleetInstance{ { - InstanceIds: []*string{aws.String("id-1"), aws.String("id-2"), aws.String("id-3")}, - InstanceType: nil, + InstanceIds: []string{"id-1", "id-2", "id-3"}, + InstanceType: "", LaunchTemplateAndOverrides: nil, - Lifecycle: nil, - Platform: nil, + Lifecycle: "", + Platform: "", }, }, }) @@ -322,12 +314,9 @@ var _ = Describe("CreateFleet Batching", func() { atomic.AddInt64(&numErrors, 1) } - var instanceIds []string - for _, rsv := range rsp.Instances { - for _, id := range rsv.InstanceIds { - instanceIds = append(instanceIds, *id) - } - } + instanceIds := lo.Flatten(lo.Map(rsp.Instances, func(rsv ec2types.CreateFleetInstance, _ int) []string { + return rsv.InstanceIds + })) Expect(instanceIds).To(Or(HaveLen(0), HaveLen(1))) if len(instanceIds) == 1 { atomic.AddInt64(&receivedInstance, 1) diff --git a/pkg/batcher/describeinstances.go b/pkg/batcher/describeinstances.go index 961402aa5dcd..2534b0dc4534 100644 --- a/pkg/batcher/describeinstances.go +++ b/pkg/batcher/describeinstances.go @@ -20,20 +20,21 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/hashstructure/v2" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/log" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type DescribeInstancesBatcher struct { batcher *Batcher[ec2.DescribeInstancesInput, ec2.DescribeInstancesOutput] } -func NewDescribeInstancesBatcher(ctx context.Context, ec2api ec2iface.EC2API) *DescribeInstancesBatcher { +func NewDescribeInstancesBatcher(ctx context.Context, ec2api sdk.EC2API) *DescribeInstancesBatcher { options := Options[ec2.DescribeInstancesInput, ec2.DescribeInstancesOutput]{ Name: "describe_instances", IdleTimeout: 100 * time.Millisecond, @@ -61,7 +62,7 @@ func FilterHasher(ctx context.Context, input *ec2.DescribeInstancesInput) uint64 return hash } -func execDescribeInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.DescribeInstancesInput, ec2.DescribeInstancesOutput] { +func execDescribeInstancesBatch(ec2api sdk.EC2API) BatchExecutor[ec2.DescribeInstancesInput, ec2.DescribeInstancesOutput] { return func(ctx context.Context, inputs []*ec2.DescribeInstancesInput) []Result[ec2.DescribeInstancesOutput] { results := make([]Result[ec2.DescribeInstancesOutput], len(inputs)) firstInput := inputs[0] @@ -69,33 +70,37 @@ func execDescribeInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.Descri for _, input := range inputs[1:] { firstInput.InstanceIds = append(firstInput.InstanceIds, input.InstanceIds...) } - missingInstanceIDs := sets.NewString(lo.Map(firstInput.InstanceIds, func(i *string, _ int) string { return *i })...) + missingInstanceIDs := sets.NewString(lo.Map(firstInput.InstanceIds, func(i string, _ int) string { return i })...) + paginator := ec2.NewDescribeInstancesPaginator(ec2api, firstInput) + + for paginator.HasMorePages() { + output, err := paginator.NextPage(ctx) + if err != nil { + break + } - // Execute fully aggregated request - // We don't care about the error here since we'll break up the batch upon any sort of failure - _ = ec2api.DescribeInstancesPagesWithContext(ctx, firstInput, func(dio *ec2.DescribeInstancesOutput, b bool) bool { - for _, r := range dio.Reservations { + for _, r := range output.Reservations { for _, instance := range r.Instances { missingInstanceIDs.Delete(*instance.InstanceId) - // Find all indexes where we are requesting this instance and populate with the result for reqID := range inputs { - if *inputs[reqID].InstanceIds[0] == *instance.InstanceId { - inst := instance // locally scoped to avoid pointer pollution in a range loop + if inputs[reqID].InstanceIds[0] == *instance.InstanceId { + inst := instance results[reqID] = Result[ec2.DescribeInstancesOutput]{Output: &ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{{ + Reservations: []ec2types.Reservation{{ OwnerId: r.OwnerId, RequesterId: r.RequesterId, ReservationId: r.ReservationId, - Instances: []*ec2.Instance{inst}, + Instances: []ec2types.Instance{inst}, }}, }} } } } } - return true - }) + } + + // If we have any missing instanceIDs, we need to describe them individually // Some or all instances may have failed to be described due to eventual consistency or transient zonal issue. // A single instance lookup failure can result in all of an availability zone's instances failing to describe. @@ -106,13 +111,14 @@ func execDescribeInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.Descri go func(instanceID string) { defer wg.Done() // try to execute separately - out, err := ec2api.DescribeInstancesWithContext(ctx, &ec2.DescribeInstancesInput{ + out, err := ec2api.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ Filters: firstInput.Filters, - InstanceIds: []*string{aws.String(instanceID)}}) + InstanceIds: []string{instanceID}, + }) // Find all indexes where we are requesting this instance and populate with the result for reqID := range inputs { - if *inputs[reqID].InstanceIds[0] == instanceID { + if inputs[reqID].InstanceIds[0] == instanceID { results[reqID] = Result[ec2.DescribeInstancesOutput]{Output: out, Err: err} } } diff --git a/pkg/batcher/describeinstances_test.go b/pkg/batcher/describeinstances_test.go index 403e8b17e25b..2c4b47b52b5b 100644 --- a/pkg/batcher/describeinstances_test.go +++ b/pkg/batcher/describeinstances_test.go @@ -19,8 +19,9 @@ import ( "sync" "sync/atomic" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/karpenter-provider-aws/pkg/batcher" "github.com/aws/karpenter-provider-aws/pkg/fake" @@ -40,7 +41,7 @@ var _ = Describe("DescribeInstances Batcher", func() { It("should batch input into a single call", func() { instanceIDs := []string{"i-1", "i-2", "i-3", "i-4", "i-5"} for _, id := range instanceIDs { - fakeEC2API.Instances.Store(id, &ec2.Instance{InstanceId: aws.String(id)}) + fakeEC2API.Instances.Store(id, ec2types.Instance{InstanceId: aws.String(id)}) } var wg sync.WaitGroup @@ -51,7 +52,7 @@ var _ = Describe("DescribeInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).To(BeNil()) atomic.AddInt64(&receivedInstance, 1) @@ -60,7 +61,6 @@ var _ = Describe("DescribeInstances Batcher", func() { }(instanceID) } wg.Wait() - Expect(receivedInstance).To(BeNumerically("==", len(instanceIDs))) Expect(fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Len()).To(BeNumerically("==", 1)) call := fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Pop() @@ -69,21 +69,21 @@ var _ = Describe("DescribeInstances Batcher", func() { It("should batch input correctly when receiving multiple calls with the same instance id", func() { instanceIDs := []string{"i-1", "i-1", "i-1", "i-2", "i-2"} for _, id := range instanceIDs { - fakeEC2API.Instances.Store(id, &ec2.Instance{InstanceId: aws.String(id)}) + fakeEC2API.Instances.Store(id, ec2types.Instance{InstanceId: aws.String(id)}) } var wg sync.WaitGroup - var receivedInstance int64 + var receivedInstance int32 for _, instanceID := range instanceIDs { wg.Add(1) go func(instanceID string) { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).To(BeNil()) - atomic.AddInt64(&receivedInstance, 1) + atomic.AddInt32(&receivedInstance, 1) Expect(rsp.Reservations).To(HaveLen(1)) Expect(rsp.Reservations[0].Instances).To(HaveLen(1)) }(instanceID) @@ -99,9 +99,9 @@ var _ = Describe("DescribeInstances Batcher", func() { instanceIDs := []string{"i-1", "i-2", "i-3"} // Output with only the first Instance fakeEC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{ + Reservations: []ec2types.Reservation{ { - Instances: []*ec2.Instance{ + Instances: []ec2types.Instance{ { InstanceId: aws.String("i-1"), }, @@ -109,53 +109,48 @@ var _ = Describe("DescribeInstances Batcher", func() { }, }, }) - runningFilter := &ec2.Filter{ + runningFilter := ec2types.Filter{ Name: aws.String("instance-state-name"), - Values: []*string{aws.String(ec2.InstanceStateNameRunning)}, + Values: []string{string(ec2types.InstanceStateNameRunning)}, } var wg sync.WaitGroup - var receivedInstance int64 - var numUnfulfilled int64 + var receivedInstance int32 + var numUnfulfilled int32 for _, instanceID := range instanceIDs { wg.Add(1) go func(instanceID string) { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, - Filters: []*ec2.Filter{runningFilter}, + InstanceIds: []string{instanceID}, + Filters: []ec2types.Filter{runningFilter}, }) Expect(err).To(BeNil()) if len(rsp.Reservations) > 0 { Expect(len(rsp.Reservations[0].Instances)).To(BeNumerically("<=", 1)) if len(rsp.Reservations[0].Instances) == 1 { - atomic.AddInt64(&receivedInstance, 1) + atomic.AddInt32(&receivedInstance, 1) } else { - atomic.AddInt64(&numUnfulfilled, 1) + atomic.AddInt32(&numUnfulfilled, 1) } } }(instanceID) } wg.Wait() - // should execute the batched call and then one for each that failed in the batched request Expect(fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Len()).To(BeNumerically("==", 3)) - lastCall := fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Pop() Expect(len(lastCall.InstanceIds)).To(BeNumerically("==", 1)) Expect(len(lastCall.Filters)).To(BeNumerically("==", 1)) Expect(*lastCall.Filters[0].Name).To(Equal("instance-state-name")) - nextToLastCall := fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Pop() Expect(len(nextToLastCall.InstanceIds)).To(BeNumerically("==", 1)) Expect(len(nextToLastCall.Filters)).To(BeNumerically("==", 1)) Expect(*lastCall.Filters[0].Name).To(Equal("instance-state-name")) - firstCall := fakeEC2API.DescribeInstancesBehavior.CalledWithInput.Pop() Expect(len(firstCall.InstanceIds)).To(BeNumerically("==", 3)) Expect(len(firstCall.Filters)).To(BeNumerically("==", 1)) Expect(*lastCall.Filters[0].Name).To(Equal("instance-state-name")) - Expect(receivedInstance).To(BeNumerically("==", 3)) Expect(numUnfulfilled).To(BeNumerically("==", 0)) }) @@ -169,7 +164,7 @@ var _ = Describe("DescribeInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() _, err := cfb.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).ToNot(BeNil()) }(instanceID) diff --git a/pkg/batcher/ec2api.go b/pkg/batcher/ec2api.go index d5a859a4c6a7..c495ae0cfdba 100644 --- a/pkg/batcher/ec2api.go +++ b/pkg/batcher/ec2api.go @@ -17,7 +17,7 @@ package batcher import ( "context" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type EC2API struct { @@ -26,7 +26,7 @@ type EC2API struct { *TerminateInstancesBatcher } -func EC2(ctx context.Context, ec2api ec2iface.EC2API) *EC2API { +func EC2(ctx context.Context, ec2api sdk.EC2API) *EC2API { return &EC2API{ CreateFleetBatcher: NewCreateFleetBatcher(ctx, ec2api), DescribeInstancesBatcher: NewDescribeInstancesBatcher(ctx, ec2api), diff --git a/pkg/batcher/terminateinstances.go b/pkg/batcher/terminateinstances.go index c1d5d6d49c37..2e2ab9ea4d08 100644 --- a/pkg/batcher/terminateinstances.go +++ b/pkg/batcher/terminateinstances.go @@ -20,19 +20,20 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/log" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type TerminateInstancesBatcher struct { batcher *Batcher[ec2.TerminateInstancesInput, ec2.TerminateInstancesOutput] } -func NewTerminateInstancesBatcher(ctx context.Context, ec2api ec2iface.EC2API) *TerminateInstancesBatcher { +func NewTerminateInstancesBatcher(ctx context.Context, ec2api sdk.EC2API) *TerminateInstancesBatcher { options := Options[ec2.TerminateInstancesInput, ec2.TerminateInstancesOutput]{ Name: "terminate_instances", IdleTimeout: 100 * time.Millisecond, @@ -52,7 +53,7 @@ func (b *TerminateInstancesBatcher) TerminateInstances(ctx context.Context, term return result.Output, result.Err } -func execTerminateInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.TerminateInstancesInput, ec2.TerminateInstancesOutput] { +func execTerminateInstancesBatch(ec2api sdk.EC2API) BatchExecutor[ec2.TerminateInstancesInput, ec2.TerminateInstancesOutput] { return func(ctx context.Context, inputs []*ec2.TerminateInstancesInput) []Result[ec2.TerminateInstancesOutput] { results := make([]Result[ec2.TerminateInstancesOutput], len(inputs)) firstInput := inputs[0] @@ -62,11 +63,11 @@ func execTerminateInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.Termi firstInput.InstanceIds = append(firstInput.InstanceIds, input.InstanceIds...) } // Create a set of all instance IDs - stillRunning := sets.NewString(lo.Map(firstInput.InstanceIds, func(i *string, _ int) string { return *i })...) + stillRunning := sets.NewString(lo.Map(firstInput.InstanceIds, func(i string, _ int) string { return i })...) // Execute fully aggregated request // We don't care about the error here since we'll break up the batch upon any sort of failure - output, err := ec2api.TerminateInstancesWithContext(ctx, firstInput) + output, err := ec2api.TerminateInstances(ctx, firstInput) if err != nil { log.FromContext(ctx).Error(err, "failed terminating instances") } @@ -78,15 +79,15 @@ func execTerminateInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.Termi // Check the fulfillment for partial or no fulfillment by checking for missing instance IDs or invalid instance states for _, instanceStateChanges := range output.TerminatingInstances { // Remove all instances that successfully terminated and separate into distinct outputs - if lo.Contains([]string{ec2.InstanceStateNameShuttingDown, ec2.InstanceStateNameTerminated}, *instanceStateChanges.CurrentState.Name) { + if lo.Contains([]string{string(ec2types.InstanceStateNameShuttingDown), string(ec2types.InstanceStateNameTerminated)}, string(instanceStateChanges.CurrentState.Name)) { stillRunning.Delete(*instanceStateChanges.InstanceId) // Find all indexes where we are requesting this instance and populate with the result for reqID := range inputs { - if *inputs[reqID].InstanceIds[0] == *instanceStateChanges.InstanceId { + if inputs[reqID].InstanceIds[0] == *instanceStateChanges.InstanceId { results[reqID] = Result[ec2.TerminateInstancesOutput]{ Output: &ec2.TerminateInstancesOutput{ - TerminatingInstances: []*ec2.InstanceStateChange{{ + TerminatingInstances: []ec2types.InstanceStateChange{{ InstanceId: instanceStateChanges.InstanceId, CurrentState: instanceStateChanges.CurrentState, PreviousState: instanceStateChanges.PreviousState, @@ -107,11 +108,11 @@ func execTerminateInstancesBatch(ec2api ec2iface.EC2API) BatchExecutor[ec2.Termi go func(instanceID string) { defer wg.Done() // try to execute separately - out, err := ec2api.TerminateInstancesWithContext(ctx, &ec2.TerminateInstancesInput{InstanceIds: []*string{aws.String(instanceID)}}) + out, err := ec2api.TerminateInstances(ctx, &ec2.TerminateInstancesInput{InstanceIds: []string{instanceID}}) // Find all indexes where we are requesting this instance and populate with the result for reqID := range inputs { - if *inputs[reqID].InstanceIds[0] == instanceID { + if inputs[reqID].InstanceIds[0] == instanceID { results[reqID] = Result[ec2.TerminateInstancesOutput]{Output: out, Err: err} } } diff --git a/pkg/batcher/terminateinstances_test.go b/pkg/batcher/terminateinstances_test.go index d6b17784da3f..149d6a6dee85 100644 --- a/pkg/batcher/terminateinstances_test.go +++ b/pkg/batcher/terminateinstances_test.go @@ -19,8 +19,9 @@ import ( "sync" "sync/atomic" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/karpenter-provider-aws/pkg/batcher" "github.com/aws/karpenter-provider-aws/pkg/fake" @@ -40,7 +41,7 @@ var _ = Describe("TerminateInstances Batcher", func() { It("should batch input into a single call", func() { instanceIDs := []string{"i-1", "i-2", "i-3", "i-4", "i-5"} for _, id := range instanceIDs { - fakeEC2API.Instances.Store(id, &ec2.Instance{}) + fakeEC2API.Instances.Store(id, ec2types.Instance{}) } var wg sync.WaitGroup @@ -51,7 +52,7 @@ var _ = Describe("TerminateInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.TerminateInstances(ctx, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).To(BeNil()) atomic.AddInt64(&receivedInstance, 1) @@ -68,7 +69,7 @@ var _ = Describe("TerminateInstances Batcher", func() { It("should batch input correctly when receiving multiple calls with the same instance id", func() { instanceIDs := []string{"i-1", "i-1", "i-1", "i-2", "i-2"} for _, id := range instanceIDs { - fakeEC2API.Instances.Store(id, &ec2.Instance{}) + fakeEC2API.Instances.Store(id, ec2types.Instance{}) } var wg sync.WaitGroup @@ -79,7 +80,7 @@ var _ = Describe("TerminateInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.TerminateInstances(ctx, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).To(BeNil()) atomic.AddInt64(&receivedInstance, 1) @@ -97,10 +98,10 @@ var _ = Describe("TerminateInstances Batcher", func() { instanceIDs := []string{"i-1", "i-2", "i-3"} // Output with only the first Terminating Instance fakeEC2API.TerminateInstancesBehavior.Output.Set(&ec2.TerminateInstancesOutput{ - TerminatingInstances: []*ec2.InstanceStateChange{ + TerminatingInstances: []ec2types.InstanceStateChange{ { - PreviousState: &ec2.InstanceState{Name: aws.String(ec2.InstanceStateNameRunning), Code: aws.Int64(16)}, - CurrentState: &ec2.InstanceState{Name: aws.String(ec2.InstanceStateNameShuttingDown), Code: aws.Int64(32)}, + PreviousState: &ec2types.InstanceState{Name: ec2types.InstanceStateNameRunning, Code: aws.Int32(16)}, + CurrentState: &ec2types.InstanceState{Name: ec2types.InstanceStateNameShuttingDown, Code: aws.Int32(32)}, InstanceId: aws.String(instanceIDs[0]), }, }, @@ -114,7 +115,7 @@ var _ = Describe("TerminateInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() rsp, err := cfb.TerminateInstances(ctx, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).To(BeNil()) Expect(len(rsp.TerminatingInstances)).To(BeNumerically("<=", 1)) @@ -148,7 +149,7 @@ var _ = Describe("TerminateInstances Batcher", func() { defer GinkgoRecover() defer wg.Done() _, err := cfb.TerminateInstances(ctx, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(instanceID)}, + InstanceIds: []string{instanceID}, }) Expect(err).ToNot(BeNil()) }(instanceID) diff --git a/pkg/cache/unavailableofferings.go b/pkg/cache/unavailableofferings.go index e909d4fce161..fc78412dfd75 100644 --- a/pkg/cache/unavailableofferings.go +++ b/pkg/cache/unavailableofferings.go @@ -19,8 +19,10 @@ import ( "fmt" "sync/atomic" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/samber/lo" + "github.com/patrickmn/go-cache" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -46,13 +48,13 @@ func NewUnavailableOfferings() *UnavailableOfferings { } // IsUnavailable returns true if the offering appears in the cache -func (u *UnavailableOfferings) IsUnavailable(instanceType, zone, capacityType string) bool { +func (u *UnavailableOfferings) IsUnavailable(instanceType ec2types.InstanceType, zone, capacityType string) bool { _, found := u.cache.Get(u.key(instanceType, zone, capacityType)) return found } // MarkUnavailable communicates recently observed temporary capacity shortages in the provided offerings -func (u *UnavailableOfferings) MarkUnavailable(ctx context.Context, unavailableReason, instanceType, zone, capacityType string) { +func (u *UnavailableOfferings) MarkUnavailable(ctx context.Context, unavailableReason string, instanceType ec2types.InstanceType, zone, capacityType string) { // even if the key is already in the cache, we still need to call Set to extend the cached entry's TTL log.FromContext(ctx).WithValues( "reason", unavailableReason, @@ -64,13 +66,13 @@ func (u *UnavailableOfferings) MarkUnavailable(ctx context.Context, unavailableR atomic.AddUint64(&u.SeqNum, 1) } -func (u *UnavailableOfferings) MarkUnavailableForFleetErr(ctx context.Context, fleetErr *ec2.CreateFleetError, capacityType string) { - instanceType := aws.StringValue(fleetErr.LaunchTemplateAndOverrides.Overrides.InstanceType) - zone := aws.StringValue(fleetErr.LaunchTemplateAndOverrides.Overrides.AvailabilityZone) - u.MarkUnavailable(ctx, aws.StringValue(fleetErr.ErrorCode), instanceType, zone, capacityType) +func (u *UnavailableOfferings) MarkUnavailableForFleetErr(ctx context.Context, fleetErr ec2types.CreateFleetError, capacityType string) { + instanceType := fleetErr.LaunchTemplateAndOverrides.Overrides.InstanceType + zone := aws.ToString(fleetErr.LaunchTemplateAndOverrides.Overrides.AvailabilityZone) + u.MarkUnavailable(ctx, lo.FromPtr(fleetErr.ErrorCode), instanceType, zone, capacityType) } -func (u *UnavailableOfferings) Delete(instanceType string, zone string, capacityType string) { +func (u *UnavailableOfferings) Delete(instanceType ec2types.InstanceType, zone string, capacityType string) { u.cache.Delete(u.key(instanceType, zone, capacityType)) } @@ -79,6 +81,6 @@ func (u *UnavailableOfferings) Flush() { } // key returns the cache key for all offerings in the cache -func (u *UnavailableOfferings) key(instanceType string, zone string, capacityType string) string { +func (u *UnavailableOfferings) key(instanceType ec2types.InstanceType, zone string, capacityType string) string { return fmt.Sprintf("%s:%s:%s", capacityType, instanceType, zone) } diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 723b1b72f3b6..2370692c42f2 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -20,7 +20,7 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/status" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" @@ -107,7 +107,7 @@ func (c *CloudProvider) Create(ctx context.Context, nodeClaim *karpv1.NodeClaim) return nil, fmt.Errorf("creating instance, %w", err) } instanceType, _ := lo.Find(instanceTypes, func(i *cloudprovider.InstanceType) bool { - return i.Name == instance.Type + return i.Name == string(instance.Type) }) nc := c.instanceToNodeClaim(instance, instanceType, nodeClass) nc.Annotations = lo.Assign(nc.Annotations, map[string]string{ @@ -279,7 +279,7 @@ func (c *CloudProvider) resolveInstanceTypeFromInstance(ctx context.Context, ins return nil, client.IgnoreNotFound(fmt.Errorf("resolving nodeclass, %w", err)) } instanceType, _ := lo.Find(instanceTypes, func(i *cloudprovider.InstanceType) bool { - return i.Name == instance.Type + return i.Name == string(instance.Type) }) return instanceType, nil } @@ -349,7 +349,7 @@ func (c *CloudProvider) instanceToNodeClaim(i *instance.Instance, instanceType * nodeClaim.Annotations = annotations nodeClaim.CreationTimestamp = metav1.Time{Time: i.LaunchTime} // Set the deletionTimestamp to be the current time if the instance is currently terminating - if i.State == ec2.InstanceStateNameShuttingDown || i.State == ec2.InstanceStateNameTerminated { + if i.State == ec2types.InstanceStateNameShuttingDown || i.State == ec2types.InstanceStateNameTerminated { nodeClaim.DeletionTimestamp = &metav1.Time{Time: time.Now()} } nodeClaim.Status.ProviderID = fmt.Sprintf("aws:///%s/%s", i.Zone, i.ID) diff --git a/pkg/cloudprovider/suite_test.go b/pkg/cloudprovider/suite_test.go index 5de4ea441c8d..b27f1e3a7606 100644 --- a/pkg/cloudprovider/suite_test.go +++ b/pkg/cloudprovider/suite_test.go @@ -32,8 +32,10 @@ import ( "k8s.io/client-go/tools/record" clock "k8s.io/utils/clock/testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + opstatus "github.com/awslabs/operatorpkg/status" "github.com/imdario/mergo" "github.com/samber/lo" @@ -284,7 +286,7 @@ var _ = Describe("CloudProvider", func() { ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CreateFleetBehavior.CalledWithInput.Len()).To(Equal(1)) createFleetInput := awsEnv.EC2API.CreateFleetBehavior.CalledWithInput.Pop() - Expect(aws.StringValue(createFleetInput.Context)).To(Equal(contextID)) + Expect(aws.ToString(createFleetInput.Context)).To(Equal(contextID)) }) It("should default to no EC2 Context", func() { ExpectApplied(ctx, env.Client, nodePool, nodeClass) @@ -304,13 +306,13 @@ var _ = Describe("CloudProvider", func() { // instances to meet the minimum requirement. instances := fake.MakeInstances() instances, _ = fake.MakeUniqueInstancesAndFamilies(instances, 2) - instances[0].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(1)} - instances[1].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(8)} + instances[0].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(1)} + instances[1].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(8)} awsEnv.EC2API.DescribeInstanceTypesOutput.Set(&ec2.DescribeInstanceTypesOutput{InstanceTypes: instances}) awsEnv.EC2API.DescribeInstanceTypeOfferingsOutput.Set(&ec2.DescribeInstanceTypeOfferingsOutput{InstanceTypeOfferings: fake.MakeInstanceOfferings(instances)}) now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), InstanceType: instances[0].InstanceType, @@ -328,7 +330,7 @@ var _ = Describe("CloudProvider", func() { Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) Expect(awsEnv.PricingProvider.UpdateSpotPricing(ctx)).To(Succeed()) - instanceNames := lo.Map(instances, func(info *ec2.InstanceTypeInfo, _ int) string { return *info.InstanceType }) + instanceNames := lo.Map(instances, func(info ec2types.InstanceTypeInfo, _ int) string { return string(info.InstanceType) }) // Define NodePool that has minValues on instance-type requirement. nodePool = coretest.NodePool(karpv1.NodePool{ @@ -392,7 +394,7 @@ var _ = Describe("CloudProvider", func() { uniqueInstanceTypes := sets.Set[string]{} for _, launchTemplateConfig := range createFleetInput.LaunchTemplateConfigs { for _, override := range launchTemplateConfig.Overrides { - uniqueInstanceTypes.Insert(*override.InstanceType) + uniqueInstanceTypes.Insert(string(override.InstanceType)) } } // This ensures that we have sent the minimum number of requirements defined in the NodePool. @@ -402,13 +404,13 @@ var _ = Describe("CloudProvider", func() { // Create fake InstanceTypes where one instances can fit 2 pods and another one can fit only 1 pod. instances := fake.MakeInstances() instances, _ = fake.MakeUniqueInstancesAndFamilies(instances, 2) - instances[0].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(1)} - instances[1].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(8)} + instances[0].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(1)} + instances[1].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(8)} awsEnv.EC2API.DescribeInstanceTypesOutput.Set(&ec2.DescribeInstanceTypesOutput{InstanceTypes: instances}) awsEnv.EC2API.DescribeInstanceTypeOfferingsOutput.Set(&ec2.DescribeInstanceTypeOfferingsOutput{InstanceTypeOfferings: fake.MakeInstanceOfferings(instances)}) now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), InstanceType: instances[0].InstanceType, @@ -426,7 +428,7 @@ var _ = Describe("CloudProvider", func() { Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) Expect(awsEnv.PricingProvider.UpdateSpotPricing(ctx)).To(Succeed()) - instanceNames := lo.Map(instances, func(info *ec2.InstanceTypeInfo, _ int) string { return *info.InstanceType }) + instanceNames := lo.Map(instances, func(info ec2types.InstanceTypeInfo, _ int) string { return string(info.InstanceType) }) // Define NodePool that has minValues on instance-type requirement. nodePool = coretest.NodePool(karpv1.NodePool{ @@ -490,7 +492,7 @@ var _ = Describe("CloudProvider", func() { uniqueInstanceTypes := sets.Set[string]{} for _, launchTemplateConfig := range createFleetInput.LaunchTemplateConfigs { for _, override := range launchTemplateConfig.Overrides { - uniqueInstanceTypes.Insert(*override.InstanceType) + uniqueInstanceTypes.Insert(string(override.InstanceType)) } } // This ensures that we have sent the minimum number of requirements defined in the NodePool. @@ -500,14 +502,14 @@ var _ = Describe("CloudProvider", func() { // Create fake InstanceTypes where 2 instances can fit 2 pods individually and one can fit only 1 pod. instances := fake.MakeInstances() uniqInstanceTypes, instanceFamilies := fake.MakeUniqueInstancesAndFamilies(instances, 3) - uniqInstanceTypes[0].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(1)} - uniqInstanceTypes[1].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(4)} - uniqInstanceTypes[2].VCpuInfo = &ec2.VCpuInfo{DefaultVCpus: aws.Int64(8)} + uniqInstanceTypes[0].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(1)} + uniqInstanceTypes[1].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(4)} + uniqInstanceTypes[2].VCpuInfo = &ec2types.VCpuInfo{DefaultVCpus: aws.Int32(8)} awsEnv.EC2API.DescribeInstanceTypesOutput.Set(&ec2.DescribeInstanceTypesOutput{InstanceTypes: uniqInstanceTypes}) awsEnv.EC2API.DescribeInstanceTypeOfferingsOutput.Set(&ec2.DescribeInstanceTypeOfferingsOutput{InstanceTypeOfferings: fake.MakeInstanceOfferings(uniqInstanceTypes)}) now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), InstanceType: uniqInstanceTypes[0].InstanceType, @@ -531,7 +533,7 @@ var _ = Describe("CloudProvider", func() { Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypes(ctx)).To(Succeed()) Expect(awsEnv.InstanceTypesProvider.UpdateInstanceTypeOfferings(ctx)).To(Succeed()) Expect(awsEnv.PricingProvider.UpdateSpotPricing(ctx)).To(Succeed()) - instanceNames := lo.Map(uniqInstanceTypes, func(info *ec2.InstanceTypeInfo, _ int) string { return *info.InstanceType }) + instanceNames := lo.Map(uniqInstanceTypes, func(info ec2types.InstanceTypeInfo, _ int) string { return string(info.InstanceType) }) // Define NodePool that has minValues in multiple requirements. nodePool = coretest.NodePool(karpv1.NodePool{ @@ -594,8 +596,8 @@ var _ = Describe("CloudProvider", func() { uniqueInstanceTypes, uniqueInstanceFamilies := sets.Set[string]{}, sets.Set[string]{} for _, launchTemplateConfig := range createFleetInput.LaunchTemplateConfigs { for _, override := range launchTemplateConfig.Overrides { - uniqueInstanceTypes.Insert(*override.InstanceType) - uniqueInstanceFamilies.Insert(strings.Split(*override.InstanceType, ".")[0]) + uniqueInstanceTypes.Insert(string(override.InstanceType)) + uniqueInstanceFamilies.Insert(strings.Split(string(override.InstanceType), ".")[0]) } } // Ensure that there are at least minimum number of unique instance types as per the requirement in the CreateFleet request. @@ -608,7 +610,7 @@ var _ = Describe("CloudProvider", func() { var armAMIID, amdAMIID string var validSecurityGroup string var selectedInstanceType *corecloudprovider.InstanceType - var instance *ec2.Instance + var instance ec2types.Instance var validSubnet1 string var validSubnet2 string BeforeEach(func() { @@ -617,13 +619,13 @@ var _ = Describe("CloudProvider", func() { validSubnet1 = fake.SubnetID() validSubnet2 = fake.SubnetID() awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(coretest.RandomName()), ImageId: aws.String(armAMIID), - Architecture: aws.String("arm64"), + Architecture: "arm64", CreationDate: aws.String("2022-08-15T12:00:00Z"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("ami-key-1"), Value: aws.String("ami-value-1"), @@ -633,9 +635,9 @@ var _ = Describe("CloudProvider", func() { { Name: aws.String(coretest.RandomName()), ImageId: aws.String(amdAMIID), - Architecture: aws.String("x86_64"), + Architecture: "x86_64", CreationDate: aws.String("2022-08-15T12:00:00Z"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("ami-key-2"), Value: aws.String("ami-value-2"), @@ -645,11 +647,11 @@ var _ = Describe("CloudProvider", func() { }, }) awsEnv.EC2API.DescribeSecurityGroupsOutput.Set(&ec2.DescribeSecurityGroupsOutput{ - SecurityGroups: []*ec2.SecurityGroup{ + SecurityGroups: []ec2types.SecurityGroup{ { GroupId: aws.String(validSecurityGroup), GroupName: aws.String("test-securitygroup"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("sg-key"), Value: aws.String("sg-value"), @@ -659,11 +661,11 @@ var _ = Describe("CloudProvider", func() { }, }) awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{ - Subnets: []*ec2.Subnet{ + Subnets: []ec2types.Subnet{ { SubnetId: aws.String(validSubnet1), AvailabilityZone: aws.String("zone-1"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("sn-key-1"), Value: aws.String("sn-value-1"), @@ -673,7 +675,7 @@ var _ = Describe("CloudProvider", func() { { SubnetId: aws.String(validSubnet2), AvailabilityZone: aws.String("zone-2"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("sn-key-2"), Value: aws.String("sn-value-2"), @@ -726,22 +728,22 @@ var _ = Describe("CloudProvider", func() { Expect(ok).To(BeTrue()) // Create the instance we want returned from the EC2 API - instance = &ec2.Instance{ + instance = ec2types.Instance{ ImageId: aws.String(amdAMIID), - InstanceType: aws.String(selectedInstanceType.Name), + InstanceType: ec2types.InstanceType(selectedInstanceType.Name), SubnetId: aws.String(validSubnet1), SpotInstanceRequestId: aws.String(coretest.RandomName()), - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, InstanceId: aws.String(fake.InstanceID()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String("test-zone-1a"), }, - SecurityGroups: []*ec2.GroupIdentifier{{GroupId: aws.String(validSecurityGroup)}}, + SecurityGroups: []ec2types.GroupIdentifier{{GroupId: aws.String(validSecurityGroup)}}, } awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{{Instances: []*ec2.Instance{instance}}}, + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, }) nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{ v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), @@ -769,6 +771,9 @@ var _ = Describe("CloudProvider", func() { It("should return drifted if the AMI is not valid", func() { // Instance is a reference to what we return in the GetInstances call instance.ImageId = aws.String(fake.ImageID()) + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).ToNot(HaveOccurred()) Expect(isDrifted).To(Equal(cloudprovider.AMIDrift)) @@ -777,7 +782,10 @@ var _ = Describe("CloudProvider", func() { // Instance is a reference to what we return in the GetInstances call instance.ImageId = aws.String(fake.ImageID()) instance.SubnetId = aws.String(fake.SubnetID()) - instance.SecurityGroups = []*ec2.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} + instance.SecurityGroups = []ec2types.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) // Assign a fake hash nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{ v1.AnnotationEC2NodeClassHash: "abcdefghijkl", @@ -789,6 +797,9 @@ var _ = Describe("CloudProvider", func() { }) It("should return drifted if the subnet is not valid", func() { instance.SubnetId = aws.String(fake.SubnetID()) + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).ToNot(HaveOccurred()) Expect(isDrifted).To(Equal(cloudprovider.SubnetDrift)) @@ -809,20 +820,26 @@ var _ = Describe("CloudProvider", func() { nodeClass.Status.SecurityGroups = []v1.SecurityGroup{} ExpectApplied(ctx, env.Client, nodeClass) // Instance is a reference to what we return in the GetInstances call - instance.SecurityGroups = []*ec2.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} + instance.SecurityGroups = []ec2types.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} _, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).To(HaveOccurred()) }) It("should return drifted if the instance security groups doesn't match the discovered values", func() { // Instance is a reference to what we return in the GetInstances call - instance.SecurityGroups = []*ec2.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} + instance.SecurityGroups = []ec2types.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}} + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).ToNot(HaveOccurred()) Expect(isDrifted).To(Equal(cloudprovider.SecurityGroupDrift)) }) It("should return drifted if there are more instance security groups present than in the discovered values", func() { // Instance is a reference to what we return in the GetInstances call - instance.SecurityGroups = []*ec2.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}, {GroupId: aws.String(validSecurityGroup)}} + instance.SecurityGroups = []ec2types.GroupIdentifier{{GroupId: aws.String(fake.SecurityGroupID())}, {GroupId: aws.String(validSecurityGroup)}} + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).ToNot(HaveOccurred()) Expect(isDrifted).To(Equal(cloudprovider.SecurityGroupDrift)) @@ -861,7 +878,7 @@ var _ = Describe("CloudProvider", func() { }) It("should error if the underlying NodeClaim doesn't exist", func() { awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{{Instances: []*ec2.Instance{}}}, + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{}}}, }) _, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).To(HaveOccurred()) @@ -878,6 +895,9 @@ var _ = Describe("CloudProvider", func() { }, } instance.ImageId = aws.String(armAMIID) + awsEnv.EC2API.DescribeInstancesBehavior.Output.Set(&ec2.DescribeInstancesOutput{ + Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, + }) ExpectApplied(ctx, env.Client, nodeClass) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) Expect(err).ToNot(HaveOccurred()) @@ -1108,12 +1128,12 @@ var _ = Describe("CloudProvider", func() { foundNonGPULT := false for _, v := range input.LaunchTemplateConfigs { for _, ov := range v.Overrides { - if *ov.InstanceType == "m5.large" { + if ov.InstanceType == "m5.large" { foundNonGPULT = true Expect(v.Overrides).To(ContainElements( - &ec2.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test1"), ImageId: ov.ImageId, InstanceType: aws.String("m5.large"), AvailabilityZone: aws.String("test-zone-1a")}, - &ec2.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test2"), ImageId: ov.ImageId, InstanceType: aws.String("m5.large"), AvailabilityZone: aws.String("test-zone-1b")}, - &ec2.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test3"), ImageId: ov.ImageId, InstanceType: aws.String("m5.large"), AvailabilityZone: aws.String("test-zone-1c")}, + ec2types.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test1"), ImageId: ov.ImageId, InstanceType: "m5.large", AvailabilityZone: aws.String("test-zone-1a")}, + ec2types.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test2"), ImageId: ov.ImageId, InstanceType: "m5.large", AvailabilityZone: aws.String("test-zone-1b")}, + ec2types.FleetLaunchTemplateOverridesRequest{SubnetId: aws.String("subnet-test3"), ImageId: ov.ImageId, InstanceType: "m5.large", AvailabilityZone: aws.String("test-zone-1c")}, )) } } @@ -1122,11 +1142,11 @@ var _ = Describe("CloudProvider", func() { }) It("should launch instances into subnet with the most available IP addresses", func() { awsEnv.SubnetCache.Flush() - awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []*ec2.Subnet{ - {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(10), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, - {SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(100), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}}, + awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []ec2types.Subnet{ + {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(10), + Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, + {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) ExpectApplied(ctx, env.Client, nodePool, nodeClass) @@ -1139,11 +1159,11 @@ var _ = Describe("CloudProvider", func() { }) It("should launch instances into subnet with the most available IP addresses in-between cache refreshes", func() { awsEnv.SubnetCache.Flush() - awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []*ec2.Subnet{ - {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(10), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, - {SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(11), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}}, + awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []ec2types.Subnet{ + {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(10), + Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, + {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) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ @@ -1167,9 +1187,9 @@ var _ = Describe("CloudProvider", func() { Expect(fake.SubnetsFromFleetRequest(createFleetInput)).To(ConsistOf("test-subnet-1")) }) It("should update in-flight IPs when a CreateFleet error occurs", func() { - awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []*ec2.Subnet{ - {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailableIpAddressCount: aws.Int64(10), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, + awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []ec2types.Subnet{ + {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailableIpAddressCount: aws.Int32(10), + Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, }}) pod1 := coretest.UnschedulablePod(coretest.PodOptions{NodeSelector: map[string]string{corev1.LabelTopologyZone: "test-zone-1a"}}) ExpectApplied(ctx, env.Client, nodePool, nodeClass, pod1) @@ -1178,11 +1198,11 @@ var _ = Describe("CloudProvider", func() { Expect(len(bindings)).To(Equal(0)) }) It("should launch instances into subnets that are excluded by another NodePool", func() { - awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []*ec2.Subnet{ - {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(10), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, - {SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1b"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(100), - Tags: []*ec2.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}}, + awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []ec2types.Subnet{ + {SubnetId: aws.String("test-subnet-1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(10), + Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-1")}}}, + {SubnetId: aws.String("test-subnet-2"), AvailabilityZone: aws.String("test-zone-1b"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(100), + Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("test-subnet-2")}}}, }}) nodeClass.Spec.SubnetSelectorTerms = []v1.SubnetSelectorTerm{{Tags: map[string]string{"Name": "test-subnet-1"}}} ExpectApplied(ctx, env.Client, nodePool, nodeClass) diff --git a/pkg/controllers/controllers.go b/pkg/controllers/controllers.go index 74db0f796d07..19900d385180 100644 --- a/pkg/controllers/controllers.go +++ b/pkg/controllers/controllers.go @@ -23,6 +23,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/karpenter/pkg/cloudprovider" + "github.com/aws/aws-sdk-go-v2/aws" + v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" nodeclasshash "github.com/aws/karpenter-provider-aws/pkg/controllers/nodeclass/hash" nodeclassstatus "github.com/aws/karpenter-provider-aws/pkg/controllers/nodeclass/status" @@ -34,7 +36,6 @@ import ( "github.com/aws/karpenter-provider-aws/pkg/providers/launchtemplate" servicesqs "github.com/aws/aws-sdk-go-v2/service/sqs" - "github.com/aws/aws-sdk-go/aws/session" "github.com/samber/lo" "k8s.io/utils/clock" "sigs.k8s.io/controller-runtime/pkg/client" @@ -54,14 +55,12 @@ import ( "github.com/aws/karpenter-provider-aws/pkg/providers/securitygroup" "github.com/aws/karpenter-provider-aws/pkg/providers/sqs" "github.com/aws/karpenter-provider-aws/pkg/providers/subnet" - - config "github.com/aws/aws-sdk-go-v2/config" ) func NewControllers( ctx context.Context, mgr manager.Manager, - sess *session.Session, + cfg aws.Config, clk clock.Clock, kubeClient client.Client, recorder events.Recorder, @@ -75,8 +74,7 @@ func NewControllers( pricingProvider pricing.Provider, amiProvider amifamily.Provider, launchTemplateProvider launchtemplate.Provider, - instanceTypeProvider *instancetype.DefaultProvider, -) []controller.Controller { + instanceTypeProvider *instancetype.DefaultProvider) []controller.Controller { controllers := []controller.Controller{ nodeclasshash.NewController(kubeClient), nodeclassstatus.NewController(kubeClient, subnetProvider, securityGroupProvider, amiProvider, instanceProfileProvider, launchTemplateProvider), @@ -90,7 +88,6 @@ func NewControllers( status.NewController[*v1.EC2NodeClass](kubeClient, mgr.GetEventRecorderFor("karpenter")), } if options.FromContext(ctx).InterruptionQueue != "" { - cfg := lo.Must(config.LoadDefaultConfig(ctx, config.WithRetryMaxAttempts(3))) sqsapi := servicesqs.NewFromConfig(cfg) out := lo.Must(sqsapi.GetQueueUrl(ctx, &servicesqs.GetQueueUrlInput{QueueName: lo.ToPtr(options.FromContext(ctx).InterruptionQueue)})) controllers = append(controllers, interruption.NewController(kubeClient, clk, recorder, lo.Must(sqs.NewDefaultProvider(sqsapi, lo.FromPtr(out.QueueUrl))), unavailableOfferings)) diff --git a/pkg/controllers/interruption/controller.go b/pkg/controllers/interruption/controller.go index 980e9e002492..ea069255c9f5 100644 --- a/pkg/controllers/interruption/controller.go +++ b/pkg/controllers/interruption/controller.go @@ -22,6 +22,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "sigs.k8s.io/karpenter/pkg/metrics" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types" "github.com/awslabs/operatorpkg/singleton" "go.uber.org/multierr" @@ -196,7 +197,7 @@ func (c *Controller) handleNodeClaim(ctx context.Context, msg messages.Message, zone := nodeClaim.Labels[corev1.LabelTopologyZone] instanceType := nodeClaim.Labels[corev1.LabelInstanceTypeStable] if zone != "" && instanceType != "" { - c.unavailableOfferingsCache.MarkUnavailable(ctx, string(msg.Kind()), instanceType, zone, karpv1.CapacityTypeSpot) + c.unavailableOfferingsCache.MarkUnavailable(ctx, string(msg.Kind()), ec2types.InstanceType(instanceType), zone, karpv1.CapacityTypeSpot) } } if action != NoAction { diff --git a/pkg/controllers/interruption/interruption_benchmark_test.go b/pkg/controllers/interruption/interruption_benchmark_test.go index 307d6bdb6e55..6e05949f02c1 100644 --- a/pkg/controllers/interruption/interruption_benchmark_test.go +++ b/pkg/controllers/interruption/interruption_benchmark_test.go @@ -26,12 +26,8 @@ import ( "github.com/avast/retry-go" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" servicesqs "github.com/aws/aws-sdk-go-v2/service/sqs" - awsclient "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/sqs/sqsiface" "github.com/go-logr/zapr" "github.com/samber/lo" "go.uber.org/multierr" @@ -163,18 +159,13 @@ func benchmarkNotificationController(b *testing.B, messageCount int) { type providerSet struct { kubeClient client.Client - sqsAPI sqsiface.SQSAPI + sqsAPI sqs.Client sqsProvider sqs.Provider } func newProviders(ctx context.Context, kubeClient client.Client) providerSet { - sess := session.Must(session.NewSession( - request.WithRetryer( - &aws.Config{STSRegionalEndpoint: endpoints.RegionalSTSEndpoint}, - awsclient.DefaultRetryer{NumMaxRetries: awsclient.DefaultRetryerMaxNumRetries}, - ), - )) - sqsAPI := servicesqs.New(sess) + cfg := lo.Must(config.LoadDefaultConfig(ctx)) + sqsAPI := servicesqs.New(cfg) out := lo.Must(sqsAPI.GetQueueUrlWithContext(ctx, &servicesqs.GetQueueUrlInput{QueueName: lo.ToPtr(options.FromContext(ctx).InterruptionQueue)})) return providerSet{ kubeClient: kubeClient, diff --git a/pkg/controllers/nodeclaim/garbagecollection/suite_test.go b/pkg/controllers/nodeclaim/garbagecollection/suite_test.go index d72ff22e0081..db2b76bf6f2b 100644 --- a/pkg/controllers/nodeclaim/garbagecollection/suite_test.go +++ b/pkg/controllers/nodeclaim/garbagecollection/suite_test.go @@ -23,8 +23,8 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -78,7 +78,7 @@ var _ = BeforeEach(func() { }) var _ = Describe("GarbageCollection", func() { - var instance *ec2.Instance + var instance *ec2types.Instance var nodeClass *v1.EC2NodeClass var providerID string @@ -99,11 +99,11 @@ var _ = Describe("GarbageCollection", func() { }, }, }) - instance = &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + instance = &ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), @@ -122,11 +122,11 @@ var _ = Describe("GarbageCollection", func() { }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", } }) AfterEach(func() { @@ -136,7 +136,7 @@ var _ = Describe("GarbageCollection", func() { It("should delete an instance if there is no NodeClaim owner", func() { // Launch time was 1m ago instance.LaunchTime = aws.Time(time.Now().Add(-time.Minute)) - awsEnv.EC2API.Instances.Store(aws.StringValue(instance.InstanceId), instance) + awsEnv.EC2API.Instances.Store(aws.ToString(instance.InstanceId), *instance) ExpectSingletonReconciled(ctx, garbageCollectionController) _, err := cloudProvider.Get(ctx, providerID) @@ -146,7 +146,7 @@ var _ = Describe("GarbageCollection", func() { It("should delete an instance along with the node if there is no NodeClaim owner (to quicken scheduling)", func() { // Launch time was 1m ago instance.LaunchTime = aws.Time(time.Now().Add(-time.Minute)) - awsEnv.EC2API.Instances.Store(aws.StringValue(instance.InstanceId), instance) + awsEnv.EC2API.Instances.Store(aws.ToString(instance.InstanceId), *instance) node := coretest.Node(coretest.NodeOptions{ ProviderID: providerID, @@ -167,11 +167,11 @@ var _ = Describe("GarbageCollection", func() { instanceID := fake.InstanceID() awsEnv.EC2API.Instances.Store( instanceID, - &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), @@ -190,13 +190,13 @@ var _ = Describe("GarbageCollection", func() { }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, // Launch time was 1m ago LaunchTime: aws.Time(time.Now().Add(-time.Minute)), InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", }, ) ids = append(ids, instanceID) @@ -225,24 +225,24 @@ var _ = Describe("GarbageCollection", func() { instanceID := fake.InstanceID() awsEnv.EC2API.Instances.Store( instanceID, - &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, // Launch time was 1m ago LaunchTime: aws.Time(time.Now().Add(-time.Minute)), InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", }, ) nodeClaim := coretest.NodeClaim(karpv1.NodeClaim{ @@ -283,7 +283,7 @@ var _ = Describe("GarbageCollection", func() { It("should not delete an instance if it is within the NodeClaim resolution window (1m)", func() { // Launch time just happened instance.LaunchTime = aws.Time(time.Now()) - awsEnv.EC2API.Instances.Store(aws.StringValue(instance.InstanceId), instance) + awsEnv.EC2API.Instances.Store(aws.ToString(instance.InstanceId), *instance) ExpectSingletonReconciled(ctx, garbageCollectionController) _, err := cloudProvider.Get(ctx, providerID) @@ -291,13 +291,13 @@ var _ = Describe("GarbageCollection", func() { }) It("should not delete an instance if it was not launched by a NodeClaim", func() { // Remove the "karpenter.sh/nodepool" tag (this isn't launched by a machine) - instance.Tags = lo.Reject(instance.Tags, func(t *ec2.Tag, _ int) bool { - return aws.StringValue(t.Key) == karpv1.NodePoolLabelKey + instance.Tags = lo.Reject(instance.Tags, func(t ec2types.Tag, _ int) bool { + return aws.ToString(t.Key) == karpv1.NodePoolLabelKey }) // Launch time was 1m ago instance.LaunchTime = aws.Time(time.Now().Add(-time.Minute)) - awsEnv.EC2API.Instances.Store(aws.StringValue(instance.InstanceId), instance) + awsEnv.EC2API.Instances.Store(aws.ToString(instance.InstanceId), *instance) ExpectSingletonReconciled(ctx, garbageCollectionController) _, err := cloudProvider.Get(ctx, providerID) @@ -306,7 +306,7 @@ var _ = Describe("GarbageCollection", func() { It("should not delete the instance or node if it already has a NodeClaim that matches it", func() { // Launch time was 1m ago instance.LaunchTime = aws.Time(time.Now().Add(-time.Minute)) - awsEnv.EC2API.Instances.Store(aws.StringValue(instance.InstanceId), instance) + awsEnv.EC2API.Instances.Store(aws.ToString(instance.InstanceId), *instance) nodeClaim := coretest.NodeClaim(karpv1.NodeClaim{ Spec: karpv1.NodeClaimSpec{ @@ -338,11 +338,11 @@ var _ = Describe("GarbageCollection", func() { instanceID := fake.InstanceID() awsEnv.EC2API.Instances.Store( instanceID, - &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), @@ -357,13 +357,13 @@ var _ = Describe("GarbageCollection", func() { }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, // Launch time was 1m ago LaunchTime: aws.Time(time.Now().Add(-time.Minute)), InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", }, ) nodeClaim := coretest.NodeClaim(karpv1.NodeClaim{ diff --git a/pkg/controllers/nodeclaim/tagging/suite_test.go b/pkg/controllers/nodeclaim/tagging/suite_test.go index a2b2dfa872e6..0005e4ce8a39 100644 --- a/pkg/controllers/nodeclaim/tagging/suite_test.go +++ b/pkg/controllers/nodeclaim/tagging/suite_test.go @@ -21,8 +21,8 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/apimachinery/pkg/apis/meta/v1" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" @@ -75,14 +75,14 @@ var _ = AfterEach(func() { }) var _ = Describe("TaggingController", func() { - var ec2Instance *ec2.Instance + var ec2Instance ec2types.Instance BeforeEach(func() { - ec2Instance = &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2Instance = ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), @@ -97,14 +97,14 @@ var _ = Describe("TaggingController", func() { }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, InstanceId: aws.String(fake.InstanceID()), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", } - awsEnv.EC2API.Instances.Store(*ec2Instance.InstanceId, ec2Instance) + awsEnv.EC2API.Instances.Store(aws.ToString(ec2Instance.InstanceId), ec2Instance) }) It("shouldn't tag instances without a Node", func() { @@ -117,7 +117,7 @@ var _ = Describe("TaggingController", func() { ExpectApplied(ctx, env.Client, nodeClaim) ExpectObjectReconciled(ctx, env.Client, taggingController, nodeClaim) Expect(nodeClaim.Annotations).To(Not(HaveKey(v1.AnnotationInstanceTagged))) - Expect(lo.ContainsBy(ec2Instance.Tags, func(tag *ec2.Tag) bool { + Expect(lo.ContainsBy(ec2Instance.Tags, func(tag ec2types.Tag) bool { return *tag.Key == v1.TagName })).To(BeFalse()) }) @@ -133,8 +133,8 @@ var _ = Describe("TaggingController", func() { ExpectApplied(ctx, env.Client, nodeClaim) ExpectObjectReconciled(ctx, env.Client, taggingController, nodeClaim) Expect(nodeClaim.Annotations).To(Not(HaveKey(v1.AnnotationInstanceTagged))) - Expect(lo.ContainsBy(ec2Instance.Tags, func(tag *ec2.Tag) bool { - return *tag.Key == v1.TagName + Expect(lo.ContainsBy(ec2Instance.Tags, func(tag ec2types.Tag) bool { + return tag.Key == &v1.TagName })).To(BeFalse()) }) @@ -180,8 +180,8 @@ var _ = Describe("TaggingController", func() { Expect(env.Client.Delete(ctx, nodeClaim)).To(Succeed()) ExpectObjectReconciled(ctx, env.Client, taggingController, nodeClaim) Expect(nodeClaim.Annotations).To(Not(HaveKey(v1.AnnotationInstanceTagged))) - Expect(lo.ContainsBy(ec2Instance.Tags, func(tag *ec2.Tag) bool { - return *tag.Key == v1.TagName + Expect(lo.ContainsBy(ec2Instance.Tags, func(tag ec2types.Tag) bool { + return tag.Key == &v1.TagName })).To(BeFalse()) }) @@ -196,12 +196,12 @@ var _ = Describe("TaggingController", func() { }) for _, tag := range customTags { - ec2Instance.Tags = append(ec2Instance.Tags, &ec2.Tag{ + ec2Instance.Tags = append(ec2Instance.Tags, ec2types.Tag{ Key: aws.String(tag), Value: aws.String("custom-tag"), }) } - awsEnv.EC2API.Instances.Store(*ec2Instance.InstanceId, ec2Instance) + awsEnv.EC2API.Instances.Store(aws.ToString(ec2Instance.InstanceId), ec2Instance) ExpectApplied(ctx, env.Client, nodeClaim) ExpectObjectReconciled(ctx, env.Client, taggingController, nodeClaim) @@ -213,7 +213,9 @@ var _ = Describe("TaggingController", func() { v1.TagNodeClaim: nodeClaim.Name, v1.EKSClusterNameTagKey: options.FromContext(ctx).ClusterName, } + ec2Instance := lo.Must(awsEnv.EC2API.Instances.Load(*ec2Instance.InstanceId)).(ec2types.Instance) instanceTags := instance.NewInstance(ec2Instance).Tags + for tag, value := range expectedTags { if lo.Contains(customTags, tag) { value = "custom-tag" diff --git a/pkg/controllers/nodeclass/hash/suite_test.go b/pkg/controllers/nodeclass/hash/suite_test.go index c0fda78c8934..10b781975385 100644 --- a/pkg/controllers/nodeclass/hash/suite_test.go +++ b/pkg/controllers/nodeclass/hash/suite_test.go @@ -22,7 +22,6 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" "github.com/awslabs/operatorpkg/object" "github.com/imdario/mergo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,6 +29,8 @@ import ( coreoptions "sigs.k8s.io/karpenter/pkg/operator/options" coretest "sigs.k8s.io/karpenter/pkg/test" + "github.com/aws/aws-sdk-go-v2/aws" + "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/controllers/nodeclass/hash" diff --git a/pkg/controllers/nodeclass/status/ami_test.go b/pkg/controllers/nodeclass/status/ami_test.go index 7d1eef409ff5..74de453016de 100644 --- a/pkg/controllers/nodeclass/status/ami_test.go +++ b/pkg/controllers/nodeclass/status/ami_test.go @@ -18,8 +18,9 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" @@ -57,13 +58,13 @@ var _ = Describe("NodeClass AMI Status Controller", func() { }, }) awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String("amd64-standard"), ImageId: aws.String("ami-amd64-standard"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -72,8 +73,8 @@ var _ = Describe("NodeClass AMI Status Controller", func() { Name: aws.String("amd64-standard-new"), ImageId: aws.String("ami-amd64-standard-new"), CreationDate: aws.String(time.Now().Add(time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -82,8 +83,8 @@ var _ = Describe("NodeClass AMI Status Controller", func() { Name: aws.String("amd64-nvidia"), ImageId: aws.String("ami-amd64-nvidia"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-nvidia")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -92,8 +93,8 @@ var _ = Describe("NodeClass AMI Status Controller", func() { Name: aws.String("amd64-neuron"), ImageId: aws.String("ami-amd64-neuron"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("amd64-neuron")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -102,8 +103,8 @@ var _ = Describe("NodeClass AMI Status Controller", func() { Name: aws.String("arm64-standard"), ImageId: aws.String("ami-arm64-standard"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("arm64"), - Tags: []*ec2.Tag{ + Architecture: "arm64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("arm64-standard")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -112,8 +113,8 @@ var _ = Describe("NodeClass AMI Status Controller", func() { Name: aws.String("arm64-nvidia"), ImageId: aws.String("ami-arm64-nvidia"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("arm64"), - Tags: []*ec2.Tag{ + Architecture: "arm64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("arm64-nvidia")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, diff --git a/pkg/controllers/nodeclass/status/launchtemplate_test.go b/pkg/controllers/nodeclass/status/launchtemplate_test.go index 8839a896d0f7..f15dfd415cd6 100644 --- a/pkg/controllers/nodeclass/status/launchtemplate_test.go +++ b/pkg/controllers/nodeclass/status/launchtemplate_test.go @@ -15,7 +15,8 @@ limitations under the License. package status_test import ( - "github.com/aws/aws-sdk-go/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks" + ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/awslabs/operatorpkg/status" "github.com/samber/lo" @@ -78,8 +79,8 @@ var _ = Describe("NodeClass Launch Template CIDR Resolution Controller", func() }) It("should resolve cluster CIDR for IPv6 clusters", func() { awsEnv.EKSAPI.DescribeClusterBehavior.Output.Set(&eks.DescribeClusterOutput{ - Cluster: &eks.Cluster{ - KubernetesNetworkConfig: &eks.KubernetesNetworkConfigResponse{ + Cluster: &ekstypes.Cluster{ + KubernetesNetworkConfig: &ekstypes.KubernetesNetworkConfigResponse{ ServiceIpv6Cidr: lo.ToPtr("2001:db8::/64"), }, }, diff --git a/pkg/controllers/nodeclass/status/securitygroup.go b/pkg/controllers/nodeclass/status/securitygroup.go index f003b2dfd3ea..b3a8756da895 100644 --- a/pkg/controllers/nodeclass/status/securitygroup.go +++ b/pkg/controllers/nodeclass/status/securitygroup.go @@ -20,7 +20,7 @@ import ( "sort" "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -45,7 +45,7 @@ func (sg *SecurityGroup) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeCla sort.Slice(securityGroups, func(i, j int) bool { return *securityGroups[i].GroupId < *securityGroups[j].GroupId }) - nodeClass.Status.SecurityGroups = lo.Map(securityGroups, func(securityGroup *ec2.SecurityGroup, _ int) v1.SecurityGroup { + nodeClass.Status.SecurityGroups = lo.Map(securityGroups, func(securityGroup ec2types.SecurityGroup, _ int) v1.SecurityGroup { return v1.SecurityGroup{ ID: *securityGroup.GroupId, Name: *securityGroup.GroupName, diff --git a/pkg/controllers/nodeclass/status/subnet.go b/pkg/controllers/nodeclass/status/subnet.go index 4e71dd0384a1..0e775022575e 100644 --- a/pkg/controllers/nodeclass/status/subnet.go +++ b/pkg/controllers/nodeclass/status/subnet.go @@ -20,7 +20,7 @@ import ( "sort" "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -48,7 +48,7 @@ func (s *Subnet) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) (rec } return *subnets[i].SubnetId < *subnets[j].SubnetId }) - nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet *ec2.Subnet, _ int) v1.Subnet { + nodeClass.Status.Subnets = lo.Map(subnets, func(ec2subnet ec2types.Subnet, _ int) v1.Subnet { return v1.Subnet{ ID: *ec2subnet.SubnetId, Zone: *ec2subnet.AvailabilityZone, diff --git a/pkg/controllers/nodeclass/status/subnet_test.go b/pkg/controllers/nodeclass/status/subnet_test.go index 21a11493614d..0b88e90b66f7 100644 --- a/pkg/controllers/nodeclass/status/subnet_test.go +++ b/pkg/controllers/nodeclass/status/subnet_test.go @@ -15,8 +15,9 @@ limitations under the License. package status_test import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" @@ -79,10 +80,10 @@ var _ = Describe("NodeClass Subnet Status Controller", func() { Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeSubnetsReady)).To(BeTrue()) }) It("Should have the correct ordering for the Subnets", func() { - awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []*ec2.Subnet{ - {SubnetId: aws.String("subnet-test1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int64(20)}, - {SubnetId: aws.String("subnet-test2"), AvailabilityZone: aws.String("test-zone-1b"), AvailabilityZoneId: aws.String("tstz1-1b"), AvailableIpAddressCount: aws.Int64(100)}, - {SubnetId: aws.String("subnet-test3"), AvailabilityZone: aws.String("test-zone-1c"), AvailabilityZoneId: aws.String("tstz1-1c"), AvailableIpAddressCount: aws.Int64(50)}, + awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{Subnets: []ec2types.Subnet{ + {SubnetId: aws.String("subnet-test1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), AvailableIpAddressCount: aws.Int32(20)}, + {SubnetId: aws.String("subnet-test2"), AvailabilityZone: aws.String("test-zone-1b"), AvailabilityZoneId: aws.String("tstz1-1b"), AvailableIpAddressCount: aws.Int32(100)}, + {SubnetId: aws.String("subnet-test3"), AvailabilityZone: aws.String("test-zone-1c"), AvailabilityZoneId: aws.String("tstz1-1c"), AvailableIpAddressCount: aws.Int32(50)}, }}) ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, statusController, nodeClass) diff --git a/pkg/controllers/nodeclass/termination/suite_test.go b/pkg/controllers/nodeclass/termination/suite_test.go index adbde65f02ca..3d3ce6c8aef8 100644 --- a/pkg/controllers/nodeclass/termination/suite_test.go +++ b/pkg/controllers/nodeclass/termination/suite_test.go @@ -26,9 +26,6 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - //used for launch template tests until they are migrated - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" "k8s.io/client-go/tools/record" @@ -113,7 +110,7 @@ var _ = Describe("NodeClass Termination", func() { }) It("should not delete the NodeClass if launch template deletion fails", func() { launchTemplateName := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2types.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) + awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, ec2types.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(launchTemplateName) Expect(ok).To(BeTrue()) controllerutil.AddFinalizer(nodeClass, v1.TerminationFinalizer) @@ -127,7 +124,7 @@ var _ = Describe("NodeClass Termination", func() { }) It("should not delete the launch template not associated with the nodeClass", func() { launchTemplateName := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) + awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, ec2types.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(launchTemplateName) Expect(ok).To(BeTrue()) controllerutil.AddFinalizer(nodeClass, v1.TerminationFinalizer) @@ -142,9 +139,9 @@ var _ = Describe("NodeClass Termination", func() { }) It("should succeed to delete the launch template", func() { ltName1 := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(ltName1, &ec2.LaunchTemplate{LaunchTemplateName: ltName1, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) + awsEnv.EC2API.LaunchTemplates.Store(ltName1, ec2types.LaunchTemplate{LaunchTemplateName: ltName1, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) ltName2 := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(ltName2, &ec2.LaunchTemplate{LaunchTemplateName: ltName2, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) + awsEnv.EC2API.LaunchTemplates.Store(ltName2, ec2types.LaunchTemplate{LaunchTemplateName: ltName2, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(ltName1) Expect(ok).To(BeTrue()) _, ok = awsEnv.EC2API.LaunchTemplates.Load(ltName2) diff --git a/pkg/controllers/providers/instancetype/capacity/suite_test.go b/pkg/controllers/providers/instancetype/capacity/suite_test.go index 2fa853d7c660..fc427c036b7c 100644 --- a/pkg/controllers/providers/instancetype/capacity/suite_test.go +++ b/pkg/controllers/providers/instancetype/capacity/suite_test.go @@ -20,7 +20,7 @@ import ( "math" "testing" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/samber/lo" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/controllers/providers/instancetype/suite_test.go b/pkg/controllers/providers/instancetype/suite_test.go index becd47153949..479ee549cc0a 100644 --- a/pkg/controllers/providers/instancetype/suite_test.go +++ b/pkg/controllers/providers/instancetype/suite_test.go @@ -24,7 +24,8 @@ import ( coreoptions "sigs.k8s.io/karpenter/pkg/operator/options" coretest "sigs.k8s.io/karpenter/pkg/test" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "github.com/aws/karpenter-provider-aws/pkg/apis" @@ -109,7 +110,7 @@ var _ = Describe("InstanceType", func() { }) Expect(err).To(BeNil()) for i := range instanceTypes { - Expect(instanceTypes[i].Name).To(Equal(lo.FromPtr(ec2InstanceTypes[i].InstanceType))) + Expect(instanceTypes[i].Name).To(Equal(string(ec2InstanceTypes[i].InstanceType))) } }) It("should update instance type offering date with response from the DescribeInstanceTypesOfferings API", func() { @@ -145,8 +146,8 @@ var _ = Describe("InstanceType", func() { Expect(len(instanceTypes)).To(BeNumerically("==", len(ec2InstanceTypes))) for x := range instanceTypes { - offering, found := lo.Find(ec2Offerings, func(off *ec2.InstanceTypeOffering) bool { - return instanceTypes[x].Name == lo.FromPtr(off.InstanceType) + offering, found := lo.Find(ec2Offerings, func(off ec2types.InstanceTypeOffering) bool { + return instanceTypes[x].Name == string(off.InstanceType) }) Expect(found).To(BeTrue()) for y := range instanceTypes[x].Offerings { diff --git a/pkg/controllers/providers/pricing/suite_test.go b/pkg/controllers/providers/pricing/suite_test.go index 86edd6c5eb4c..e9f51d155bf2 100644 --- a/pkg/controllers/providers/pricing/suite_test.go +++ b/pkg/controllers/providers/pricing/suite_test.go @@ -22,9 +22,10 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - awspricing "github.com/aws/aws-sdk-go/service/pricing" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + awspricing "github.com/aws/aws-sdk-go-v2/service/pricing" "github.com/samber/lo" coreoptions "sigs.k8s.io/karpenter/pkg/operator/options" coretest "sigs.k8s.io/karpenter/pkg/test" @@ -82,7 +83,7 @@ var _ = AfterEach(func() { var _ = Describe("Pricing", func() { DescribeTable( "should return correct static data for all partitions", - func(staticPricing map[string]map[string]float64) { + func(staticPricing map[string]map[ec2types.InstanceType]float64) { for region, prices := range staticPricing { provider := pricing.NewDefaultProvider(ctx, awsEnv.PricingAPI, awsEnv.EC2API, region) for instance, price := range prices { @@ -114,7 +115,7 @@ var _ = Describe("Pricing", func() { // modify our API before creating the pricing provider as it performs an initial update on creation. The pricing // API provides on-demand prices, the ec2 API provides spot prices awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + PriceList: []string{ fake.NewOnDemandPrice("c98.large", 1.20), fake.NewOnDemandPrice("c99.large", 1.23), }, @@ -132,35 +133,36 @@ var _ = Describe("Pricing", func() { It("should update spot pricing with response from the pricing API", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.23"), Timestamp: &now, }, { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c98.large"), + InstanceType: "c98.large", SpotPrice: aws.String("1.20"), Timestamp: &now, }, { AvailabilityZone: aws.String("test-zone-1b"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.50"), Timestamp: &now, }, { AvailabilityZone: aws.String("test-zone-1b"), - InstanceType: aws.String("c98.large"), + InstanceType: "c98.large", SpotPrice: aws.String("1.10"), Timestamp: &now, }, }, }) awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + + PriceList: []string{ fake.NewOnDemandPrice("c98.large", 1.20), fake.NewOnDemandPrice("c99.large", 1.23), }, @@ -178,23 +180,23 @@ var _ = Describe("Pricing", func() { It("should update zonal pricing with data from the spot pricing API", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.23"), Timestamp: &now, }, { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c98.large"), + InstanceType: "c98.large", SpotPrice: aws.String("1.20"), Timestamp: &now, }, }, }) awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + PriceList: []string{ fake.NewOnDemandPrice("c98.large", 1.20), fake.NewOnDemandPrice("c99.large", 1.23), }, @@ -211,17 +213,17 @@ var _ = Describe("Pricing", func() { It("should respond with false if price doesn't exist in zone", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.23"), Timestamp: &now, }, }, }) awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + PriceList: []string{ fake.NewOnDemandPrice("c98.large", 1.20), fake.NewOnDemandPrice("c99.large", 1.23), }, @@ -238,24 +240,24 @@ var _ = Describe("Pricing", func() { // need to search for both values. updateStart := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.23"), Timestamp: &updateStart, }, }, }) awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + PriceList: []string{ fake.NewOnDemandPrice("c98.large", 1.20), fake.NewOnDemandPrice("c99.large", 1.23), }, }) ExpectSingletonReconciled(ctx, controller) inp := awsEnv.EC2API.DescribeSpotPriceHistoryInput.Clone() - Expect(lo.Map(inp.ProductDescriptions, func(x *string, _ int) string { return *x })). + Expect(lo.Map(inp.ProductDescriptions, func(x string, _ int) string { return x })). To(ContainElements("Linux/UNIX", "Linux/UNIX (Amazon VPC)")) }) It("should return static on-demand data when in isolated-vpc", func() { @@ -264,16 +266,16 @@ var _ = Describe("Pricing", func() { })) now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1b"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.50"), Timestamp: &now, }, { AvailabilityZone: aws.String("test-zone-1b"), - InstanceType: aws.String("c98.large"), + InstanceType: "c98.large", SpotPrice: aws.String("1.10"), Timestamp: &now, }, @@ -283,9 +285,9 @@ var _ = Describe("Pricing", func() { awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ // these are incorrect prices which are here to ensure that // results from only static pricing are used - PriceList: []aws.JSONValue{ - fake.NewOnDemandPrice("c3.2xlarge", 1.20), - fake.NewOnDemandPrice("c5.xlarge", 1.23), + PriceList: []string{ + fake.NewOnDemandPrice("c98.large", 1.20), + fake.NewOnDemandPrice("c99.large", 1.23), }, }) ExpectSingletonReconciled(ctx, controller) @@ -303,17 +305,17 @@ var _ = Describe("Pricing", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("c99.large"), + InstanceType: "c99.large", SpotPrice: aws.String("1.23"), Timestamp: &now, }, }, }) awsEnv.PricingAPI.GetProductsOutput.Set(&awspricing.GetProductsOutput{ - PriceList: []aws.JSONValue{ + PriceList: []string{ fake.NewOnDemandPriceWithCurrency("c98.large", 1.20, "CNY"), fake.NewOnDemandPriceWithCurrency("c99.large", 1.23, "CNY"), }, diff --git a/pkg/controllers/providers/ssm/invalidation/suite_test.go b/pkg/controllers/providers/ssm/invalidation/suite_test.go index 110fcc302eaf..62fff9768498 100644 --- a/pkg/controllers/providers/ssm/invalidation/suite_test.go +++ b/pkg/controllers/providers/ssm/invalidation/suite_test.go @@ -19,7 +19,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" coreoptions "sigs.k8s.io/karpenter/pkg/operator/options" coretest "sigs.k8s.io/karpenter/pkg/test" @@ -150,12 +151,12 @@ func getSSMCacheEntries() map[string]string { func deprecateAMIs(amiIDs ...string) { awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: lo.Map(amiIDs, func(amiID string, _ int) *ec2.Image { - return &ec2.Image{ + Images: lo.Map(amiIDs, func(amiID string, _ int) ec2types.Image { + return ec2types.Image{ Name: lo.ToPtr(coretest.RandomName()), ImageId: lo.ToPtr(amiID), CreationDate: lo.ToPtr(awsEnv.Clock.Now().Add(-24 * time.Hour).Format(time.RFC3339)), - Architecture: lo.ToPtr("x86_64"), + Architecture: "x86_64", DeprecationTime: lo.ToPtr(awsEnv.Clock.Now().Add(-12 * time.Hour).Format(time.RFC3339)), } }), diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 3f5a2ca9af0a..0c73d57d1a94 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -17,14 +17,8 @@ package errors import ( "errors" - //v2 imports + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/smithy-go" - - //V1 imports - - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/iam" "k8s.io/apimachinery/pkg/util/sets" ) @@ -39,13 +33,10 @@ var ( launchTemplateNameNotFoundCode, "InvalidLaunchTemplateId.NotFound", "QueueDoesNotExist", - iam.ErrCodeNoSuchEntityException, - - //v2 error codes - "NoSuchEntityException", + "NoSuchEntity", ) alreadyExistsErrorCodes = sets.New[string]( - iam.ErrCodeEntityAlreadyExistsException, + "EntityAlreadyExists", ) // unfulfillableCapacityErrorCodes signify that capacity is temporarily unable to be launched unfulfillableCapacityErrorCodes = sets.New[string]( @@ -65,9 +56,9 @@ func IsNotFound(err error) bool { if err == nil { return false } - var awsError awserr.Error - if errors.As(err, &awsError) { - return notFoundErrorCodes.Has(awsError.Code()) + var apiErr smithy.APIError + if errors.As(err, &apiErr) { + return notFoundErrorCodes.Has(apiErr.ErrorCode()) } return false } @@ -83,9 +74,9 @@ func IsAlreadyExists(err error) bool { if err == nil { return false } - var awsError awserr.Error - if errors.As(err, &awsError) { - return alreadyExistsErrorCodes.Has(awsError.Code()) + var apiErr smithy.APIError + if errors.As(err, &apiErr) { + return alreadyExistsErrorCodes.Has(apiErr.ErrorCode()) } return false } @@ -100,40 +91,17 @@ func IgnoreAlreadyExists(err error) error { // IsUnfulfillableCapacity returns true if the Fleet err means // capacity is temporarily unavailable for launching. // This could be due to account limits, insufficient ec2 capacity, etc. -func IsUnfulfillableCapacity(err *ec2.CreateFleetError) bool { +func IsUnfulfillableCapacity(err ec2types.CreateFleetError) bool { return unfulfillableCapacityErrorCodes.Has(*err.ErrorCode) } func IsLaunchTemplateNotFound(err error) bool { - if err == nil { - return false - } - var awsError awserr.Error - if errors.As(err, &awsError) { - return awsError.Code() == launchTemplateNameNotFoundCode - } - return false -} - -//V2 will become new full file when every provider is migrated - -// IsNotFound returns true if the err is an AWS error (even if it's -// wrapped) and is a known to mean "not found" (as opposed to a more -// serious or unexpected error) -func IsNotFoundV2(err error) bool { if err == nil { return false } var apiErr smithy.APIError if errors.As(err, &apiErr) { - return notFoundErrorCodes.Has(apiErr.ErrorCode()) + return apiErr.ErrorCode() == launchTemplateNameNotFoundCode } return false } - -func IgnoreNotFoundV2(err error) error { - if IsNotFoundV2(err) { - return nil - } - return err -} diff --git a/pkg/fake/ec2api.go b/pkg/fake/ec2api.go index 4412514d3c16..623058db84b9 100644 --- a/pkg/fake/ec2api.go +++ b/pkg/fake/ec2api.go @@ -22,17 +22,17 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/Pallinder/go-randomdata" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/smithy-go" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/sets" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" + "sigs.k8s.io/karpenter/pkg/test" "sigs.k8s.io/karpenter/pkg/utils/atomic" ) @@ -68,7 +68,7 @@ type EC2Behavior struct { } type EC2API struct { - ec2iface.EC2API + sdk.EC2API EC2Behavior } @@ -77,7 +77,7 @@ func NewEC2API() *EC2API { } // DefaultSupportedUsageClasses is a var because []*string can't be a const -var DefaultSupportedUsageClasses = aws.StringSlice([]string{"on-demand", "spot"}) +var DefaultSupportedUsageClasses = []ec2types.UsageClassType{ec2types.UsageClassType("on-demand"), ec2types.UsageClassType("spot")} // Reset must be called between tests otherwise tests will pollute // each other. @@ -109,16 +109,16 @@ func (e *EC2API) Reset() { } // nolint: gocyclo -func (e *EC2API) CreateFleetWithContext(_ context.Context, input *ec2.CreateFleetInput, _ ...request.Option) (*ec2.CreateFleetOutput, error) { +func (e *EC2API) CreateFleet(_ context.Context, input *ec2.CreateFleetInput, _ ...func(*ec2.Options)) (*ec2.CreateFleetOutput, error) { return e.CreateFleetBehavior.Invoke(input, func(input *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) { if input.LaunchTemplateConfigs[0].LaunchTemplateSpecification.LaunchTemplateName == nil { return nil, fmt.Errorf("missing launch template name") } - var instanceIds []*string + var instanceIds []string var skippedPools []CapacityPool var spotInstanceRequestID *string - if aws.StringValue(input.TargetCapacitySpecification.DefaultTargetCapacityType) == karpv1.CapacityTypeSpot { + if string(input.TargetCapacitySpecification.DefaultTargetCapacityType) == karpv1.CapacityTypeSpot { spotInstanceRequestID = aws.String(test.RandomName()) } @@ -127,9 +127,9 @@ func (e *EC2API) CreateFleetWithContext(_ context.Context, input *ec2.CreateFlee for _, override := range ltc.Overrides { skipInstance := false e.InsufficientCapacityPools.Range(func(pool CapacityPool) bool { - if pool.InstanceType == aws.StringValue(override.InstanceType) && - pool.Zone == aws.StringValue(override.AvailabilityZone) && - pool.CapacityType == aws.StringValue(input.TargetCapacitySpecification.DefaultTargetCapacityType) { + if pool.InstanceType == string(override.InstanceType) && + pool.Zone == aws.ToString(override.AvailabilityZone) && + pool.CapacityType == string(input.TargetCapacitySpecification.DefaultTargetCapacityType) { skippedPools = append(skippedPools, pool) skipInstance = true return false @@ -145,34 +145,34 @@ func (e *EC2API) CreateFleetWithContext(_ context.Context, input *ec2.CreateFlee amiID = lt.LaunchTemplateData.ImageId e.CalledWithCreateLaunchTemplateInput.Add(lt) } - instanceState := ec2.InstanceStateNameRunning + instanceState := ec2types.InstanceStateNameRunning for ; fulfilled < int(*input.TargetCapacitySpecification.TotalTargetCapacity); fulfilled++ { - instance := &ec2.Instance{ + instance := ec2types.Instance{ ImageId: aws.String(*amiID), InstanceId: aws.String(test.RandomName()), - Placement: &ec2.Placement{AvailabilityZone: input.LaunchTemplateConfigs[0].Overrides[0].AvailabilityZone}, + Placement: &ec2types.Placement{AvailabilityZone: input.LaunchTemplateConfigs[0].Overrides[0].AvailabilityZone}, PrivateDnsName: aws.String(randomdata.IpV4Address()), InstanceType: input.LaunchTemplateConfigs[0].Overrides[0].InstanceType, SpotInstanceRequestId: spotInstanceRequestID, - State: &ec2.InstanceState{ - Name: &instanceState, + State: &ec2types.InstanceState{ + Name: instanceState, }, } e.Instances.Store(*instance.InstanceId, instance) - instanceIds = append(instanceIds, instance.InstanceId) + instanceIds = append(instanceIds, *instance.InstanceId) } } if fulfilled == int(*input.TargetCapacitySpecification.TotalTargetCapacity) { break } } - result := &ec2.CreateFleetOutput{Instances: []*ec2.CreateFleetInstance{ + result := &ec2.CreateFleetOutput{Instances: []ec2types.CreateFleetInstance{ { InstanceIds: instanceIds, InstanceType: input.LaunchTemplateConfigs[0].Overrides[0].InstanceType, - Lifecycle: input.TargetCapacitySpecification.DefaultTargetCapacityType, - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - Overrides: &ec2.FleetLaunchTemplateOverrides{ + Lifecycle: ec2types.InstanceLifecycle(input.TargetCapacitySpecification.DefaultTargetCapacityType), + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ SubnetId: input.LaunchTemplateConfigs[0].Overrides[0].SubnetId, ImageId: input.LaunchTemplateConfigs[0].Overrides[0].ImageId, InstanceType: input.LaunchTemplateConfigs[0].Overrides[0].InstanceType, @@ -182,11 +182,11 @@ func (e *EC2API) CreateFleetWithContext(_ context.Context, input *ec2.CreateFlee }, }} for _, pool := range skippedPools { - result.Errors = append(result.Errors, &ec2.CreateFleetError{ + result.Errors = append(result.Errors, ec2types.CreateFleetError{ ErrorCode: aws.String("InsufficientInstanceCapacity"), - LaunchTemplateAndOverrides: &ec2.LaunchTemplateAndOverridesResponse{ - Overrides: &ec2.FleetLaunchTemplateOverrides{ - InstanceType: aws.String(pool.InstanceType), + LaunchTemplateAndOverrides: &ec2types.LaunchTemplateAndOverridesResponse{ + Overrides: &ec2types.FleetLaunchTemplateOverrides{ + InstanceType: ec2types.InstanceType(pool.InstanceType), AvailabilityZone: aws.String(pool.Zone), }, }, @@ -196,16 +196,15 @@ func (e *EC2API) CreateFleetWithContext(_ context.Context, input *ec2.CreateFlee }) } -func (e *EC2API) TerminateInstancesWithContext(_ context.Context, input *ec2.TerminateInstancesInput, _ ...request.Option) (*ec2.TerminateInstancesOutput, error) { +func (e *EC2API) TerminateInstances(_ context.Context, input *ec2.TerminateInstancesInput, _ ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { return e.TerminateInstancesBehavior.Invoke(input, func(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { - var instanceStateChanges []*ec2.InstanceStateChange + var instanceStateChanges []ec2types.InstanceStateChange for _, id := range input.InstanceIds { - instanceID := *id - if _, ok := e.Instances.LoadAndDelete(instanceID); ok { - instanceStateChanges = append(instanceStateChanges, &ec2.InstanceStateChange{ - PreviousState: &ec2.InstanceState{Name: aws.String(ec2.InstanceStateNameRunning), Code: aws.Int64(16)}, - CurrentState: &ec2.InstanceState{Name: aws.String(ec2.InstanceStateNameShuttingDown), Code: aws.Int64(32)}, - InstanceId: aws.String(instanceID), + if _, ok := e.Instances.LoadAndDelete(id); ok { + instanceStateChanges = append(instanceStateChanges, ec2types.InstanceStateChange{ + PreviousState: &ec2types.InstanceState{Name: ec2types.InstanceStateNameRunning, Code: aws.Int32(16)}, + CurrentState: &ec2types.InstanceState{Name: ec2types.InstanceStateNameShuttingDown, Code: aws.Int32(32)}, + InstanceId: aws.String(id), }) } } @@ -213,106 +212,98 @@ func (e *EC2API) TerminateInstancesWithContext(_ context.Context, input *ec2.Ter }) } -func (e *EC2API) CreateLaunchTemplateWithContext(_ context.Context, input *ec2.CreateLaunchTemplateInput, _ ...request.Option) (*ec2.CreateLaunchTemplateOutput, error) { +func (e *EC2API) CreateLaunchTemplate(_ context.Context, input *ec2.CreateLaunchTemplateInput, _ ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() } e.CalledWithCreateLaunchTemplateInput.Add(input) - launchTemplate := &ec2.LaunchTemplate{LaunchTemplateName: input.LaunchTemplateName} + launchTemplate := ec2types.LaunchTemplate{LaunchTemplateName: input.LaunchTemplateName} e.LaunchTemplates.Store(input.LaunchTemplateName, launchTemplate) - return &ec2.CreateLaunchTemplateOutput{LaunchTemplate: launchTemplate}, nil + return &ec2.CreateLaunchTemplateOutput{LaunchTemplate: lo.ToPtr(launchTemplate)}, nil } -func (e *EC2API) CreateTagsWithContext(_ context.Context, input *ec2.CreateTagsInput, _ ...request.Option) (*ec2.CreateTagsOutput, error) { +func (e *EC2API) CreateTags(_ context.Context, input *ec2.CreateTagsInput, _ ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error) { return e.CreateTagsBehavior.Invoke(input, func(input *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { // Update passed in instances with the passed tags for _, id := range input.Resources { - raw, ok := e.Instances.Load(aws.StringValue(id)) + raw, ok := e.Instances.Load(id) if !ok { - return nil, fmt.Errorf("instance with id '%s' does not exist", aws.StringValue(id)) + return nil, fmt.Errorf("instance with id '%s' does not exist", id) } - instance := raw.(*ec2.Instance) + instance := raw.(ec2types.Instance) // Upsert any tags that have the same key - tagsToMap := func(tag *ec2.Tag) (string, string) { + tagsToMap := func(tag ec2types.Tag) (string, string) { return *tag.Key, *tag.Value } tags := lo.Assign(lo.SliceToMap(instance.Tags, tagsToMap), lo.SliceToMap(input.Tags, tagsToMap)) - instance.Tags = lo.MapToSlice(tags, func(key, value string) *ec2.Tag { - return &ec2.Tag{Key: aws.String(key), Value: aws.String(value)} + instance.Tags = lo.MapToSlice(tags, func(key, value string) ec2types.Tag { + return ec2types.Tag{Key: aws.String(key), Value: aws.String(value)} }) + e.Instances.Swap(lo.FromPtr(instance.InstanceId), instance) } return nil, nil }) } -func (e *EC2API) DescribeInstancesWithContext(_ context.Context, input *ec2.DescribeInstancesInput, _ ...request.Option) (*ec2.DescribeInstancesOutput, error) { +func (e *EC2API) DescribeInstances(_ context.Context, input *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { return e.DescribeInstancesBehavior.Invoke(input, func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { - var instances []*ec2.Instance + var instances []ec2types.Instance // If it's a list call and no instance ids are specified - if len(aws.StringValueSlice(input.InstanceIds)) == 0 { + if len(input.InstanceIds) == 0 { e.Instances.Range(func(k interface{}, v interface{}) bool { - instances = append(instances, v.(*ec2.Instance)) + instances = append(instances, v.(ec2types.Instance)) return true }) } for _, instanceID := range input.InstanceIds { - instance, _ := e.Instances.Load(*instanceID) + instance, _ := e.Instances.Load(instanceID) if instance == nil { continue } - instances = append(instances, instance.(*ec2.Instance)) + instances = append(instances, instance.(ec2types.Instance)) } return &ec2.DescribeInstancesOutput{ - Reservations: []*ec2.Reservation{{Instances: filterInstances(instances, input.Filters)}}, + Reservations: []ec2types.Reservation{{Instances: filterInstances(instances, input.Filters)}}, }, nil }) } -func (e *EC2API) DescribeInstancesPagesWithContext(ctx context.Context, input *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { - output, err := e.DescribeInstancesWithContext(ctx, input, opts...) - if err != nil { - return err - } - fn(output, false) - return nil -} - //nolint:gocyclo -func filterInstances(instances []*ec2.Instance, filters []*ec2.Filter) []*ec2.Instance { - var ret []*ec2.Instance +func filterInstances(instances []ec2types.Instance, filters []ec2types.Filter) []ec2types.Instance { + var ret []ec2types.Instance for _, instance := range instances { passesFilter := true OUTER: for _, filter := range filters { switch { - case aws.StringValue(filter.Name) == "instance-state-name": - if !sets.New(aws.StringValueSlice(filter.Values)...).Has(aws.StringValue(instance.State.Name)) { + case aws.ToString(filter.Name) == "instance-state-name": + if !sets.New(filter.Values...).Has(string(instance.State.Name)) { passesFilter = false break OUTER } - case aws.StringValue(filter.Name) == "tag-key": - values := sets.New(aws.StringValueSlice(filter.Values)...) - if _, ok := lo.Find(instance.Tags, func(t *ec2.Tag) bool { - return values.Has(aws.StringValue(t.Key)) + case aws.ToString(filter.Name) == "tag-key": + values := sets.New(filter.Values...) + if _, ok := lo.Find(instance.Tags, func(t ec2types.Tag) bool { + return values.Has(aws.ToString(t.Key)) }); !ok { passesFilter = false break OUTER } - case strings.HasPrefix(aws.StringValue(filter.Name), "tag:"): - k := strings.TrimPrefix(aws.StringValue(filter.Name), "tag:") - tag, ok := lo.Find(instance.Tags, func(t *ec2.Tag) bool { - return aws.StringValue(t.Key) == k + case strings.HasPrefix(aws.ToString(filter.Name), "tag:"): + k := strings.TrimPrefix(aws.ToString(filter.Name), "tag:") + tag, ok := lo.Find(instance.Tags, func(t ec2types.Tag) bool { + return aws.ToString(t.Key) == k }) if !ok { passesFilter = false break OUTER } switch { - case lo.Contains(aws.StringValueSlice(filter.Values), "*"): - case lo.Contains(aws.StringValueSlice(filter.Values), aws.StringValue(tag.Value)): + case lo.Contains(filter.Values, "*"): + case lo.Contains(filter.Values, aws.ToString(tag.Value)): default: passesFilter = false break OUTER @@ -326,7 +317,7 @@ func filterInstances(instances []*ec2.Instance, filters []*ec2.Filter) []*ec2.In return ret } -func (e *EC2API) DescribeImagesWithContext(_ context.Context, input *ec2.DescribeImagesInput, _ ...request.Option) (*ec2.DescribeImagesOutput, error) { +func (e *EC2API) DescribeImages(_ context.Context, input *ec2.DescribeImagesInput, _ ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -337,31 +328,22 @@ func (e *EC2API) DescribeImagesWithContext(_ context.Context, input *ec2.Describ describeImagesOutput.Images = FilterDescribeImages(describeImagesOutput.Images, input.Filters) return describeImagesOutput, nil } - if aws.StringValue(input.Filters[0].Values[0]) == "invalid" { + if input.Filters[0].Values[0] == "invalid" { return &ec2.DescribeImagesOutput{}, nil } return &ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(test.RandomName()), ImageId: aws.String(test.RandomName()), CreationDate: aws.String(time.Now().Format(time.UnixDate)), - Architecture: aws.String("x86_64"), + Architecture: "x86_64", }, }, }, nil } -func (e *EC2API) DescribeImagesPagesWithContext(ctx context.Context, input *ec2.DescribeImagesInput, fn func(*ec2.DescribeImagesOutput, bool) bool, _ ...request.Option) error { - out, err := e.DescribeImagesWithContext(ctx, input) - if err != nil { - return err - } - fn(out, false) - return nil -} - -func (e *EC2API) DescribeLaunchTemplatesWithContext(_ context.Context, input *ec2.DescribeLaunchTemplatesInput, _ ...request.Option) (*ec2.DescribeLaunchTemplatesOutput, error) { +func (e *EC2API) DescribeLaunchTemplates(_ context.Context, input *ec2.DescribeLaunchTemplatesInput, _ ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplatesOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -371,8 +353,8 @@ func (e *EC2API) DescribeLaunchTemplatesWithContext(_ context.Context, input *ec } output := &ec2.DescribeLaunchTemplatesOutput{} e.LaunchTemplates.Range(func(key, value interface{}) bool { - launchTemplate := value.(*ec2.LaunchTemplate) - if lo.Contains(aws.StringValueSlice(input.LaunchTemplateNames), aws.StringValue(launchTemplate.LaunchTemplateName)) || len(input.Filters) != 0 && Filter(input.Filters, aws.StringValue(launchTemplate.LaunchTemplateId), aws.StringValue(launchTemplate.LaunchTemplateName), launchTemplate.Tags) { + launchTemplate := value.(ec2types.LaunchTemplate) + if lo.Contains(aws.StringSlice(input.LaunchTemplateNames), launchTemplate.LaunchTemplateName) || len(input.Filters) != 0 && Filter(input.Filters, aws.ToString(launchTemplate.LaunchTemplateId), aws.ToString(launchTemplate.LaunchTemplateName), launchTemplate.Tags) { output.LaunchTemplates = append(output.LaunchTemplates, launchTemplate) } return true @@ -381,21 +363,15 @@ func (e *EC2API) DescribeLaunchTemplatesWithContext(_ context.Context, input *ec return output, nil } if len(output.LaunchTemplates) == 0 { - return nil, awserr.New("InvalidLaunchTemplateName.NotFoundException", "not found", nil) + return nil, &smithy.GenericAPIError{ + Code: "InvalidLaunchTemplateName.NotFoundException", + Message: "At least one of the launch templates specified in the request does not exist.", + } } return output, nil } -func (e *EC2API) DescribeLaunchTemplatesPagesWithContext(ctx context.Context, input *ec2.DescribeLaunchTemplatesInput, fn func(*ec2.DescribeLaunchTemplatesOutput, bool) bool, _ ...request.Option) error { - out, err := e.DescribeLaunchTemplatesWithContext(ctx, input) - if err != nil { - return err - } - fn(out, false) - return nil -} - -func (e *EC2API) DeleteLaunchTemplateWithContext(_ context.Context, input *ec2.DeleteLaunchTemplateInput, _ ...request.Option) (*ec2.DeleteLaunchTemplateOutput, error) { +func (e *EC2API) DeleteLaunchTemplate(_ context.Context, input *ec2.DeleteLaunchTemplateInput, _ ...func(*ec2.Options)) (*ec2.DeleteLaunchTemplateOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -404,7 +380,7 @@ func (e *EC2API) DeleteLaunchTemplateWithContext(_ context.Context, input *ec2.D return nil, nil } -func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.DescribeSubnetsInput, _ ...request.Option) (*ec2.DescribeSubnetsOutput, error) { +func (e *EC2API) DescribeSubnets(_ context.Context, input *ec2.DescribeSubnetsInput, _ ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -414,14 +390,14 @@ func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.Descri describeSubnetsOutput.Subnets = FilterDescribeSubnets(describeSubnetsOutput.Subnets, input.Filters) return describeSubnetsOutput, nil } - subnets := []*ec2.Subnet{ + subnets := []ec2types.Subnet{ { SubnetId: aws.String("subnet-test1"), AvailabilityZone: aws.String("test-zone-1a"), AvailabilityZoneId: aws.String("tstz1-1a"), - AvailableIpAddressCount: aws.Int64(100), + AvailableIpAddressCount: aws.Int32(100), MapPublicIpOnLaunch: aws.Bool(false), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-subnet-1")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -430,9 +406,9 @@ func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.Descri SubnetId: aws.String("subnet-test2"), AvailabilityZone: aws.String("test-zone-1b"), AvailabilityZoneId: aws.String("tstz1-1b"), - AvailableIpAddressCount: aws.Int64(100), + AvailableIpAddressCount: aws.Int32(100), MapPublicIpOnLaunch: aws.Bool(true), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-subnet-2")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -441,8 +417,8 @@ func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.Descri SubnetId: aws.String("subnet-test3"), AvailabilityZone: aws.String("test-zone-1c"), AvailabilityZoneId: aws.String("tstz1-1c"), - AvailableIpAddressCount: aws.Int64(100), - Tags: []*ec2.Tag{ + AvailableIpAddressCount: aws.Int32(100), + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-subnet-3")}, {Key: aws.String("TestTag")}, {Key: aws.String("foo"), Value: aws.String("bar")}, @@ -452,9 +428,9 @@ func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.Descri SubnetId: aws.String("subnet-test4"), AvailabilityZone: aws.String("test-zone-1a-local"), AvailabilityZoneId: aws.String("tstz1-1alocal"), - AvailableIpAddressCount: aws.Int64(100), + AvailableIpAddressCount: aws.Int32(100), MapPublicIpOnLaunch: aws.Bool(true), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-subnet-4")}, }, }, @@ -465,7 +441,7 @@ func (e *EC2API) DescribeSubnetsWithContext(_ context.Context, input *ec2.Descri return &ec2.DescribeSubnetsOutput{Subnets: FilterDescribeSubnets(subnets, input.Filters)}, nil } -func (e *EC2API) DescribeSecurityGroupsWithContext(_ context.Context, input *ec2.DescribeSecurityGroupsInput, _ ...request.Option) (*ec2.DescribeSecurityGroupsOutput, error) { +func (e *EC2API) DescribeSecurityGroups(_ context.Context, input *ec2.DescribeSecurityGroupsInput, _ ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -475,11 +451,11 @@ func (e *EC2API) DescribeSecurityGroupsWithContext(_ context.Context, input *ec2 describeSecurityGroupsOutput.SecurityGroups = FilterDescribeSecurtyGroups(describeSecurityGroupsOutput.SecurityGroups, input.Filters) return e.DescribeSecurityGroupsOutput.Clone(), nil } - sgs := []*ec2.SecurityGroup{ + sgs := []ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-security-group-1")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -487,7 +463,7 @@ func (e *EC2API) DescribeSecurityGroupsWithContext(_ context.Context, input *ec2 { GroupId: aws.String("sg-test2"), GroupName: aws.String("securityGroup-test2"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-security-group-2")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -495,7 +471,7 @@ func (e *EC2API) DescribeSecurityGroupsWithContext(_ context.Context, input *ec2 { GroupId: aws.String("sg-test3"), GroupName: aws.String("securityGroup-test3"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-security-group-3")}, {Key: aws.String("TestTag")}, {Key: aws.String("foo"), Value: aws.String("bar")}, @@ -508,7 +484,7 @@ func (e *EC2API) DescribeSecurityGroupsWithContext(_ context.Context, input *ec2 return &ec2.DescribeSecurityGroupsOutput{SecurityGroups: FilterDescribeSecurtyGroups(sgs, input.Filters)}, nil } -func (e *EC2API) DescribeAvailabilityZonesWithContext(context.Context, *ec2.DescribeAvailabilityZonesInput, ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error) { +func (e *EC2API) DescribeAvailabilityZones(context.Context, *ec2.DescribeAvailabilityZonesInput, ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -516,7 +492,7 @@ func (e *EC2API) DescribeAvailabilityZonesWithContext(context.Context, *ec2.Desc if !e.DescribeAvailabilityZonesOutput.IsNil() { return e.DescribeAvailabilityZonesOutput.Clone(), nil } - return &ec2.DescribeAvailabilityZonesOutput{AvailabilityZones: []*ec2.AvailabilityZone{ + return &ec2.DescribeAvailabilityZonesOutput{AvailabilityZones: []ec2types.AvailabilityZone{ {ZoneName: aws.String("test-zone-1a"), ZoneId: aws.String("tstz1-1a"), ZoneType: aws.String("availability-zone")}, {ZoneName: aws.String("test-zone-1b"), ZoneId: aws.String("tstz1-1b"), ZoneType: aws.String("availability-zone")}, {ZoneName: aws.String("test-zone-1c"), ZoneId: aws.String("tstz1-1c"), ZoneType: aws.String("availability-zone")}, @@ -524,7 +500,7 @@ func (e *EC2API) DescribeAvailabilityZonesWithContext(context.Context, *ec2.Desc }}, nil } -func (e *EC2API) DescribeInstanceTypesWithContext(_ context.Context, _ *ec2.DescribeInstanceTypesInput, _ ...request.Option) (*ec2.DescribeInstanceTypesOutput, error) { +func (e *EC2API) DescribeInstanceTypes(_ context.Context, _ *ec2.DescribeInstanceTypesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstanceTypesOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -535,16 +511,7 @@ func (e *EC2API) DescribeInstanceTypesWithContext(_ context.Context, _ *ec2.Desc return defaultDescribeInstanceTypesOutput, nil } -func (e *EC2API) DescribeInstanceTypesPagesWithContext(ctx context.Context, input *ec2.DescribeInstanceTypesInput, fn func(*ec2.DescribeInstanceTypesOutput, bool) bool, _ ...request.Option) error { - out, err := e.DescribeInstanceTypesWithContext(ctx, input) - if err != nil { - return err - } - fn(out, false) - return nil -} - -func (e *EC2API) DescribeInstanceTypeOfferingsWithContext(_ context.Context, _ *ec2.DescribeInstanceTypeOfferingsInput, _ ...request.Option) (*ec2.DescribeInstanceTypeOfferingsOutput, error) { +func (e *EC2API) DescribeInstanceTypeOfferings(_ context.Context, _ *ec2.DescribeInstanceTypeOfferingsInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstanceTypeOfferingsOutput, error) { if !e.NextError.IsNil() { defer e.NextError.Reset() return nil, e.NextError.Get() @@ -553,137 +520,128 @@ func (e *EC2API) DescribeInstanceTypeOfferingsWithContext(_ context.Context, _ * return e.DescribeInstanceTypeOfferingsOutput.Clone(), nil } return &ec2.DescribeInstanceTypeOfferingsOutput{ - InstanceTypeOfferings: []*ec2.InstanceTypeOffering{ + InstanceTypeOfferings: []ec2types.InstanceTypeOffering{ { - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", Location: aws.String("test-zone-1c"), }, { - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", Location: aws.String("test-zone-1a-local"), }, { - InstanceType: aws.String("m5.xlarge"), + InstanceType: "m5.xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.xlarge"), + InstanceType: "m5.xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("m5.2xlarge"), + InstanceType: "m5.2xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.4xlarge"), + InstanceType: "m5.4xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.8xlarge"), + InstanceType: "m5.8xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("p3.8xlarge"), + InstanceType: "p3.8xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("p3.8xlarge"), + InstanceType: "p3.8xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("dl1.24xlarge"), + InstanceType: "dl1.24xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("dl1.24xlarge"), + InstanceType: "dl1.24xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("g4dn.8xlarge"), + InstanceType: "g4dn.8xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("g4dn.8xlarge"), + InstanceType: "g4dn.8xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("g4ad.16xlarge"), + InstanceType: "g4ad.16xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("g4ad.16xlarge"), + InstanceType: "g4ad.16xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("t3.large"), + InstanceType: "t3.large", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("t3.large"), + InstanceType: "t3.large", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("inf2.xlarge"), + InstanceType: "inf2.xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("inf2.24xlarge"), + InstanceType: "inf2.24xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("trn1.2xlarge"), + InstanceType: "trn1.2xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("c6g.large"), + InstanceType: "c6g.large", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.metal"), + InstanceType: "m5.metal", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m5.metal"), + InstanceType: "m5.metal", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("m5.metal"), + InstanceType: "m5.metal", Location: aws.String("test-zone-1c"), }, { - InstanceType: aws.String("m6idn.32xlarge"), + InstanceType: "m6idn.32xlarge", Location: aws.String("test-zone-1a"), }, { - InstanceType: aws.String("m6idn.32xlarge"), + InstanceType: "m6idn.32xlarge", Location: aws.String("test-zone-1b"), }, { - InstanceType: aws.String("m6idn.32xlarge"), + InstanceType: "m6idn.32xlarge", Location: aws.String("test-zone-1c"), }, }, }, nil } -func (e *EC2API) DescribeInstanceTypeOfferingsPagesWithContext(ctx context.Context, input *ec2.DescribeInstanceTypeOfferingsInput, fn func(*ec2.DescribeInstanceTypeOfferingsOutput, bool) bool, _ ...request.Option) error { - out, err := e.DescribeInstanceTypeOfferingsWithContext(ctx, input) - if err != nil { - return err - } - fn(out, false) - return nil -} - -func (e *EC2API) DescribeSpotPriceHistoryWithContext(_ aws.Context, input *ec2.DescribeSpotPriceHistoryInput, _ ...request.Option) (*ec2.DescribeSpotPriceHistoryOutput, error) { +func (e *EC2API) DescribeSpotPriceHistory(_ context.Context, input *ec2.DescribeSpotPriceHistoryInput, _ ...func(*ec2.Options)) (*ec2.DescribeSpotPriceHistoryOutput, error) { e.DescribeSpotPriceHistoryInput.Set(input) if !e.NextError.IsNil() { defer e.NextError.Reset() @@ -695,12 +653,3 @@ func (e *EC2API) DescribeSpotPriceHistoryWithContext(_ aws.Context, input *ec2.D // fail if the test doesn't provide specific data which causes our pricing provider to use its static price list return nil, errors.New("no pricing data provided") } - -func (e *EC2API) DescribeSpotPriceHistoryPagesWithContext(ctx aws.Context, input *ec2.DescribeSpotPriceHistoryInput, fn func(*ec2.DescribeSpotPriceHistoryOutput, bool) bool, _ ...request.Option) error { - out, err := e.DescribeSpotPriceHistoryWithContext(ctx, input) - if err != nil { - return err - } - fn(out, false) - return nil -} diff --git a/pkg/fake/eksapi.go b/pkg/fake/eksapi.go index 8d39706dd9e5..93560bb5e7e8 100644 --- a/pkg/fake/eksapi.go +++ b/pkg/fake/eksapi.go @@ -17,10 +17,11 @@ package fake import ( "context" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/eks" - "github.com/aws/aws-sdk-go/service/eks/eksiface" + "github.com/aws/aws-sdk-go-v2/service/eks" + ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/samber/lo" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) const () @@ -32,7 +33,7 @@ type EKSAPIBehavior struct { } type EKSAPI struct { - eksiface.EKSAPI + sdk.EKSAPI EKSAPIBehavior } @@ -46,11 +47,11 @@ func (s *EKSAPI) Reset() { s.DescribeClusterBehavior.Reset() } -func (s *EKSAPI) DescribeClusterWithContext(_ context.Context, input *eks.DescribeClusterInput, _ ...request.Option) (*eks.DescribeClusterOutput, error) { +func (s *EKSAPI) DescribeCluster(_ context.Context, input *eks.DescribeClusterInput, _ ...func(*eks.Options)) (*eks.DescribeClusterOutput, error) { return s.DescribeClusterBehavior.Invoke(input, func(*eks.DescribeClusterInput) (*eks.DescribeClusterOutput, error) { return &eks.DescribeClusterOutput{ - Cluster: &eks.Cluster{ - KubernetesNetworkConfig: &eks.KubernetesNetworkConfigResponse{ + Cluster: &ekstypes.Cluster{ + KubernetesNetworkConfig: &ekstypes.KubernetesNetworkConfigResponse{ ServiceIpv4Cidr: lo.ToPtr("10.100.0.0/16"), }, }, diff --git a/pkg/fake/iamapi.go b/pkg/fake/iamapi.go index dd5588232683..50cbcd8283e6 100644 --- a/pkg/fake/iamapi.go +++ b/pkg/fake/iamapi.go @@ -73,7 +73,7 @@ func (s *IAMAPI) GetInstanceProfile(_ context.Context, input *iam.GetInstancePro return &iam.GetInstanceProfileOutput{InstanceProfile: i}, nil } return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s cannot be found", aws.ToString(input.InstanceProfileName)), } @@ -87,7 +87,7 @@ func (s *IAMAPI) CreateInstanceProfile(_ context.Context, input *iam.CreateInsta if _, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { return nil, &smithy.GenericAPIError{ - Code: "EntityAlreadyExistsException", + Code: "EntityAlreadyExists", Message: fmt.Sprintf("Instance Profile %s already exists", aws.ToString(input.InstanceProfileName)), } @@ -121,7 +121,7 @@ func (s *IAMAPI) DeleteInstanceProfile(_ context.Context, input *iam.DeleteInsta return &iam.DeleteInstanceProfileOutput{}, nil } return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s cannot be found", aws.ToString(input.InstanceProfileName)), } @@ -140,7 +140,7 @@ func (s *IAMAPI) TagInstanceProfile(_ context.Context, input *iam.TagInstancePro return nil, nil } return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s cannot be found", aws.ToString(input.InstanceProfileName)), } @@ -164,7 +164,7 @@ func (s *IAMAPI) AddRoleToInstanceProfile(_ context.Context, input *iam.AddRoleT return nil, nil } return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s cannot be found", aws.ToString(input.InstanceProfileName)), } @@ -182,7 +182,7 @@ func (s *IAMAPI) RemoveRoleFromInstanceProfile(_ context.Context, input *iam.Rem }) if len(i.Roles) == len(newRoles) { return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s does not have role %s", aws.ToString(input.InstanceProfileName), aws.ToString(input.RoleName)), } @@ -191,7 +191,7 @@ func (s *IAMAPI) RemoveRoleFromInstanceProfile(_ context.Context, input *iam.Rem return nil, nil } return nil, &smithy.GenericAPIError{ - Code: "NoSuchEntityException", + Code: "NoSuchEntity", Message: fmt.Sprintf("Instance Profile %s cannot be found", aws.ToString(input.InstanceProfileName)), } diff --git a/pkg/fake/pricingapi.go b/pkg/fake/pricingapi.go index 5cf0dfcf5fd1..c9a61cb6aa17 100644 --- a/pkg/fake/pricingapi.go +++ b/pkg/fake/pricingapi.go @@ -15,17 +15,18 @@ limitations under the License. package fake import ( + "context" + "encoding/json" "errors" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/pricing" - "github.com/aws/aws-sdk-go/service/pricing/pricingiface" + "github.com/aws/aws-sdk-go-v2/service/pricing" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type PricingAPI struct { - pricingiface.PricingAPI + sdk.PricingAPI PricingBehavior } type PricingBehavior struct { @@ -38,24 +39,24 @@ func (p *PricingAPI) Reset() { p.GetProductsOutput.Reset() } -func (p *PricingAPI) GetProductsPagesWithContext(_ aws.Context, _ *pricing.GetProductsInput, fn func(*pricing.GetProductsOutput, bool) bool, _ ...request.Option) error { +func (p *PricingAPI) GetProducts(_ context.Context, _ *pricing.GetProductsInput, _ ...func(*pricing.Options)) (*pricing.GetProductsOutput, error) { if !p.NextError.IsNil() { - return p.NextError.Get() + return &pricing.GetProductsOutput{}, p.NextError.Get() } if !p.GetProductsOutput.IsNil() { - fn(p.GetProductsOutput.Clone(), false) - return nil + return p.GetProductsOutput.Clone(), nil } // fail if the test doesn't provide specific data which causes our pricing provider to use its static price list - return errors.New("no pricing data provided") + return &pricing.GetProductsOutput{}, errors.New("no pricing data provided") } -func NewOnDemandPrice(instanceType string, price float64) aws.JSONValue { +func NewOnDemandPrice(instanceType string, price float64) string { return NewOnDemandPriceWithCurrency(instanceType, price, "USD") + } -func NewOnDemandPriceWithCurrency(instanceType string, price float64, currency string) aws.JSONValue { - return aws.JSONValue{ +func NewOnDemandPriceWithCurrency(instanceType string, price float64, currency string) string { + data := map[string]interface{}{ "product": map[string]interface{}{ "attributes": map[string]interface{}{ "instanceType": instanceType, @@ -74,4 +75,6 @@ func NewOnDemandPriceWithCurrency(instanceType string, price float64, currency s }, }, } + ondemand, _ := json.Marshal(data) + return string(ondemand) } diff --git a/pkg/fake/utils.go b/pkg/fake/utils.go index 6e3067c41174..cb35646ce374 100644 --- a/pkg/fake/utils.go +++ b/pkg/fake/utils.go @@ -20,8 +20,9 @@ import ( "strings" "github.com/Pallinder/go-randomdata" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/sets" @@ -74,7 +75,7 @@ func PrivateDNSName() string { // SubnetsFromFleetRequest returns a unique slice of subnetIDs passed as overrides from a CreateFleetInput func SubnetsFromFleetRequest(createFleetInput *ec2.CreateFleetInput) []string { - return lo.Uniq(lo.Flatten(lo.Map(createFleetInput.LaunchTemplateConfigs, func(ltReq *ec2.FleetLaunchTemplateConfigRequest, _ int) []string { + return lo.Uniq(lo.Flatten(lo.Map(createFleetInput.LaunchTemplateConfigs, func(ltReq ec2types.FleetLaunchTemplateConfigRequest, _ int) []string { var subnets []string for _, override := range ltReq.Overrides { if override.SubnetId != nil { @@ -87,39 +88,39 @@ func SubnetsFromFleetRequest(createFleetInput *ec2.CreateFleetInput) []string { // FilterDescribeSecurtyGroups filters the passed in security groups based on the filters passed in. // Filters are chained with a logical "AND" -func FilterDescribeSecurtyGroups(sgs []*ec2.SecurityGroup, filters []*ec2.Filter) []*ec2.SecurityGroup { - return lo.Filter(sgs, func(group *ec2.SecurityGroup, _ int) bool { +func FilterDescribeSecurtyGroups(sgs []ec2types.SecurityGroup, filters []ec2types.Filter) []ec2types.SecurityGroup { + return lo.Filter(sgs, func(group ec2types.SecurityGroup, _ int) bool { return Filter(filters, *group.GroupId, *group.GroupName, group.Tags) }) } // FilterDescribeSubnets filters the passed in subnets based on the filters passed in. // Filters are chained with a logical "AND" -func FilterDescribeSubnets(subnets []*ec2.Subnet, filters []*ec2.Filter) []*ec2.Subnet { - return lo.Filter(subnets, func(subnet *ec2.Subnet, _ int) bool { +func FilterDescribeSubnets(subnets []ec2types.Subnet, filters []ec2types.Filter) []ec2types.Subnet { + return lo.Filter(subnets, func(subnet ec2types.Subnet, _ int) bool { return Filter(filters, *subnet.SubnetId, "", subnet.Tags) }) } -func FilterDescribeImages(images []*ec2.Image, filters []*ec2.Filter) []*ec2.Image { - return lo.Filter(images, func(image *ec2.Image, _ int) bool { +func FilterDescribeImages(images []ec2types.Image, filters []ec2types.Filter) []ec2types.Image { + return lo.Filter(images, func(image ec2types.Image, _ int) bool { return Filter(filters, *image.ImageId, *image.Name, image.Tags) }) } //nolint:gocyclo -func Filter(filters []*ec2.Filter, id, name string, tags []*ec2.Tag) bool { - return lo.EveryBy(filters, func(filter *ec2.Filter) bool { - switch filterName := aws.StringValue(filter.Name); { +func Filter(filters []ec2types.Filter, id, name string, tags []ec2types.Tag) bool { + return lo.EveryBy(filters, func(filter ec2types.Filter) bool { + switch filterName := aws.ToString(filter.Name); { case filterName == "subnet-id" || filterName == "group-id" || filterName == "image-id": for _, val := range filter.Values { - if id == aws.StringValue(val) { + if id == val { return true } } case filterName == "group-name" || filterName == "name": for _, val := range filter.Values { - if name == aws.StringValue(val) { + if name == val { return true } } @@ -128,7 +129,7 @@ func Filter(filters []*ec2.Filter, id, name string, tags []*ec2.Tag) bool { return true } default: - panic(fmt.Sprintf("Unsupported mock filter %q", filter)) + panic(fmt.Sprintf("Unsupported mock filter %v", filter)) } return false }) @@ -136,23 +137,23 @@ func Filter(filters []*ec2.Filter, id, name string, tags []*ec2.Tag) bool { // matchTags is a predicate that matches a slice of tags with a tag: or tag-keys filter // nolint: gocyclo -func matchTags(tags []*ec2.Tag, filter *ec2.Filter) bool { +func matchTags(tags []ec2types.Tag, filter ec2types.Filter) bool { if strings.HasPrefix(*filter.Name, "tag:") { tagKey := strings.Split(*filter.Name, ":")[1] for _, val := range filter.Values { for _, tag := range tags { - if tagKey == *tag.Key && (*val == "*" || *val == *tag.Value) { + if tagKey == *tag.Key && (val == "*" || val == *tag.Value) { return true } } } } else if strings.HasPrefix(*filter.Name, "tag-key") { for _, v := range filter.Values { - if aws.StringValue(v) == "*" { + if v == "*" { return true } for _, t := range tags { - if aws.StringValue(t.Key) == aws.StringValue(v) { + if lo.FromPtr(t.Key) == v { return true } } @@ -161,30 +162,30 @@ func matchTags(tags []*ec2.Tag, filter *ec2.Filter) bool { return false } -func MakeInstances() []*ec2.InstanceTypeInfo { - var instanceTypes []*ec2.InstanceTypeInfo +func MakeInstances() []ec2types.InstanceTypeInfo { + var instanceTypes []ec2types.InstanceTypeInfo ctx := options.ToContext(context.Background(), &options.Options{IsolatedVPC: true}) // Use keys from the static pricing data so that we guarantee pricing for the data // Create uniform instance data so all of them schedule for a given pod for _, it := range pricing.NewDefaultProvider(ctx, nil, nil, "us-east-1").InstanceTypes() { - instanceTypes = append(instanceTypes, &ec2.InstanceTypeInfo{ - InstanceType: aws.String(it), - ProcessorInfo: &ec2.ProcessorInfo{ - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + instanceTypes = append(instanceTypes, ec2types.InstanceTypeInfo{ + InstanceType: it, + ProcessorInfo: &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ec2types.ArchitectureTypeX8664}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(1), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(1), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(8192), }, - NetworkInfo: &ec2.NetworkInfo{ - Ipv4AddressesPerInterface: aws.Int64(10), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{{ - NetworkCardIndex: lo.ToPtr(int64(0)), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkInfo: &ec2types.NetworkInfo{ + Ipv4AddressesPerInterface: aws.Int32(10), + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{{ + NetworkCardIndex: lo.ToPtr(int32(0)), + MaximumNetworkInterfaces: aws.Int32(3), }}, }, SupportedUsageClasses: DefaultSupportedUsageClasses, @@ -193,20 +194,20 @@ func MakeInstances() []*ec2.InstanceTypeInfo { return instanceTypes } -func MakeUniqueInstancesAndFamilies(instances []*ec2.InstanceTypeInfo, numInstanceFamilies int) ([]*ec2.InstanceTypeInfo, sets.Set[string]) { - var instanceTypes []*ec2.InstanceTypeInfo +func MakeUniqueInstancesAndFamilies(instances []ec2types.InstanceTypeInfo, numInstanceFamilies int) ([]ec2types.InstanceTypeInfo, sets.Set[string]) { + var instanceTypes []ec2types.InstanceTypeInfo instanceFamilies := sets.Set[string]{} for _, it := range instances { var found bool for instFamily := range instanceFamilies { - if strings.Split(*it.InstanceType, ".")[0] == instFamily { + if strings.Split(string(it.InstanceType), ".")[0] == instFamily { found = true break } } if !found { instanceTypes = append(instanceTypes, it) - instanceFamilies.Insert(strings.Split(*it.InstanceType, ".")[0]) + instanceFamilies.Insert(strings.Split(string(it.InstanceType), ".")[0]) if len(instanceFamilies) == numInstanceFamilies { break } @@ -215,12 +216,12 @@ func MakeUniqueInstancesAndFamilies(instances []*ec2.InstanceTypeInfo, numInstan return instanceTypes, instanceFamilies } -func MakeInstanceOfferings(instanceTypes []*ec2.InstanceTypeInfo) []*ec2.InstanceTypeOffering { - var instanceTypeOfferings []*ec2.InstanceTypeOffering +func MakeInstanceOfferings(instanceTypes []ec2types.InstanceTypeInfo) []ec2types.InstanceTypeOffering { + var instanceTypeOfferings []ec2types.InstanceTypeOffering // Create uniform instance offering data so all of them schedule for a given pod for _, instanceType := range instanceTypes { - instanceTypeOfferings = append(instanceTypeOfferings, &ec2.InstanceTypeOffering{ + instanceTypeOfferings = append(instanceTypeOfferings, ec2types.InstanceTypeOffering{ InstanceType: instanceType.InstanceType, Location: aws.String("test-zone-1a"), }) diff --git a/pkg/fake/zz_generated.describe_instance_types.go b/pkg/fake/zz_generated.describe_instance_types.go index d1a1dca2114a..4a3b89a232cd 100644 --- a/pkg/fake/zz_generated.describe_instance_types.go +++ b/pkg/fake/zz_generated.describe_instance_types.go @@ -17,8 +17,9 @@ limitations under the License. package fake import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) // GENERATED FILE. DO NOT EDIT DIRECTLY. @@ -26,840 +27,840 @@ import ( // You can add instance types by adding to the --instance-types CLI flag var defaultDescribeInstanceTypesOutput = &ec2.DescribeInstanceTypesOutput{ - InstanceTypes: []*ec2.InstanceTypeInfo{ + InstanceTypes: []ec2types.InstanceTypeInfo{ { - InstanceType: aws.String("c6g.large"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "c6g.large", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AWS"), - SupportedArchitectures: aws.StringSlice([]string{"arm64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"arm64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(2), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(2), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(4096), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(630), - BaselineIops: aws.Int64(3600), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(630), + BaselineIops: aws.Int32(3600), BaselineThroughputInMBps: aws.Float64(78.75), - MaximumBandwidthInMbps: aws.Int64(4750), - MaximumIops: aws.Int64(20000), + MaximumBandwidthInMbps: aws.Int32(4750), + MaximumIops: aws.Int32(20000), MaximumThroughputInMBps: aws.Float64(593.75), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(3), - Ipv4AddressesPerInterface: aws.Int64(10), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(3), + Ipv4AddressesPerInterface: aws.Int32(10), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(3), }, }, }, }, { - InstanceType: aws.String("dl1.24xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "dl1.24xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(48), - DefaultVCpus: aws.Int64(96), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(48), + DefaultVCpus: aws.Int32(96), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(786432), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(19000), - BaselineIops: aws.Int64(80000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(19000), + BaselineIops: aws.Int32(80000), BaselineThroughputInMBps: aws.Float64(2375.00), - MaximumBandwidthInMbps: aws.Int64(19000), - MaximumIops: aws.Int64(80000), + MaximumBandwidthInMbps: aws.Int32(19000), + MaximumIops: aws.Int32(80000), MaximumThroughputInMBps: aws.Float64(2375.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - GpuInfo: &ec2.GpuInfo{ - Gpus: []*ec2.GpuDeviceInfo{ + GpuInfo: &ec2types.GpuInfo{ + Gpus: []ec2types.GpuDeviceInfo{ { Name: aws.String("Gaudi HL-205"), Manufacturer: aws.String("Habana"), - Count: aws.Int64(8), - MemoryInfo: &ec2.GpuDeviceMemoryInfo{ - SizeInMiB: aws.Int64(32768), + Count: aws.Int32(8), + MemoryInfo: &ec2types.GpuDeviceMemoryInfo{ + SizeInMiB: aws.Int32(32768), }, }, }, }, - InstanceStorageInfo: &ec2.InstanceStorageInfo{NvmeSupport: aws.String("required"), + InstanceStorageInfo: &ec2types.InstanceStorageInfo{NvmeSupport: "required", TotalSizeInGB: aws.Int64(4000), }, - NetworkInfo: &ec2.NetworkInfo{ - EfaInfo: &ec2.EfaInfo{ - MaximumEfaInterfaces: aws.Int64(4), + NetworkInfo: &ec2types.NetworkInfo{ + EfaInfo: &ec2types.EfaInfo{ + MaximumEfaInterfaces: aws.Int32(4), }, - MaximumNetworkInterfaces: aws.Int64(60), - Ipv4AddressesPerInterface: aws.Int64(50), + MaximumNetworkInterfaces: aws.Int32(60), + Ipv4AddressesPerInterface: aws.Int32(50), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(15), }, { - NetworkCardIndex: aws.Int64(1), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(1), + MaximumNetworkInterfaces: aws.Int32(15), }, { - NetworkCardIndex: aws.Int64(2), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(2), + MaximumNetworkInterfaces: aws.Int32(15), }, { - NetworkCardIndex: aws.Int64(3), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(3), + MaximumNetworkInterfaces: aws.Int32(15), }, }, }, }, { - InstanceType: aws.String("g4ad.16xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "g4ad.16xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AMD"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(32), - DefaultVCpus: aws.Int64(64), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(32), + DefaultVCpus: aws.Int32(64), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(262144), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(6300), - BaselineIops: aws.Int64(26667), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(6300), + BaselineIops: aws.Int32(26667), BaselineThroughputInMBps: aws.Float64(787.50), - MaximumBandwidthInMbps: aws.Int64(6300), - MaximumIops: aws.Int64(26667), + MaximumBandwidthInMbps: aws.Int32(6300), + MaximumIops: aws.Int32(26667), MaximumThroughputInMBps: aws.Float64(787.50), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - GpuInfo: &ec2.GpuInfo{ - Gpus: []*ec2.GpuDeviceInfo{ + GpuInfo: &ec2types.GpuInfo{ + Gpus: []ec2types.GpuDeviceInfo{ { Name: aws.String("Radeon Pro V520"), Manufacturer: aws.String("AMD"), - Count: aws.Int64(4), - MemoryInfo: &ec2.GpuDeviceMemoryInfo{ - SizeInMiB: aws.Int64(8192), + Count: aws.Int32(4), + MemoryInfo: &ec2types.GpuDeviceMemoryInfo{ + SizeInMiB: aws.Int32(8192), }, }, }, }, - InstanceStorageInfo: &ec2.InstanceStorageInfo{NvmeSupport: aws.String("required"), + InstanceStorageInfo: &ec2types.InstanceStorageInfo{NvmeSupport: "required", TotalSizeInGB: aws.Int64(2400), }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(8), - Ipv4AddressesPerInterface: aws.Int64(30), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(8), + Ipv4AddressesPerInterface: aws.Int32(30), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(8), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(8), }, }, }, }, { - InstanceType: aws.String("g4dn.8xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "g4dn.8xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(16), - DefaultVCpus: aws.Int64(32), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(16), + DefaultVCpus: aws.Int32(32), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(131072), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(9500), - BaselineIops: aws.Int64(40000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(9500), + BaselineIops: aws.Int32(40000), BaselineThroughputInMBps: aws.Float64(1187.50), - MaximumBandwidthInMbps: aws.Int64(9500), - MaximumIops: aws.Int64(40000), + MaximumBandwidthInMbps: aws.Int32(9500), + MaximumIops: aws.Int32(40000), MaximumThroughputInMBps: aws.Float64(1187.50), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - GpuInfo: &ec2.GpuInfo{ - Gpus: []*ec2.GpuDeviceInfo{ + GpuInfo: &ec2types.GpuInfo{ + Gpus: []ec2types.GpuDeviceInfo{ { Name: aws.String("T4"), Manufacturer: aws.String("NVIDIA"), - Count: aws.Int64(1), - MemoryInfo: &ec2.GpuDeviceMemoryInfo{ - SizeInMiB: aws.Int64(16384), + Count: aws.Int32(1), + MemoryInfo: &ec2types.GpuDeviceMemoryInfo{ + SizeInMiB: aws.Int32(16384), }, }, }, }, - InstanceStorageInfo: &ec2.InstanceStorageInfo{NvmeSupport: aws.String("required"), + InstanceStorageInfo: &ec2types.InstanceStorageInfo{NvmeSupport: "required", TotalSizeInGB: aws.Int64(900), }, - NetworkInfo: &ec2.NetworkInfo{ - EfaInfo: &ec2.EfaInfo{ - MaximumEfaInterfaces: aws.Int64(1), + NetworkInfo: &ec2types.NetworkInfo{ + EfaInfo: &ec2types.EfaInfo{ + MaximumEfaInterfaces: aws.Int32(1), }, - MaximumNetworkInterfaces: aws.Int64(4), - Ipv4AddressesPerInterface: aws.Int64(15), + MaximumNetworkInterfaces: aws.Int32(4), + Ipv4AddressesPerInterface: aws.Int32(15), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(4), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(4), }, }, }, }, { - InstanceType: aws.String("inf2.24xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "inf2.24xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AMD"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(48), - DefaultVCpus: aws.Int64(96), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(48), + DefaultVCpus: aws.Int32(96), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(393216), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(30000), - BaselineIops: aws.Int64(120000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(30000), + BaselineIops: aws.Int32(120000), BaselineThroughputInMBps: aws.Float64(3750.00), - MaximumBandwidthInMbps: aws.Int64(30000), - MaximumIops: aws.Int64(120000), + MaximumBandwidthInMbps: aws.Int32(30000), + MaximumIops: aws.Int32(120000), MaximumThroughputInMBps: aws.Float64(3750.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NeuronInfo: &ec2.NeuronInfo{ - NeuronDevices: []*ec2.NeuronDeviceInfo{ + NeuronInfo: &ec2types.NeuronInfo{ + NeuronDevices: []ec2types.NeuronDeviceInfo{ { - Count: aws.Int64(6), + Count: aws.Int32(6), Name: aws.String("Inferentia2"), - CoreInfo: &ec2.NeuronDeviceCoreInfo{ - Count: aws.Int64(2), - Version: aws.Int64(2), + CoreInfo: &ec2types.NeuronDeviceCoreInfo{ + Count: aws.Int32(2), + Version: aws.Int32(2), }, - MemoryInfo: &ec2.NeuronDeviceMemoryInfo{ - SizeInMiB: aws.Int64(32768), + MemoryInfo: &ec2types.NeuronDeviceMemoryInfo{ + SizeInMiB: aws.Int32(32768), }, }, }, }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(15), - Ipv4AddressesPerInterface: aws.Int64(50), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(15), + Ipv4AddressesPerInterface: aws.Int32(50), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(15), }, }, }, }, { - InstanceType: aws.String("inf2.xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "inf2.xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AMD"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(2), - DefaultVCpus: aws.Int64(4), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(2), + DefaultVCpus: aws.Int32(4), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(16384), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(1250), - BaselineIops: aws.Int64(6000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(1250), + BaselineIops: aws.Int32(6000), BaselineThroughputInMBps: aws.Float64(156.25), - MaximumBandwidthInMbps: aws.Int64(10000), - MaximumIops: aws.Int64(40000), + MaximumBandwidthInMbps: aws.Int32(10000), + MaximumIops: aws.Int32(40000), MaximumThroughputInMBps: aws.Float64(1250.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NeuronInfo: &ec2.NeuronInfo{ - NeuronDevices: []*ec2.NeuronDeviceInfo{ + NeuronInfo: &ec2types.NeuronInfo{ + NeuronDevices: []ec2types.NeuronDeviceInfo{ { - Count: aws.Int64(1), + Count: aws.Int32(1), Name: aws.String("Inferentia2"), - CoreInfo: &ec2.NeuronDeviceCoreInfo{ - Count: aws.Int64(2), - Version: aws.Int64(2), + CoreInfo: &ec2types.NeuronDeviceCoreInfo{ + Count: aws.Int32(2), + Version: aws.Int32(2), }, - MemoryInfo: &ec2.NeuronDeviceMemoryInfo{ - SizeInMiB: aws.Int64(32768), + MemoryInfo: &ec2types.NeuronDeviceMemoryInfo{ + SizeInMiB: aws.Int32(32768), }, }, }, }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(4), - Ipv4AddressesPerInterface: aws.Int64(15), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(4), + Ipv4AddressesPerInterface: aws.Int32(15), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(4), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(4), }, }, }, }, { - InstanceType: aws.String("m5.large"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "m5.large", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(1), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(1), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(8192), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(650), - BaselineIops: aws.Int64(3600), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(650), + BaselineIops: aws.Int32(3600), BaselineThroughputInMBps: aws.Float64(81.25), - MaximumBandwidthInMbps: aws.Int64(4750), - MaximumIops: aws.Int64(18750), + MaximumBandwidthInMbps: aws.Int32(4750), + MaximumIops: aws.Int32(18750), MaximumThroughputInMBps: aws.Float64(593.75), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(3), - Ipv4AddressesPerInterface: aws.Int64(10), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(3), + Ipv4AddressesPerInterface: aws.Int32(10), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(3), }, }, }, }, { - InstanceType: aws.String("m5.metal"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "m5.metal", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(true), - Hypervisor: aws.String(""), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(48), - DefaultVCpus: aws.Int64(96), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(48), + DefaultVCpus: aws.Int32(96), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(393216), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(19000), - BaselineIops: aws.Int64(80000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(19000), + BaselineIops: aws.Int32(80000), BaselineThroughputInMBps: aws.Float64(2375.00), - MaximumBandwidthInMbps: aws.Int64(19000), - MaximumIops: aws.Int64(80000), + MaximumBandwidthInMbps: aws.Int32(19000), + MaximumIops: aws.Int32(80000), MaximumThroughputInMBps: aws.Float64(2375.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(15), - Ipv4AddressesPerInterface: aws.Int64(50), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(15), + Ipv4AddressesPerInterface: aws.Int32(50), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(15), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(15), }, }, }, }, { - InstanceType: aws.String("m5.xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "m5.xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(2), - DefaultVCpus: aws.Int64(4), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(2), + DefaultVCpus: aws.Int32(4), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(16384), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(1150), - BaselineIops: aws.Int64(6000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(1150), + BaselineIops: aws.Int32(6000), BaselineThroughputInMBps: aws.Float64(143.75), - MaximumBandwidthInMbps: aws.Int64(4750), - MaximumIops: aws.Int64(18750), + MaximumBandwidthInMbps: aws.Int32(4750), + MaximumIops: aws.Int32(18750), MaximumThroughputInMBps: aws.Float64(593.75), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(4), - Ipv4AddressesPerInterface: aws.Int64(15), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(4), + Ipv4AddressesPerInterface: aws.Int32(15), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(4), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(4), }, }, }, }, { - InstanceType: aws.String("m6idn.32xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "m6idn.32xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(64), - DefaultVCpus: aws.Int64(128), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(64), + DefaultVCpus: aws.Int32(128), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(524288), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(100000), - BaselineIops: aws.Int64(400000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(100000), + BaselineIops: aws.Int32(400000), BaselineThroughputInMBps: aws.Float64(12500.00), - MaximumBandwidthInMbps: aws.Int64(100000), - MaximumIops: aws.Int64(400000), + MaximumBandwidthInMbps: aws.Int32(100000), + MaximumIops: aws.Int32(400000), MaximumThroughputInMBps: aws.Float64(12500.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - InstanceStorageInfo: &ec2.InstanceStorageInfo{NvmeSupport: aws.String("required"), + InstanceStorageInfo: &ec2types.InstanceStorageInfo{NvmeSupport: "required", TotalSizeInGB: aws.Int64(7600), }, - NetworkInfo: &ec2.NetworkInfo{ - EfaInfo: &ec2.EfaInfo{ - MaximumEfaInterfaces: aws.Int64(2), + NetworkInfo: &ec2types.NetworkInfo{ + EfaInfo: &ec2types.EfaInfo{ + MaximumEfaInterfaces: aws.Int32(2), }, - MaximumNetworkInterfaces: aws.Int64(16), - Ipv4AddressesPerInterface: aws.Int64(50), + MaximumNetworkInterfaces: aws.Int32(16), + Ipv4AddressesPerInterface: aws.Int32(50), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(8), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(8), }, { - NetworkCardIndex: aws.Int64(1), - MaximumNetworkInterfaces: aws.Int64(8), + NetworkCardIndex: aws.Int32(1), + MaximumNetworkInterfaces: aws.Int32(8), }, }, }, }, { - InstanceType: aws.String("p3.8xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "p3.8xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("xen"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "xen", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(16), - DefaultVCpus: aws.Int64(32), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(16), + DefaultVCpus: aws.Int32(32), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(249856), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(7000), - BaselineIops: aws.Int64(40000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(7000), + BaselineIops: aws.Int32(40000), BaselineThroughputInMBps: aws.Float64(875.00), - MaximumBandwidthInMbps: aws.Int64(7000), - MaximumIops: aws.Int64(40000), + MaximumBandwidthInMbps: aws.Int32(7000), + MaximumIops: aws.Int32(40000), MaximumThroughputInMBps: aws.Float64(875.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("unsupported"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "unsupported", }, - GpuInfo: &ec2.GpuInfo{ - Gpus: []*ec2.GpuDeviceInfo{ + GpuInfo: &ec2types.GpuInfo{ + Gpus: []ec2types.GpuDeviceInfo{ { Name: aws.String("V100"), Manufacturer: aws.String("NVIDIA"), - Count: aws.Int64(4), - MemoryInfo: &ec2.GpuDeviceMemoryInfo{ - SizeInMiB: aws.Int64(16384), + Count: aws.Int32(4), + MemoryInfo: &ec2types.GpuDeviceMemoryInfo{ + SizeInMiB: aws.Int32(16384), }, }, }, }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(8), - Ipv4AddressesPerInterface: aws.Int64(30), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(8), + Ipv4AddressesPerInterface: aws.Int32(30), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(8), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(8), }, }, }, }, { - InstanceType: aws.String("t3.large"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "t3.large", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(true), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(1), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(1), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(8192), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(695), - BaselineIops: aws.Int64(4000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(695), + BaselineIops: aws.Int32(4000), BaselineThroughputInMBps: aws.Float64(86.88), - MaximumBandwidthInMbps: aws.Int64(2780), - MaximumIops: aws.Int64(15700), + MaximumBandwidthInMbps: aws.Int32(2780), + MaximumIops: aws.Int32(15700), MaximumThroughputInMBps: aws.Float64(347.50), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(3), - Ipv4AddressesPerInterface: aws.Int64(12), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(3), + Ipv4AddressesPerInterface: aws.Int32(12), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(3), }, }, }, }, { - InstanceType: aws.String("t4g.medium"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "t4g.medium", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(true), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AWS"), - SupportedArchitectures: aws.StringSlice([]string{"arm64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"arm64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(2), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(2), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(4096), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(347), - BaselineIops: aws.Int64(2000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(347), + BaselineIops: aws.Int32(2000), BaselineThroughputInMBps: aws.Float64(43.38), - MaximumBandwidthInMbps: aws.Int64(2085), - MaximumIops: aws.Int64(11800), + MaximumBandwidthInMbps: aws.Int32(2085), + MaximumIops: aws.Int32(11800), MaximumThroughputInMBps: aws.Float64(260.62), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(3), - Ipv4AddressesPerInterface: aws.Int64(6), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(3), + Ipv4AddressesPerInterface: aws.Int32(6), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(3), }, }, }, }, { - InstanceType: aws.String("t4g.small"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "t4g.small", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(true), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AWS"), - SupportedArchitectures: aws.StringSlice([]string{"arm64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"arm64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(2), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(2), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(2048), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(174), - BaselineIops: aws.Int64(1000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(174), + BaselineIops: aws.Int32(1000), BaselineThroughputInMBps: aws.Float64(21.75), - MaximumBandwidthInMbps: aws.Int64(2085), - MaximumIops: aws.Int64(11800), + MaximumBandwidthInMbps: aws.Int32(2085), + MaximumIops: aws.Int32(11800), MaximumThroughputInMBps: aws.Float64(260.62), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(3), - Ipv4AddressesPerInterface: aws.Int64(4), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(3), + Ipv4AddressesPerInterface: aws.Int32(4), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(3), }, }, }, }, { - InstanceType: aws.String("t4g.xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "t4g.xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(true), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("AWS"), - SupportedArchitectures: aws.StringSlice([]string{"arm64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"arm64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(4), - DefaultVCpus: aws.Int64(4), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(4), + DefaultVCpus: aws.Int32(4), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(16384), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(695), - BaselineIops: aws.Int64(4000), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(695), + BaselineIops: aws.Int32(4000), BaselineThroughputInMBps: aws.Float64(86.88), - MaximumBandwidthInMbps: aws.Int64(2780), - MaximumIops: aws.Int64(15700), + MaximumBandwidthInMbps: aws.Int32(2780), + MaximumIops: aws.Int32(15700), MaximumThroughputInMBps: aws.Float64(347.50), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(4), - Ipv4AddressesPerInterface: aws.Int64(15), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(4), + Ipv4AddressesPerInterface: aws.Int32(15), EncryptionInTransitSupported: aws.Bool(false), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(4), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(4), }, }, }, }, { - InstanceType: aws.String("trn1.2xlarge"), - SupportedUsageClasses: aws.StringSlice([]string{"on-demand", "spot"}), - SupportedVirtualizationTypes: aws.StringSlice([]string{"hvm"}), + InstanceType: "trn1.2xlarge", + SupportedUsageClasses: []ec2types.UsageClassType{"on-demand", "spot"}, + SupportedVirtualizationTypes: []ec2types.VirtualizationType{"hvm"}, BurstablePerformanceSupported: aws.Bool(false), BareMetal: aws.Bool(false), - Hypervisor: aws.String("nitro"), - ProcessorInfo: &ec2.ProcessorInfo{ + Hypervisor: "nitro", + ProcessorInfo: &ec2types.ProcessorInfo{ Manufacturer: aws.String("Intel"), - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + SupportedArchitectures: []ec2types.ArchitectureType{"x86_64"}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(4), - DefaultVCpus: aws.Int64(8), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(4), + DefaultVCpus: aws.Int32(8), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(32768), }, - EbsInfo: &ec2.EbsInfo{ - EbsOptimizedInfo: &ec2.EbsOptimizedInfo{ - BaselineBandwidthInMbps: aws.Int64(5000), - BaselineIops: aws.Int64(16250), + EbsInfo: &ec2types.EbsInfo{ + EbsOptimizedInfo: &ec2types.EbsOptimizedInfo{ + BaselineBandwidthInMbps: aws.Int32(5000), + BaselineIops: aws.Int32(16250), BaselineThroughputInMBps: aws.Float64(625.00), - MaximumBandwidthInMbps: aws.Int64(20000), - MaximumIops: aws.Int64(65000), + MaximumBandwidthInMbps: aws.Int32(20000), + MaximumIops: aws.Int32(65000), MaximumThroughputInMBps: aws.Float64(2500.00), }, - EbsOptimizedSupport: aws.String("default"), - EncryptionSupport: aws.String("supported"), - NvmeSupport: aws.String("required"), + EbsOptimizedSupport: "default", + EncryptionSupport: "supported", + NvmeSupport: "required", }, - NeuronInfo: &ec2.NeuronInfo{ - NeuronDevices: []*ec2.NeuronDeviceInfo{ + NeuronInfo: &ec2types.NeuronInfo{ + NeuronDevices: []ec2types.NeuronDeviceInfo{ { - Count: aws.Int64(1), + Count: aws.Int32(1), Name: aws.String("Trainium"), - CoreInfo: &ec2.NeuronDeviceCoreInfo{ - Count: aws.Int64(2), - Version: aws.Int64(2), + CoreInfo: &ec2types.NeuronDeviceCoreInfo{ + Count: aws.Int32(2), + Version: aws.Int32(2), }, - MemoryInfo: &ec2.NeuronDeviceMemoryInfo{ - SizeInMiB: aws.Int64(32768), + MemoryInfo: &ec2types.NeuronDeviceMemoryInfo{ + SizeInMiB: aws.Int32(32768), }, }, }, }, - InstanceStorageInfo: &ec2.InstanceStorageInfo{NvmeSupport: aws.String("required"), + InstanceStorageInfo: &ec2types.InstanceStorageInfo{NvmeSupport: "required", TotalSizeInGB: aws.Int64(474), }, - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(4), - Ipv4AddressesPerInterface: aws.Int64(15), + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(4), + Ipv4AddressesPerInterface: aws.Int32(15), EncryptionInTransitSupported: aws.Bool(true), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{ + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{ { - NetworkCardIndex: aws.Int64(0), - MaximumNetworkInterfaces: aws.Int64(4), + NetworkCardIndex: aws.Int32(0), + MaximumNetworkInterfaces: aws.Int32(4), }, }, }, diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 417776b02f53..a46922a937b0 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -22,23 +22,16 @@ import ( "net" "os" - configV2 "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/middleware" + config "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" - iamV2 "github.com/aws/aws-sdk-go-v2/service/iam" - + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/ssm" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - awsclient "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/eks" - "github.com/aws/aws-sdk-go/service/eks/eksiface" - prometheusv1 "github.com/jonathan-innis/aws-sdk-go-prometheus/v1" + + "github.com/aws/smithy-go" "github.com/patrickmn/go-cache" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -52,6 +45,9 @@ import ( karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" "sigs.k8s.io/karpenter/pkg/operator" + prometheusv2 "github.com/jonathan-innis/aws-sdk-go-prometheus/v2" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" awscache "github.com/aws/karpenter-provider-aws/pkg/cache" "github.com/aws/karpenter-provider-aws/pkg/operator/options" "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily" @@ -73,8 +69,7 @@ func init() { // Operator is injected into the AWS CloudProvider's factories type Operator struct { *operator.Operator - - Session *session.Session + Config aws.Config UnavailableOfferingsCache *awscache.UnavailableOfferings SSMCache *cache.Cache SubnetProvider subnet.Provider @@ -91,40 +86,27 @@ type Operator struct { } func NewOperator(ctx context.Context, operator *operator.Operator) (context.Context, *Operator) { - //v1 - config := &aws.Config{ - STSRegionalEndpoint: endpoints.RegionalSTSEndpoint, - } - - // prometheusv1.WithPrometheusMetrics is used until the upstream aws-sdk-go or aws-sdk-go-v2 supports - // Prometheus metrics for client-side metrics out-of-the-box - // See: https://github.com/aws/aws-sdk-go-v2/issues/1744 - sess := prometheusv1.WithPrometheusMetrics(WithUserAgent(session.Must(session.NewSession( - request.WithRetryer( - config, - awsclient.DefaultRetryer{NumMaxRetries: awsclient.DefaultRetryerMaxNumRetries}, - ), - ))), crmetrics.Registry) - - if *sess.Config.Region == "" { + cfg := prometheusv2.WithPrometheusMetrics(WithUserAgent(lo.Must(config.LoadDefaultConfig(ctx))), crmetrics.Registry) + if cfg.Region == "" { log.FromContext(ctx).V(1).Info("retrieving region from IMDS") - region, err := ec2metadata.New(sess).Region() - *sess.Config.Region = lo.Must(region, err, "failed to get region from metadata server") + region := lo.Must(imds.NewFromConfig(cfg).GetRegion(ctx, nil)) + cfg.Region = region.Region } - ec2api := ec2.New(sess) + ec2api := ec2.NewFromConfig(cfg) + eksapi := eks.NewFromConfig(cfg) + log.FromContext(ctx).WithValues("region", cfg.Region).V(1).Info("discovered region") if err := CheckEC2Connectivity(ctx, ec2api); err != nil { log.FromContext(ctx).Error(err, "ec2 api connectivity check failed") os.Exit(1) } - log.FromContext(ctx).WithValues("region", *sess.Config.Region).V(1).Info("discovered region") - clusterEndpoint, err := ResolveClusterEndpoint(ctx, eks.New(sess)) + log.FromContext(ctx).WithValues("region", cfg.Region).V(1).Info("discovered region") + clusterEndpoint, err := ResolveClusterEndpoint(ctx, eksapi) if err != nil { log.FromContext(ctx).Error(err, "failed detecting cluster endpoint") os.Exit(1) } else { log.FromContext(ctx).WithValues("cluster-endpoint", clusterEndpoint).V(1).Info("discovered cluster endpoint") } - // We perform best-effort on resolving the kube-dns IP kubeDNSIP, err := KubeDNSIP(ctx, operator.KubernetesInterface) if err != nil { // If we fail to get the kube-dns IP, we don't want to crash because this causes issues with custom DNS setups @@ -133,27 +115,17 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont } else { log.FromContext(ctx).WithValues("kube-dns-ip", kubeDNSIP).V(1).Info("discovered kube dns") } - - //v2 - //Once everything is migrated we will need to update prometheus metrics to v2 - cfg := lo.Must(configV2.LoadDefaultConfig(ctx, configV2.WithRetryMaxAttempts(3))) - if cfg.Region == "" { - log.FromContext(ctx).V(1).Info("retrieving region from IMDS") - metaDataClient := imds.NewFromConfig(cfg) - cfg.Region = lo.Must(metaDataClient.GetRegion(ctx, nil)).Region - } - unavailableOfferingsCache := awscache.NewUnavailableOfferings() ssmCache := cache.New(awscache.SSMCacheTTL, awscache.DefaultCleanupInterval) subnetProvider := subnet.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AvailableIPAddressTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AssociatePublicIPAddressTTL, awscache.DefaultCleanupInterval)) securityGroupProvider := securitygroup.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval)) - instanceProfileProvider := instanceprofile.NewDefaultProvider(cfg.Region, iamV2.NewFromConfig(cfg), cache.New(awscache.InstanceProfileTTL, awscache.DefaultCleanupInterval)) + instanceProfileProvider := instanceprofile.NewDefaultProvider(cfg.Region, iam.NewFromConfig(cfg), cache.New(awscache.InstanceProfileTTL, awscache.DefaultCleanupInterval)) pricingProvider := pricing.NewDefaultProvider( ctx, - pricing.NewAPI(sess, *sess.Config.Region), + pricing.NewAPI(cfg), ec2api, - *sess.Config.Region, + cfg.Region, ) versionProvider := version.NewDefaultProvider(operator.KubernetesInterface, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval)) ssmProvider := ssmp.NewDefaultProvider(ssm.NewFromConfig(cfg), ssmCache) @@ -163,7 +135,7 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont ctx, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval), ec2api, - eks.New(sess), + eksapi, amiResolver, securityGroupProvider, subnetProvider, @@ -177,11 +149,11 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont cache.New(awscache.DiscoveredCapacityCacheTTL, awscache.DefaultCleanupInterval), ec2api, subnetProvider, - instancetype.NewDefaultResolver(*sess.Config.Region, pricingProvider, unavailableOfferingsCache), + instancetype.NewDefaultResolver(cfg.Region, pricingProvider, unavailableOfferingsCache), ) instanceProvider := instance.NewDefaultProvider( ctx, - aws.StringValue(sess.Config.Region), + cfg.Region, ec2api, unavailableOfferingsCache, subnetProvider, @@ -190,7 +162,7 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont return ctx, &Operator{ Operator: operator, - Session: sess, + Config: cfg, UnavailableOfferingsCache: unavailableOfferingsCache, SSMCache: ssmCache, SubnetProvider: subnetProvider, @@ -208,29 +180,33 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont } // WithUserAgent adds a karpenter specific user-agent string to AWS session -func WithUserAgent(sess *session.Session) *session.Session { +func WithUserAgent(cfg aws.Config) aws.Config { userAgent := fmt.Sprintf("karpenter.sh-%s", operator.Version) - sess.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler(userAgent)) - return sess + cfg.APIOptions = append(cfg.APIOptions, + middleware.AddUserAgentKey(userAgent), + ) + return cfg } // CheckEC2Connectivity makes a dry-run call to DescribeInstanceTypes. If it fails, we provide an early indicator that we // are having issues connecting to the EC2 API. -func CheckEC2Connectivity(ctx context.Context, api ec2iface.EC2API) error { - _, err := api.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{DryRun: aws.Bool(true)}) - var aerr awserr.Error - if errors.As(err, &aerr) && aerr.Code() == "DryRunOperation" { +func CheckEC2Connectivity(ctx context.Context, api sdk.EC2API) error { + _, err := api.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{ + DryRun: aws.Bool(true), + }) + var apiErr smithy.APIError + if errors.As(err, &apiErr) && apiErr.ErrorCode() == "DryRunOperation" { return nil } return err } -func ResolveClusterEndpoint(ctx context.Context, eksAPI eksiface.EKSAPI) (string, error) { +func ResolveClusterEndpoint(ctx context.Context, eksAPI sdk.EKSAPI) (string, error) { clusterEndpointFromOptions := options.FromContext(ctx).ClusterEndpoint if clusterEndpointFromOptions != "" { return clusterEndpointFromOptions, nil // cluster endpoint is explicitly set } - out, err := eksAPI.DescribeClusterWithContext(ctx, &eks.DescribeClusterInput{ + out, err := eksAPI.DescribeCluster(ctx, &eks.DescribeClusterInput{ Name: aws.String(options.FromContext(ctx).ClusterName), }) if err != nil { diff --git a/pkg/operator/suite_test.go b/pkg/operator/suite_test.go index 13aed00a0fd2..57e46f95df88 100644 --- a/pkg/operator/suite_test.go +++ b/pkg/operator/suite_test.go @@ -21,7 +21,8 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks" + ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/samber/lo" coretest "sigs.k8s.io/karpenter/pkg/test" @@ -84,7 +85,7 @@ var _ = Describe("Operator", func() { })) fakeEKSAPI.DescribeClusterBehavior.Output.Set( &eks.DescribeClusterOutput{ - Cluster: &eks.Cluster{ + Cluster: &ekstypes.Cluster{ Endpoint: lo.ToPtr("https://cluster-endpoint.test-cluster.k8s.local"), }, }, diff --git a/pkg/providers/amifamily/al2.go b/pkg/providers/amifamily/al2.go index f5d7162ecfe4..ce6e400edf53 100644 --- a/pkg/providers/amifamily/al2.go +++ b/pkg/providers/amifamily/al2.go @@ -18,10 +18,10 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" corev1 "k8s.io/api/core/v1" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "sigs.k8s.io/karpenter/pkg/scheduling" @@ -73,9 +73,9 @@ func (a AL2) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k } return DescribeImageQuery{ - Filters: []*ec2.Filter{{ + Filters: []ec2types.Filter{{ Name: lo.ToPtr("image-id"), - Values: lo.ToSlicePtr(lo.Keys(ids)), + Values: lo.Keys(ids), }}, KnownRequirements: lo.MapValues(ids, func(variants []Variant, _ string) []scheduling.Requirements { return lo.Map(variants, func(v Variant, _ int) scheduling.Requirements { return v.Requirements() }) diff --git a/pkg/providers/amifamily/al2023.go b/pkg/providers/amifamily/al2023.go index dbb73e8078a0..a6a315f408e6 100644 --- a/pkg/providers/amifamily/al2023.go +++ b/pkg/providers/amifamily/al2023.go @@ -23,7 +23,7 @@ import ( "sigs.k8s.io/karpenter/pkg/cloudprovider" "sigs.k8s.io/karpenter/pkg/scheduling" - "github.com/aws/aws-sdk-go/service/ec2" + 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/providers/amifamily/bootstrap" @@ -38,8 +38,8 @@ type AL2023 struct { func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider, k8sVersion string, amiVersion string) (DescribeImageQuery, error) { ids := map[string]Variant{} for arch, variants := range map[string][]Variant{ - "x86_64": []Variant{VariantStandard, VariantNvidia, VariantNeuron}, - "arm64": []Variant{VariantStandard}, + "x86_64": {VariantStandard, VariantNvidia, VariantNeuron}, + "arm64": {VariantStandard}, } { for _, variant := range variants { path := a.resolvePath(arch, string(variant), k8sVersion, amiVersion) @@ -59,9 +59,9 @@ func (a AL2023) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provider } return DescribeImageQuery{ - Filters: []*ec2.Filter{{ + Filters: []ec2types.Filter{{ Name: lo.ToPtr("image-id"), - Values: lo.ToSlicePtr(lo.Keys(ids)), + Values: lo.Keys(ids), }}, KnownRequirements: lo.MapValues(ids, func(v Variant, _ string) []scheduling.Requirements { return []scheduling.Requirements{v.Requirements()} diff --git a/pkg/providers/amifamily/ami.go b/pkg/providers/amifamily/ami.go index 1ddd264bcbbe..b99b6d6f67b0 100644 --- a/pkg/providers/amifamily/ami.go +++ b/pkg/providers/amifamily/ami.go @@ -19,9 +19,9 @@ import ( "fmt" "sync" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" "github.com/samber/lo" @@ -29,6 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/aws/karpenter-provider-aws/pkg/providers/version" "sigs.k8s.io/karpenter/pkg/cloudprovider" @@ -47,13 +48,13 @@ type DefaultProvider struct { clk clock.Clock cache *cache.Cache - ec2api ec2iface.EC2API + ec2api sdk.EC2API cm *pretty.ChangeMonitor versionProvider version.Provider ssmProvider ssm.Provider } -func NewDefaultProvider(clk clock.Clock, versionProvider version.Provider, ssmProvider ssm.Provider, ec2api ec2iface.EC2API, cache *cache.Cache) *DefaultProvider { +func NewDefaultProvider(clk clock.Clock, versionProvider version.Provider, ssmProvider ssm.Provider, ec2api sdk.EC2API, cache *cache.Cache) *DefaultProvider { return &DefaultProvider{ clk: clk, cache: cache, @@ -100,12 +101,12 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v return []DescribeImageQuery{query}, nil } - idFilter := &ec2.Filter{Name: aws.String("image-id")} + idFilter := ec2types.Filter{Name: aws.String("image-id")} queries := []DescribeImageQuery{} for _, term := range nodeClass.Spec.AMISelectorTerms { switch { case term.ID != "": - idFilter.Values = append(idFilter.Values, aws.String(term.ID)) + idFilter.Values = append(idFilter.Values, term.ID) default: query := DescribeImageQuery{ Owners: lo.Ternary(term.Owner != "", []string{term.Owner}, []string{}), @@ -116,22 +117,22 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v query = DescribeImageQuery{ Owners: lo.Ternary(term.Owner != "", []string{term.Owner}, []string{"self", "amazon"}), } - query.Filters = append(query.Filters, &ec2.Filter{ + query.Filters = append(query.Filters, ec2types.Filter{ Name: aws.String("name"), - Values: aws.StringSlice([]string{term.Name}), + Values: []string{term.Name}, }) } for k, v := range term.Tags { if v == "*" { - query.Filters = append(query.Filters, &ec2.Filter{ + query.Filters = append(query.Filters, ec2types.Filter{ Name: aws.String("tag-key"), - Values: []*string{aws.String(k)}, + Values: []string{k}, }) } else { - query.Filters = append(query.Filters, &ec2.Filter{ + query.Filters = append(query.Filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", k)), - Values: []*string{aws.String(v)}, + Values: []string{v}, }) } } @@ -139,7 +140,7 @@ func (p *DefaultProvider) DescribeImageQueries(ctx context.Context, nodeClass *v } } if len(idFilter.Values) > 0 { - queries = append(queries, DescribeImageQuery{Filters: []*ec2.Filter{idFilter}}) + queries = append(queries, DescribeImageQuery{Filters: []ec2types.Filter{idFilter}}) } return queries, nil } @@ -157,9 +158,14 @@ func (p *DefaultProvider) amis(ctx context.Context, queries []DescribeImageQuery } images := map[uint64]AMI{} for _, query := range queries { - if err = p.ec2api.DescribeImagesPagesWithContext(ctx, query.DescribeImagesInput(), func(page *ec2.DescribeImagesOutput, _ bool) bool { + paginator := ec2.NewDescribeImagesPaginator(p.ec2api, query.DescribeImagesInput()) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("describing images, %w", err) + } for _, image := range page.Images { - arch, ok := v1.AWSToKubeArchitectures[lo.FromPtr(image.Architecture)] + arch, ok := v1.AWSToKubeArchitectures[string(image.Architecture)] if !ok { continue } @@ -187,9 +193,6 @@ func (p *DefaultProvider) amis(ctx context.Context, queries []DescribeImageQuery images[reqsHash] = ami } } - return true - }); err != nil { - return nil, fmt.Errorf("describing images, %w", err) } } p.cache.SetDefault(fmt.Sprintf("%d", hash), AMIs(lo.Values(images))) diff --git a/pkg/providers/amifamily/bootstrap/bottlerocket.go b/pkg/providers/amifamily/bootstrap/bottlerocket.go index c4205e0befa2..9dfa60042ce1 100644 --- a/pkg/providers/amifamily/bootstrap/bottlerocket.go +++ b/pkg/providers/amifamily/bootstrap/bottlerocket.go @@ -22,7 +22,7 @@ import ( "github.com/imdario/mergo" "github.com/samber/lo" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" ) diff --git a/pkg/providers/amifamily/bootstrap/custom.go b/pkg/providers/amifamily/bootstrap/custom.go index 3dfa647a90fe..e85c88dba494 100644 --- a/pkg/providers/amifamily/bootstrap/custom.go +++ b/pkg/providers/amifamily/bootstrap/custom.go @@ -17,7 +17,7 @@ package bootstrap import ( "encoding/base64" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" ) type Custom struct { @@ -25,5 +25,5 @@ type Custom struct { } func (e Custom) Script() (string, error) { - return base64.StdEncoding.EncodeToString([]byte(aws.StringValue(e.Options.CustomUserData))), nil + return base64.StdEncoding.EncodeToString([]byte(aws.ToString(e.Options.CustomUserData))), nil } diff --git a/pkg/providers/amifamily/bottlerocket.go b/pkg/providers/amifamily/bottlerocket.go index 404ac9db3831..b0498976b52b 100644 --- a/pkg/providers/amifamily/bottlerocket.go +++ b/pkg/providers/amifamily/bottlerocket.go @@ -28,8 +28,8 @@ import ( "sigs.k8s.io/karpenter/pkg/cloudprovider" "sigs.k8s.io/karpenter/pkg/scheduling" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) @@ -64,9 +64,9 @@ func (b Bottlerocket) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Pr } return DescribeImageQuery{ - Filters: []*ec2.Filter{{ + Filters: []ec2types.Filter{{ Name: lo.ToPtr("image-id"), - Values: lo.ToSlicePtr(lo.Keys(ids)), + Values: lo.Keys(ids), }}, KnownRequirements: lo.MapValues(ids, func(variants []Variant, _ string) []scheduling.Requirements { return lo.Map(variants, func(v Variant, _ int) scheduling.Requirements { return v.Requirements() }) diff --git a/pkg/providers/amifamily/resolver.go b/pkg/providers/amifamily/resolver.go index b1ae185f4721..8566b05a4d8a 100644 --- a/pkg/providers/amifamily/resolver.go +++ b/pkg/providers/amifamily/resolver.go @@ -19,8 +19,8 @@ import ( "fmt" "net" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -37,7 +37,7 @@ import ( var DefaultEBS = v1.BlockDevice{ Encrypted: aws.Bool(true), - VolumeType: aws.String(ec2.VolumeTypeGp3), + VolumeType: aws.String(string(ec2types.VolumeTypeGp3)), VolumeSize: lo.ToPtr(resource.MustParse("20Gi")), } @@ -177,10 +177,10 @@ func GetAMIFamily(amiFamily string, options *Options) AMIFamily { func (o Options) DefaultMetadataOptions() *v1.MetadataOptions { return &v1.MetadataOptions{ - HTTPEndpoint: aws.String(ec2.LaunchTemplateInstanceMetadataEndpointStateEnabled), - HTTPProtocolIPv6: aws.String(lo.Ternary(o.KubeDNSIP == nil || o.KubeDNSIP.To4() != nil, ec2.LaunchTemplateInstanceMetadataProtocolIpv6Disabled, ec2.LaunchTemplateInstanceMetadataProtocolIpv6Enabled)), + HTTPEndpoint: aws.String(string(ec2types.InstanceMetadataEndpointStateDisabled)), + HTTPProtocolIPv6: aws.String(lo.Ternary(o.KubeDNSIP == nil || o.KubeDNSIP.To4() != nil, string(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Disabled), string(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Enabled))), HTTPPutResponseHopLimit: aws.Int64(2), - HTTPTokens: aws.String(ec2.LaunchTemplateHttpTokensStateRequired), + HTTPTokens: aws.String(string(ec2types.LaunchTemplateHttpTokensStateRequired)), } } @@ -236,7 +236,7 @@ func (r DefaultResolver) resolveLaunchTemplate(nodeClass *v1.EC2NodeClass, nodeC ), BlockDeviceMappings: nodeClass.Spec.BlockDeviceMappings, MetadataOptions: nodeClass.Spec.MetadataOptions, - DetailedMonitoring: aws.BoolValue(nodeClass.Spec.DetailedMonitoring), + DetailedMonitoring: aws.ToBool(nodeClass.Spec.DetailedMonitoring), AMIID: amiID, InstanceTypes: instanceTypes, EFACount: efaCount, diff --git a/pkg/providers/amifamily/suite_test.go b/pkg/providers/amifamily/suite_test.go index d6f29dd39346..f0844e25831a 100644 --- a/pkg/providers/amifamily/suite_test.go +++ b/pkg/providers/amifamily/suite_test.go @@ -24,8 +24,9 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -74,13 +75,13 @@ var _ = BeforeSuite(func() { var _ = BeforeEach(func() { // Set up the DescribeImages API so that we can call it by ID with the mock parameters that we generate awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(amd64AMI), ImageId: aws.String("amd64-ami-id"), CreationDate: aws.String(time.Time{}.Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -89,8 +90,8 @@ var _ = BeforeEach(func() { Name: aws.String(arm64AMI), ImageId: aws.String("arm64-ami-id"), CreationDate: aws.String(time.Time{}.Add(time.Minute).Format(time.RFC3339)), - Architecture: aws.String("arm64"), - Tags: []*ec2.Tag{ + Architecture: "arm64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(arm64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -99,8 +100,8 @@ var _ = BeforeEach(func() { Name: aws.String(amd64NvidiaAMI), ImageId: aws.String("amd64-nvidia-ami-id"), CreationDate: aws.String(time.Time{}.Add(2 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64NvidiaAMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -109,8 +110,8 @@ var _ = BeforeEach(func() { Name: aws.String(arm64NvidiaAMI), ImageId: aws.String("arm64-nvidia-ami-id"), CreationDate: aws.String(time.Time{}.Add(2 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("arm64"), - Tags: []*ec2.Tag{ + Architecture: "arm64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(arm64NvidiaAMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -264,14 +265,14 @@ var _ = Describe("AMIProvider", func() { }) }) Context("AMI Tag Requirements", func() { - var img *ec2.Image + var img ec2types.Image BeforeEach(func() { - img = &ec2.Image{ + img = ec2types.Image{ Name: aws.String(amd64AMI), ImageId: aws.String("amd64-ami-id"), CreationDate: aws.String(time.Now().Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, {Key: aws.String(corev1.LabelInstanceTypeStable), Value: aws.String("m5.large")}, @@ -279,7 +280,7 @@ var _ = Describe("AMIProvider", func() { }, } awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ img, }, }) @@ -294,9 +295,9 @@ var _ = Describe("AMIProvider", func() { Expect(err).ToNot(HaveOccurred()) Expect(amis).To(HaveLen(1)) Expect(amis).To(ConsistOf(amifamily.AMI{ - Name: aws.StringValue(img.Name), - AmiID: aws.StringValue(img.ImageId), - CreationDate: aws.StringValue(img.CreationDate), + Name: aws.ToString(img.Name), + AmiID: aws.ToString(img.ImageId), + CreationDate: aws.ToString(img.CreationDate), Requirements: scheduling.NewRequirements( scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, karpv1.ArchitectureAmd64), ), @@ -317,14 +318,14 @@ var _ = Describe("AMIProvider", func() { // Here we have two AMIs one which is deprecated and newer and one which is older and non-deprecated without a deprecation time // List operation will priortize the non-deprecated AMI without the deprecation time awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(amd64AMI), ImageId: aws.String("ami-5678"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(-1 * time.Hour).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -333,8 +334,8 @@ var _ = Describe("AMIProvider", func() { Name: aws.String(amd64AMI), ImageId: aws.String("ami-1234"), CreationDate: aws.String("2020-08-31T00:08:42.000Z"), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -357,14 +358,14 @@ var _ = Describe("AMIProvider", func() { // Here we have two AMIs one which is deprecated and one which is non-deprecated both with the same creation time // List operation will priortize the non-deprecated AMI awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(amd64AMI), ImageId: aws.String("ami-5678"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(-10 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -374,8 +375,8 @@ var _ = Describe("AMIProvider", func() { ImageId: aws.String("ami-1234"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(10 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -399,14 +400,14 @@ var _ = Describe("AMIProvider", func() { // Here we have two AMIs one which is deprecated and one which is non-deprecated both with the same creation time but with different names // List operation will priortize the non-deprecated AMI awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String("test-ami-2"), ImageId: aws.String("ami-5678"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(-10 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-2")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -416,8 +417,8 @@ var _ = Describe("AMIProvider", func() { ImageId: aws.String("ami-1234"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(10 * time.Minute).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String("test-ami-1")}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -440,14 +441,14 @@ var _ = Describe("AMIProvider", func() { It("should priortize the newer ami if both are deprecated", func() { //Both amis are deprecated and have the same deprecation time awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{ - Images: []*ec2.Image{ + Images: []ec2types.Image{ { Name: aws.String(amd64AMI), ImageId: aws.String("ami-5678"), CreationDate: aws.String("2021-08-31T00:12:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(-1 * time.Hour).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -457,8 +458,8 @@ var _ = Describe("AMIProvider", func() { ImageId: aws.String("ami-1234"), CreationDate: aws.String("2020-08-31T00:08:42.000Z"), DeprecationTime: aws.String(awsEnv.Clock.Now().Add(-1 * time.Hour).Format(time.RFC3339)), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{ + Architecture: "x86_64", + Tags: []ec2types.Tag{ {Key: aws.String("Name"), Value: aws.String(amd64AMI)}, {Key: aws.String("foo"), Value: aws.String("bar")}, }, @@ -495,10 +496,10 @@ var _ = Describe("AMIProvider", func() { Expect(err).To(BeNil()) ExpectConsistsOfAMIQueries([]amifamily.DescribeImageQuery{ { - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("tag:Name"), - Values: aws.StringSlice([]string{"my-ami"}), + Values: []string{"my-ami"}, }, }, Owners: []string{}, @@ -516,10 +517,10 @@ var _ = Describe("AMIProvider", func() { Expect(err).To(BeNil()) ExpectConsistsOfAMIQueries([]amifamily.DescribeImageQuery{ { - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("name"), - Values: aws.StringSlice([]string{"my-ami"}), + Values: []string{"my-ami"}, }, }, Owners: []string{ @@ -545,10 +546,10 @@ var _ = Describe("AMIProvider", func() { Expect(err).To(BeNil()) ExpectConsistsOfAMIQueries([]amifamily.DescribeImageQuery{ { - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("image-id"), - Values: aws.StringSlice([]string{"ami-abcd1234", "ami-cafeaced"}), + Values: []string{"ami-abcd1234", "ami-cafeaced"}, }, }, }, @@ -596,19 +597,19 @@ var _ = Describe("AMIProvider", func() { ExpectConsistsOfAMIQueries([]amifamily.DescribeImageQuery{ { Owners: []string{"0123456789"}, - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("name"), - Values: aws.StringSlice([]string{"my-name"}), + Values: []string{"my-name"}, }, }, }, { Owners: []string{"self"}, - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("name"), - Values: aws.StringSlice([]string{"my-name"}), + Values: []string{"my-name"}, }, }, }, @@ -806,7 +807,7 @@ func ExpectConsistsOfAMIQueries(expected, actual []amifamily.DescribeImageQuery) for _, elem := range list { for _, f := range elem.Filters { sort.Slice(f.Values, func(i, j int) bool { - return lo.FromPtr(f.Values[i]) < lo.FromPtr(f.Values[j]) + return f.Values[i] < f.Values[j] }) } sort.Slice(elem.Owners, func(i, j int) bool { return elem.Owners[i] < elem.Owners[j] }) diff --git a/pkg/providers/amifamily/types.go b/pkg/providers/amifamily/types.go index e37399d1927e..469109350c31 100644 --- a/pkg/providers/amifamily/types.go +++ b/pkg/providers/amifamily/types.go @@ -20,8 +20,9 @@ import ( "sort" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -96,7 +97,7 @@ func (v Variant) Requirements() scheduling.Requirements { } type DescribeImageQuery struct { - Filters []*ec2.Filter + Filters []ec2types.Filter Owners []string // KnownRequirements is a map from image IDs to a set of known requirements. // When discovering image IDs via SSM we know additional requirements which aren't surfaced by ec2:DescribeImage (e.g. GPU / Neuron compatibility) @@ -109,9 +110,9 @@ func (q DescribeImageQuery) DescribeImagesInput() *ec2.DescribeImagesInput { return &ec2.DescribeImagesInput{ // Don't include filters in the Describe Images call as EC2 API doesn't allow empty filters. Filters: lo.Ternary(len(q.Filters) > 0, q.Filters, nil), - Owners: lo.Ternary(len(q.Owners) > 0, lo.ToSlicePtr(q.Owners), nil), + Owners: lo.Ternary(len(q.Owners) > 0, q.Owners, nil), IncludeDeprecated: aws.Bool(true), - MaxResults: aws.Int64(1000), + MaxResults: aws.Int32(1000), } } diff --git a/pkg/providers/amifamily/windows.go b/pkg/providers/amifamily/windows.go index f9f20f1ca539..08e284176c81 100644 --- a/pkg/providers/amifamily/windows.go +++ b/pkg/providers/amifamily/windows.go @@ -25,8 +25,8 @@ import ( "github.com/samber/lo" "k8s.io/apimachinery/pkg/api/resource" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap" "github.com/aws/karpenter-provider-aws/pkg/providers/ssm" @@ -55,12 +55,12 @@ func (w Windows) DescribeImageQuery(ctx context.Context, ssmProvider ssm.Provide return DescribeImageQuery{}, fmt.Errorf(`failed to discover any AMIs for alias "windows%s@%s"`, w.Version, amiVersion) } return DescribeImageQuery{ - Filters: []*ec2.Filter{&ec2.Filter{ + Filters: []ec2types.Filter{{ Name: lo.ToPtr("image-id"), - Values: []*string{lo.ToPtr(imageID)}, + Values: []string{imageID}, }}, KnownRequirements: map[string][]scheduling.Requirements{ - imageID: []scheduling.Requirements{scheduling.NewRequirements( + imageID: {scheduling.NewRequirements( scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpIn, string(corev1.Windows)), scheduling.NewRequirement(corev1.LabelWindowsBuild, corev1.NodeSelectorOpIn, w.Build), )}, diff --git a/pkg/providers/instance/instance.go b/pkg/providers/instance/instance.go index 0fa6e54eccfe..bced1867a0d3 100644 --- a/pkg/providers/instance/instance.go +++ b/pkg/providers/instance/instance.go @@ -22,10 +22,12 @@ import ( "sort" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" + + "github.com/aws/aws-sdk-go-v2/aws" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "go.uber.org/multierr" corev1 "k8s.io/api/core/v1" @@ -54,9 +56,15 @@ const ( ) var ( - instanceStateFilter = &ec2.Filter{ - Name: aws.String("instance-state-name"), - Values: aws.StringSlice([]string{ec2.InstanceStateNamePending, ec2.InstanceStateNameRunning, ec2.InstanceStateNameStopping, ec2.InstanceStateNameStopped, ec2.InstanceStateNameShuttingDown}), + instanceStateFilter = ec2types.Filter{ + Name: aws.String("instance-state-name"), + Values: []string{ + string(ec2types.InstanceStateNamePending), + string(ec2types.InstanceStateNameRunning), + string(ec2types.InstanceStateNameStopping), + string(ec2types.InstanceStateNameStopped), + string(ec2types.InstanceStateNameShuttingDown), + }, } ) @@ -70,14 +78,14 @@ type Provider interface { type DefaultProvider struct { region string - ec2api ec2iface.EC2API + ec2api sdk.EC2API unavailableOfferings *cache.UnavailableOfferings subnetProvider subnet.Provider launchTemplateProvider launchtemplate.Provider ec2Batcher *batcher.EC2API } -func NewDefaultProvider(ctx context.Context, region string, ec2api ec2iface.EC2API, unavailableOfferings *cache.UnavailableOfferings, +func NewDefaultProvider(ctx context.Context, region string, ec2api sdk.EC2API, unavailableOfferings *cache.UnavailableOfferings, subnetProvider subnet.Provider, launchTemplateProvider launchtemplate.Provider) *DefaultProvider { return &DefaultProvider{ region: region, @@ -117,8 +125,8 @@ func (p *DefaultProvider) Create(ctx context.Context, nodeClass *v1.EC2NodeClass func (p *DefaultProvider) Get(ctx context.Context, id string) (*Instance, error) { out, err := p.ec2Batcher.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ - InstanceIds: aws.StringSlice([]string{id}), - Filters: []*ec2.Filter{instanceStateFilter}, + InstanceIds: []string{id}, + Filters: []ec2types.Filter{instanceStateFilter}, }) if awserrors.IsNotFound(err) { return nil, cloudprovider.NewNodeClaimNotFoundError(err) @@ -138,28 +146,31 @@ func (p *DefaultProvider) Get(ctx context.Context, id string) (*Instance, error) func (p *DefaultProvider) List(ctx context.Context) ([]*Instance, error) { var out = &ec2.DescribeInstancesOutput{} - err := p.ec2api.DescribeInstancesPagesWithContext(ctx, &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + + paginator := ec2.NewDescribeInstancesPaginator(p.ec2api, &ec2.DescribeInstancesInput{ + Filters: []ec2types.Filter{ { Name: aws.String("tag-key"), - Values: aws.StringSlice([]string{karpv1.NodePoolLabelKey}), + Values: []string{karpv1.NodePoolLabelKey}, }, { Name: aws.String("tag-key"), - Values: aws.StringSlice([]string{v1.LabelNodeClass}), + Values: []string{v1.LabelNodeClass}, }, { Name: aws.String("tag-key"), - Values: aws.StringSlice([]string{fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)}), + Values: []string{fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)}, }, instanceStateFilter, }, - }, func(page *ec2.DescribeInstancesOutput, _ bool) bool { - out.Reservations = append(out.Reservations, page.Reservations...) - return true }) - if err != nil { - return nil, fmt.Errorf("describing ec2 instances, %w", err) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + out.Reservations = append(out.Reservations, page.Reservations...) + if err != nil { + return nil, fmt.Errorf("describing ec2 instances, %w", err) + } } instances, err := instancesFromOutput(out) return instances, cloudprovider.IgnoreNodeClaimNotFoundError(err) @@ -167,7 +178,7 @@ func (p *DefaultProvider) List(ctx context.Context) ([]*Instance, error) { func (p *DefaultProvider) Delete(ctx context.Context, id string) error { if _, err := p.ec2Batcher.TerminateInstances(ctx, &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(id)}, + InstanceIds: []string{id}, }); err != nil { if awserrors.IsNotFound(err) { return cloudprovider.NewNodeClaimNotFoundError(fmt.Errorf("instance already terminated")) @@ -184,11 +195,11 @@ func (p *DefaultProvider) Delete(ctx context.Context, id string) error { } func (p *DefaultProvider) CreateTags(ctx context.Context, id string, tags map[string]string) error { - ec2Tags := lo.MapToSlice(tags, func(key, value string) *ec2.Tag { - return &ec2.Tag{Key: aws.String(key), Value: aws.String(value)} + ec2Tags := lo.MapToSlice(tags, func(key, value string) ec2types.Tag { + return ec2types.Tag{Key: aws.String(key), Value: aws.String(value)} }) - if _, err := p.ec2api.CreateTagsWithContext(ctx, &ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{id}), + if _, err := p.ec2api.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{id}, Tags: ec2Tags, }); err != nil { if awserrors.IsNotFound(err) { @@ -199,40 +210,40 @@ func (p *DefaultProvider) CreateTags(ctx context.Context, id string, tags map[st return nil } -func (p *DefaultProvider) launchInstance(ctx context.Context, nodeClass *v1.EC2NodeClass, nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, tags map[string]string) (*ec2.CreateFleetInstance, error) { +func (p *DefaultProvider) launchInstance(ctx context.Context, nodeClass *v1.EC2NodeClass, nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, tags map[string]string) (ec2types.CreateFleetInstance, error) { capacityType := p.getCapacityType(nodeClaim, instanceTypes) zonalSubnets, err := p.subnetProvider.ZonalSubnetsForLaunch(ctx, nodeClass, instanceTypes, capacityType) if err != nil { - return nil, fmt.Errorf("getting subnets, %w", err) + return ec2types.CreateFleetInstance{}, fmt.Errorf("getting subnets, %w", err) } // Get Launch Template Configs, which may differ due to GPU or Architecture requirements launchTemplateConfigs, err := p.getLaunchTemplateConfigs(ctx, nodeClass, nodeClaim, instanceTypes, zonalSubnets, capacityType, tags) if err != nil { - return nil, fmt.Errorf("getting launch template configs, %w", err) + return ec2types.CreateFleetInstance{}, fmt.Errorf("getting launch template configs, %w", err) } if err := p.checkODFallback(nodeClaim, instanceTypes, launchTemplateConfigs); err != nil { log.FromContext(ctx).Error(err, "failed while checking on-demand fallback") } // Create fleet createFleetInput := &ec2.CreateFleetInput{ - Type: aws.String(ec2.FleetTypeInstant), + Type: ec2types.FleetTypeInstant, Context: nodeClass.Spec.Context, LaunchTemplateConfigs: launchTemplateConfigs, - TargetCapacitySpecification: &ec2.TargetCapacitySpecificationRequest{ - DefaultTargetCapacityType: aws.String(capacityType), - TotalTargetCapacity: aws.Int64(1), + TargetCapacitySpecification: &ec2types.TargetCapacitySpecificationRequest{ + DefaultTargetCapacityType: ec2types.DefaultTargetCapacityType(capacityType), + TotalTargetCapacity: aws.Int32(1), }, - TagSpecifications: []*ec2.TagSpecification{ - {ResourceType: aws.String(ec2.ResourceTypeInstance), Tags: utils.MergeTags(tags)}, - {ResourceType: aws.String(ec2.ResourceTypeVolume), Tags: utils.MergeTags(tags)}, - {ResourceType: aws.String(ec2.ResourceTypeFleet), Tags: utils.MergeTags(tags)}, + TagSpecifications: []ec2types.TagSpecification{ + {ResourceType: ec2types.ResourceTypeInstance, Tags: utils.MergeTags(tags)}, + {ResourceType: ec2types.ResourceTypeVolume, Tags: utils.MergeTags(tags)}, + {ResourceType: ec2types.ResourceTypeFleet, Tags: utils.MergeTags(tags)}, }, } if capacityType == karpv1.CapacityTypeSpot { - createFleetInput.SpotOptions = &ec2.SpotOptionsRequest{AllocationStrategy: aws.String(ec2.SpotAllocationStrategyPriceCapacityOptimized)} + createFleetInput.SpotOptions = &ec2types.SpotOptionsRequest{AllocationStrategy: ec2types.SpotAllocationStrategyPriceCapacityOptimized} } else { - createFleetInput.OnDemandOptions = &ec2.OnDemandOptionsRequest{AllocationStrategy: aws.String(ec2.FleetOnDemandAllocationStrategyLowestPrice)} + createFleetInput.OnDemandOptions = &ec2types.OnDemandOptionsRequest{AllocationStrategy: ec2types.FleetOnDemandAllocationStrategyLowestPrice} } createFleetOutput, err := p.ec2Batcher.CreateFleet(ctx, createFleetInput) @@ -240,19 +251,19 @@ func (p *DefaultProvider) launchInstance(ctx context.Context, nodeClass *v1.EC2N if err != nil { if awserrors.IsLaunchTemplateNotFound(err) { for _, lt := range launchTemplateConfigs { - p.launchTemplateProvider.InvalidateCache(ctx, aws.StringValue(lt.LaunchTemplateSpecification.LaunchTemplateName), aws.StringValue(lt.LaunchTemplateSpecification.LaunchTemplateId)) + p.launchTemplateProvider.InvalidateCache(ctx, aws.ToString(lt.LaunchTemplateSpecification.LaunchTemplateName), aws.ToString(lt.LaunchTemplateSpecification.LaunchTemplateId)) } - return nil, fmt.Errorf("creating fleet %w", err) + return ec2types.CreateFleetInstance{}, fmt.Errorf("creating fleet %w", err) } - var reqFailure awserr.RequestFailure - if errors.As(err, &reqFailure) { - return nil, fmt.Errorf("creating fleet %w (%s)", err, reqFailure.RequestID()) + var reqErr *awshttp.ResponseError + if errors.As(err, &reqErr) { + return ec2types.CreateFleetInstance{}, fmt.Errorf("creating fleet %w (%v)", err, reqErr.ServiceRequestID()) } - return nil, fmt.Errorf("creating fleet %w", err) + return ec2types.CreateFleetInstance{}, fmt.Errorf("creating fleet %w", err) } p.updateUnavailableOfferingsCache(ctx, createFleetOutput.Errors, capacityType) if len(createFleetOutput.Instances) == 0 || len(createFleetOutput.Instances[0].InstanceIds) == 0 { - return nil, combineFleetErrors(createFleetOutput.Errors) + return ec2types.CreateFleetInstance{}, combineFleetErrors(createFleetOutput.Errors) } return createFleetOutput.Instances[0], nil } @@ -267,7 +278,7 @@ func getTags(ctx context.Context, nodeClass *v1.EC2NodeClass, nodeClaim *karpv1. return lo.Assign(nodeClass.Spec.Tags, staticTags) } -func (p *DefaultProvider) checkODFallback(nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, launchTemplateConfigs []*ec2.FleetLaunchTemplateConfigRequest) error { +func (p *DefaultProvider) checkODFallback(nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, launchTemplateConfigs []ec2types.FleetLaunchTemplateConfigRequest) error { // only evaluate for on-demand fallback if the capacity type for the request is OD and both OD and spot are allowed in requirements if p.getCapacityType(nodeClaim, instanceTypes) != karpv1.CapacityTypeOnDemand || !scheduling.NewNodeSelectorRequirementsWithMinValues(nodeClaim.Spec.Requirements...).Get(karpv1.CapacityTypeLabelKey).Has(karpv1.CapacityTypeSpot) { return nil @@ -277,11 +288,8 @@ func (p *DefaultProvider) checkODFallback(nodeClaim *karpv1.NodeClaim, instanceT instanceTypeZones := map[string]struct{}{} for _, ltc := range launchTemplateConfigs { for _, override := range ltc.Overrides { - if override.InstanceType != nil { - instanceTypeZones[*override.InstanceType] = struct{}{} - } + instanceTypeZones[string(override.InstanceType)] = struct{}{} } - } if len(instanceTypes) < instanceTypeFlexibilityThreshold { return fmt.Errorf("at least %d instance types are recommended when flexible to spot but requesting on-demand, "+ @@ -291,8 +299,8 @@ func (p *DefaultProvider) checkODFallback(nodeClaim *karpv1.NodeClaim, instanceT } func (p *DefaultProvider) getLaunchTemplateConfigs(ctx context.Context, nodeClass *v1.EC2NodeClass, nodeClaim *karpv1.NodeClaim, - instanceTypes []*cloudprovider.InstanceType, zonalSubnets map[string]*subnet.Subnet, capacityType string, tags map[string]string) ([]*ec2.FleetLaunchTemplateConfigRequest, error) { - var launchTemplateConfigs []*ec2.FleetLaunchTemplateConfigRequest + instanceTypes []*cloudprovider.InstanceType, zonalSubnets map[string]*subnet.Subnet, capacityType string, tags map[string]string) ([]ec2types.FleetLaunchTemplateConfigRequest, error) { + var launchTemplateConfigs []ec2types.FleetLaunchTemplateConfigRequest launchTemplates, err := p.launchTemplateProvider.EnsureAll(ctx, nodeClass, nodeClaim, instanceTypes, capacityType, tags) if err != nil { return nil, fmt.Errorf("getting launch templates, %w", err) @@ -300,9 +308,9 @@ func (p *DefaultProvider) getLaunchTemplateConfigs(ctx context.Context, nodeClas requirements := scheduling.NewNodeSelectorRequirementsWithMinValues(nodeClaim.Spec.Requirements...) requirements[karpv1.CapacityTypeLabelKey] = scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, capacityType) for _, launchTemplate := range launchTemplates { - launchTemplateConfig := &ec2.FleetLaunchTemplateConfigRequest{ + launchTemplateConfig := ec2types.FleetLaunchTemplateConfigRequest{ Overrides: p.getOverrides(launchTemplate.InstanceTypes, zonalSubnets, requirements, launchTemplate.ImageID), - LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ + LaunchTemplateSpecification: &ec2types.FleetLaunchTemplateSpecificationRequest{ LaunchTemplateName: aws.String(launchTemplate.Name), Version: aws.String("$Latest"), }, @@ -319,24 +327,24 @@ func (p *DefaultProvider) getLaunchTemplateConfigs(ctx context.Context, nodeClas // getOverrides creates and returns launch template overrides for the cross product of InstanceTypes and subnets (with subnets being constrained by // zones and the offerings in InstanceTypes) -func (p *DefaultProvider) getOverrides(instanceTypes []*cloudprovider.InstanceType, zonalSubnets map[string]*subnet.Subnet, reqs scheduling.Requirements, image string) []*ec2.FleetLaunchTemplateOverridesRequest { +func (p *DefaultProvider) getOverrides(instanceTypes []*cloudprovider.InstanceType, zonalSubnets map[string]*subnet.Subnet, reqs scheduling.Requirements, image string) []ec2types.FleetLaunchTemplateOverridesRequest { // Unwrap all the offerings to a flat slice that includes a pointer // to the parent instance type name type offeringWithParentName struct { cloudprovider.Offering - parentInstanceTypeName string + parentInstanceTypeName ec2types.InstanceType } var unwrappedOfferings []offeringWithParentName for _, it := range instanceTypes { ofs := lo.Map(it.Offerings.Available(), func(of cloudprovider.Offering, _ int) offeringWithParentName { return offeringWithParentName{ Offering: of, - parentInstanceTypeName: it.Name, + parentInstanceTypeName: ec2types.InstanceType(it.Name), } }) unwrappedOfferings = append(unwrappedOfferings, ofs...) } - var overrides []*ec2.FleetLaunchTemplateOverridesRequest + var overrides []ec2types.FleetLaunchTemplateOverridesRequest for _, offering := range unwrappedOfferings { if reqs.Compatible(offering.Requirements, scheduling.AllowUndefinedWellKnownLabels) != nil { continue @@ -345,8 +353,8 @@ func (p *DefaultProvider) getOverrides(instanceTypes []*cloudprovider.InstanceTy if !ok { continue } - overrides = append(overrides, &ec2.FleetLaunchTemplateOverridesRequest{ - InstanceType: aws.String(offering.parentInstanceTypeName), + overrides = append(overrides, ec2types.FleetLaunchTemplateOverridesRequest{ + InstanceType: offering.parentInstanceTypeName, SubnetId: lo.ToPtr(subnet.ID), ImageId: aws.String(image), // This is technically redundant, but is useful if we have to parse insufficient capacity errors from @@ -357,7 +365,7 @@ func (p *DefaultProvider) getOverrides(instanceTypes []*cloudprovider.InstanceTy return overrides } -func (p *DefaultProvider) updateUnavailableOfferingsCache(ctx context.Context, errors []*ec2.CreateFleetError, capacityType string) { +func (p *DefaultProvider) updateUnavailableOfferingsCache(ctx context.Context, errors []ec2types.CreateFleetError, capacityType string) { for _, err := range errors { if awserrors.IsUnfulfillableCapacity(err) { p.unavailableOfferings.MarkUnavailableForFleetErr(ctx, err, capacityType) @@ -480,7 +488,7 @@ func instancesFromOutput(out *ec2.DescribeInstancesOutput) ([]*Instance, error) if len(out.Reservations) == 0 { return nil, cloudprovider.NewNodeClaimNotFoundError(fmt.Errorf("instance not found")) } - instances := lo.Flatten(lo.Map(out.Reservations, func(r *ec2.Reservation, _ int) []*ec2.Instance { + instances := lo.Flatten(lo.Map(out.Reservations, func(r ec2types.Reservation, _ int) []ec2types.Instance { return r.Instances })) if len(instances) == 0 { @@ -488,21 +496,21 @@ func instancesFromOutput(out *ec2.DescribeInstancesOutput) ([]*Instance, error) } // Get a consistent ordering for instances sort.Slice(instances, func(i, j int) bool { - return aws.StringValue(instances[i].InstanceId) < aws.StringValue(instances[j].InstanceId) + return aws.ToString(instances[i].InstanceId) < aws.ToString(instances[j].InstanceId) }) - return lo.Map(instances, func(i *ec2.Instance, _ int) *Instance { return NewInstance(i) }), nil + return lo.Map(instances, func(i ec2types.Instance, _ int) *Instance { return NewInstance(i) }), nil } -func combineFleetErrors(fleetErrs []*ec2.CreateFleetError) (errs error) { +func combineFleetErrors(fleetErrs []ec2types.CreateFleetError) (errs error) { unique := sets.NewString() for _, err := range fleetErrs { - unique.Insert(fmt.Sprintf("%s: %s", aws.StringValue(err.ErrorCode), aws.StringValue(err.ErrorMessage))) + unique.Insert(fmt.Sprintf("%s: %s", aws.ToString(err.ErrorCode), aws.ToString(err.ErrorMessage))) } for errorCode := range unique { errs = multierr.Append(errs, errors.New(errorCode)) } // If all the Fleet errors are ICE errors then we should wrap the combined error in the generic ICE error - iceErrorCount := lo.CountBy(fleetErrs, func(err *ec2.CreateFleetError) bool { return awserrors.IsUnfulfillableCapacity(err) }) + iceErrorCount := lo.CountBy(fleetErrs, func(err ec2types.CreateFleetError) bool { return awserrors.IsUnfulfillableCapacity(err) }) if iceErrorCount == len(fleetErrs) { return cloudprovider.NewInsufficientCapacityError(fmt.Errorf("with fleet error(s), %w", errs)) } diff --git a/pkg/providers/instance/suite_test.go b/pkg/providers/instance/suite_test.go index a006d3db8f35..d9e701b8b9c4 100644 --- a/pkg/providers/instance/suite_test.go +++ b/pkg/providers/instance/suite_test.go @@ -22,8 +22,8 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -144,11 +144,11 @@ var _ = Describe("InstanceProvider", func() { instanceID := fake.InstanceID() awsEnv.EC2API.Instances.Store( instanceID, - &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: aws.String("owned"), @@ -167,13 +167,13 @@ var _ = Describe("InstanceProvider", func() { }, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, // Launch time was 1m ago LaunchTime: aws.Time(time.Now().Add(-time.Minute)), - InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceId: lo.ToPtr(instanceID), + InstanceType: "m5.large", }, ) ids.Insert(instanceID) @@ -183,18 +183,18 @@ var _ = Describe("InstanceProvider", func() { instanceID := fake.InstanceID() awsEnv.EC2API.Instances.Store( instanceID, - &ec2.Instance{ - State: &ec2.InstanceState{ - Name: aws.String(ec2.InstanceStateNameRunning), + ec2types.Instance{ + State: &ec2types.InstanceState{ + Name: ec2types.InstanceStateNameRunning, }, PrivateDnsName: aws.String(fake.PrivateDNSName()), - Placement: &ec2.Placement{ + Placement: &ec2types.Placement{ AvailabilityZone: aws.String(fake.DefaultRegion), }, // Launch time was 1m ago LaunchTime: aws.Time(time.Now().Add(-time.Minute)), - InstanceId: aws.String(instanceID), - InstanceType: aws.String("m5.large"), + InstanceId: lo.ToPtr(instanceID), + InstanceType: "m5.large", }, ) } diff --git a/pkg/providers/instance/types.go b/pkg/providers/instance/types.go index 5ee8e3b3a930..49f1ea7aaec7 100644 --- a/pkg/providers/instance/types.go +++ b/pkg/providers/instance/types.go @@ -17,8 +17,8 @@ package instance import ( "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" @@ -28,10 +28,10 @@ import ( // It contains all the common data that is needed to inject into the Machine from either of these responses type Instance struct { LaunchTime time.Time - State string + State ec2types.InstanceStateName ID string ImageID string - Type string + Type ec2types.InstanceType Zone string CapacityType string SecurityGroupIDs []string @@ -40,37 +40,37 @@ type Instance struct { EFAEnabled bool } -func NewInstance(out *ec2.Instance) *Instance { +func NewInstance(out ec2types.Instance) *Instance { return &Instance{ - LaunchTime: aws.TimeValue(out.LaunchTime), - State: aws.StringValue(out.State.Name), - ID: aws.StringValue(out.InstanceId), - ImageID: aws.StringValue(out.ImageId), - Type: aws.StringValue(out.InstanceType), - Zone: aws.StringValue(out.Placement.AvailabilityZone), + LaunchTime: aws.ToTime(out.LaunchTime), + State: out.State.Name, + ID: aws.ToString(out.InstanceId), + ImageID: aws.ToString(out.ImageId), + Type: out.InstanceType, + Zone: aws.ToString(out.Placement.AvailabilityZone), CapacityType: lo.Ternary(out.SpotInstanceRequestId != nil, karpv1.CapacityTypeSpot, karpv1.CapacityTypeOnDemand), - SecurityGroupIDs: lo.Map(out.SecurityGroups, func(securitygroup *ec2.GroupIdentifier, _ int) string { - return aws.StringValue(securitygroup.GroupId) + SecurityGroupIDs: lo.Map(out.SecurityGroups, func(securitygroup ec2types.GroupIdentifier, _ int) string { + return aws.ToString(securitygroup.GroupId) }), - SubnetID: aws.StringValue(out.SubnetId), - Tags: lo.SliceToMap(out.Tags, func(t *ec2.Tag) (string, string) { return aws.StringValue(t.Key), aws.StringValue(t.Value) }), - EFAEnabled: lo.ContainsBy(out.NetworkInterfaces, func(ni *ec2.InstanceNetworkInterface) bool { - return ni != nil && lo.FromPtr(ni.InterfaceType) == ec2.NetworkInterfaceTypeEfa + SubnetID: aws.ToString(out.SubnetId), + Tags: lo.SliceToMap(out.Tags, func(t ec2types.Tag) (string, string) { return aws.ToString(t.Key), aws.ToString(t.Value) }), + EFAEnabled: lo.ContainsBy(out.NetworkInterfaces, func(item ec2types.InstanceNetworkInterface) bool { + return item.InterfaceType != nil && *item.InterfaceType == string(ec2types.NetworkInterfaceTypeEfa) }), } } -func NewInstanceFromFleet(out *ec2.CreateFleetInstance, tags map[string]string, efaEnabled bool) *Instance { +func NewInstanceFromFleet(out ec2types.CreateFleetInstance, tags map[string]string, efaEnabled bool) *Instance { return &Instance{ LaunchTime: time.Now(), // estimate the launch time since we just launched - State: ec2.StatePending, - ID: aws.StringValue(out.InstanceIds[0]), - ImageID: aws.StringValue(out.LaunchTemplateAndOverrides.Overrides.ImageId), - Type: aws.StringValue(out.InstanceType), - Zone: aws.StringValue(out.LaunchTemplateAndOverrides.Overrides.AvailabilityZone), - CapacityType: aws.StringValue(out.Lifecycle), - SubnetID: aws.StringValue(out.LaunchTemplateAndOverrides.Overrides.SubnetId), + State: ec2types.InstanceStateNamePending, + ID: out.InstanceIds[0], + ImageID: aws.ToString(out.LaunchTemplateAndOverrides.Overrides.ImageId), + Type: out.InstanceType, + Zone: aws.ToString(out.LaunchTemplateAndOverrides.Overrides.AvailabilityZone), + CapacityType: string(out.Lifecycle), + SubnetID: aws.ToString(out.LaunchTemplateAndOverrides.Overrides.SubnetId), Tags: tags, EFAEnabled: efaEnabled, } diff --git a/pkg/providers/instanceprofile/instanceprofile.go b/pkg/providers/instanceprofile/instanceprofile.go index 0b5df39461a9..c16d8c5d9cd5 100644 --- a/pkg/providers/instanceprofile/instanceprofile.go +++ b/pkg/providers/instanceprofile/instanceprofile.go @@ -71,7 +71,7 @@ func (p *DefaultProvider) Create(ctx context.Context, m ResourceOwner) (string, var instanceProfile *iamtypes.InstanceProfile out, err := p.iamapi.GetInstanceProfile(ctx, &iam.GetInstanceProfileInput{InstanceProfileName: aws.String(profileName)}) if err != nil { - if !awserrors.IsNotFoundV2(err) { + if !awserrors.IsNotFound(err) { return "", fmt.Errorf("getting instance profile %q, %w", profileName, err) } o, err := p.iamapi.CreateInstanceProfile(ctx, &iam.CreateInstanceProfileInput{ @@ -124,7 +124,7 @@ func (p *DefaultProvider) Delete(ctx context.Context, m ResourceOwner) error { InstanceProfileName: aws.String(profileName), }) if err != nil { - return awserrors.IgnoreNotFoundV2(fmt.Errorf("getting instance profile %q, %w", profileName, err)) + return awserrors.IgnoreNotFound(fmt.Errorf("getting instance profile %q, %w", profileName, err)) } // Instance profiles can only have a single role assigned to them so this profile either has 1 or 0 roles // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html @@ -139,7 +139,7 @@ func (p *DefaultProvider) Delete(ctx context.Context, m ResourceOwner) error { if _, err = p.iamapi.DeleteInstanceProfile(ctx, &iam.DeleteInstanceProfileInput{ InstanceProfileName: aws.String(profileName), }); err != nil { - return awserrors.IgnoreNotFoundV2(fmt.Errorf("deleting instance profile %q, %w", profileName, err)) + return awserrors.IgnoreNotFound(fmt.Errorf("deleting instance profile %q, %w", profileName, err)) } return nil } diff --git a/pkg/providers/instancetype/instancetype.go b/pkg/providers/instancetype/instancetype.go index b0c28a8eecd0..81ff8ab5a730 100644 --- a/pkg/providers/instancetype/instancetype.go +++ b/pkg/providers/instancetype/instancetype.go @@ -34,12 +34,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "k8s.io/apimachinery/pkg/util/sets" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/aws/karpenter-provider-aws/pkg/providers/subnet" @@ -52,7 +54,7 @@ type Provider interface { } type DefaultProvider struct { - ec2api ec2iface.EC2API + ec2api sdk.EC2API subnetProvider subnet.Provider instanceTypesResolver Resolver @@ -62,7 +64,7 @@ type DefaultProvider struct { muInstanceTypesInfo sync.RWMutex // TODO @engedaam: Look into only storing the needed EC2InstanceTypeInfo - instanceTypesInfo []*ec2.InstanceTypeInfo + instanceTypesInfo []ec2types.InstanceTypeInfo muInstanceTypesOfferings sync.RWMutex instanceTypesOfferings map[string]sets.Set[string] @@ -76,11 +78,11 @@ type DefaultProvider struct { instanceTypesOfferingsSeqNum uint64 } -func NewDefaultProvider(instanceTypesCache *cache.Cache, discoveredCapacityCache *cache.Cache, ec2api ec2iface.EC2API, subnetProvider subnet.Provider, instanceTypesResolver Resolver) *DefaultProvider { +func NewDefaultProvider(instanceTypesCache *cache.Cache, discoveredCapacityCache *cache.Cache, ec2api sdk.EC2API, subnetProvider subnet.Provider, instanceTypesResolver Resolver) *DefaultProvider { return &DefaultProvider{ ec2api: ec2api, subnetProvider: subnetProvider, - instanceTypesInfo: []*ec2.InstanceTypeInfo{}, + instanceTypesInfo: []ec2types.InstanceTypeInfo{}, instanceTypesOfferings: map[string]sets.Set[string]{}, instanceTypesResolver: instanceTypesResolver, instanceTypesCache: instanceTypesCache, @@ -144,16 +146,16 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) subnetZoneToID := lo.SliceToMap(nodeClass.Status.Subnets, func(s v1.Subnet) (string, string) { return s.Zone, s.ZoneID }) - result := lo.Map(p.instanceTypesInfo, func(i *ec2.InstanceTypeInfo, _ int) *cloudprovider.InstanceType { + result := lo.Map(p.instanceTypesInfo, func(i ec2types.InstanceTypeInfo, _ int) *cloudprovider.InstanceType { instanceTypeVCPU.With(prometheus.Labels{ - instanceTypeLabel: *i.InstanceType, + instanceTypeLabel: string(i.InstanceType), }).Set(float64(lo.FromPtr(i.VCpuInfo.DefaultVCpus))) instanceTypeMemory.With(prometheus.Labels{ - instanceTypeLabel: *i.InstanceType, + instanceTypeLabel: string(i.InstanceType), }).Set(float64(lo.FromPtr(i.MemoryInfo.SizeInMiB) * 1024 * 1024)) zoneData := lo.Map(allZones.UnsortedList(), func(zoneName string, _ int) ZoneData { - if !p.instanceTypesOfferings[lo.FromPtr(i.InstanceType)].Has(zoneName) || !subnetZones.Has(zoneName) { + if !p.instanceTypesOfferings[string(i.InstanceType)].Has(zoneName) || !subnetZones.Has(zoneName) { return ZoneData{ Name: zoneName, Available: false, @@ -195,24 +197,29 @@ func (p *DefaultProvider) UpdateInstanceTypes(ctx context.Context) error { // TODO @joinnis: This can be made more efficient by holding a Read lock and only obtaining the Write if not in cache p.muInstanceTypesInfo.Lock() defer p.muInstanceTypesInfo.Unlock() - var instanceTypes []*ec2.InstanceTypeInfo - if err := p.ec2api.DescribeInstanceTypesPagesWithContext(ctx, &ec2.DescribeInstanceTypesInput{ - Filters: []*ec2.Filter{ + var instanceTypes []ec2types.InstanceTypeInfo + + paginator := ec2.NewDescribeInstanceTypesPaginator(p.ec2api, &ec2.DescribeInstanceTypesInput{ + Filters: []ec2types.Filter{ { - Name: lo.ToPtr("supported-virtualization-type"), - Values: lo.ToSlicePtr([]string{"hvm"}), + Name: aws.String("supported-virtualization-type"), + Values: []string{"hvm"}, }, { - Name: lo.ToPtr("processor-info.supported-architecture"), - Values: lo.ToSlicePtr([]string{"x86_64", "arm64"}), + Name: aws.String("processor-info.supported-architecture"), + Values: []string{"x86_64", "arm64"}, }, }, - }, func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool { + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return fmt.Errorf("describing instance types, %w", err) + } + instanceTypes = append(instanceTypes, page.InstanceTypes...) - return true - }); err != nil { - return fmt.Errorf("describing instance types, %w", err) } if p.cm.HasChanged("instance-types", instanceTypes) { @@ -237,18 +244,25 @@ func (p *DefaultProvider) UpdateInstanceTypeOfferings(ctx context.Context) error // Get offerings from EC2 instanceTypeOfferings := map[string]sets.Set[string]{} - if err := p.ec2api.DescribeInstanceTypeOfferingsPagesWithContext(ctx, &ec2.DescribeInstanceTypeOfferingsInput{LocationType: lo.ToPtr("availability-zone")}, - func(output *ec2.DescribeInstanceTypeOfferingsOutput, lastPage bool) bool { - for _, offering := range output.InstanceTypeOfferings { - if _, ok := instanceTypeOfferings[lo.FromPtr(offering.InstanceType)]; !ok { - instanceTypeOfferings[lo.FromPtr(offering.InstanceType)] = sets.New[string]() - } - instanceTypeOfferings[lo.FromPtr(offering.InstanceType)].Insert(lo.FromPtr(offering.Location)) + + paginator := ec2.NewDescribeInstanceTypeOfferingsPaginator(p.ec2api, &ec2.DescribeInstanceTypeOfferingsInput{ + LocationType: ec2types.LocationTypeAvailabilityZone, + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return fmt.Errorf("describing instance type zone offerings, %w", err) + } + + for _, offering := range page.InstanceTypeOfferings { + if _, ok := instanceTypeOfferings[string(offering.InstanceType)]; !ok { + instanceTypeOfferings[string(offering.InstanceType)] = sets.New[string]() } - return true - }); err != nil { - return fmt.Errorf("describing instance type zone offerings, %w", err) + instanceTypeOfferings[string(offering.InstanceType)].Insert(lo.FromPtr(offering.Location)) + } } + if p.cm.HasChanged("instance-type-offering", instanceTypeOfferings) { // Only update instanceTypesSeqNun with the instance type offerings have been changed // This is to not create new keys with duplicate instance type offerings option @@ -296,7 +310,7 @@ func (p *DefaultProvider) UpdateInstanceTypeCapacityFromNode(ctx context.Context } func (p *DefaultProvider) Reset() { - p.instanceTypesInfo = []*ec2.InstanceTypeInfo{} + p.instanceTypesInfo = []ec2types.InstanceTypeInfo{} p.instanceTypesOfferings = map[string]sets.Set[string]{} p.instanceTypesCache.Flush() p.discoveredCapacityCache.Flush() diff --git a/pkg/providers/instancetype/suite_test.go b/pkg/providers/instancetype/suite_test.go index d397a6794f37..f1fc9d77ae5f 100644 --- a/pkg/providers/instancetype/suite_test.go +++ b/pkg/providers/instancetype/suite_test.go @@ -28,8 +28,9 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/status" "github.com/imdario/mergo" . "github.com/onsi/ginkgo/v2" @@ -422,7 +423,7 @@ var _ = Describe("InstanceTypeProvider", func() { return iPrice < jPrice }) // Expect that the launch template overrides gives the 100 cheapest instance types - expected := sets.NewString(lo.Map(its[:100], func(i *corecloudprovider.InstanceType, _ int) string { + expected := sets.New(lo.Map(its[:100], func(i *corecloudprovider.InstanceType, _ int) string { return i.Name })...) Expect(awsEnv.EC2API.CreateFleetBehavior.CalledWithInput.Len()).To(Equal(1)) @@ -431,7 +432,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(call.LaunchTemplateConfigs[0].Overrides).To(HaveLen(60)) for _, override := range call.LaunchTemplateConfigs[0].Overrides { - Expect(expected.Has(aws.StringValue(override.InstanceType))).To(BeTrue(), fmt.Sprintf("expected %s to exist in set", aws.StringValue(override.InstanceType))) + Expect(expected.Has(string(override.InstanceType))).To(BeTrue(), fmt.Sprintf("expected %s to exist in set", string(override.InstanceType))) } }) It("should order the instance types by price and only consider the spot types that are cheaper than the cheapest on-demand", func() { @@ -493,7 +494,7 @@ var _ = Describe("InstanceTypeProvider", func() { // find the cheapest OD price that works cheapestODPrice := math.MaxFloat64 for _, override := range call.LaunchTemplateConfigs[0].Overrides { - odPrice, ok := awsEnv.PricingProvider.OnDemandPrice(*override.InstanceType) + odPrice, ok := awsEnv.PricingProvider.OnDemandPrice(override.InstanceType) Expect(ok).To(BeTrue()) if odPrice < cheapestODPrice { cheapestODPrice = odPrice @@ -501,7 +502,7 @@ var _ = Describe("InstanceTypeProvider", func() { } // and our spot prices should be cheaper than the OD price for _, override := range call.LaunchTemplateConfigs[0].Overrides { - spotPrice, ok := awsEnv.PricingProvider.SpotPrice(*override.InstanceType, *override.AvailabilityZone) + spotPrice, ok := awsEnv.PricingProvider.SpotPrice(override.InstanceType, *override.AvailabilityZone) Expect(ok).To(BeTrue()) Expect(spotPrice).To(BeNumerically("<", cheapestODPrice)) } @@ -536,7 +537,7 @@ var _ = Describe("InstanceTypeProvider", func() { var expensiveInstanceType bool for _, ltc := range call.LaunchTemplateConfigs { for _, ovr := range ltc.Overrides { - if strings.Contains(aws.StringValue(ovr.InstanceType), "metal") { + if strings.Contains(string(ovr.InstanceType), "metal") { expensiveInstanceType = true } } @@ -558,7 +559,7 @@ var _ = Describe("InstanceTypeProvider", func() { call := awsEnv.EC2API.CreateFleetBehavior.CalledWithInput.Pop() for _, ltc := range call.LaunchTemplateConfigs { for _, ovr := range ltc.Overrides { - Expect(strings.Contains(aws.StringValue(ovr.InstanceType), "metal")).To(BeFalse()) + Expect(strings.Contains(string(ovr.InstanceType), "metal")).To(BeFalse()) } } }) @@ -577,7 +578,7 @@ var _ = Describe("InstanceTypeProvider", func() { call := awsEnv.EC2API.CreateFleetBehavior.CalledWithInput.Pop() for _, ltc := range call.LaunchTemplateConfigs { for _, ovr := range ltc.Overrides { - Expect(strings.HasPrefix(aws.StringValue(ovr.InstanceType), "g")).To(BeFalse()) + Expect(strings.HasPrefix(string(ovr.InstanceType), "g")).To(BeFalse()) } } }) @@ -638,25 +639,25 @@ var _ = Describe("InstanceTypeProvider", func() { It("should not launch instance type for vpc.amazonaws.com/PrivateIPv4Address if VPC resource controller doesn't advertise it", func() { // Create a "test" instance type that has PrivateIPv4Addresses but isn't advertised in the VPC limits config awsEnv.EC2API.DescribeInstanceTypesOutput.Set(&ec2.DescribeInstanceTypesOutput{ - InstanceTypes: []*ec2.InstanceTypeInfo{ + InstanceTypes: []ec2types.InstanceTypeInfo{ { - InstanceType: aws.String("test"), - ProcessorInfo: &ec2.ProcessorInfo{ - SupportedArchitectures: aws.StringSlice([]string{"x86_64"}), + InstanceType: "test", + ProcessorInfo: &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ec2types.ArchitectureTypeX8664}, }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultCores: aws.Int64(1), - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultCores: aws.Int32(1), + DefaultVCpus: aws.Int32(2), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(8192), }, - NetworkInfo: &ec2.NetworkInfo{ - Ipv4AddressesPerInterface: aws.Int64(10), - DefaultNetworkCardIndex: aws.Int64(0), - NetworkCards: []*ec2.NetworkCardInfo{{ - NetworkCardIndex: lo.ToPtr(int64(0)), - MaximumNetworkInterfaces: aws.Int64(3), + NetworkInfo: &ec2types.NetworkInfo{ + Ipv4AddressesPerInterface: aws.Int32(10), + DefaultNetworkCardIndex: aws.Int32(0), + NetworkCards: []ec2types.NetworkCardInfo{{ + NetworkCardIndex: lo.ToPtr(int32(0)), + MaximumNetworkInterfaces: aws.Int32(3), }}, }, SupportedUsageClasses: fake.DefaultSupportedUsageClasses, @@ -664,9 +665,9 @@ var _ = Describe("InstanceTypeProvider", func() { }, }) awsEnv.EC2API.DescribeInstanceTypeOfferingsOutput.Set(&ec2.DescribeInstanceTypeOfferingsOutput{ - InstanceTypeOfferings: []*ec2.InstanceTypeOffering{ + InstanceTypeOfferings: []ec2types.InstanceTypeOffering{ { - InstanceType: aws.String("test"), + InstanceType: "test", Location: aws.String("test-zone-1a"), }, }, @@ -936,7 +937,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(*node.Status.Capacity.StorageEphemeral()).To(Equal(resource.MustParse("7600G"))) }) It("should not set pods to 110 if using ENI-based pod density", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{} for _, info := range instanceInfo.InstanceTypes { @@ -958,7 +959,7 @@ var _ = Describe("InstanceTypeProvider", func() { } }) It("should set pods to 110 if AMI Family doesn't support", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{} for _, info := range instanceInfo.InstanceTypes { @@ -991,7 +992,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(ok).To(BeTrue()) Expect(metric).To(Not(BeNil())) value := metric.GetGauge().Value - Expect(aws.Float64Value(value)).To(BeNumerically(">", 0)) + Expect(aws.ToFloat64(value)).To(BeNumerically(">", 0)) } }) It("should expose memory metrics for instance types", func() { @@ -1005,7 +1006,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(ok).To(BeTrue()) Expect(metric).To(Not(BeNil())) value := metric.GetGauge().Value - Expect(aws.Float64Value(value)).To(BeNumerically(">", 0)) + Expect(aws.ToFloat64(value)).To(BeNumerically(">", 0)) } }) It("should expose availability metrics for instance types", func() { @@ -1022,7 +1023,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(ok).To(BeTrue()) Expect(metric).To(Not(BeNil())) value := metric.GetGauge().Value - Expect(aws.Float64Value(value)).To(BeNumerically("==", lo.Ternary(of.Available, 1, 0))) + Expect(aws.ToFloat64(value)).To(BeNumerically("==", lo.Ternary(of.Available, 1, 0))) } } }) @@ -1040,7 +1041,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(ok).To(BeTrue()) Expect(metric).To(Not(BeNil())) value := metric.GetGauge().Value - Expect(aws.Float64Value(value)).To(BeNumerically("==", of.Price)) + Expect(aws.ToFloat64(value)).To(BeNumerically("==", of.Price)) } } }) @@ -1064,17 +1065,17 @@ var _ = Describe("InstanceTypeProvider", func() { ExpectScheduled(ctx, env.Client, pod) }) Context("Overhead", func() { - var info *ec2.InstanceTypeInfo + var info ec2types.InstanceTypeInfo BeforeEach(func() { ctx = options.ToContext(ctx, test.Options(test.OptionsFields{ ClusterName: lo.ToPtr("karpenter-cluster"), })) var ok bool - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) - info, ok = lo.Find(instanceInfo.InstanceTypes, func(i *ec2.InstanceTypeInfo) bool { - return aws.StringValue(i.InstanceType) == "m5.xlarge" + info, ok = lo.Find(instanceInfo.InstanceTypes, func(i ec2types.InstanceTypeInfo) bool { + return i.InstanceType == "m5.xlarge" }) Expect(ok).To(BeTrue()) }) @@ -1535,11 +1536,11 @@ var _ = Describe("InstanceTypeProvider", func() { }) }) It("should default max pods based off of network interfaces", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{} for _, info := range instanceInfo.InstanceTypes { - if *info.InstanceType == "t3.large" { + if info.InstanceType == "t3.large" { it := instancetype.NewInstanceType(ctx, info, fake.DefaultRegion, @@ -1556,7 +1557,7 @@ var _ = Describe("InstanceTypeProvider", func() { ) Expect(it.Capacity.Pods().Value()).To(BeNumerically("==", 35)) } - if *info.InstanceType == "m6idn.32xlarge" { + if info.InstanceType == "m6idn.32xlarge" { it := instancetype.NewInstanceType(ctx, info, fake.DefaultRegion, @@ -1576,7 +1577,7 @@ var _ = Describe("InstanceTypeProvider", func() { } }) It("should set max-pods to user-defined value if specified", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ MaxPods: lo.ToPtr(int32(10)), @@ -1600,7 +1601,7 @@ var _ = Describe("InstanceTypeProvider", func() { } }) It("should override max-pods value", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ MaxPods: lo.ToPtr(int32(10)), @@ -1628,10 +1629,10 @@ var _ = Describe("InstanceTypeProvider", func() { ReservedENIs: lo.ToPtr(1), })) - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) - t3Large, ok := lo.Find(instanceInfo.InstanceTypes, func(info *ec2.InstanceTypeInfo) bool { - return *info.InstanceType == "t3.large" + t3Large, ok := lo.Find(instanceInfo.InstanceTypes, func(info ec2types.InstanceTypeInfo) bool { + return info.InstanceType == "t3.large" }) Expect(ok).To(Equal(true)) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{} @@ -1662,10 +1663,10 @@ var _ = Describe("InstanceTypeProvider", func() { ReservedENIs: lo.ToPtr(1_000_000), })) - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) - t3Large, ok := lo.Find(instanceInfo.InstanceTypes, func(info *ec2.InstanceTypeInfo) bool { - return *info.InstanceType == "t3.large" + t3Large, ok := lo.Find(instanceInfo.InstanceTypes, func(info ec2types.InstanceTypeInfo) bool { + return info.InstanceType == "t3.large" }) Expect(ok).To(Equal(true)) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{} @@ -1693,7 +1694,7 @@ var _ = Describe("InstanceTypeProvider", func() { Expect(it.Capacity.Pods().Value()).To(BeNumerically("==", maxPods)) }) It("should override pods-per-core value", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ PodsPerCore: lo.ToPtr(int32(1)), @@ -1717,7 +1718,7 @@ var _ = Describe("InstanceTypeProvider", func() { } }) It("should take the minimum of pods-per-core and max-pods", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ PodsPerCore: lo.ToPtr(int32(4)), @@ -1738,11 +1739,11 @@ var _ = Describe("InstanceTypeProvider", func() { nodeClass.AMIFamily(), nil, ) - Expect(it.Capacity.Pods().Value()).To(BeNumerically("==", lo.Min([]int64{20, lo.FromPtr(info.VCpuInfo.DefaultVCpus) * 4}))) + Expect(it.Capacity.Pods().Value()).To(BeNumerically("==", lo.Min([]int32{20, lo.FromPtr(info.VCpuInfo.DefaultVCpus) * 4}))) } }) It("should ignore pods-per-core when using Bottlerocket AMI", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Alias: "bottlerocket@latest"}} nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ @@ -1768,13 +1769,13 @@ var _ = Describe("InstanceTypeProvider", func() { } }) It("should take limited pod density to be the default pods number when pods-per-core is 0", func() { - instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}) + instanceInfo, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) Expect(err).To(BeNil()) nodeClass.Spec.Kubelet = &v1.KubeletConfiguration{ PodsPerCore: lo.ToPtr(int32(0)), } for _, info := range instanceInfo.InstanceTypes { - if *info.InstanceType == "t3.large" { + if info.InstanceType == "t3.large" { it := instancetype.NewInstanceType(ctx, info, fake.DefaultRegion, @@ -1791,7 +1792,7 @@ var _ = Describe("InstanceTypeProvider", func() { ) Expect(it.Capacity.Pods().Value()).To(BeNumerically("==", 35)) } - if *info.InstanceType == "m6idn.32xlarge" { + if info.InstanceType == "m6idn.32xlarge" { it := instancetype.NewInstanceType(ctx, info, fake.DefaultRegion, @@ -1812,7 +1813,7 @@ var _ = Describe("InstanceTypeProvider", func() { }) It("shouldn't report more resources than are actually available on instances", func() { awsEnv.EC2API.DescribeSubnetsOutput.Set(&ec2.DescribeSubnetsOutput{ - Subnets: []*ec2.Subnet{ + Subnets: []ec2types.Subnet{ { AvailabilityZone: aws.String("us-west-2a"), SubnetId: aws.String("subnet-12345"), @@ -1820,31 +1821,31 @@ var _ = Describe("InstanceTypeProvider", func() { }, }) awsEnv.EC2API.DescribeInstanceTypeOfferingsOutput.Set(&ec2.DescribeInstanceTypeOfferingsOutput{ - InstanceTypeOfferings: []*ec2.InstanceTypeOffering{ + InstanceTypeOfferings: []ec2types.InstanceTypeOffering{ { - InstanceType: aws.String("t4g.small"), + InstanceType: "t4g.small", Location: aws.String("us-west-2a"), }, { - InstanceType: aws.String("t4g.medium"), + InstanceType: "t4g.medium", Location: aws.String("us-west-2a"), }, { - InstanceType: aws.String("t4g.xlarge"), + InstanceType: "t4g.xlarge", Location: aws.String("us-west-2a"), }, { - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", Location: aws.String("us-west-2a"), }, }, }) awsEnv.EC2API.DescribeInstanceTypesOutput.Set(&ec2.DescribeInstanceTypesOutput{ - InstanceTypes: []*ec2.InstanceTypeInfo{ - {InstanceType: aws.String("t4g.small")}, - {InstanceType: aws.String("t4g.medium")}, - {InstanceType: aws.String("t4g.xlarge")}, - {InstanceType: aws.String("m5.large")}, + InstanceTypes: []ec2types.InstanceTypeInfo{ + {InstanceType: "t4g.small"}, + {InstanceType: "t4g.medium"}, + {InstanceType: "t4g.xlarge"}, + {InstanceType: "m5.large"}, }, }) @@ -2040,12 +2041,26 @@ var _ = Describe("InstanceTypeProvider", func() { HaveKeyWithValue(corev1.LabelTopologyZone, "test-zone-1b"))) }) It("should launch on-demand capacity if flexible to both spot and on-demand, but spot is unavailable", func() { - Expect(awsEnv.EC2API.DescribeInstanceTypesPagesWithContext(ctx, &ec2.DescribeInstanceTypesInput{}, func(dito *ec2.DescribeInstanceTypesOutput, b bool) bool { - for _, it := range dito.InstanceTypes { - awsEnv.EC2API.InsufficientCapacityPools.Add(fake.CapacityPool{CapacityType: karpv1.CapacityTypeSpot, InstanceType: aws.StringValue(it.InstanceType), Zone: "test-zone-1a"}) + Expect(func() error { + output, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}) + if err != nil { + return err } - return true - })).To(Succeed()) + + // Call the function with the output and false (indicating it's not the last page) + _ = func(dito *ec2.DescribeInstanceTypesOutput) bool { + for _, it := range dito.InstanceTypes { + awsEnv.EC2API.InsufficientCapacityPools.Add(fake.CapacityPool{ + CapacityType: karpv1.CapacityTypeSpot, + InstanceType: string(it.InstanceType), + Zone: "test-zone-1a", + }) + } + return true + }(output) + + return nil + }()).To(Succeed()) nodePool.Spec.Template.Spec.Requirements = []karpv1.NodeSelectorRequirementWithMinValues{ {NodeSelectorRequirement: corev1.NodeSelectorRequirement{Key: karpv1.CapacityTypeLabelKey, Operator: corev1.NodeSelectorOpIn, Values: []string{karpv1.CapacityTypeSpot, karpv1.CapacityTypeOnDemand}}}, {NodeSelectorRequirement: corev1.NodeSelectorRequirement{Key: corev1.LabelTopologyZone, Operator: corev1.NodeSelectorOpIn, Values: []string{"test-zone-1a"}}}, @@ -2134,10 +2149,10 @@ var _ = Describe("InstanceTypeProvider", func() { It("should fail to launch capacity when there is no zonal availability for spot", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", SpotPrice: aws.String("0.004"), Timestamp: &now, }, @@ -2160,10 +2175,10 @@ var _ = Describe("InstanceTypeProvider", func() { It("should succeed to launch spot instance when zonal availability exists", func() { now := time.Now() awsEnv.EC2API.DescribeSpotPriceHistoryOutput.Set(&ec2.DescribeSpotPriceHistoryOutput{ - SpotPriceHistory: []*ec2.SpotPrice{ + SpotPriceHistory: []ec2types.SpotPrice{ { AvailabilityZone: aws.String("test-zone-1a"), - InstanceType: aws.String("m5.large"), + InstanceType: "m5.large", SpotPrice: aws.String("0.004"), Timestamp: &now, }, @@ -2271,18 +2286,18 @@ var _ = Describe("InstanceTypeProvider", func() { ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically(">=", 1)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint).To(Equal(ec2.LaunchTemplateInstanceMetadataEndpointStateEnabled)) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6).To(Equal(ec2.LaunchTemplateInstanceMetadataProtocolIpv6Disabled)) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit).To(Equal(int64(1))) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpTokens).To(Equal(ec2.LaunchTemplateHttpTokensStateRequired)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint).To(Equal(ec2types.LaunchTemplateInstanceMetadataEndpointStateEnabled)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6).To(Equal(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Disabled)) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit)).To(Equal(int32(1))) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpTokens).To(Equal(ec2types.LaunchTemplateHttpTokensStateRequired)) }) }) It("should set metadata options on generated launch template from nodePool configuration", func() { nodeClass.Spec.MetadataOptions = &v1.MetadataOptions{ - HTTPEndpoint: aws.String(ec2.LaunchTemplateInstanceMetadataEndpointStateDisabled), - HTTPProtocolIPv6: aws.String(ec2.LaunchTemplateInstanceMetadataProtocolIpv6Enabled), + HTTPEndpoint: aws.String(string(ec2types.LaunchTemplateInstanceMetadataEndpointStateDisabled)), + HTTPProtocolIPv6: aws.String(string(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Enabled)), HTTPPutResponseHopLimit: aws.Int64(1), - HTTPTokens: aws.String(ec2.LaunchTemplateHttpTokensStateOptional), + HTTPTokens: aws.String(string(ec2types.LaunchTemplateHttpTokensStateOptional)), } ExpectApplied(ctx, env.Client, nodePool, nodeClass) pod := coretest.UnschedulablePod() @@ -2290,10 +2305,10 @@ var _ = Describe("InstanceTypeProvider", func() { ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically(">=", 1)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint).To(Equal(ec2.LaunchTemplateInstanceMetadataEndpointStateDisabled)) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6).To(Equal(ec2.LaunchTemplateInstanceMetadataProtocolIpv6Enabled)) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit).To(Equal(int64(1))) - Expect(*ltInput.LaunchTemplateData.MetadataOptions.HttpTokens).To(Equal(ec2.LaunchTemplateHttpTokensStateOptional)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint).To(Equal(ec2types.LaunchTemplateInstanceMetadataEndpointStateDisabled)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6).To(Equal(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Enabled)) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit)).To(Equal(int32(1))) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpTokens).To(Equal(ec2types.LaunchTemplateHttpTokensStateOptional)) }) }) }) @@ -2499,11 +2514,11 @@ func generateSpotPricing(cp *cloudprovider.CloudProvider, nodePool *karpv1.NodeP } zone := o.Requirements.Get(corev1.LabelTopologyZone).Any() spotPrice := fmt.Sprintf("%0.3f", onDemandPrice*0.5) - rsp.SpotPriceHistory = append(rsp.SpotPriceHistory, &ec2.SpotPrice{ - AvailabilityZone: &zone, - InstanceType: &instanceType.Name, - SpotPrice: &spotPrice, - Timestamp: &t, + rsp.SpotPriceHistory = append(rsp.SpotPriceHistory, ec2types.SpotPrice{ + AvailabilityZone: lo.ToPtr(zone), + InstanceType: ec2types.InstanceType(instanceType.Name), + SpotPrice: lo.ToPtr(spotPrice), + Timestamp: lo.ToPtr(t), }) } } diff --git a/pkg/providers/instancetype/types.go b/pkg/providers/instancetype/types.go index 3d1496df8b6a..1151cecf81ef 100644 --- a/pkg/providers/instancetype/types.go +++ b/pkg/providers/instancetype/types.go @@ -22,8 +22,8 @@ import ( "strconv" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/hashstructure/v2" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -62,7 +62,7 @@ type Resolver interface { // CacheKey tells the InstanceType cache if something changes about the InstanceTypes or Offerings based on the NodeClass. CacheKey(nodeClass *v1.EC2NodeClass) string // Resolve generates an InstanceType based on raw InstanceTypeInfo and NodeClass setting data - Resolve(ctx context.Context, info *ec2.InstanceTypeInfo, zoneData []ZoneData, nodeClass *v1.EC2NodeClass) *cloudprovider.InstanceType + Resolve(ctx context.Context, info ec2types.InstanceTypeInfo, zoneData []ZoneData, nodeClass *v1.EC2NodeClass) *cloudprovider.InstanceType } type DefaultResolver struct { @@ -95,7 +95,7 @@ func (d *DefaultResolver) CacheKey(nodeClass *v1.EC2NodeClass) string { ) } -func (d *DefaultResolver) Resolve(ctx context.Context, info *ec2.InstanceTypeInfo, zoneData []ZoneData, nodeClass *v1.EC2NodeClass) *cloudprovider.InstanceType { +func (d *DefaultResolver) Resolve(ctx context.Context, info ec2types.InstanceTypeInfo, zoneData []ZoneData, nodeClass *v1.EC2NodeClass) *cloudprovider.InstanceType { // !!! Important !!! // Any changes to the values passed into the NewInstanceType method will require making updates to the cache key // so that Karpenter is able to cache the set of InstanceTypes based on values that alter the set of instance types @@ -117,31 +117,31 @@ func (d *DefaultResolver) Resolve(ctx context.Context, info *ec2.InstanceTypeInf // offering, you can do the following thanks to this invariant: // // offering.Requirements.Get(v1.TopologyLabelZone).Any() -func (d *DefaultResolver) createOfferings(ctx context.Context, instanceType *ec2.InstanceTypeInfo, zoneData []ZoneData) []cloudprovider.Offering { +func (d *DefaultResolver) createOfferings(ctx context.Context, instanceType ec2types.InstanceTypeInfo, zoneData []ZoneData) []cloudprovider.Offering { var offerings []cloudprovider.Offering for _, zone := range zoneData { // while usage classes should be a distinct set, there's no guarantee of that - for capacityType := range sets.NewString(aws.StringValueSlice(instanceType.SupportedUsageClasses)...) { + for capacityType := range sets.New((instanceType.SupportedUsageClasses)...) { // exclude any offerings that have recently seen an insufficient capacity error from EC2 - isUnavailable := d.unavailableOfferings.IsUnavailable(*instanceType.InstanceType, zone.Name, capacityType) + isUnavailable := d.unavailableOfferings.IsUnavailable(instanceType.InstanceType, zone.Name, string(capacityType)) var price float64 var ok bool switch capacityType { - case ec2.UsageClassTypeSpot: - price, ok = d.pricingProvider.SpotPrice(*instanceType.InstanceType, zone.Name) - case ec2.UsageClassTypeOnDemand: - price, ok = d.pricingProvider.OnDemandPrice(*instanceType.InstanceType) + case ec2types.UsageClassTypeSpot: + price, ok = d.pricingProvider.SpotPrice(instanceType.InstanceType, zone.Name) + case ec2types.UsageClassTypeOnDemand: + price, ok = d.pricingProvider.OnDemandPrice(instanceType.InstanceType) case "capacity-block": // ignore since karpenter doesn't support it yet, but do not log an unknown capacity type error continue default: - log.FromContext(ctx).WithValues("capacity-type", capacityType, "instance-type", *instanceType.InstanceType).Error(fmt.Errorf("received unknown capacity type"), "failed parsing offering") + log.FromContext(ctx).WithValues("capacity-type", capacityType, "instance-type", instanceType.InstanceType).Error(fmt.Errorf("received unknown capacity type"), "failed parsing offering") continue } available := !isUnavailable && ok && zone.Available offering := cloudprovider.Offering{ Requirements: scheduling.NewRequirements( - scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, capacityType), + scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, string(capacityType)), scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, zone.Name), ), Price: price, @@ -156,14 +156,14 @@ func (d *DefaultResolver) createOfferings(ctx context.Context, instanceType *ec2 return offerings } -func NewInstanceType(ctx context.Context, info *ec2.InstanceTypeInfo, region string, +func NewInstanceType(ctx context.Context, info ec2types.InstanceTypeInfo, region string, blockDeviceMappings []*v1.BlockDeviceMapping, instanceStorePolicy *v1.InstanceStorePolicy, maxPods *int32, podsPerCore *int32, kubeReserved map[string]string, systemReserved map[string]string, evictionHard map[string]string, evictionSoft map[string]string, amiFamilyType string, offerings cloudprovider.Offerings) *cloudprovider.InstanceType { amiFamily := amifamily.GetAMIFamily(amiFamilyType, &amifamily.Options{}) it := &cloudprovider.InstanceType{ - Name: aws.StringValue(info.InstanceType), + Name: string(info.InstanceType), Requirements: computeRequirements(info, offerings, region, amiFamily), Offerings: offerings, Capacity: computeCapacity(ctx, info, amiFamily, blockDeviceMappings, instanceStorePolicy, maxPods, podsPerCore), @@ -174,16 +174,16 @@ func NewInstanceType(ctx context.Context, info *ec2.InstanceTypeInfo, region str }, } if it.Requirements.Compatible(scheduling.NewRequirements(scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpIn, string(corev1.Windows)))) == nil { - it.Capacity[v1.ResourcePrivateIPv4Address] = *privateIPv4Address(aws.StringValue(info.InstanceType)) + it.Capacity[v1.ResourcePrivateIPv4Address] = *privateIPv4Address(string(info.InstanceType)) } return it } //nolint:gocyclo -func computeRequirements(info *ec2.InstanceTypeInfo, offerings cloudprovider.Offerings, region string, amiFamily amifamily.AMIFamily) scheduling.Requirements { +func computeRequirements(info ec2types.InstanceTypeInfo, offerings cloudprovider.Offerings, region string, amiFamily amifamily.AMIFamily) scheduling.Requirements { requirements := scheduling.NewRequirements( // Well Known Upstream - scheduling.NewRequirement(corev1.LabelInstanceTypeStable, corev1.NodeSelectorOpIn, aws.StringValue(info.InstanceType)), + scheduling.NewRequirement(corev1.LabelInstanceTypeStable, corev1.NodeSelectorOpIn, string(info.InstanceType)), scheduling.NewRequirement(corev1.LabelArchStable, corev1.NodeSelectorOpIn, getArchitecture(info)), scheduling.NewRequirement(corev1.LabelOSStable, corev1.NodeSelectorOpIn, getOS(info, amiFamily)...), scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, lo.Map(offerings.Available(), func(o cloudprovider.Offering, _ int) string { @@ -196,9 +196,9 @@ func computeRequirements(info *ec2.InstanceTypeInfo, offerings cloudprovider.Off return o.Requirements.Get(karpv1.CapacityTypeLabelKey).Any() })...), // Well Known to AWS - scheduling.NewRequirement(v1.LabelInstanceCPU, corev1.NodeSelectorOpIn, fmt.Sprint(aws.Int64Value(info.VCpuInfo.DefaultVCpus))), + scheduling.NewRequirement(v1.LabelInstanceCPU, corev1.NodeSelectorOpIn, fmt.Sprint(lo.FromPtr(info.VCpuInfo.DefaultVCpus))), scheduling.NewRequirement(v1.LabelInstanceCPUManufacturer, corev1.NodeSelectorOpDoesNotExist), - scheduling.NewRequirement(v1.LabelInstanceMemory, corev1.NodeSelectorOpIn, fmt.Sprint(aws.Int64Value(info.MemoryInfo.SizeInMiB))), + scheduling.NewRequirement(v1.LabelInstanceMemory, corev1.NodeSelectorOpIn, fmt.Sprint(lo.FromPtr(info.MemoryInfo.SizeInMiB))), scheduling.NewRequirement(v1.LabelInstanceEBSBandwidth, corev1.NodeSelectorOpDoesNotExist), scheduling.NewRequirement(v1.LabelInstanceNetworkBandwidth, corev1.NodeSelectorOpDoesNotExist), scheduling.NewRequirement(v1.LabelInstanceCategory, corev1.NodeSelectorOpDoesNotExist), @@ -213,8 +213,8 @@ func computeRequirements(info *ec2.InstanceTypeInfo, offerings cloudprovider.Off scheduling.NewRequirement(v1.LabelInstanceAcceleratorName, corev1.NodeSelectorOpDoesNotExist), scheduling.NewRequirement(v1.LabelInstanceAcceleratorManufacturer, corev1.NodeSelectorOpDoesNotExist), scheduling.NewRequirement(v1.LabelInstanceAcceleratorCount, corev1.NodeSelectorOpDoesNotExist), - scheduling.NewRequirement(v1.LabelInstanceHypervisor, corev1.NodeSelectorOpIn, aws.StringValue(info.Hypervisor)), - scheduling.NewRequirement(v1.LabelInstanceEncryptionInTransitSupported, corev1.NodeSelectorOpIn, fmt.Sprint(aws.BoolValue(info.NetworkInfo.EncryptionInTransitSupported))), + scheduling.NewRequirement(v1.LabelInstanceHypervisor, corev1.NodeSelectorOpIn, string(info.Hypervisor)), + scheduling.NewRequirement(v1.LabelInstanceEncryptionInTransitSupported, corev1.NodeSelectorOpIn, fmt.Sprint(aws.ToBool(info.NetworkInfo.EncryptionInTransitSupported))), ) // Only add zone-id label when available in offerings. It may not be available if a user has upgraded from a // previous version of Karpenter w/o zone-id support and the nodeclass subnet status has not yet updated. @@ -225,44 +225,44 @@ func computeRequirements(info *ec2.InstanceTypeInfo, offerings cloudprovider.Off requirements.Add(scheduling.NewRequirement(v1.LabelTopologyZoneID, corev1.NodeSelectorOpIn, zoneIDs...)) } // Instance Type Labels - instanceFamilyParts := instanceTypeScheme.FindStringSubmatch(aws.StringValue(info.InstanceType)) + instanceFamilyParts := instanceTypeScheme.FindStringSubmatch(string(info.InstanceType)) if len(instanceFamilyParts) == 4 { requirements[v1.LabelInstanceCategory].Insert(instanceFamilyParts[1]) requirements[v1.LabelInstanceGeneration].Insert(instanceFamilyParts[3]) } - instanceTypeParts := strings.Split(aws.StringValue(info.InstanceType), ".") + instanceTypeParts := strings.Split(string(info.InstanceType), ".") if len(instanceTypeParts) == 2 { requirements.Get(v1.LabelInstanceFamily).Insert(instanceTypeParts[0]) requirements.Get(v1.LabelInstanceSize).Insert(instanceTypeParts[1]) } - if info.InstanceStorageInfo != nil && aws.StringValue(info.InstanceStorageInfo.NvmeSupport) != ec2.EphemeralNvmeSupportUnsupported && info.InstanceStorageInfo.TotalSizeInGB != nil { - requirements[v1.LabelInstanceLocalNVME].Insert(fmt.Sprint(aws.Int64Value(info.InstanceStorageInfo.TotalSizeInGB))) + if info.InstanceStorageInfo != nil && info.InstanceStorageInfo.NvmeSupport != ec2types.EphemeralNvmeSupportUnsupported && info.InstanceStorageInfo.TotalSizeInGB != nil { + requirements[v1.LabelInstanceLocalNVME].Insert(fmt.Sprint(lo.FromPtr(info.InstanceStorageInfo.TotalSizeInGB))) } // Network bandwidth - if bandwidth, ok := InstanceTypeBandwidthMegabits[aws.StringValue(info.InstanceType)]; ok { + if bandwidth, ok := InstanceTypeBandwidthMegabits[string(info.InstanceType)]; ok { requirements[v1.LabelInstanceNetworkBandwidth].Insert(fmt.Sprint(bandwidth)) } // GPU Labels if info.GpuInfo != nil && len(info.GpuInfo.Gpus) == 1 { gpu := info.GpuInfo.Gpus[0] - requirements.Get(v1.LabelInstanceGPUName).Insert(lowerKabobCase(aws.StringValue(gpu.Name))) - requirements.Get(v1.LabelInstanceGPUManufacturer).Insert(lowerKabobCase(aws.StringValue(gpu.Manufacturer))) - requirements.Get(v1.LabelInstanceGPUCount).Insert(fmt.Sprint(aws.Int64Value(gpu.Count))) - requirements.Get(v1.LabelInstanceGPUMemory).Insert(fmt.Sprint(aws.Int64Value(gpu.MemoryInfo.SizeInMiB))) + requirements.Get(v1.LabelInstanceGPUName).Insert(lowerKabobCase(aws.ToString(gpu.Name))) + requirements.Get(v1.LabelInstanceGPUManufacturer).Insert(lowerKabobCase(aws.ToString(gpu.Manufacturer))) + requirements.Get(v1.LabelInstanceGPUCount).Insert(fmt.Sprint(lo.FromPtr(gpu.Count))) + requirements.Get(v1.LabelInstanceGPUMemory).Insert(fmt.Sprint(lo.FromPtr(gpu.MemoryInfo.SizeInMiB))) } // Accelerators - excluding Neuron if info.InferenceAcceleratorInfo != nil && len(info.InferenceAcceleratorInfo.Accelerators) == 1 && info.NeuronInfo == nil { accelerator := info.InferenceAcceleratorInfo.Accelerators[0] - requirements.Get(v1.LabelInstanceAcceleratorName).Insert(lowerKabobCase(aws.StringValue(accelerator.Name))) - requirements.Get(v1.LabelInstanceAcceleratorManufacturer).Insert(lowerKabobCase(aws.StringValue(accelerator.Manufacturer))) - requirements.Get(v1.LabelInstanceAcceleratorCount).Insert(fmt.Sprint(aws.Int64Value(accelerator.Count))) + requirements.Get(v1.LabelInstanceAcceleratorName).Insert(lowerKabobCase(aws.ToString(accelerator.Name))) + requirements.Get(v1.LabelInstanceAcceleratorManufacturer).Insert(lowerKabobCase(aws.ToString(accelerator.Manufacturer))) + requirements.Get(v1.LabelInstanceAcceleratorCount).Insert(fmt.Sprint(lo.FromPtr(accelerator.Count))) } // Neuron if info.NeuronInfo != nil && len(info.NeuronInfo.NeuronDevices) == 1 { device := info.NeuronInfo.NeuronDevices[0] - requirements.Get(v1.LabelInstanceAcceleratorName).Insert(lowerKabobCase(aws.StringValue(device.Name))) + requirements.Get(v1.LabelInstanceAcceleratorName).Insert(lowerKabobCase(lo.FromPtr(device.Name))) requirements.Get(v1.LabelInstanceAcceleratorManufacturer).Insert(lowerKabobCase("aws")) - requirements.Get(v1.LabelInstanceAcceleratorCount).Insert(fmt.Sprint(aws.Int64Value(device.Count))) + requirements.Get(v1.LabelInstanceAcceleratorCount).Insert(fmt.Sprint(lo.FromPtr(device.Count))) } // Windows Build Version Labels if family, ok := amiFamily.(*amifamily.Windows); ok { @@ -270,16 +270,16 @@ func computeRequirements(info *ec2.InstanceTypeInfo, offerings cloudprovider.Off } // CPU Manufacturer, valid options: aws, intel, amd if info.ProcessorInfo != nil { - requirements.Get(v1.LabelInstanceCPUManufacturer).Insert(lowerKabobCase(aws.StringValue(info.ProcessorInfo.Manufacturer))) + requirements.Get(v1.LabelInstanceCPUManufacturer).Insert(lowerKabobCase(aws.ToString(info.ProcessorInfo.Manufacturer))) } // EBS Max Bandwidth - if info.EbsInfo != nil && info.EbsInfo.EbsOptimizedInfo != nil && aws.StringValue(info.EbsInfo.EbsOptimizedSupport) == ec2.EbsOptimizedSupportDefault { - requirements.Get(v1.LabelInstanceEBSBandwidth).Insert(fmt.Sprint(aws.Int64Value(info.EbsInfo.EbsOptimizedInfo.MaximumBandwidthInMbps))) + if info.EbsInfo != nil && info.EbsInfo.EbsOptimizedInfo != nil && info.EbsInfo.EbsOptimizedSupport == ec2types.EbsOptimizedSupportDefault { + requirements.Get(v1.LabelInstanceEBSBandwidth).Insert(fmt.Sprint(lo.FromPtr(info.EbsInfo.EbsOptimizedInfo.MaximumBandwidthInMbps))) } return requirements } -func getOS(info *ec2.InstanceTypeInfo, amiFamily amifamily.AMIFamily) []string { +func getOS(info ec2types.InstanceTypeInfo, amiFamily amifamily.AMIFamily) []string { if _, ok := amiFamily.(*amifamily.Windows); ok { if getArchitecture(info) == karpv1.ArchitectureAmd64 { return []string{string(corev1.Windows)} @@ -289,16 +289,16 @@ func getOS(info *ec2.InstanceTypeInfo, amiFamily amifamily.AMIFamily) []string { return []string{string(corev1.Linux)} } -func getArchitecture(info *ec2.InstanceTypeInfo) string { +func getArchitecture(info ec2types.InstanceTypeInfo) string { for _, architecture := range info.ProcessorInfo.SupportedArchitectures { - if value, ok := v1.AWSToKubeArchitectures[aws.StringValue(architecture)]; ok { + if value, ok := v1.AWSToKubeArchitectures[string(architecture)]; ok { return value } } - return fmt.Sprint(aws.StringValueSlice(info.ProcessorInfo.SupportedArchitectures)) // Unrecognized, but used for error printing + return fmt.Sprint(info.ProcessorInfo.SupportedArchitectures) // Unrecognized, but used for error printing } -func computeCapacity(ctx context.Context, info *ec2.InstanceTypeInfo, amiFamily amifamily.AMIFamily, +func computeCapacity(ctx context.Context, info ec2types.InstanceTypeInfo, amiFamily amifamily.AMIFamily, blockDeviceMapping []*v1.BlockDeviceMapping, instanceStorePolicy *v1.InstanceStorePolicy, maxPods *int32, podsPerCore *int32) corev1.ResourceList { @@ -307,7 +307,7 @@ func computeCapacity(ctx context.Context, info *ec2.InstanceTypeInfo, amiFamily corev1.ResourceMemory: *memory(ctx, info), corev1.ResourceEphemeralStorage: *ephemeralStorage(info, amiFamily, blockDeviceMapping, instanceStorePolicy), corev1.ResourcePods: *pods(ctx, info, amiFamily, maxPods, podsPerCore), - v1.ResourceAWSPodENI: *awsPodENI(aws.StringValue(info.InstanceType)), + v1.ResourceAWSPodENI: *awsPodENI(string(info.InstanceType)), v1.ResourceNVIDIAGPU: *nvidiaGPUs(info), v1.ResourceAMDGPU: *amdGPUs(info), v1.ResourceAWSNeuron: *awsNeuronDevices(info), @@ -318,14 +318,14 @@ func computeCapacity(ctx context.Context, info *ec2.InstanceTypeInfo, amiFamily return resourceList } -func cpu(info *ec2.InstanceTypeInfo) *resource.Quantity { +func cpu(info ec2types.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(*info.VCpuInfo.DefaultVCpus)) } -func memory(ctx context.Context, info *ec2.InstanceTypeInfo) *resource.Quantity { +func memory(ctx context.Context, info ec2types.InstanceTypeInfo) *resource.Quantity { sizeInMib := *info.MemoryInfo.SizeInMiB // Gravitons have an extra 64 MiB of cma reserved memory that we can't use - if len(info.ProcessorInfo.SupportedArchitectures) > 0 && *info.ProcessorInfo.SupportedArchitectures[0] == "arm64" { + if len(info.ProcessorInfo.SupportedArchitectures) > 0 && info.ProcessorInfo.SupportedArchitectures[0] == "arm64" { sizeInMib -= 64 } mem := resources.Quantity(fmt.Sprintf("%dMi", sizeInMib)) @@ -335,7 +335,7 @@ func memory(ctx context.Context, info *ec2.InstanceTypeInfo) *resource.Quantity } // Setting ephemeral-storage to be either the default value, what is defined in blockDeviceMappings, or the combined size of local store volumes. -func ephemeralStorage(info *ec2.InstanceTypeInfo, amiFamily amifamily.AMIFamily, blockDeviceMappings []*v1.BlockDeviceMapping, instanceStorePolicy *v1.InstanceStorePolicy) *resource.Quantity { +func ephemeralStorage(info ec2types.InstanceTypeInfo, amiFamily amifamily.AMIFamily, blockDeviceMappings []*v1.BlockDeviceMapping, instanceStorePolicy *v1.InstanceStorePolicy) *resource.Quantity { // If local store disks have been configured for node ephemeral-storage, use the total size of the disks. if lo.FromPtr(instanceStorePolicy) == v1.InstanceStorePolicyRAID0 { if info.InstanceStorageInfo != nil && info.InstanceStorageInfo.TotalSizeInGB != nil { @@ -382,8 +382,8 @@ func awsPodENI(instanceTypeName string) *resource.Quantity { return resources.Quantity("0") } -func nvidiaGPUs(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) +func nvidiaGPUs(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) if info.GpuInfo != nil { for _, gpu := range info.GpuInfo.Gpus { if *gpu.Manufacturer == "NVIDIA" { @@ -394,8 +394,8 @@ func nvidiaGPUs(info *ec2.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(count)) } -func amdGPUs(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) +func amdGPUs(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) if info.GpuInfo != nil { for _, gpu := range info.GpuInfo.Gpus { if *gpu.Manufacturer == "AMD" { @@ -406,8 +406,8 @@ func amdGPUs(info *ec2.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(count)) } -func awsNeuronCores(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) +func awsNeuronCores(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) if info.NeuronInfo != nil { neuronDevice := info.NeuronInfo.NeuronDevices[0] neuronCorePerDevice := neuronDevice.CoreInfo.Count @@ -416,8 +416,8 @@ func awsNeuronCores(info *ec2.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(count)) } -func awsNeuronDevices(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) +func awsNeuronDevices(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) if info.NeuronInfo != nil { for _, device := range info.NeuronInfo.NeuronDevices { count += *device.Count @@ -426,8 +426,8 @@ func awsNeuronDevices(info *ec2.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(count)) } -func habanaGaudis(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) +func habanaGaudis(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) if info.GpuInfo != nil { for _, gpu := range info.GpuInfo.Gpus { if *gpu.Manufacturer == "Habana" { @@ -438,15 +438,15 @@ func habanaGaudis(info *ec2.InstanceTypeInfo) *resource.Quantity { return resources.Quantity(fmt.Sprint(count)) } -func efas(info *ec2.InstanceTypeInfo) *resource.Quantity { - count := int64(0) - if info.NetworkInfo != nil && info.NetworkInfo.EfaInfo != nil { - count = lo.FromPtr(info.NetworkInfo.EfaInfo.MaximumEfaInterfaces) +func efas(info ec2types.InstanceTypeInfo) *resource.Quantity { + count := int32(0) + if info.NetworkInfo != nil && info.NetworkInfo.EfaInfo != nil && info.NetworkInfo.EfaInfo.MaximumEfaInterfaces != nil { + count = *info.NetworkInfo.EfaInfo.MaximumEfaInterfaces } return resources.Quantity(fmt.Sprint(count)) } -func ENILimitedPods(ctx context.Context, info *ec2.InstanceTypeInfo) *resource.Quantity { +func ENILimitedPods(ctx context.Context, info ec2types.InstanceTypeInfo) *resource.Quantity { // The number of pods per node is calculated using the formula: // max number of ENIs * (IPv4 Addresses per ENI -1) + 2 // https://github.com/awslabs/amazon-eks-ami/blob/main/templates/shared/runtime/eni-max-pods.txt @@ -454,12 +454,12 @@ func ENILimitedPods(ctx context.Context, info *ec2.InstanceTypeInfo) *resource.Q // VPC CNI only uses the default network interface // https://github.com/aws/amazon-vpc-cni-k8s/blob/3294231c0dce52cfe473bf6c62f47956a3b333b6/scripts/gen_vpc_ip_limits.go#L162 networkInterfaces := *info.NetworkInfo.NetworkCards[*info.NetworkInfo.DefaultNetworkCardIndex].MaximumNetworkInterfaces - usableNetworkInterfaces := lo.Max([]int64{networkInterfaces - int64(options.FromContext(ctx).ReservedENIs), 0}) + usableNetworkInterfaces := lo.Max([]int64{int64(int(networkInterfaces) - options.FromContext(ctx).ReservedENIs), 0}) if usableNetworkInterfaces == 0 { return resource.NewQuantity(0, resource.DecimalSI) } addressesPerInterface := *info.NetworkInfo.Ipv4AddressesPerInterface - return resources.Quantity(fmt.Sprint(usableNetworkInterfaces*(addressesPerInterface-1) + 2)) + return resources.Quantity(fmt.Sprint(usableNetworkInterfaces*(int64(addressesPerInterface)-1) + 2)) } func privateIPv4Address(instanceTypeName string) *resource.Quantity { @@ -541,7 +541,7 @@ func evictionThreshold(memory *resource.Quantity, storage *resource.Quantity, am return lo.Assign(overhead, override) } -func pods(ctx context.Context, info *ec2.InstanceTypeInfo, amiFamily amifamily.AMIFamily, maxPods *int32, podsPerCore *int32) *resource.Quantity { +func pods(ctx context.Context, info ec2types.InstanceTypeInfo, amiFamily amifamily.AMIFamily, maxPods *int32, podsPerCore *int32) *resource.Quantity { var count int64 switch { case maxPods != nil: @@ -553,7 +553,7 @@ func pods(ctx context.Context, info *ec2.InstanceTypeInfo, amiFamily amifamily.A } if lo.FromPtr(podsPerCore) > 0 && amiFamily.FeatureFlags().PodsPerCoreEnabled { - count = lo.Min([]int64{int64(lo.FromPtr(podsPerCore)) * lo.FromPtr(info.VCpuInfo.DefaultVCpus), count}) + count = lo.Min([]int64{int64(lo.FromPtr(podsPerCore)) * int64(lo.FromPtr(info.VCpuInfo.DefaultVCpus)), count}) } return resources.Quantity(fmt.Sprint(count)) } diff --git a/pkg/providers/launchtemplate/launchtemplate.go b/pkg/providers/launchtemplate/launchtemplate.go index 48a58022d0fe..fd0fd5128fbd 100644 --- a/pkg/providers/launchtemplate/launchtemplate.go +++ b/pkg/providers/launchtemplate/launchtemplate.go @@ -27,11 +27,10 @@ import ( "go.uber.org/multierr" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/eks" - "github.com/aws/aws-sdk-go/service/eks/eksiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" "github.com/samber/lo" @@ -50,6 +49,8 @@ import ( "sigs.k8s.io/karpenter/pkg/cloudprovider" "sigs.k8s.io/karpenter/pkg/utils/pretty" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type Provider interface { @@ -59,7 +60,6 @@ type Provider interface { InvalidateCache(context.Context, string, string) ResolveClusterCIDR(context.Context) error } - type LaunchTemplate struct { Name string InstanceTypes []*cloudprovider.InstanceType @@ -68,8 +68,8 @@ type LaunchTemplate struct { type DefaultProvider struct { sync.Mutex - ec2api ec2iface.EC2API - eksapi eksiface.EKSAPI + ec2api sdk.EC2API + eksapi sdk.EKSAPI amiFamily amifamily.Resolver securityGroupProvider securitygroup.Provider subnetProvider subnet.Provider @@ -81,7 +81,7 @@ type DefaultProvider struct { ClusterCIDR atomic.Pointer[string] } -func NewDefaultProvider(ctx context.Context, cache *cache.Cache, ec2api ec2iface.EC2API, eksapi eksiface.EKSAPI, amiFamily amifamily.Resolver, +func NewDefaultProvider(ctx context.Context, cache *cache.Cache, ec2api sdk.EC2API, eksapi sdk.EKSAPI, amiFamily amifamily.Resolver, securityGroupProvider securitygroup.Provider, subnetProvider subnet.Provider, caBundle *string, startAsync <-chan struct{}, kubeDNSIP net.IP, clusterEndpoint string) *DefaultProvider { l := &DefaultProvider{ @@ -108,13 +108,10 @@ func NewDefaultProvider(ctx context.Context, cache *cache.Cache, ec2api ec2iface }() return l } - func (p *DefaultProvider) EnsureAll(ctx context.Context, nodeClass *v1.EC2NodeClass, nodeClaim *karpv1.NodeClaim, instanceTypes []*cloudprovider.InstanceType, capacityType string, tags map[string]string) ([]*LaunchTemplate, error) { - p.Lock() defer p.Unlock() - options, err := p.createAMIOptions(ctx, nodeClass, lo.Assign(nodeClaim.Labels, map[string]string{karpv1.CapacityTypeLabelKey: capacityType}), tags) if err != nil { return nil, err @@ -145,11 +142,9 @@ func (p *DefaultProvider) InvalidateCache(ctx context.Context, ltName string, lt log.FromContext(ctx).V(1).Info("invalidating launch template in the cache because it no longer exists") p.cache.Delete(ltName) } - func LaunchTemplateName(options *amifamily.LaunchTemplate) string { return fmt.Sprintf("%s/%d", apis.Group, lo.Must(hashstructure.Hash(options, hashstructure.FormatV2, &hashstructure.HashOptions{SlicesAsSets: true}))) } - func (p *DefaultProvider) createAMIOptions(ctx context.Context, nodeClass *v1.EC2NodeClass, labels, tags map[string]string) (*amifamily.Options, error) { // Remove any labels passed into userData that are prefixed with "node-restriction.kubernetes.io" or "kops.k8s.io" since the kubelet can't // register the node with any labels from this domain: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction @@ -185,29 +180,29 @@ func (p *DefaultProvider) createAMIOptions(ctx context.Context, nodeClass *v1.EC }, nil } -func (p *DefaultProvider) ensureLaunchTemplate(ctx context.Context, options *amifamily.LaunchTemplate) (*ec2.LaunchTemplate, error) { - var launchTemplate *ec2.LaunchTemplate +func (p *DefaultProvider) ensureLaunchTemplate(ctx context.Context, options *amifamily.LaunchTemplate) (ec2types.LaunchTemplate, error) { + var launchTemplate ec2types.LaunchTemplate name := LaunchTemplateName(options) ctx = log.IntoContext(ctx, log.FromContext(ctx).WithValues("launch-template-name", name)) // Read from cache if launchTemplate, ok := p.cache.Get(name); ok { p.cache.SetDefault(name, launchTemplate) - return launchTemplate.(*ec2.LaunchTemplate), nil + return launchTemplate.(ec2types.LaunchTemplate), nil } // Attempt to find an existing LT. - output, err := p.ec2api.DescribeLaunchTemplatesWithContext(ctx, &ec2.DescribeLaunchTemplatesInput{ - LaunchTemplateNames: []*string{aws.String(name)}, + output, err := p.ec2api.DescribeLaunchTemplates(ctx, &ec2.DescribeLaunchTemplatesInput{ + LaunchTemplateNames: []string{name}, }) // Create LT if one doesn't exist if awserrors.IsNotFound(err) { launchTemplate, err = p.createLaunchTemplate(ctx, options) if err != nil { - return nil, fmt.Errorf("creating launch template, %w", err) + return ec2types.LaunchTemplate{}, fmt.Errorf("creating launch template, %w", err) } } else if err != nil { - return nil, fmt.Errorf("describing launch templates, %w", err) + return ec2types.LaunchTemplate{}, fmt.Errorf("describing launch templates, %w", err) } else if len(output.LaunchTemplates) != 1 { - return nil, fmt.Errorf("expected to find one launch template, but found %d", len(output.LaunchTemplates)) + return ec2types.LaunchTemplate{}, fmt.Errorf("expected to find one launch template, but found %d", len(output.LaunchTemplates)) } else { if p.cm.HasChanged("launchtemplate-"+name, name) { log.FromContext(ctx).V(1).Info("discovered launch template") @@ -218,72 +213,74 @@ func (p *DefaultProvider) ensureLaunchTemplate(ctx context.Context, options *ami return launchTemplate, nil } -func (p *DefaultProvider) createLaunchTemplate(ctx context.Context, options *amifamily.LaunchTemplate) (*ec2.LaunchTemplate, error) { +func (p *DefaultProvider) createLaunchTemplate(ctx context.Context, options *amifamily.LaunchTemplate) (ec2types.LaunchTemplate, error) { userData, err := options.UserData.Script() if err != nil { - return nil, err + return ec2types.LaunchTemplate{}, err } - launchTemplateDataTags := []*ec2.LaunchTemplateTagSpecificationRequest{ - {ResourceType: aws.String(ec2.ResourceTypeNetworkInterface), Tags: utils.MergeTags(options.Tags)}, + launchTemplateDataTags := []ec2types.LaunchTemplateTagSpecificationRequest{ + {ResourceType: ec2types.ResourceTypeNetworkInterface, Tags: utils.MergeTags(options.Tags)}, } - // Add the spot-instances-request tag if trying to launch spot capacity if options.CapacityType == karpv1.CapacityTypeSpot { - launchTemplateDataTags = append(launchTemplateDataTags, &ec2.LaunchTemplateTagSpecificationRequest{ResourceType: aws.String(ec2.ResourceTypeSpotInstancesRequest), Tags: utils.MergeTags(options.Tags)}) + launchTemplateDataTags = append(launchTemplateDataTags, ec2types.LaunchTemplateTagSpecificationRequest{ResourceType: ec2types.ResourceTypeSpotInstancesRequest, Tags: utils.MergeTags(options.Tags)}) } networkInterfaces := p.generateNetworkInterfaces(options) - output, err := p.ec2api.CreateLaunchTemplateWithContext(ctx, &ec2.CreateLaunchTemplateInput{ + output, err := p.ec2api.CreateLaunchTemplate(ctx, &ec2.CreateLaunchTemplateInput{ LaunchTemplateName: aws.String(LaunchTemplateName(options)), - LaunchTemplateData: &ec2.RequestLaunchTemplateData{ + LaunchTemplateData: &ec2types.RequestLaunchTemplateData{ BlockDeviceMappings: p.blockDeviceMappings(options.BlockDeviceMappings), - IamInstanceProfile: &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{ + IamInstanceProfile: &ec2types.LaunchTemplateIamInstanceProfileSpecificationRequest{ Name: aws.String(options.InstanceProfile), }, - Monitoring: &ec2.LaunchTemplatesMonitoringRequest{ + Monitoring: &ec2types.LaunchTemplatesMonitoringRequest{ Enabled: aws.Bool(options.DetailedMonitoring), }, // If the network interface is defined, the security groups are defined within it - SecurityGroupIds: lo.Ternary(networkInterfaces != nil, nil, lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) *string { return aws.String(s.ID) })), + SecurityGroupIds: lo.Ternary(networkInterfaces != nil, nil, lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) string { return s.ID })), UserData: aws.String(userData), ImageId: aws.String(options.AMIID), - MetadataOptions: &ec2.LaunchTemplateInstanceMetadataOptionsRequest{ - HttpEndpoint: options.MetadataOptions.HTTPEndpoint, - HttpProtocolIpv6: options.MetadataOptions.HTTPProtocolIPv6, - HttpPutResponseHopLimit: options.MetadataOptions.HTTPPutResponseHopLimit, - HttpTokens: options.MetadataOptions.HTTPTokens, + MetadataOptions: &ec2types.LaunchTemplateInstanceMetadataOptionsRequest{ + HttpEndpoint: ec2types.LaunchTemplateInstanceMetadataEndpointState(lo.FromPtr(options.MetadataOptions.HTTPEndpoint)), + HttpProtocolIpv6: ec2types.LaunchTemplateInstanceMetadataProtocolIpv6(lo.FromPtr(options.MetadataOptions.HTTPProtocolIPv6)), + //Will be removed when we update options.MetadataOptions.HTTPPutResponseHopLimit type to be int32 + //nolint: gosec + HttpPutResponseHopLimit: lo.ToPtr(int32(lo.FromPtr(options.MetadataOptions.HTTPPutResponseHopLimit))), + HttpTokens: ec2types.LaunchTemplateHttpTokensState(lo.FromPtr(options.MetadataOptions.HTTPTokens)), // We statically set the InstanceMetadataTags to "disabled" for all new instances since // account-wide defaults can override instance defaults on metadata settings // This can cause instance failure on accounts that default to instance tags since Karpenter // can't support instance tags with its current tags (e.g. kubernetes.io/cluster/*, karpenter.k8s.aws/ec2nodeclass) // See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-options.html#instance-metadata-options-order-of-precedence - InstanceMetadataTags: lo.ToPtr(ec2.InstanceMetadataTagsStateDisabled), + InstanceMetadataTags: ec2types.LaunchTemplateInstanceMetadataTagsStateDisabled, }, NetworkInterfaces: networkInterfaces, TagSpecifications: launchTemplateDataTags, }, - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeLaunchTemplate), + ResourceType: ec2types.ResourceTypeLaunchTemplate, Tags: utils.MergeTags(options.Tags, map[string]string{v1.TagManagedLaunchTemplate: options.ClusterName, v1.LabelNodeClass: options.NodeClassName}), }, }, }) if err != nil { - return nil, err + return ec2types.LaunchTemplate{}, err } - log.FromContext(ctx).WithValues("id", aws.StringValue(output.LaunchTemplate.LaunchTemplateId)).V(1).Info("created launch template") - return output.LaunchTemplate, nil + log.FromContext(ctx).WithValues("id", aws.ToString(output.LaunchTemplate.LaunchTemplateId)).V(1).Info("created launch template") + return lo.FromPtr(output.LaunchTemplate), nil } // generateNetworkInterfaces generates network interfaces for the launch template. -func (p *DefaultProvider) generateNetworkInterfaces(options *amifamily.LaunchTemplate) []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest { +func (p *DefaultProvider) generateNetworkInterfaces(options *amifamily.LaunchTemplate) []ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest { if options.EFACount != 0 { - return lo.Times(options.EFACount, func(i int) *ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest { - return &ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ - NetworkCardIndex: lo.ToPtr(int64(i)), + return lo.Times(options.EFACount, func(i int) ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest { + return ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ + //nolint: gosec + NetworkCardIndex: lo.ToPtr(int32(i)), // Some networking magic to ensure that one network card has higher priority than all the others (important if an instance needs a public IP w/o adding an EIP to every network card) - DeviceIndex: lo.ToPtr(lo.Ternary[int64](i == 0, 0, 1)), - InterfaceType: lo.ToPtr(ec2.NetworkInterfaceTypeEfa), - Groups: lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) *string { return aws.String(s.ID) }), + DeviceIndex: lo.ToPtr(lo.Ternary[int32](i == 0, 0, 1)), + InterfaceType: lo.ToPtr(string(ec2types.NetworkInterfaceTypeEfa)), + Groups: lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) string { return s.ID }), // Instances launched with multiple pre-configured network interfaces cannot set AssociatePublicIPAddress to true. This is an EC2 limitation. However, this does not apply for instances // with a single EFA network interface, and we should support those use cases. Launch failures with multiple enis should be considered user misconfiguration. AssociatePublicIpAddress: options.AssociatePublicIPAddress, @@ -292,35 +289,40 @@ func (p *DefaultProvider) generateNetworkInterfaces(options *amifamily.LaunchTem } if options.AssociatePublicIPAddress != nil { - return []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ + return []ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ { AssociatePublicIpAddress: options.AssociatePublicIPAddress, - DeviceIndex: aws.Int64(0), - Groups: lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) *string { return aws.String(s.ID) }), + DeviceIndex: aws.Int32(0), + Groups: lo.Map(options.SecurityGroups, func(s v1.SecurityGroup, _ int) string { + return s.ID + }), }, } } return nil } -func (p *DefaultProvider) blockDeviceMappings(blockDeviceMappings []*v1.BlockDeviceMapping) []*ec2.LaunchTemplateBlockDeviceMappingRequest { +func (p *DefaultProvider) blockDeviceMappings(blockDeviceMappings []*v1.BlockDeviceMapping) []ec2types.LaunchTemplateBlockDeviceMappingRequest { if len(blockDeviceMappings) == 0 { // The EC2 API fails with empty slices and expects nil. return nil } - var blockDeviceMappingsRequest []*ec2.LaunchTemplateBlockDeviceMappingRequest + var blockDeviceMappingsRequest []ec2types.LaunchTemplateBlockDeviceMappingRequest for _, blockDeviceMapping := range blockDeviceMappings { - blockDeviceMappingsRequest = append(blockDeviceMappingsRequest, &ec2.LaunchTemplateBlockDeviceMappingRequest{ + blockDeviceMappingsRequest = append(blockDeviceMappingsRequest, ec2types.LaunchTemplateBlockDeviceMappingRequest{ DeviceName: blockDeviceMapping.DeviceName, - Ebs: &ec2.LaunchTemplateEbsBlockDeviceRequest{ + Ebs: &ec2types.LaunchTemplateEbsBlockDeviceRequest{ DeleteOnTermination: blockDeviceMapping.EBS.DeleteOnTermination, Encrypted: blockDeviceMapping.EBS.Encrypted, - VolumeType: blockDeviceMapping.EBS.VolumeType, - Iops: blockDeviceMapping.EBS.IOPS, - Throughput: blockDeviceMapping.EBS.Throughput, - KmsKeyId: blockDeviceMapping.EBS.KMSKeyID, - SnapshotId: blockDeviceMapping.EBS.SnapshotID, - VolumeSize: p.volumeSize(blockDeviceMapping.EBS.VolumeSize), + VolumeType: ec2types.VolumeType(aws.ToString(blockDeviceMapping.EBS.VolumeType)), + //Lints here can be removed when we update options.EBS.IOPS and Throughput type to be int32 + //nolint: gosec + Iops: lo.EmptyableToPtr(int32(lo.FromPtr(blockDeviceMapping.EBS.IOPS))), + //nolint: gosec + Throughput: lo.EmptyableToPtr(int32(lo.FromPtr(blockDeviceMapping.EBS.Throughput))), + KmsKeyId: blockDeviceMapping.EBS.KMSKeyID, + SnapshotId: blockDeviceMapping.EBS.SnapshotID, + VolumeSize: p.volumeSize(blockDeviceMapping.EBS.VolumeSize), }, }) } @@ -328,12 +330,12 @@ func (p *DefaultProvider) blockDeviceMappings(blockDeviceMappings []*v1.BlockDev } // volumeSize returns a GiB scaled value from a resource quantity or nil if the resource quantity passed in is nil -func (p *DefaultProvider) volumeSize(quantity *resource.Quantity) *int64 { +func (p *DefaultProvider) volumeSize(quantity *resource.Quantity) *int32 { if quantity == nil { return nil } // Converts the value to Gi and rounds up the value to the nearest Gi - return aws.Int64(int64(math.Ceil(quantity.AsApproximateFloat64() / math.Pow(2, 30)))) + return lo.ToPtr(int32(math.Ceil(quantity.AsApproximateFloat64() / math.Pow(2, 30)))) } // hydrateCache queries for existing Launch Templates created by Karpenter for the current cluster and adds to the LT cache. @@ -341,18 +343,29 @@ func (p *DefaultProvider) volumeSize(quantity *resource.Quantity) *int64 { func (p *DefaultProvider) hydrateCache(ctx context.Context) { clusterName := options.FromContext(ctx).ClusterName ctx = log.IntoContext(ctx, log.FromContext(ctx).WithValues("tag-key", v1.TagManagedLaunchTemplate, "tag-value", clusterName)) - if err := p.ec2api.DescribeLaunchTemplatesPagesWithContext(ctx, &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{{Name: aws.String(fmt.Sprintf("tag:%s", v1.TagManagedLaunchTemplate)), Values: []*string{aws.String(clusterName)}}}, - }, func(output *ec2.DescribeLaunchTemplatesOutput, _ bool) bool { - for _, lt := range output.LaunchTemplates { + + paginator := ec2.NewDescribeLaunchTemplatesPaginator(p.ec2api, &ec2.DescribeLaunchTemplatesInput{ + Filters: []ec2types.Filter{ + { + Name: aws.String(fmt.Sprintf("tag:%s", v1.TagManagedLaunchTemplate)), + Values: []string{clusterName}, + }, + }, + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + log.FromContext(ctx).Error(err, "unable to hydrate the AWS launch template cache") + return + } + + for _, lt := range page.LaunchTemplates { p.cache.SetDefault(*lt.LaunchTemplateName, lt) } - return true - }); err != nil { - log.FromContext(ctx).Error(err, "unable to hydrate the AWS launch template cache") - } else { - log.FromContext(ctx).WithValues("count", p.cache.ItemCount()).V(1).Info("hydrated launch template cache") } + + log.FromContext(ctx).WithValues("count", p.cache.ItemCount()).V(1).Info("hydrated launch template cache") } func (p *DefaultProvider) cachedEvictedFunc(ctx context.Context) func(string, interface{}) { @@ -362,14 +375,14 @@ func (p *DefaultProvider) cachedEvictedFunc(ctx context.Context) func(string, in if _, expiration, _ := p.cache.GetWithExpiration(key); expiration.After(time.Now()) { return } - launchTemplate := lt.(*ec2.LaunchTemplate) - if _, err := p.ec2api.DeleteLaunchTemplateWithContext(ctx, &ec2.DeleteLaunchTemplateInput{LaunchTemplateId: launchTemplate.LaunchTemplateId}); awserrors.IgnoreNotFound(err) != nil { + launchTemplate := lt.(ec2types.LaunchTemplate) + if _, err := p.ec2api.DeleteLaunchTemplate(ctx, &ec2.DeleteLaunchTemplateInput{LaunchTemplateId: launchTemplate.LaunchTemplateId}); awserrors.IgnoreNotFound(err) != nil { log.FromContext(ctx).WithValues("launch-template", launchTemplate.LaunchTemplateName).Error(err, "failed to delete launch template") return } log.FromContext(ctx).WithValues( - "id", aws.StringValue(launchTemplate.LaunchTemplateId), - "name", aws.StringValue(launchTemplate.LaunchTemplateName), + "id", aws.ToString(launchTemplate.LaunchTemplateId), + "name", aws.ToString(launchTemplate.LaunchTemplateName), ).V(1).Info("deleted launch template") } } @@ -377,39 +390,49 @@ func (p *DefaultProvider) cachedEvictedFunc(ctx context.Context) func(string, in func (p *DefaultProvider) DeleteAll(ctx context.Context, nodeClass *v1.EC2NodeClass) error { clusterName := options.FromContext(ctx).ClusterName var ltNames []*string - if err := p.ec2api.DescribeLaunchTemplatesPagesWithContext(ctx, &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{ - {Name: aws.String(fmt.Sprintf("tag:%s", v1.TagManagedLaunchTemplate)), Values: []*string{aws.String(clusterName)}}, - {Name: aws.String(fmt.Sprintf("tag:%s", v1.LabelNodeClass)), Values: []*string{aws.String(nodeClass.Name)}}, + + paginator := ec2.NewDescribeLaunchTemplatesPaginator(p.ec2api, &ec2.DescribeLaunchTemplatesInput{ + Filters: []ec2types.Filter{ + { + Name: aws.String(fmt.Sprintf("tag:%s", v1.TagManagedLaunchTemplate)), + Values: []string{clusterName}, + }, + { + Name: aws.String(fmt.Sprintf("tag:%s", v1.LabelNodeClass)), + Values: []string{nodeClass.Name}, + }, }, - }, func(output *ec2.DescribeLaunchTemplatesOutput, _ bool) bool { - for _, lt := range output.LaunchTemplates { + }) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return fmt.Errorf("fetching launch templates, %w", err) + } + + for _, lt := range page.LaunchTemplates { ltNames = append(ltNames, lt.LaunchTemplateName) } - return true - }); err != nil { - return fmt.Errorf("fetching launch templates, %w", err) } var deleteErr error for _, name := range ltNames { - _, err := p.ec2api.DeleteLaunchTemplateWithContext(ctx, &ec2.DeleteLaunchTemplateInput{LaunchTemplateName: name}) + _, err := p.ec2api.DeleteLaunchTemplate(ctx, &ec2.DeleteLaunchTemplateInput{LaunchTemplateName: name}) deleteErr = multierr.Append(deleteErr, err) } if len(ltNames) > 0 { - log.FromContext(ctx).WithValues("launchTemplates", utils.PrettySlice(aws.StringValueSlice(ltNames), 5)).V(1).Info("deleted launch templates") + log.FromContext(ctx).WithValues("launchTemplates", utils.PrettySlice(ltNames, 5)).V(1).Info("deleted launch templates") } if deleteErr != nil { return fmt.Errorf("deleting launch templates, %w", deleteErr) } return nil } - func (p *DefaultProvider) ResolveClusterCIDR(ctx context.Context) error { if p.ClusterCIDR.Load() != nil { return nil } - out, err := p.eksapi.DescribeClusterWithContext(ctx, &eks.DescribeClusterInput{ + out, err := p.eksapi.DescribeCluster(ctx, &eks.DescribeClusterInput{ Name: aws.String(options.FromContext(ctx).ClusterName), }) if err != nil { diff --git a/pkg/providers/launchtemplate/suite_test.go b/pkg/providers/launchtemplate/suite_test.go index fab8710b443a..ab62a470ac89 100644 --- a/pkg/providers/launchtemplate/suite_test.go +++ b/pkg/providers/launchtemplate/suite_test.go @@ -28,9 +28,10 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/smithy-go" admv1alpha1 "github.com/awslabs/amazon-eks-ami/nodeadm/api/v1alpha1" "github.com/awslabs/operatorpkg/object" opstatus "github.com/awslabs/operatorpkg/status" @@ -291,7 +292,7 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(len(createFleetInput.LaunchTemplateConfigs)).To(BeNumerically("==", awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len())) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - launchTemplate, ok := lo.Find(createFleetInput.LaunchTemplateConfigs, func(ltConfig *ec2.FleetLaunchTemplateConfigRequest) bool { + launchTemplate, ok := lo.Find(createFleetInput.LaunchTemplateConfigs, func(ltConfig ec2types.FleetLaunchTemplateConfigRequest) bool { return *ltConfig.LaunchTemplateSpecification.LaunchTemplateName == *ltInput.LaunchTemplateName }) Expect(ok).To(BeTrue()) @@ -387,13 +388,16 @@ var _ = Describe("LaunchTemplate Provider", func() { ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 2)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - ltName := aws.StringValue(ltInput.LaunchTemplateName) + ltName := aws.ToString(ltInput.LaunchTemplateName) lt, ok := awsEnv.LaunchTemplateCache.Get(ltName) Expect(ok).To(Equal(true)) // Remove expiration from cached LT awsEnv.LaunchTemplateCache.Set(ltName, lt, -1) }) - awsEnv.EC2API.CreateFleetBehavior.Error.Set(awserr.New("InvalidLaunchTemplateName.NotFoundException", "", nil), fake.MaxCalls(1)) + awsEnv.EC2API.CreateFleetBehavior.Error.Set(&smithy.GenericAPIError{ + Code: "InvalidLaunchTemplateName.NotFoundException", + Message: "The launch template name is invalid.", + }, fake.MaxCalls(1)) pod = coretest.UnschedulablePod() ExpectProvisioned(ctx, env.Client, cluster, cloudProvider, prov, pod) ExpectScheduled(ctx, env.Client, pod) @@ -531,13 +535,13 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(createFleetInput.TagSpecifications).To(HaveLen(3)) // tags should be included in instance, volume, and fleet tag specification - Expect(*createFleetInput.TagSpecifications[0].ResourceType).To(Equal(ec2.ResourceTypeInstance)) + Expect(createFleetInput.TagSpecifications[0].ResourceType).To(Equal(ec2types.ResourceTypeInstance)) ExpectTags(createFleetInput.TagSpecifications[0].Tags, nodeClass.Spec.Tags) - Expect(*createFleetInput.TagSpecifications[1].ResourceType).To(Equal(ec2.ResourceTypeVolume)) + Expect(createFleetInput.TagSpecifications[1].ResourceType).To(Equal(ec2types.ResourceTypeVolume)) ExpectTags(createFleetInput.TagSpecifications[1].Tags, nodeClass.Spec.Tags) - Expect(*createFleetInput.TagSpecifications[2].ResourceType).To(Equal(ec2.ResourceTypeFleet)) + Expect(createFleetInput.TagSpecifications[2].ResourceType).To(Equal(ec2types.ResourceTypeFleet)) ExpectTags(createFleetInput.TagSpecifications[2].Tags, nodeClass.Spec.Tags) }) It("should request that tags be applied to both network interfaces and spot instance requests", func() { @@ -562,10 +566,10 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(i.LaunchTemplateData.TagSpecifications).To(HaveLen(2)) // tags should be included in instance, volume, and fleet tag specification - Expect(*i.LaunchTemplateData.TagSpecifications[0].ResourceType).To(Equal(ec2.ResourceTypeNetworkInterface)) + Expect(i.LaunchTemplateData.TagSpecifications[0].ResourceType).To(Equal(ec2types.ResourceTypeNetworkInterface)) ExpectTags(i.LaunchTemplateData.TagSpecifications[0].Tags, nodeClass.Spec.Tags) - Expect(*i.LaunchTemplateData.TagSpecifications[1].ResourceType).To(Equal(ec2.ResourceTypeSpotInstancesRequest)) + Expect(i.LaunchTemplateData.TagSpecifications[1].ResourceType).To(Equal(ec2types.ResourceTypeSpotInstancesRequest)) ExpectTags(i.LaunchTemplateData.TagSpecifications[1].Tags, nodeClass.Spec.Tags) }) }) @@ -583,13 +587,13 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(createFleetInput.TagSpecifications).To(HaveLen(3)) // tags should be included in instance, volume, and fleet tag specification - Expect(*createFleetInput.TagSpecifications[0].ResourceType).To(Equal(ec2.ResourceTypeInstance)) + Expect(createFleetInput.TagSpecifications[0].ResourceType).To(Equal(ec2types.ResourceTypeInstance)) ExpectTags(createFleetInput.TagSpecifications[0].Tags, nodeClass.Spec.Tags) - Expect(*createFleetInput.TagSpecifications[1].ResourceType).To(Equal(ec2.ResourceTypeVolume)) + Expect(createFleetInput.TagSpecifications[1].ResourceType).To(Equal(ec2types.ResourceTypeVolume)) ExpectTags(createFleetInput.TagSpecifications[1].Tags, nodeClass.Spec.Tags) - Expect(*createFleetInput.TagSpecifications[2].ResourceType).To(Equal(ec2.ResourceTypeFleet)) + Expect(createFleetInput.TagSpecifications[2].ResourceType).To(Equal(ec2types.ResourceTypeFleet)) ExpectTags(createFleetInput.TagSpecifications[2].Tags, nodeClass.Spec.Tags) }) }) @@ -603,9 +607,9 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { Expect(len(ltInput.LaunchTemplateData.BlockDeviceMappings)).To(Equal(1)) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize).To(Equal(int64(20))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal("gp3")) - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops).To(BeNil()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(Equal(int32(20))) + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal(ec2types.VolumeType("gp3"))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops)).To(Equal(int32(0))) }) }) It("should default AL2023 block device mappings", func() { @@ -619,9 +623,9 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { Expect(len(ltInput.LaunchTemplateData.BlockDeviceMappings)).To(Equal(1)) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize).To(Equal(int64(20))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal("gp3")) - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops).To(BeNil()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(Equal(int32(20))) + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal(ec2types.VolumeType("gp3"))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops)).To(Equal(int32(0))) }) }) It("should use custom block device mapping", func() { @@ -655,18 +659,18 @@ var _ = Describe("LaunchTemplate Provider", func() { ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs).To(Equal(&ec2.LaunchTemplateEbsBlockDeviceRequest{ - VolumeSize: aws.Int64(187), - VolumeType: aws.String("io2"), - Iops: aws.Int64(10_000), + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs).To(Equal(&ec2types.LaunchTemplateEbsBlockDeviceRequest{ + VolumeSize: aws.Int32(187), + VolumeType: ec2types.VolumeType("io2"), + Iops: aws.Int32(10_000), DeleteOnTermination: aws.Bool(true), Encrypted: aws.Bool(true), KmsKeyId: aws.String("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"), })) - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs).To(Equal(&ec2.LaunchTemplateEbsBlockDeviceRequest{ - VolumeSize: aws.Int64(200), - VolumeType: aws.String("io2"), - Iops: aws.Int64(10_000), + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs).To(Equal(&ec2types.LaunchTemplateEbsBlockDeviceRequest{ + VolumeSize: aws.Int32(200), + VolumeType: "io2", + Iops: aws.Int32(10_000), DeleteOnTermination: aws.Bool(true), Encrypted: aws.Bool(true), KmsKeyId: aws.String("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"), @@ -705,8 +709,8 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { // Both of these values are rounded up when converting to Gibibytes - Expect(aws.Int64Value(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(BeNumerically("==", 4)) - Expect(aws.Int64Value(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeSize)).To(BeNumerically("==", 2)) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(BeNumerically("==", 4)) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeSize)).To(BeNumerically("==", 2)) }) }) It("should default bottlerocket second volume with root volume size", func() { @@ -719,13 +723,13 @@ var _ = Describe("LaunchTemplate Provider", func() { awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { Expect(len(ltInput.LaunchTemplateData.BlockDeviceMappings)).To(Equal(2)) // Bottlerocket control volume - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize).To(Equal(int64(4))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal("gp3")) - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops).To(BeNil()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(Equal(int32(4))) + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal(ec2types.VolumeType("gp3"))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops)).To(Equal(int32(0))) // Bottlerocket user volume - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeSize).To(Equal(int64(20))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeType).To(Equal("gp3")) - Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.Iops).To(BeNil()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeSize)).To(Equal(int32(20))) + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.VolumeType).To(Equal(ec2types.VolumeType("gp3"))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[1].Ebs.Iops)).To(Equal(int32(0))) }) }) It("should not default block device mappings for custom AMIFamilies", func() { @@ -763,12 +767,12 @@ var _ = Describe("LaunchTemplate Provider", func() { Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 2)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { Expect(len(ltInput.LaunchTemplateData.BlockDeviceMappings)).To(Equal(1)) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize).To(Equal(int64(40))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal("io2")) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops).To(Equal(int64(10_000))) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.DeleteOnTermination).To(BeTrue()) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Encrypted).To(BeTrue()) - Expect(*ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.KmsKeyId).To(Equal("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab")) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeSize)).To(Equal(int32(40))) + Expect(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.VolumeType).To(Equal(ec2types.VolumeType("io2"))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Iops)).To(Equal(int32(10_000))) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.DeleteOnTermination)).To(BeTrue()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.Encrypted)).To(BeTrue()) + Expect(lo.FromPtr(ltInput.LaunchTemplateData.BlockDeviceMappings[0].Ebs.KmsKeyId)).To(Equal("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab")) }) }) }) @@ -976,28 +980,26 @@ var _ = Describe("LaunchTemplate Provider", func() { }) }) Context("AL2", func() { - var info *ec2.InstanceTypeInfo + var info ec2types.InstanceTypeInfo BeforeEach(func() { var ok bool - var instanceInfo []*ec2.InstanceTypeInfo - err := awsEnv.EC2API.DescribeInstanceTypesPagesWithContext(ctx, &ec2.DescribeInstanceTypesInput{ - Filters: []*ec2.Filter{ + var instanceInfo []ec2types.InstanceTypeInfo + out, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{ + Filters: []ec2types.Filter{ { Name: aws.String("supported-virtualization-type"), - Values: []*string{aws.String("hvm")}, + Values: []string{"hvm"}, }, { Name: aws.String("processor-info.supported-architecture"), - Values: aws.StringSlice([]string{"x86_64", "arm64"}), + Values: []string{"x86_64", "arm64"}, }, }, - }, func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool { - instanceInfo = append(instanceInfo, page.InstanceTypes...) - return true }) + instanceInfo = out.InstanceTypes Expect(err).To(BeNil()) - info, ok = lo.Find(instanceInfo, func(i *ec2.InstanceTypeInfo) bool { - return aws.StringValue(i.InstanceType) == "m5.xlarge" + info, ok = lo.Find(instanceInfo, func(i ec2types.InstanceTypeInfo) bool { + return i.InstanceType == "m5.xlarge" }) Expect(ok).To(BeTrue()) }) @@ -1029,28 +1031,26 @@ var _ = Describe("LaunchTemplate Provider", func() { }) }) Context("Bottlerocket", func() { - var info *ec2.InstanceTypeInfo + var info ec2types.InstanceTypeInfo BeforeEach(func() { var ok bool - var instanceInfo []*ec2.InstanceTypeInfo - err := awsEnv.EC2API.DescribeInstanceTypesPagesWithContext(ctx, &ec2.DescribeInstanceTypesInput{ - Filters: []*ec2.Filter{ + var instanceInfo []ec2types.InstanceTypeInfo + out, err := awsEnv.EC2API.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{ + Filters: []ec2types.Filter{ { Name: aws.String("supported-virtualization-type"), - Values: []*string{aws.String("hvm")}, + Values: []string{"hvm"}, }, { Name: aws.String("processor-info.supported-architecture"), - Values: aws.StringSlice([]string{"x86_64", "arm64"}), + Values: []string{"x86_64", "arm64"}, }, }, - }, func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool { - instanceInfo = append(instanceInfo, page.InstanceTypes...) - return true }) + instanceInfo = out.InstanceTypes Expect(err).To(BeNil()) - info, ok = lo.Find(instanceInfo, func(i *ec2.InstanceTypeInfo) bool { - return aws.StringValue(i.InstanceType) == "m5.xlarge" + info, ok = lo.Find(instanceInfo, func(i ec2types.InstanceTypeInfo) bool { + return i.InstanceType == "m5.xlarge" }) Expect(ok).To(BeTrue()) }) @@ -1939,19 +1939,19 @@ essential = true It("should correctly use ami selector with specific IDs in EC2NodeClass", func() { nodeClass.Spec.AMIFamily = lo.ToPtr(v1.AMIFamilyCustom) nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{ID: "ami-123"}, {ID: "ami-456"}} - awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []*ec2.Image{ + awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []ec2types.Image{ { Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-123"), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{{Key: aws.String(corev1.LabelInstanceTypeStable), Value: aws.String("m5.large")}}, + Architecture: "x86_64", + Tags: []ec2types.Tag{{Key: aws.String(corev1.LabelInstanceTypeStable), Value: aws.String("m5.large")}}, CreationDate: aws.String("2022-08-15T12:00:00Z"), }, { Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-456"), - Architecture: aws.String("x86_64"), - Tags: []*ec2.Tag{{Key: aws.String(corev1.LabelInstanceTypeStable), Value: aws.String("m5.xlarge")}}, + Architecture: "x86_64", + Tags: []ec2types.Tag{{Key: aws.String(corev1.LabelInstanceTypeStable), Value: aws.String("m5.xlarge")}}, CreationDate: aws.String("2022-08-15T12:00:00Z"), }, }}) @@ -1963,10 +1963,10 @@ essential = true Expect(err).To(BeNil()) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically(">=", 2)) actualFilter := awsEnv.EC2API.CalledWithDescribeImagesInput.Pop().Filters - expectedFilter := []*ec2.Filter{ + expectedFilter := []ec2types.Filter{ { Name: aws.String("image-id"), - Values: aws.StringSlice([]string{"ami-123", "ami-456"}), + Values: []string{"ami-123", "ami-456"}, }, } Expect(actualFilter).To(Equal(expectedFilter)) @@ -2001,24 +2001,24 @@ essential = true Expect(expectedImageIds.Equal(actualImageIds)).To(BeTrue()) }) It("should create a launch template with the newest compatible AMI when multiple amis are discovered", func() { - awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []*ec2.Image{ + awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []ec2types.Image{ { Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-123"), - Architecture: aws.String("x86_64"), + Architecture: "x86_64", CreationDate: aws.String("2020-01-01T12:00:00Z"), }, { Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-456"), - Architecture: aws.String("x86_64"), + Architecture: "x86_64", CreationDate: aws.String("2021-01-01T12:00:00Z"), }, { // Incompatible because required ARM64 Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-789"), - Architecture: aws.String("arm64"), + Architecture: "arm64", CreationDate: aws.String("2022-01-01T12:00:00Z"), }, }}) @@ -2047,7 +2047,7 @@ essential = true }) It("should fail if no amis match selector.", func() { - awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []*ec2.Image{}}) + awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []ec2types.Image{}}) nodeClass.Spec.AMIFamily = lo.ToPtr(v1.AMIFamilyCustom) nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Tags: map[string]string{"*": "*"}}} nodeClass.Status.AMIs = []v1.AMI{} @@ -2058,8 +2058,8 @@ essential = true Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(Equal(0)) }) It("should fail if no instanceType matches ami requirements.", func() { - awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []*ec2.Image{ - {Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-123"), Architecture: aws.String("newnew"), CreationDate: aws.String("2022-01-01T12:00:00Z")}}}) + awsEnv.EC2API.DescribeImagesOutput.Set(&ec2.DescribeImagesOutput{Images: []ec2types.Image{ + {Name: aws.String(coretest.RandomName()), ImageId: aws.String("ami-123"), Architecture: "newnew", CreationDate: aws.String("2022-01-01T12:00:00Z")}}}) nodeClass.Spec.AMIFamily = lo.ToPtr(v1.AMIFamilyCustom) nodeClass.Spec.AMISelectorTerms = []v1.AMISelectorTerm{{Tags: map[string]string{"*": "*"}}} nodeClass.Status.AMIs = []v1.AMI{ @@ -2180,7 +2180,7 @@ essential = true ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(aws.BoolValue(ltInput.LaunchTemplateData.Monitoring.Enabled)).To(BeFalse()) + Expect(aws.ToBool(ltInput.LaunchTemplateData.Monitoring.Enabled)).To(BeFalse()) }) }) It("should pass detailed monitoring setting to the launch template at creation", func() { @@ -2191,7 +2191,7 @@ essential = true ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(aws.BoolValue(ltInput.LaunchTemplateData.Monitoring.Enabled)).To(BeTrue()) + Expect(aws.ToBool(ltInput.LaunchTemplateData.Monitoring.Enabled)).To(BeTrue()) }) }) }) @@ -2203,10 +2203,10 @@ essential = true ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint)).To(Equal(ec2.InstanceMetadataEndpointStateEnabled)) - Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6)).To(Equal(ec2.InstanceMetadataEndpointStateDisabled)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpEndpoint).To(Equal(ec2types.LaunchTemplateInstanceMetadataEndpointStateEnabled)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpProtocolIpv6).To(Equal(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Disabled)) Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpPutResponseHopLimit)).To(BeNumerically("==", 1)) - Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.HttpTokens)).To(Equal(ec2.HttpTokensStateRequired)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.HttpTokens).To(Equal(ec2types.LaunchTemplateHttpTokensStateRequired)) }) }) It("should set instance metadata tags to disabled", func() { @@ -2216,16 +2216,16 @@ essential = true ExpectScheduled(ctx, env.Client, pod) Expect(awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.Len()).To(BeNumerically("==", 5)) awsEnv.EC2API.CalledWithCreateLaunchTemplateInput.ForEach(func(ltInput *ec2.CreateLaunchTemplateInput) { - Expect(lo.FromPtr(ltInput.LaunchTemplateData.MetadataOptions.InstanceMetadataTags)).To(Equal(ec2.InstanceMetadataTagsStateDisabled)) + Expect(ltInput.LaunchTemplateData.MetadataOptions.InstanceMetadataTags).To(Equal(ec2types.LaunchTemplateInstanceMetadataTagsStateDisabled)) }) }) }) }) // ExpectTags verifies that the expected tags are a subset of the tags found -func ExpectTags(tags []*ec2.Tag, expected map[string]string) { +func ExpectTags(tags []ec2types.Tag, expected map[string]string) { GinkgoHelper() - existingTags := lo.SliceToMap(tags, func(t *ec2.Tag) (string, string) { return *t.Key, *t.Value }) + existingTags := lo.SliceToMap(tags, func(t ec2types.Tag) (string, string) { return *t.Key, *t.Value }) for expKey, expValue := range expected { foundValue, ok := existingTags[expKey] Expect(ok).To(BeTrue(), fmt.Sprintf("expected to find tag %s in %s", expKey, existingTags)) diff --git a/pkg/providers/pricing/pricing.go b/pkg/providers/pricing/pricing.go index e5b8677696b6..22eaf895d51e 100644 --- a/pkg/providers/pricing/pricing.go +++ b/pkg/providers/pricing/pricing.go @@ -15,10 +15,10 @@ limitations under the License. package pricing import ( - "bytes" "context" "encoding/json" "fmt" + "maps" "net/http" "strconv" "strings" @@ -27,14 +27,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/aws/karpenter-provider-aws/pkg/operator/options" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "github.com/aws/aws-sdk-go/service/pricing" - "github.com/aws/aws-sdk-go/service/pricing/pricingiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/pricing" + pricingtypes "github.com/aws/aws-sdk-go-v2/service/pricing/types" "github.com/samber/lo" "go.uber.org/multierr" "sigs.k8s.io/karpenter/pkg/utils/pretty" @@ -44,9 +44,9 @@ var initialOnDemandPrices = lo.Assign(InitialOnDemandPricesAWS, InitialOnDemandP type Provider interface { LivenessProbe(*http.Request) error - InstanceTypes() []string - OnDemandPrice(string) (float64, bool) - SpotPrice(string, string) (float64, bool) + InstanceTypes() []ec2types.InstanceType + OnDemandPrice(ec2types.InstanceType) (float64, bool) + SpotPrice(ec2types.InstanceType, string) (float64, bool) UpdateOnDemandPricing(context.Context) error UpdateSpotPricing(context.Context) error } @@ -58,16 +58,16 @@ type Provider interface { // fails, the previous pricing information is retained and used which may be the static initial pricing data if pricing // updates never succeed. type DefaultProvider struct { - ec2 ec2iface.EC2API - pricing pricingiface.PricingAPI + ec2 sdk.EC2API + pricing sdk.PricingAPI region string cm *pretty.ChangeMonitor muOnDemand sync.RWMutex - onDemandPrices map[string]float64 + onDemandPrices map[ec2types.InstanceType]float64 muSpot sync.RWMutex - spotPrices map[string]zonal + spotPrices map[ec2types.InstanceType]zonal spotPricingUpdated bool } @@ -89,23 +89,23 @@ func newZonalPricing(defaultPrice float64) zonal { } // NewPricingAPI returns a pricing API configured based on a particular region -func NewAPI(sess *session.Session, region string) pricingiface.PricingAPI { - if sess == nil { - return nil - } +func NewAPI(cfg aws.Config) *pricing.Client { // pricing API doesn't have an endpoint in all regions pricingAPIRegion := "us-east-1" - if strings.HasPrefix(region, "ap-") { + if strings.HasPrefix(cfg.Region, "ap-") { pricingAPIRegion = "ap-south-1" - } else if strings.HasPrefix(region, "cn-") { + } else if strings.HasPrefix(cfg.Region, "cn-") { pricingAPIRegion = "cn-northwest-1" - } else if strings.HasPrefix(region, "eu-") { + } else if strings.HasPrefix(cfg.Region, "eu-") { pricingAPIRegion = "eu-central-1" } - return pricing.New(sess, &aws.Config{Region: aws.String(pricingAPIRegion)}) + //create pricing config using pricing endpoint + pricingCfg := cfg.Copy() + pricingCfg.Region = pricingAPIRegion + return pricing.NewFromConfig(pricingCfg) } -func NewDefaultProvider(_ context.Context, pricing pricingiface.PricingAPI, ec2Api ec2iface.EC2API, region string) *DefaultProvider { +func NewDefaultProvider(_ context.Context, pricing sdk.PricingAPI, ec2Api sdk.EC2API, region string) *DefaultProvider { p := &DefaultProvider{ region: region, ec2: ec2Api, @@ -119,7 +119,7 @@ func NewDefaultProvider(_ context.Context, pricing pricingiface.PricingAPI, ec2A } // InstanceTypes returns the list of all instance types for which either a spot or on-demand price is known. -func (p *DefaultProvider) InstanceTypes() []string { +func (p *DefaultProvider) InstanceTypes() []ec2types.InstanceType { p.muOnDemand.RLock() p.muSpot.RLock() defer p.muOnDemand.RUnlock() @@ -129,7 +129,7 @@ func (p *DefaultProvider) InstanceTypes() []string { // OnDemandPrice returns the last known on-demand price for a given instance type, returning an error if there is no // known on-demand pricing for the instance type. -func (p *DefaultProvider) OnDemandPrice(instanceType string) (float64, bool) { +func (p *DefaultProvider) OnDemandPrice(instanceType ec2types.InstanceType) (float64, bool) { p.muOnDemand.RLock() defer p.muOnDemand.RUnlock() price, ok := p.onDemandPrices[instanceType] @@ -141,7 +141,7 @@ func (p *DefaultProvider) OnDemandPrice(instanceType string) (float64, bool) { // SpotPrice returns the last known spot price for a given instance type and zone, returning an error // if there is no known spot pricing for that instance type or zone -func (p *DefaultProvider) SpotPrice(instanceType string, zone string) (float64, bool) { +func (p *DefaultProvider) SpotPrice(instanceType ec2types.InstanceType, zone string) (float64, bool) { p.muSpot.RLock() defer p.muSpot.RUnlock() if val, ok := p.spotPrices[instanceType]; ok { @@ -159,7 +159,7 @@ func (p *DefaultProvider) SpotPrice(instanceType string, zone string) (float64, func (p *DefaultProvider) UpdateOnDemandPricing(ctx context.Context) error { // standard on-demand instances var wg sync.WaitGroup - var onDemandPrices, onDemandMetalPrices map[string]float64 + var onDemandPrices, onDemandMetalPrices map[ec2types.InstanceType]float64 var onDemandErr, onDemandMetalErr error // if we are in isolated vpc, skip updating on demand pricing @@ -178,14 +178,14 @@ func (p *DefaultProvider) UpdateOnDemandPricing(ctx context.Context) error { go func() { defer wg.Done() onDemandPrices, onDemandErr = p.fetchOnDemandPricing(ctx, - &pricing.Filter{ + pricingtypes.Filter{ Field: aws.String("tenancy"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Shared"), }, - &pricing.Filter{ + pricingtypes.Filter{ Field: aws.String("productFamily"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Compute Instance"), }) }() @@ -195,14 +195,14 @@ func (p *DefaultProvider) UpdateOnDemandPricing(ctx context.Context) error { go func() { defer wg.Done() onDemandMetalPrices, onDemandMetalErr = p.fetchOnDemandPricing(ctx, - &pricing.Filter{ + pricingtypes.Filter{ Field: aws.String("tenancy"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Dedicated"), }, - &pricing.Filter{ + pricingtypes.Filter{ Field: aws.String("productFamily"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Compute Instance (bare metal)"), }) }() @@ -225,85 +225,90 @@ func (p *DefaultProvider) UpdateOnDemandPricing(ctx context.Context) error { return nil } -func (p *DefaultProvider) fetchOnDemandPricing(ctx context.Context, additionalFilters ...*pricing.Filter) (map[string]float64, error) { - prices := map[string]float64{} - filters := append([]*pricing.Filter{ +func (p *DefaultProvider) fetchOnDemandPricing(ctx context.Context, additionalFilters ...pricingtypes.Filter) (map[ec2types.InstanceType]float64, error) { + prices := map[ec2types.InstanceType]float64{} + filters := append([]pricingtypes.Filter{ { Field: aws.String("regionCode"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String(p.region), }, { Field: aws.String("serviceCode"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("AmazonEC2"), }, { Field: aws.String("preInstalledSw"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("NA"), }, { Field: aws.String("operatingSystem"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Linux"), }, { Field: aws.String("capacitystatus"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("Used"), }, { Field: aws.String("marketoption"), - Type: aws.String("TERM_MATCH"), + Type: "TERM_MATCH", Value: aws.String("OnDemand"), }}, additionalFilters...) - err := p.pricing.GetProductsPagesWithContext( - ctx, - &pricing.GetProductsInput{ - Filters: filters, - ServiceCode: aws.String("AmazonEC2"), - }, - p.onDemandPage(ctx, prices), - ) - if err != nil { - return nil, err + input := &pricing.GetProductsInput{ + Filters: filters, + ServiceCode: aws.String("AmazonEC2"), + } + + paginator := pricing.NewGetProductsPaginator(p.pricing, input) + for paginator.HasMorePages() { + output, err := paginator.NextPage(ctx) + + if err != nil { + return nil, fmt.Errorf("getting pricing data, %w", err) + } + prices = lo.Assign(prices, p.onDemandPage(ctx, output)) } return prices, nil } -func (p *DefaultProvider) spotPage(ctx context.Context, prices map[string]map[string]float64) func(output *ec2.DescribeSpotPriceHistoryOutput, b bool) bool { - return func(output *ec2.DescribeSpotPriceHistoryOutput, b bool) bool { - for _, sph := range output.SpotPriceHistory { - spotPriceStr := aws.StringValue(sph.SpotPrice) - spotPrice, err := strconv.ParseFloat(spotPriceStr, 64) - // these errors shouldn't occur, but if pricing API does have an error, we ignore the record - if err != nil { - log.FromContext(ctx).V(1).Info(fmt.Sprintf("unable to parse price record %#v", sph)) - continue - } - if sph.Timestamp == nil { - continue - } - instanceType := aws.StringValue(sph.InstanceType) - az := aws.StringValue(sph.AvailabilityZone) - _, ok := prices[instanceType] - if !ok { - prices[instanceType] = map[string]float64{} +func (p *DefaultProvider) spotPage(ctx context.Context, output *ec2.DescribeSpotPriceHistoryOutput) map[ec2types.InstanceType]zonal { + result := map[ec2types.InstanceType]zonal{} + for _, sph := range output.SpotPriceHistory { + spotPriceStr := aws.ToString(sph.SpotPrice) + spotPrice, err := strconv.ParseFloat(spotPriceStr, 64) + // these errors shouldn't occur, but if pricing API does have an error, we ignore the record + if err != nil { + log.FromContext(ctx).V(1).Info(fmt.Sprintf("unable to parse price record %#v", sph)) + continue + } + if sph.Timestamp == nil { + continue + } + instanceType := sph.InstanceType + az := aws.ToString(sph.AvailabilityZone) + _, ok := result[instanceType] + if !ok { + result[instanceType] = zonal{ + prices: map[string]float64{}, } - prices[instanceType][az] = spotPrice } - return true + result[instanceType].prices[az] = spotPrice + } + return result } // turning off cyclo here, it measures as a 12 due to all of the type checks of the pricing data which returns a deeply // nested map[string]interface{} // nolint: gocyclo -func (p *DefaultProvider) onDemandPage(ctx context.Context, prices map[string]float64) func(output *pricing.GetProductsOutput, b bool) bool { +func (p *DefaultProvider) onDemandPage(ctx context.Context, output *pricing.GetProductsOutput) map[ec2types.InstanceType]float64 { // this isn't the full pricing struct, just the portions we care about type priceItem struct { Product struct { @@ -320,60 +325,56 @@ func (p *DefaultProvider) onDemandPage(ctx context.Context, prices map[string]fl } } - return func(output *pricing.GetProductsOutput, b bool) bool { - currency := "USD" - if strings.HasPrefix(p.region, "cn-") { - currency = "CNY" + result := map[ec2types.InstanceType]float64{} + currency := "USD" + if strings.HasPrefix(p.region, "cn-") { + currency = "CNY" + } + for _, outer := range output.PriceList { + pItem := &priceItem{} + if err := json.Unmarshal([]byte(outer), pItem); err != nil { + log.FromContext(ctx).Error(err, "failed unmarshaling pricing data") } - for _, outer := range output.PriceList { - var buf bytes.Buffer - enc := json.NewEncoder(&buf) - if err := enc.Encode(outer); err != nil { - log.FromContext(ctx).Error(err, "failed encoding pricing data") - } - dec := json.NewDecoder(&buf) - var pItem priceItem - if err := dec.Decode(&pItem); err != nil { - log.FromContext(ctx).Error(err, "failed decoding pricing data") - } - if pItem.Product.Attributes.InstanceType == "" { - continue - } - for _, term := range pItem.Terms.OnDemand { - for _, v := range term.PriceDimensions { - price, err := strconv.ParseFloat(v.PricePerUnit[currency], 64) - if err != nil || price == 0 { - continue - } - prices[pItem.Product.Attributes.InstanceType] = price + if pItem.Product.Attributes.InstanceType == "" { + continue + } + for _, term := range pItem.Terms.OnDemand { + for _, v := range term.PriceDimensions { + price, err := strconv.ParseFloat(v.PricePerUnit[currency], 64) + if err != nil || price == 0 { + continue } + result[ec2types.InstanceType(pItem.Product.Attributes.InstanceType)] = price } } - return true } + + return result } // nolint: gocyclo func (p *DefaultProvider) UpdateSpotPricing(ctx context.Context) error { - prices := map[string]map[string]float64{} + prices := map[ec2types.InstanceType]zonal{} p.muSpot.Lock() defer p.muSpot.Unlock() - err := p.ec2.DescribeSpotPriceHistoryPagesWithContext( - ctx, - &ec2.DescribeSpotPriceHistoryInput{ - ProductDescriptions: []*string{ - aws.String("Linux/UNIX"), - aws.String("Linux/UNIX (Amazon VPC)"), - }, - // get the latest spot price for each instance type - StartTime: aws.Time(time.Now()), + + input := &ec2.DescribeSpotPriceHistoryInput{ + ProductDescriptions: []string{ + "Linux/UNIX", + "Linux/UNIX (Amazon VPC)", }, - p.spotPage(ctx, prices), - ) + // get the latest spot price for each instance type + StartTime: aws.Time(time.Now()), + } - if err != nil { - return fmt.Errorf("retrieving spot pricing data, %w", err) + paginator := ec2.NewDescribeSpotPriceHistoryPaginator(p.ec2, input) + for paginator.HasMorePages() { + output, err := paginator.NextPage(ctx) + if err != nil { + return fmt.Errorf("retrieving spot pricing data, %w", err) + } + prices = lo.Assign(prices, p.spotPage(ctx, output)) } if len(prices) == 0 { return fmt.Errorf("no spot pricing found") @@ -384,10 +385,8 @@ func (p *DefaultProvider) UpdateSpotPricing(ctx context.Context) error { if _, ok := p.spotPrices[it]; !ok { p.spotPrices[it] = newZonalPricing(0) } - for zone, price := range zoneData { - p.spotPrices[it].prices[zone] = price - } - totalOfferings += len(zoneData) + maps.Copy(p.spotPrices[it].prices, zoneData.prices) + totalOfferings += len(zoneData.prices) } p.spotPricingUpdated = true @@ -409,8 +408,8 @@ func (p *DefaultProvider) LivenessProbe(_ *http.Request) error { return nil } -func populateInitialSpotPricing(pricing map[string]float64) map[string]zonal { - m := map[string]zonal{} +func populateInitialSpotPricing(pricing map[ec2types.InstanceType]float64) map[ec2types.InstanceType]zonal { + m := map[ec2types.InstanceType]zonal{} for it, price := range pricing { m[it] = newZonalPricing(price) } diff --git a/pkg/providers/pricing/zz_generated.pricing_aws.go b/pkg/providers/pricing/zz_generated.pricing_aws.go index c79d0130b5f0..9f337299104b 100644 --- a/pkg/providers/pricing/zz_generated.pricing_aws.go +++ b/pkg/providers/pricing/zz_generated.pricing_aws.go @@ -16,9 +16,11 @@ limitations under the License. package pricing -// generated at 2024-09-30T13:12:07Z for us-east-1 +// generated at 2024-11-05T04:33:42Z for us-east-1 -var InitialOnDemandPricesAWS = map[string]map[string]float64{ +import ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + +var InitialOnDemandPricesAWS = map[string]map[ec2types.InstanceType]float64{ // us-east-1 "us-east-1": { // a1 family diff --git a/pkg/providers/pricing/zz_generated.pricing_aws_cn.go b/pkg/providers/pricing/zz_generated.pricing_aws_cn.go index a3f2ec8f3ec6..ca547046422d 100644 --- a/pkg/providers/pricing/zz_generated.pricing_aws_cn.go +++ b/pkg/providers/pricing/zz_generated.pricing_aws_cn.go @@ -18,8 +18,9 @@ package pricing // generated at 2024-11-06T01:00:11Z for cn-north-1 -var InitialOnDemandPricesCN = map[string]map[string]float64{ - // cn-north-1 +import ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + +var InitialOnDemandPricesCN = map[string]map[ec2types.InstanceType]float64{ "cn-north-1": { // c3 family "c3.2xlarge": 4.217000, "c3.4xlarge": 8.434000, "c3.8xlarge": 16.869000, "c3.large": 1.054000, diff --git a/pkg/providers/pricing/zz_generated.pricing_aws_us_gov.go b/pkg/providers/pricing/zz_generated.pricing_aws_us_gov.go index 6078a7c19179..4f5c25460f42 100644 --- a/pkg/providers/pricing/zz_generated.pricing_aws_us_gov.go +++ b/pkg/providers/pricing/zz_generated.pricing_aws_us_gov.go @@ -16,9 +16,11 @@ limitations under the License. package pricing -// generated at 2024-10-28T13:12:26Z for us-east-1 +// generated at 2024-11-05T04:33:46Z for us-east-1 -var InitialOnDemandPricesUSGov = map[string]map[string]float64{ +import ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + +var InitialOnDemandPricesUSGov = map[string]map[ec2types.InstanceType]float64{ // us-gov-east-1 "us-gov-east-1": { // c5 family diff --git a/pkg/providers/securitygroup/securitygroup.go b/pkg/providers/securitygroup/securitygroup.go index 998498277f32..983a2e1ee195 100644 --- a/pkg/providers/securitygroup/securitygroup.go +++ b/pkg/providers/securitygroup/securitygroup.go @@ -19,9 +19,9 @@ import ( "fmt" "sync" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" "github.com/samber/lo" @@ -30,20 +30,21 @@ import ( "sigs.k8s.io/karpenter/pkg/utils/pretty" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type Provider interface { - List(context.Context, *v1.EC2NodeClass) ([]*ec2.SecurityGroup, error) + List(context.Context, *v1.EC2NodeClass) ([]ec2types.SecurityGroup, error) } type DefaultProvider struct { sync.Mutex - ec2api ec2iface.EC2API + ec2api sdk.EC2API cache *cache.Cache cm *pretty.ChangeMonitor } -func NewDefaultProvider(ec2api ec2iface.EC2API, cache *cache.Cache) *DefaultProvider { +func NewDefaultProvider(ec2api sdk.EC2API, cache *cache.Cache) *DefaultProvider { return &DefaultProvider{ ec2api: ec2api, cm: pretty.NewChangeMonitor(), @@ -52,7 +53,7 @@ func NewDefaultProvider(ec2api ec2iface.EC2API, cache *cache.Cache) *DefaultProv } } -func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]*ec2.SecurityGroup, error) { +func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]ec2types.SecurityGroup, error) { p.Lock() defer p.Unlock() @@ -62,7 +63,7 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) if err != nil { return nil, err } - securityGroupIDs := lo.Map(securityGroups, func(s *ec2.SecurityGroup, _ int) string { return aws.StringValue(s.GroupId) }) + securityGroupIDs := lo.Map(securityGroups, func(s ec2types.SecurityGroup, _ int) string { return aws.ToString(s.GroupId) }) if p.cm.HasChanged(fmt.Sprintf("security-groups/%s", nodeClass.Name), securityGroupIDs) { log.FromContext(ctx). WithValues("security-groups", securityGroupIDs). @@ -71,7 +72,7 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) return securityGroups, nil } -func (p *DefaultProvider) getSecurityGroups(ctx context.Context, filterSets [][]*ec2.Filter) ([]*ec2.SecurityGroup, error) { +func (p *DefaultProvider) getSecurityGroups(ctx context.Context, filterSets [][]ec2types.Filter) ([]ec2types.SecurityGroup, error) { hash, err := hashstructure.Hash(filterSets, hashstructure.FormatV2, &hashstructure.HashOptions{SlicesAsSets: true}) if err != nil { return nil, err @@ -79,11 +80,11 @@ func (p *DefaultProvider) getSecurityGroups(ctx context.Context, filterSets [][] if sg, ok := p.cache.Get(fmt.Sprint(hash)); ok { // Ensure what's returned from this function is a shallow-copy of the slice (not a deep-copy of the data itself) // so that modifications to the ordering of the data don't affect the original - return append([]*ec2.SecurityGroup{}, sg.([]*ec2.SecurityGroup)...), nil + return append([]ec2types.SecurityGroup{}, sg.([]ec2types.SecurityGroup)...), nil } - securityGroups := map[string]*ec2.SecurityGroup{} + securityGroups := map[string]ec2types.SecurityGroup{} for _, filters := range filterSets { - output, err := p.ec2api.DescribeSecurityGroupsWithContext(ctx, &ec2.DescribeSecurityGroupsInput{Filters: filters}) + output, err := p.ec2api.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{Filters: filters}) if err != nil { return nil, fmt.Errorf("describing security groups %+v, %w", filterSets, err) } @@ -95,27 +96,27 @@ func (p *DefaultProvider) getSecurityGroups(ctx context.Context, filterSets [][] return lo.Values(securityGroups), nil } -func getFilterSets(terms []v1.SecurityGroupSelectorTerm) (res [][]*ec2.Filter) { - idFilter := &ec2.Filter{Name: aws.String("group-id")} - nameFilter := &ec2.Filter{Name: aws.String("group-name")} +func getFilterSets(terms []v1.SecurityGroupSelectorTerm) (res [][]ec2types.Filter) { + idFilter := ec2types.Filter{Name: aws.String("group-id")} + nameFilter := ec2types.Filter{Name: aws.String("group-name")} for _, term := range terms { switch { case term.ID != "": - idFilter.Values = append(idFilter.Values, aws.String(term.ID)) + idFilter.Values = append(idFilter.Values, term.ID) case term.Name != "": - nameFilter.Values = append(nameFilter.Values, aws.String(term.Name)) + nameFilter.Values = append(nameFilter.Values, term.Name) default: - var filters []*ec2.Filter + var filters []ec2types.Filter for k, v := range term.Tags { if v == "*" { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String("tag-key"), - Values: []*string{aws.String(k)}, + Values: []string{k}, }) } else { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", k)), - Values: []*string{aws.String(v)}, + Values: []string{v}, }) } } @@ -123,10 +124,10 @@ func getFilterSets(terms []v1.SecurityGroupSelectorTerm) (res [][]*ec2.Filter) { } } if len(idFilter.Values) > 0 { - res = append(res, []*ec2.Filter{idFilter}) + res = append(res, []ec2types.Filter{idFilter}) } if len(nameFilter.Values) > 0 { - res = append(res, []*ec2.Filter{nameFilter}) + res = append(res, []ec2types.Filter{nameFilter}) } return res } diff --git a/pkg/providers/securitygroup/suite_test.go b/pkg/providers/securitygroup/suite_test.go index 6a2e18fe8111..629ebd4ade56 100644 --- a/pkg/providers/securitygroup/suite_test.go +++ b/pkg/providers/securitygroup/suite_test.go @@ -22,8 +22,9 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "github.com/aws/karpenter-provider-aws/pkg/apis" @@ -100,7 +101,7 @@ var _ = Describe("SecurityGroupProvider", func() { It("should default to the clusters security groups", func() { securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), @@ -116,13 +117,13 @@ var _ = Describe("SecurityGroupProvider", func() { }, securityGroups) }) It("should discover security groups by tag", func() { - awsEnv.EC2API.DescribeSecurityGroupsOutput.Set(&ec2.DescribeSecurityGroupsOutput{SecurityGroups: []*ec2.SecurityGroup{ - {GroupName: aws.String("test-sgName-1"), GroupId: aws.String("test-sg-1"), Tags: []*ec2.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-1")}}}, - {GroupName: aws.String("test-sgName-2"), GroupId: aws.String("test-sg-2"), Tags: []*ec2.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-2")}}}, + awsEnv.EC2API.DescribeSecurityGroupsOutput.Set(&ec2.DescribeSecurityGroupsOutput{SecurityGroups: []ec2types.SecurityGroup{ + {GroupName: aws.String("test-sgName-1"), GroupId: aws.String("test-sg-1"), Tags: []ec2types.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-1")}}}, + {GroupName: aws.String("test-sgName-2"), GroupId: aws.String("test-sg-2"), Tags: []ec2types.Tag{{Key: aws.String("kubernetes.io/cluster/test-cluster"), Value: aws.String("test-sg-2")}}}, }}) securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("test-sg-1"), GroupName: aws.String("test-sgName-1"), @@ -144,7 +145,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), @@ -163,7 +164,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), @@ -181,7 +182,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), @@ -205,7 +206,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test1"), GroupName: aws.String("securityGroup-test1"), @@ -225,7 +226,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test2"), GroupName: aws.String("securityGroup-test2"), @@ -243,7 +244,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test2"), GroupName: aws.String("securityGroup-test2"), @@ -263,7 +264,7 @@ var _ = Describe("SecurityGroupProvider", func() { } securityGroups, err := awsEnv.SecurityGroupProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSecurityGroups([]*ec2.SecurityGroup{ + ExpectConsistsOfSecurityGroups([]ec2types.SecurityGroup{ { GroupId: aws.String("sg-test3"), GroupName: aws.String("securityGroup-test3"), @@ -285,9 +286,9 @@ var _ = Describe("SecurityGroupProvider", func() { } for _, cachedObject := range awsEnv.SecurityGroupCache.Items() { - cachedSecurityGroup := cachedObject.Object.([]*ec2.SecurityGroup) + cachedSecurityGroup := cachedObject.Object.([]ec2types.SecurityGroup) Expect(cachedSecurityGroup).To(HaveLen(1)) - lo.Contains(expectedSecurityGroups, cachedSecurityGroup[0]) + lo.Contains(lo.ToSlicePtr(expectedSecurityGroups), lo.ToPtr(cachedSecurityGroup[0])) } }) It("should resolve security groups from cache that are filtered by Name", func() { @@ -304,15 +305,15 @@ var _ = Describe("SecurityGroupProvider", func() { } for _, cachedObject := range awsEnv.SecurityGroupCache.Items() { - cachedSecurityGroup := cachedObject.Object.([]*ec2.SecurityGroup) + cachedSecurityGroup := cachedObject.Object.([]ec2types.SecurityGroup) Expect(cachedSecurityGroup).To(HaveLen(1)) - lo.Contains(expectedSecurityGroups, cachedSecurityGroup[0]) + lo.Contains(lo.ToSlicePtr(expectedSecurityGroups), lo.ToPtr(cachedSecurityGroup[0])) } }) It("should resolve security groups from cache that are filtered by tags", func() { expectedSecurityGroups := awsEnv.EC2API.DescribeSecurityGroupsOutput.Clone().SecurityGroups - tagSet := lo.Map(expectedSecurityGroups, func(sg *ec2.SecurityGroup, _ int) map[string]string { - tag, _ := lo.Find(sg.Tags, func(tag *ec2.Tag) bool { + tagSet := lo.Map(expectedSecurityGroups, func(sg ec2types.SecurityGroup, _ int) map[string]string { + tag, _ := lo.Find(sg.Tags, func(tag ec2types.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }) return map[string]string{"Name": lo.FromPtr(tag.Value)} @@ -329,9 +330,9 @@ var _ = Describe("SecurityGroupProvider", func() { } for _, cachedObject := range awsEnv.SubnetCache.Items() { - cachedSecurityGroup := cachedObject.Object.([]*ec2.SecurityGroup) + cachedSecurityGroup := cachedObject.Object.([]ec2types.SecurityGroup) Expect(cachedSecurityGroup).To(HaveLen(1)) - lo.Contains(expectedSecurityGroups, cachedSecurityGroup[0]) + lo.Contains(lo.ToSlicePtr(expectedSecurityGroups), lo.ToPtr(cachedSecurityGroup[0])) } }) }) @@ -350,11 +351,11 @@ var _ = Describe("SecurityGroupProvider", func() { sort.Slice(securityGroups, func(i, j int) bool { return *securityGroups[i].GroupId < *securityGroups[j].GroupId }) - Expect(securityGroups).To(BeEquivalentTo([]*ec2.SecurityGroup{ + Expect(securityGroups).To(BeEquivalentTo([]ec2types.SecurityGroup{ { GroupId: lo.ToPtr("sg-test1"), GroupName: lo.ToPtr("securityGroup-test1"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-security-group-1"), @@ -368,7 +369,7 @@ var _ = Describe("SecurityGroupProvider", func() { { GroupId: lo.ToPtr("sg-test2"), GroupName: lo.ToPtr("securityGroup-test2"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-security-group-2"), @@ -382,7 +383,7 @@ var _ = Describe("SecurityGroupProvider", func() { { GroupId: lo.ToPtr("sg-test3"), GroupName: lo.ToPtr("securityGroup-test3"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-security-group-3"), @@ -403,11 +404,11 @@ var _ = Describe("SecurityGroupProvider", func() { }) }) -func ExpectConsistsOfSecurityGroups(expected, actual []*ec2.SecurityGroup) { +func ExpectConsistsOfSecurityGroups(expected, actual []ec2types.SecurityGroup) { GinkgoHelper() Expect(actual).To(HaveLen(len(expected))) for _, elem := range expected { - _, ok := lo.Find(actual, func(s *ec2.SecurityGroup) bool { + _, ok := lo.Find(actual, func(s ec2types.SecurityGroup) bool { return lo.FromPtr(s.GroupId) == lo.FromPtr(elem.GroupId) && lo.FromPtr(s.GroupName) == lo.FromPtr(elem.GroupName) }) diff --git a/pkg/providers/subnet/subnet.go b/pkg/providers/subnet/subnet.go index 58fc1776804c..959b8bc4ddc8 100644 --- a/pkg/providers/subnet/subnet.go +++ b/pkg/providers/subnet/subnet.go @@ -20,9 +20,10 @@ import ( "net/http" "sync" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/mitchellh/hashstructure/v2" "github.com/patrickmn/go-cache" "github.com/samber/lo" @@ -35,33 +36,35 @@ import ( "sigs.k8s.io/karpenter/pkg/cloudprovider" "sigs.k8s.io/karpenter/pkg/scheduling" "sigs.k8s.io/karpenter/pkg/utils/pretty" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) type Provider interface { LivenessProbe(*http.Request) error - List(context.Context, *v1.EC2NodeClass) ([]*ec2.Subnet, error) + List(context.Context, *v1.EC2NodeClass) ([]ec2types.Subnet, error) ZonalSubnetsForLaunch(context.Context, *v1.EC2NodeClass, []*cloudprovider.InstanceType, string) (map[string]*Subnet, error) UpdateInflightIPs(*ec2.CreateFleetInput, *ec2.CreateFleetOutput, []*cloudprovider.InstanceType, []*Subnet, string) } type DefaultProvider struct { sync.Mutex - ec2api ec2iface.EC2API + ec2api sdk.EC2API cache *cache.Cache availableIPAddressCache *cache.Cache associatePublicIPAddressCache *cache.Cache cm *pretty.ChangeMonitor - inflightIPs map[string]int64 + inflightIPs map[string]int32 } type Subnet struct { ID string Zone string ZoneID string - AvailableIPAddressCount int64 + AvailableIPAddressCount int32 } -func NewDefaultProvider(ec2api ec2iface.EC2API, cache *cache.Cache, availableIPAddressCache *cache.Cache, associatePublicIPAddressCache *cache.Cache) *DefaultProvider { +func NewDefaultProvider(ec2api sdk.EC2API, cache *cache.Cache, availableIPAddressCache *cache.Cache, associatePublicIPAddressCache *cache.Cache) *DefaultProvider { return &DefaultProvider{ ec2api: ec2api, cm: pretty.NewChangeMonitor(), @@ -71,16 +74,16 @@ func NewDefaultProvider(ec2api ec2iface.EC2API, cache *cache.Cache, availableIPA availableIPAddressCache: availableIPAddressCache, associatePublicIPAddressCache: associatePublicIPAddressCache, // inflightIPs is used to track IPs from known launched instances - inflightIPs: map[string]int64{}, + inflightIPs: map[string]int32{}, } } -func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]*ec2.Subnet, error) { +func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) ([]ec2types.Subnet, error) { p.Lock() defer p.Unlock() filterSets := getFilterSets(nodeClass.Spec.SubnetSelectorTerms) if len(filterSets) == 0 { - return []*ec2.Subnet{}, nil + return []ec2types.Subnet{}, nil } hash, err := hashstructure.Hash(filterSets, hashstructure.FormatV2, &hashstructure.HashOptions{SlicesAsSets: true}) if err != nil { @@ -89,13 +92,12 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) if subnets, ok := p.cache.Get(fmt.Sprint(hash)); ok { // Ensure what's returned from this function is a shallow-copy of the slice (not a deep-copy of the data itself) // so that modifications to the ordering of the data don't affect the original - return append([]*ec2.Subnet{}, subnets.([]*ec2.Subnet)...), nil + return append([]ec2types.Subnet{}, subnets.([]ec2types.Subnet)...), nil } - // Ensure that all the subnets that are returned here are unique - subnets := map[string]*ec2.Subnet{} + subnets := map[string]ec2types.Subnet{} for _, filters := range filterSets { - output, err := p.ec2api.DescribeSubnetsWithContext(ctx, &ec2.DescribeSubnetsInput{Filters: filters}) + output, err := p.ec2api.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{Filters: filters}) if err != nil { return nil, fmt.Errorf("describing subnets %s, %w", pretty.Concise(filters), err) } @@ -111,7 +113,7 @@ func (p *DefaultProvider) List(ctx context.Context, nodeClass *v1.EC2NodeClass) p.cache.SetDefault(fmt.Sprint(hash), lo.Values(subnets)) if p.cm.HasChanged(fmt.Sprintf("subnets/%s", nodeClass.Name), lo.Keys(subnets)) { log.FromContext(ctx). - WithValues("subnets", lo.Map(lo.Values(subnets), func(s *ec2.Subnet, _ int) v1.Subnet { + WithValues("subnets", lo.Map(lo.Values(subnets), func(s ec2types.Subnet, _ int) v1.Subnet { return v1.Subnet{ ID: lo.FromPtr(s.SubnetId), Zone: lo.FromPtr(s.AvailabilityZone), @@ -132,10 +134,10 @@ func (p *DefaultProvider) ZonalSubnetsForLaunch(ctx context.Context, nodeClass * defer p.Unlock() zonalSubnets := map[string]*Subnet{} - availableIPAddressCount := map[string]int64{} + availableIPAddressCount := map[string]int32{} for _, subnet := range nodeClass.Status.Subnets { if subnetAvailableIP, ok := p.availableIPAddressCache.Get(subnet.ID); ok { - availableIPAddressCount[subnet.ID] = subnetAvailableIP.(int64) + availableIPAddressCount[subnet.ID] = subnetAvailableIP.(int32) } } @@ -178,11 +180,8 @@ func (p *DefaultProvider) UpdateInflightIPs(createFleetInput *ec2.CreateFleetInp defer p.Unlock() // Process the CreateFleetInput to pull out all the requested subnetIDs - fleetInputSubnets := lo.Compact(lo.Uniq(lo.FlatMap(createFleetInput.LaunchTemplateConfigs, func(req *ec2.FleetLaunchTemplateConfigRequest, _ int) []string { - return lo.Map(req.Overrides, func(override *ec2.FleetLaunchTemplateOverridesRequest, _ int) string { - if override == nil { - return "" - } + fleetInputSubnets := lo.Compact(lo.Uniq(lo.FlatMap(createFleetInput.LaunchTemplateConfigs, func(req ec2types.FleetLaunchTemplateConfigRequest, _ int) []string { + return lo.Map(req.Overrides, func(override ec2types.FleetLaunchTemplateOverridesRequest, _ int) string { return lo.FromPtr(override.SubnetId) }) }))) @@ -190,8 +189,8 @@ func (p *DefaultProvider) UpdateInflightIPs(createFleetInput *ec2.CreateFleetInp // Process the CreateFleetOutput to pull out all the fulfilled subnetIDs var fleetOutputSubnets []string if createFleetOutput != nil { - fleetOutputSubnets = lo.Compact(lo.Uniq(lo.Map(createFleetOutput.Instances, func(fleetInstance *ec2.CreateFleetInstance, _ int) string { - if fleetInstance == nil || fleetInstance.LaunchTemplateAndOverrides == nil || fleetInstance.LaunchTemplateAndOverrides.Overrides == nil { + fleetOutputSubnets = lo.Compact(lo.Uniq(lo.Map(createFleetOutput.Instances, func(fleetInstance ec2types.CreateFleetInstance, _ int) string { + if fleetInstance.LaunchTemplateAndOverrides == nil || fleetInstance.LaunchTemplateAndOverrides.Overrides == nil { return "" } return lo.FromPtr(fleetInstance.LaunchTemplateAndOverrides.Overrides.SubnetId) @@ -202,8 +201,8 @@ func (p *DefaultProvider) UpdateInflightIPs(createFleetInput *ec2.CreateFleetInp subnetIDsToAddBackIPs, _ := lo.Difference(fleetInputSubnets, fleetOutputSubnets) // Aggregate all the cached subnets ip address count - cachedAvailableIPAddressMap := lo.MapEntries(p.availableIPAddressCache.Items(), func(k string, v cache.Item) (string, int64) { - return k, v.Object.(int64) + cachedAvailableIPAddressMap := lo.MapEntries(p.availableIPAddressCache.Items(), func(k string, v cache.Item) (string, int32) { + return k, v.Object.(int32) }) // Update the inflight IP tracking of subnets stored in the cache that have not be synchronized since the initial @@ -240,7 +239,7 @@ func (p *DefaultProvider) LivenessProbe(_ *http.Request) error { return nil } -func (p *DefaultProvider) minPods(instanceTypes []*cloudprovider.InstanceType, reqs scheduling.Requirements) int64 { +func (p *DefaultProvider) minPods(instanceTypes []*cloudprovider.InstanceType, reqs scheduling.Requirements) int32 { // filter for instance types available in the zone and capacity type being requested filteredInstanceTypes := lo.Filter(instanceTypes, func(it *cloudprovider.InstanceType, _ int) bool { return it.Offerings.Available().HasCompatible(reqs) @@ -252,27 +251,28 @@ func (p *DefaultProvider) minPods(instanceTypes []*cloudprovider.InstanceType, r pods, _ := lo.MinBy(filteredInstanceTypes, func(i *cloudprovider.InstanceType, j *cloudprovider.InstanceType) bool { return i.Capacity.Pods().Cmp(*j.Capacity.Pods()) < 0 }).Capacity.Pods().AsInt64() - return pods + //nolint:gosec + return int32(pods) } -func getFilterSets(terms []v1.SubnetSelectorTerm) (res [][]*ec2.Filter) { - idFilter := &ec2.Filter{Name: aws.String("subnet-id")} +func getFilterSets(terms []v1.SubnetSelectorTerm) (res [][]ec2types.Filter) { + idFilter := ec2types.Filter{Name: aws.String("subnet-id")} for _, term := range terms { switch { case term.ID != "": - idFilter.Values = append(idFilter.Values, aws.String(term.ID)) + idFilter.Values = append(idFilter.Values, term.ID) default: - var filters []*ec2.Filter + var filters []ec2types.Filter for k, v := range term.Tags { if v == "*" { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String("tag-key"), - Values: []*string{aws.String(k)}, + Values: []string{k}, }) } else { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", k)), - Values: []*string{aws.String(v)}, + Values: []string{v}, }) } } @@ -280,7 +280,7 @@ func getFilterSets(terms []v1.SubnetSelectorTerm) (res [][]*ec2.Filter) { } } if len(idFilter.Values) > 0 { - res = append(res, []*ec2.Filter{idFilter}) + res = append(res, []ec2types.Filter{idFilter}) } return res } diff --git a/pkg/providers/subnet/suite_test.go b/pkg/providers/subnet/suite_test.go index f6dedfa1d2e7..7c16a485a55d 100644 --- a/pkg/providers/subnet/suite_test.go +++ b/pkg/providers/subnet/suite_test.go @@ -22,7 +22,7 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" "github.com/aws/karpenter-provider-aws/pkg/apis" @@ -105,12 +105,12 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test1"), AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -125,18 +125,18 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test1"), AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, { SubnetId: lo.ToPtr("subnet-test2"), AvailabilityZone: lo.ToPtr("test-zone-1b"), AvailabilityZoneId: lo.ToPtr("tstz1-1b"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -153,18 +153,18 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test1"), AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, { SubnetId: lo.ToPtr("subnet-test2"), AvailabilityZone: lo.ToPtr("test-zone-1b"), AvailabilityZoneId: lo.ToPtr("tstz1-1b"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -176,12 +176,12 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test1"), AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -196,18 +196,18 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test1"), AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, { SubnetId: lo.ToPtr("subnet-test2"), AvailabilityZone: lo.ToPtr("test-zone-1b"), AvailabilityZoneId: lo.ToPtr("tstz1-1b"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -220,12 +220,12 @@ var _ = Describe("SubnetProvider", func() { } subnets, err := awsEnv.SubnetProvider.List(ctx, nodeClass) Expect(err).To(BeNil()) - ExpectConsistsOfSubnets([]*ec2.Subnet{ + ExpectConsistsOfSubnets([]ec2types.Subnet{ { SubnetId: lo.ToPtr("subnet-test2"), AvailabilityZone: lo.ToPtr("test-zone-1b"), AvailabilityZoneId: lo.ToPtr("tstz1-1b"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), }, }, subnets) }) @@ -245,15 +245,15 @@ var _ = Describe("SubnetProvider", func() { } for _, cachedObject := range awsEnv.SubnetCache.Items() { - cachedSubnet := cachedObject.Object.([]*ec2.Subnet) + cachedSubnet := cachedObject.Object.([]ec2types.Subnet) Expect(cachedSubnet).To(HaveLen(1)) - lo.Contains(expectedSubnets, cachedSubnet[0]) + lo.Contains(lo.ToSlicePtr(expectedSubnets), lo.ToPtr(cachedSubnet[0])) } }) It("should resolve subnets from cache that are filtered by tags", func() { expectedSubnets := awsEnv.EC2API.DescribeSubnetsOutput.Clone().Subnets - tagSet := lo.Map(expectedSubnets, func(subnet *ec2.Subnet, _ int) map[string]string { - tag, _ := lo.Find(subnet.Tags, func(tag *ec2.Tag) bool { + tagSet := lo.Map(expectedSubnets, func(subnet ec2types.Subnet, _ int) map[string]string { + tag, _ := lo.Find(subnet.Tags, func(tag ec2types.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }) return map[string]string{"Name": lo.FromPtr(tag.Value)} @@ -270,9 +270,9 @@ var _ = Describe("SubnetProvider", func() { } for _, cachedObject := range awsEnv.SubnetCache.Items() { - cachedSubnet := cachedObject.Object.([]*ec2.Subnet) + cachedSubnet := cachedObject.Object.([]ec2types.Subnet) Expect(cachedSubnet).To(HaveLen(1)) - lo.Contains(expectedSubnets, cachedSubnet[0]) + lo.Contains(lo.ToSlicePtr(expectedSubnets), lo.ToPtr(cachedSubnet[0])) } }) }) @@ -294,14 +294,14 @@ var _ = Describe("SubnetProvider", func() { } return *subnets[i].SubnetId < *subnets[j].SubnetId }) - Expect(subnets).To(BeEquivalentTo([]*ec2.Subnet{ + Expect(subnets).To(BeEquivalentTo([]ec2types.Subnet{ { AvailabilityZone: lo.ToPtr("test-zone-1a"), AvailabilityZoneId: lo.ToPtr("tstz1-1a"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), SubnetId: lo.ToPtr("subnet-test1"), MapPublicIpOnLaunch: lo.ToPtr(false), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-subnet-1"), @@ -315,11 +315,11 @@ var _ = Describe("SubnetProvider", func() { { AvailabilityZone: lo.ToPtr("test-zone-1b"), AvailabilityZoneId: lo.ToPtr("tstz1-1b"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), MapPublicIpOnLaunch: lo.ToPtr(true), SubnetId: lo.ToPtr("subnet-test2"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-subnet-2"), @@ -333,9 +333,9 @@ var _ = Describe("SubnetProvider", func() { { AvailabilityZone: lo.ToPtr("test-zone-1c"), AvailabilityZoneId: lo.ToPtr("tstz1-1c"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), SubnetId: lo.ToPtr("subnet-test3"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-subnet-3"), @@ -352,10 +352,10 @@ var _ = Describe("SubnetProvider", func() { { AvailabilityZone: lo.ToPtr("test-zone-1a-local"), AvailabilityZoneId: lo.ToPtr("tstz1-1alocal"), - AvailableIpAddressCount: lo.ToPtr[int64](100), + AvailableIpAddressCount: lo.ToPtr[int32](100), SubnetId: lo.ToPtr("subnet-test4"), MapPublicIpOnLaunch: lo.ToPtr(true), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: lo.ToPtr("Name"), Value: lo.ToPtr("test-subnet-4"), @@ -369,11 +369,11 @@ var _ = Describe("SubnetProvider", func() { }) }) -func ExpectConsistsOfSubnets(expected, actual []*ec2.Subnet) { +func ExpectConsistsOfSubnets(expected, actual []ec2types.Subnet) { GinkgoHelper() Expect(actual).To(HaveLen(len(expected))) for _, elem := range expected { - _, ok := lo.Find(actual, func(s *ec2.Subnet) bool { + _, ok := lo.Find(actual, func(s ec2types.Subnet) bool { return lo.FromPtr(s.SubnetId) == lo.FromPtr(elem.SubnetId) && lo.FromPtr(s.AvailabilityZoneId) == lo.FromPtr(elem.AvailabilityZoneId) && lo.FromPtr(s.AvailabilityZone) == lo.FromPtr(elem.AvailabilityZone) && diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 1b86c37b0bf9..d558b0790223 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -21,8 +21,9 @@ import ( "strconv" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/samber/lo" ) @@ -47,9 +48,9 @@ func ParseInstanceID(providerID string) (string, error) { // MergeTags takes a variadic list of maps and merges them together into a list of // EC2 tags to be passed into EC2 API calls -func MergeTags(tags ...map[string]string) []*ec2.Tag { - return lo.MapToSlice(lo.Assign(tags...), func(k, v string) *ec2.Tag { - return &ec2.Tag{Key: aws.String(k), Value: aws.String(v)} +func MergeTags(tags ...map[string]string) []ec2types.Tag { + return lo.MapToSlice(lo.Assign(tags...), func(k, v string) ec2types.Tag { + return ec2types.Tag{Key: aws.String(k), Value: aws.String(v)} }) } diff --git a/test/hack/soak/get_clusters.go b/test/hack/soak/get_clusters.go index 0c954986bbca..c845237cdfee 100644 --- a/test/hack/soak/get_clusters.go +++ b/test/hack/soak/get_clusters.go @@ -22,9 +22,10 @@ import ( "strings" "time" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/eks" - "github.com/aws/aws-sdk-go/aws" + "github.com/samber/lo" ) diff --git a/test/hack/soak/go.mod b/test/hack/soak/go.mod index cfe28ff8f3d1..61520713af24 100644 --- a/test/hack/soak/go.mod +++ b/test/hack/soak/go.mod @@ -3,14 +3,13 @@ module github.com/aws/karpenter/test/hack/soak go 1.22 require ( - github.com/aws/aws-sdk-go v1.47.9 + github.com/aws/aws-sdk-go-v2 v1.22.2 github.com/aws/aws-sdk-go-v2/config v1.23.0 github.com/aws/aws-sdk-go-v2/service/eks v1.32.0 github.com/samber/lo v1.38.1 ) require ( - github.com/aws/aws-sdk-go-v2 v1.22.2 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.15.2 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 // indirect diff --git a/test/hack/soak/go.sum b/test/hack/soak/go.sum index b2102b04b21d..720bdc089e11 100644 --- a/test/hack/soak/go.sum +++ b/test/hack/soak/go.sum @@ -1,5 +1,3 @@ -github.com/aws/aws-sdk-go v1.47.9 h1:rarTsos0mA16q+huicGx0e560aYRtOucV5z2Mw23JRY= -github.com/aws/aws-sdk-go v1.47.9/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.22.2 h1:lV0U8fnhAnPz8YcdmZVV60+tr6CakHzqA6P8T46ExJI= github.com/aws/aws-sdk-go-v2 v1.22.2/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c= github.com/aws/aws-sdk-go-v2/config v1.23.0 h1:kqzEfGGDIrRJpfJckgwuZfFTbU9NB1jZnRcaO9MpOqE= diff --git a/test/pkg/environment/aws/environment.go b/test/pkg/environment/aws/environment.go index 0a5a6fefeb5c..bf2957850ad1 100644 --- a/test/pkg/environment/aws/environment.go +++ b/test/pkg/environment/aws/environment.go @@ -22,21 +22,18 @@ import ( coretest "sigs.k8s.io/karpenter/pkg/test" - awsv2 "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws" + config "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/fis" "github.com/aws/aws-sdk-go-v2/service/iam" servicesqs "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/eks" - "github.com/aws/aws-sdk-go/service/timestreamwrite" - "github.com/aws/aws-sdk-go/service/timestreamwrite/timestreamwriteiface" + "github.com/aws/aws-sdk-go-v2/service/timestreamwrite" + . "github.com/onsi/ginkgo/v2" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -44,9 +41,8 @@ import ( karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" - config "github.com/aws/aws-sdk-go-v2/config" - v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/aws/karpenter-provider-aws/pkg/providers/sqs" "github.com/aws/karpenter-provider-aws/pkg/test" "github.com/aws/karpenter-provider-aws/test/pkg/environment/common" @@ -65,12 +61,12 @@ type Environment struct { Region string STSAPI *sts.Client - EC2API *ec2.EC2 + EC2API *ec2.Client SSMAPI *ssm.Client IAMAPI *iam.Client FISAPI *fis.Client - EKSAPI *eks.EKS - TimeStreamAPI timestreamwriteiface.TimestreamWriteAPI + EKSAPI *eks.Client + TimeStreamAPI sdk.TimestreamWriteAPI SQSProvider sqs.Provider @@ -89,28 +85,19 @@ type ZoneInfo struct { func NewEnvironment(t *testing.T) *Environment { env := common.NewEnvironment(t) - cfg := lo.Must(config.LoadDefaultConfig(context.Background(), config.WithRetryMaxAttempts(3))) - session := session.Must(session.NewSessionWithOptions( - session.Options{ - Config: *request.WithRetryer( - &aws.Config{STSRegionalEndpoint: endpoints.RegionalSTSEndpoint}, - client.DefaultRetryer{NumMaxRetries: 10}, - ), - SharedConfigState: session.SharedConfigEnable, - }, - )) + cfg := lo.Must(config.LoadDefaultConfig(env.Context)) awsEnv := &Environment{ - Region: *session.Config.Region, + Region: cfg.Region, Environment: env, STSAPI: sts.NewFromConfig(cfg), - EC2API: ec2.New(session), + EC2API: ec2.NewFromConfig(cfg), SSMAPI: ssm.NewFromConfig(cfg), IAMAPI: iam.NewFromConfig(cfg), FISAPI: fis.NewFromConfig(cfg), - EKSAPI: eks.New(session), - TimeStreamAPI: GetTimeStreamAPI(session), + EKSAPI: eks.NewFromConfig(cfg), + TimeStreamAPI: GetTimeStreamAPI(env.Context, cfg), ClusterName: lo.Must(os.LookupEnv("CLUSTER_NAME")), ClusterEndpoint: lo.Must(os.LookupEnv("CLUSTER_ENDPOINT")), @@ -124,11 +111,11 @@ func NewEnvironment(t *testing.T) *Environment { // Initialize the provider only if the INTERRUPTION_QUEUE environment variable is defined if v, ok := os.LookupEnv("INTERRUPTION_QUEUE"); ok { sqsapi := servicesqs.NewFromConfig(cfg) - out := lo.Must(sqsapi.GetQueueUrl(env.Context, &servicesqs.GetQueueUrlInput{QueueName: awsv2.String(v)})) + out := lo.Must(sqsapi.GetQueueUrl(env.Context, &servicesqs.GetQueueUrlInput{QueueName: aws.String(v)})) awsEnv.SQSProvider = lo.Must(sqs.NewDefaultProvider(sqsapi, lo.FromPtr(out.QueueUrl))) } // Populate ZoneInfo for all AZs in the region - awsEnv.ZoneInfo = lo.Map(lo.Must(awsEnv.EC2API.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{})).AvailabilityZones, func(zone *ec2.AvailabilityZone, _ int) ZoneInfo { + awsEnv.ZoneInfo = lo.Map(lo.Must(awsEnv.EC2API.DescribeAvailabilityZones(env.Context, &ec2.DescribeAvailabilityZonesInput{})).AvailabilityZones, func(zone ec2types.AvailabilityZone, _ int) ZoneInfo { return ZoneInfo{ Zone: lo.FromPtr(zone.ZoneName), ZoneID: lo.FromPtr(zone.ZoneId), @@ -138,10 +125,12 @@ func NewEnvironment(t *testing.T) *Environment { return awsEnv } -func GetTimeStreamAPI(session *session.Session) timestreamwriteiface.TimestreamWriteAPI { +func GetTimeStreamAPI(ctx context.Context, cfg aws.Config) sdk.TimestreamWriteAPI { if lo.Must(env.GetBool("ENABLE_METRICS", false)) { By("enabling metrics firing for this suite") - return timestreamwrite.New(session, &aws.Config{Region: awsv2.String(env.GetString("METRICS_REGION", metricsDefaultRegion))}) + timeCfg := cfg.Copy() + timeCfg.Region = env.GetString("METRICS_REGION", metricsDefaultRegion) + return timestreamwrite.NewFromConfig(timeCfg) } return &NoOpTimeStreamAPI{} } diff --git a/test/pkg/environment/aws/expectations.go b/test/pkg/environment/aws/expectations.go index 88be1d079bca..00da2aab74c6 100644 --- a/test/pkg/environment/aws/expectations.go +++ b/test/pkg/environment/aws/expectations.go @@ -23,13 +23,14 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/fis" fistypes "github.com/aws/aws-sdk-go-v2/service/fis/types" "github.com/aws/aws-sdk-go-v2/service/iam" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/mitchellh/hashstructure/v2" "github.com/samber/lo" "go.uber.org/multierr" @@ -143,7 +144,7 @@ func (env *Environment) GetInstanceProfileName(nodeClass *v1.EC2NodeClass) strin return fmt.Sprintf("%s_%d", env.ClusterName, lo.Must(hashstructure.Hash(fmt.Sprintf("%s%s", env.Region, nodeClass.Name), hashstructure.FormatV2, nil))) } -func (env *Environment) GetInstance(nodeName string) ec2.Instance { +func (env *Environment) GetInstance(nodeName string) ec2types.Instance { node := env.Environment.GetNode(nodeName) return env.GetInstanceByID(env.ExpectParsedProviderID(node.Spec.ProviderID)) } @@ -151,9 +152,9 @@ func (env *Environment) GetInstance(nodeName string) ec2.Instance { func (env *Environment) ExpectInstanceStopped(nodeName string) { GinkgoHelper() node := env.Environment.GetNode(nodeName) - _, err := env.EC2API.StopInstances(&ec2.StopInstancesInput{ + _, err := env.EC2API.StopInstances(env.Context, &ec2.StopInstancesInput{ Force: aws.Bool(true), - InstanceIds: aws.StringSlice([]string{env.ExpectParsedProviderID(node.Spec.ProviderID)}), + InstanceIds: []string{env.ExpectParsedProviderID(node.Spec.ProviderID)}, }) Expect(err).To(Succeed()) } @@ -161,52 +162,55 @@ func (env *Environment) ExpectInstanceStopped(nodeName string) { func (env *Environment) ExpectInstanceTerminated(nodeName string) { GinkgoHelper() node := env.Environment.GetNode(nodeName) - _, err := env.EC2API.TerminateInstances(&ec2.TerminateInstancesInput{ - InstanceIds: aws.StringSlice([]string{env.ExpectParsedProviderID(node.Spec.ProviderID)}), + _, err := env.EC2API.TerminateInstances(env.Context, &ec2.TerminateInstancesInput{ + InstanceIds: []string{env.ExpectParsedProviderID(node.Spec.ProviderID)}, }) Expect(err).To(Succeed()) } -func (env *Environment) GetInstanceByID(instanceID string) ec2.Instance { +func (env *Environment) GetInstanceByID(instanceID string) ec2types.Instance { GinkgoHelper() - instance, err := env.EC2API.DescribeInstances(&ec2.DescribeInstancesInput{ - InstanceIds: aws.StringSlice([]string{instanceID}), + instance, err := env.EC2API.DescribeInstances(env.Context, &ec2.DescribeInstancesInput{ + InstanceIds: []string{instanceID}, }) Expect(err).ToNot(HaveOccurred()) Expect(instance.Reservations).To(HaveLen(1)) Expect(instance.Reservations[0].Instances).To(HaveLen(1)) - return *instance.Reservations[0].Instances[0] + return instance.Reservations[0].Instances[0] } -func (env *Environment) GetVolume(id *string) *ec2.Volume { +func (env *Environment) GetVolume(id string) ec2types.Volume { volumes := env.GetVolumes(id) Expect(volumes).To(HaveLen(1)) return volumes[0] } -func (env *Environment) GetVolumes(ids ...*string) []*ec2.Volume { +func (env *Environment) GetVolumes(ids ...string) []ec2types.Volume { GinkgoHelper() - dvo, err := env.EC2API.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: ids}) + dvo, err := env.EC2API.DescribeVolumes(env.Context, &ec2.DescribeVolumesInput{VolumeIds: ids}) Expect(err).ToNot(HaveOccurred()) + return dvo.Volumes } -func (env *Environment) GetNetworkInterface(id *string) *ec2.NetworkInterface { +func (env *Environment) GetNetworkInterface(id string) ec2types.NetworkInterface { networkInterfaces := env.GetNetworkInterfaces(id) Expect(networkInterfaces).To(HaveLen(1)) return networkInterfaces[0] } -func (env *Environment) GetNetworkInterfaces(ids ...*string) []*ec2.NetworkInterface { +func (env *Environment) GetNetworkInterfaces(ids ...string) []ec2types.NetworkInterface { GinkgoHelper() - dnio, err := env.EC2API.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{NetworkInterfaceIds: ids}) + dnio, err := env.EC2API.DescribeNetworkInterfaces(env.Context, &ec2.DescribeNetworkInterfacesInput{NetworkInterfaceIds: ids}) Expect(err).ToNot(HaveOccurred()) return dnio.NetworkInterfaces } -func (env *Environment) GetSpotInstanceRequest(id *string) *ec2.SpotInstanceRequest { +func (env *Environment) GetSpotInstance(id string) ec2types.SpotInstanceRequest { GinkgoHelper() - siro, err := env.EC2API.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{SpotInstanceRequestIds: []*string{id}}) + siro, err := env.EC2API.DescribeSpotInstanceRequests(env.Context, &ec2.DescribeSpotInstanceRequestsInput{ + SpotInstanceRequestIds: []string{id}, + }) Expect(err).ToNot(HaveOccurred()) Expect(siro.SpotInstanceRequests).To(HaveLen(1)) return siro.SpotInstanceRequests[0] @@ -215,21 +219,30 @@ func (env *Environment) GetSpotInstanceRequest(id *string) *ec2.SpotInstanceRequ // GetSubnets returns all subnets matching the label selector // mapped from AZ -> {subnet-ids...} func (env *Environment) GetSubnets(tags map[string]string) map[string][]string { - var filters []*ec2.Filter + var filters []ec2types.Filter for key, val := range tags { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", key)), - Values: []*string{aws.String(val)}, + Values: []string{val}, }) } subnets := map[string][]string{} - err := env.EC2API.DescribeSubnetsPages(&ec2.DescribeSubnetsInput{Filters: filters}, func(dso *ec2.DescribeSubnetsOutput, _ bool) bool { - for _, subnet := range dso.Subnets { + input := &ec2.DescribeSubnetsInput{ + Filters: filters, + } + + paginator := ec2.NewDescribeSubnetsPaginator(env.EC2API, input) + for paginator.HasMorePages() { + output, err := paginator.NextPage(env.Context) + if err != nil { + Expect(err).To(BeNil()) + } + + for _, subnet := range output.Subnets { subnets[*subnet.AvailabilityZone] = append(subnets[*subnet.AvailabilityZone], *subnet.SubnetId) } - return true - }) - Expect(err).To(BeNil()) + } + return subnets } @@ -242,18 +255,28 @@ type SubnetInfo struct { // GetSubnetInfo returns all subnets matching the label selector func (env *Environment) GetSubnetInfo(tags map[string]string) []SubnetInfo { - var filters []*ec2.Filter + var filters []ec2types.Filter for key, val := range tags { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", key)), - Values: []*string{aws.String(val)}, + Values: []string{val}, }) } var subnetInfo []SubnetInfo - err := env.EC2API.DescribeSubnetsPages(&ec2.DescribeSubnetsInput{Filters: filters}, func(dso *ec2.DescribeSubnetsOutput, _ bool) bool { - subnetInfo = lo.Map(dso.Subnets, func(s *ec2.Subnet, _ int) SubnetInfo { + input := &ec2.DescribeSubnetsInput{ + Filters: filters, + } + + paginator := ec2.NewDescribeSubnetsPaginator(env.EC2API, input) + for paginator.HasMorePages() { + output, err := paginator.NextPage(env.Context) + if err != nil { + Expect(err).To(BeNil()) + } + + subnetInfo = append(subnetInfo, lo.Map(output.Subnets, func(s ec2types.Subnet, _ int) SubnetInfo { elem := SubnetInfo{ID: aws.ToString(s.SubnetId)} - if tag, ok := lo.Find(s.Tags, func(t *ec2.Tag) bool { return aws.ToString(t.Key) == "Name" }); ok { + if tag, ok := lo.Find(s.Tags, func(t ec2types.Tag) bool { return aws.ToString(t.Key) == "Name" }); ok { elem.Name = aws.ToString(tag.Value) } if info, ok := lo.Find(env.ZoneInfo, func(info ZoneInfo) bool { @@ -262,38 +285,46 @@ func (env *Environment) GetSubnetInfo(tags map[string]string) []SubnetInfo { elem.ZoneInfo = info } return elem - }) - return true - }) - Expect(err).To(BeNil()) + })...) + } + return subnetInfo } type SecurityGroup struct { - ec2.GroupIdentifier - Tags []*ec2.Tag + ec2types.GroupIdentifier + Tags []ec2types.Tag } // GetSecurityGroups returns all getSecurityGroups matching the label selector func (env *Environment) GetSecurityGroups(tags map[string]string) []SecurityGroup { - var filters []*ec2.Filter + var filters []ec2types.Filter for key, val := range tags { - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String(fmt.Sprintf("tag:%s", key)), - Values: []*string{aws.String(val)}, + Values: []string{val}, }) } var securityGroups []SecurityGroup - err := env.EC2API.DescribeSecurityGroupsPages(&ec2.DescribeSecurityGroupsInput{Filters: filters}, func(dso *ec2.DescribeSecurityGroupsOutput, _ bool) bool { - for _, sg := range dso.SecurityGroups { + input := &ec2.DescribeSecurityGroupsInput{ + Filters: filters, + } + + paginator := ec2.NewDescribeSecurityGroupsPaginator(env.EC2API, input) + for paginator.HasMorePages() { + output, err := paginator.NextPage(env.Context) + if err != nil { + Expect(err).To(BeNil()) + } + + for _, sg := range output.SecurityGroups { securityGroups = append(securityGroups, SecurityGroup{ Tags: sg.Tags, - GroupIdentifier: ec2.GroupIdentifier{GroupId: sg.GroupId, GroupName: sg.GroupName}, + GroupIdentifier: ec2types.GroupIdentifier{GroupId: sg.GroupId, GroupName: sg.GroupName}, }) } - return true - }) - Expect(err).To(BeNil()) + } + return securityGroups } @@ -365,15 +396,15 @@ func (env *Environment) GetAMIBySSMPath(ssmPath string) string { } func (env *Environment) GetDeprecatedAMI(amiID string, amifamily string) string { - out, err := env.EC2API.DescribeImages(&ec2.DescribeImagesInput{ - Filters: []*ec2.Filter{ + out, err := env.EC2API.DescribeImages(env.Context, &ec2.DescribeImagesInput{ + Filters: []ec2types.Filter{ { Name: lo.ToPtr(fmt.Sprintf("tag:%s", coretest.DiscoveryLabel)), - Values: []*string{lo.ToPtr(env.K8sVersion())}, + Values: []string{env.K8sVersion()}, }, { Name: lo.ToPtr("tag:amiFamily"), - Values: []*string{lo.ToPtr(amifamily)}, + Values: []string{amifamily}, }, }, IncludeDeprecated: lo.ToPtr(true), @@ -387,8 +418,8 @@ func (env *Environment) GetDeprecatedAMI(amiID string, amifamily string) string SourceImageId: lo.ToPtr(amiID), Name: lo.ToPtr(fmt.Sprintf("deprecated-%s-%s-%s", amiID, amifamily, env.K8sVersion())), SourceRegion: lo.ToPtr(env.Region), - TagSpecifications: []*ec2.TagSpecification{ - {ResourceType: lo.ToPtr(ec2.ResourceTypeImage), Tags: []*ec2.Tag{ + TagSpecifications: []ec2types.TagSpecification{ + {ResourceType: ec2types.ResourceTypeImage, Tags: []ec2types.Tag{ { Key: lo.ToPtr(coretest.DiscoveryLabel), Value: lo.ToPtr(env.K8sVersion()), @@ -400,10 +431,10 @@ func (env *Environment) GetDeprecatedAMI(amiID string, amifamily string) string }}, }, } - output, err := env.EC2API.CopyImage(input) + output, err := env.EC2API.CopyImage(env.Context, input) Expect(err).To(BeNil()) - deprecated, err := env.EC2API.EnableImageDeprecationWithContext(env.Context, &ec2.EnableImageDeprecationInput{ + deprecated, err := env.EC2API.EnableImageDeprecation(env.Context, &ec2.EnableImageDeprecationInput{ ImageId: output.ImageId, DeprecateAt: lo.ToPtr(time.Now()), }) @@ -413,20 +444,23 @@ func (env *Environment) GetDeprecatedAMI(amiID string, amifamily string) string return lo.FromPtr(output.ImageId) } -func (env *Environment) EventuallyExpectRunInstances(instanceInput *ec2.RunInstancesInput) *ec2.Reservation { +func (env *Environment) EventuallyExpectRunInstances(instanceInput *ec2.RunInstancesInput) ec2types.Reservation { GinkgoHelper() // implement IMDSv2 - instanceInput.MetadataOptions = &ec2.InstanceMetadataOptionsRequest{ - HttpEndpoint: aws.String("enabled"), - HttpTokens: aws.String("required"), + instanceInput.MetadataOptions = &ec2types.InstanceMetadataOptionsRequest{ + HttpEndpoint: "enabled", + HttpTokens: "required", } - var out *ec2.Reservation - var err error + var reservation ec2types.Reservation Eventually(func(g Gomega) { - out, err = env.EC2API.RunInstances(instanceInput) + out, err := env.EC2API.RunInstances(env.Context, instanceInput) g.Expect(err).ToNot(HaveOccurred()) + g.Expect(out.Instances).ToNot(BeEmpty()) + reservation = ec2types.Reservation{ + Instances: out.Instances, + } }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).Should(Succeed()) - return out + return reservation } func (env *Environment) ExpectSpotInterruptionRole() *iamtypes.Role { diff --git a/test/pkg/environment/aws/metrics.go b/test/pkg/environment/aws/metrics.go index 60a30e5f1b0c..459fe1929707 100644 --- a/test/pkg/environment/aws/metrics.go +++ b/test/pkg/environment/aws/metrics.go @@ -19,12 +19,13 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/timestreamwrite" - "github.com/aws/aws-sdk-go/service/timestreamwrite/timestreamwriteiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/timestreamwrite" + timestreamwritetypes "github.com/aws/aws-sdk-go-v2/service/timestreamwrite/types" "github.com/samber/lo" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" + "github.com/aws/karpenter-provider-aws/test/pkg/environment/common" . "github.com/onsi/ginkgo/v2" @@ -37,13 +38,11 @@ const ( tableName = "scaleTestDurations" ) -var _ timestreamwriteiface.TimestreamWriteAPI = (*NoOpTimeStreamAPI)(nil) - type NoOpTimeStreamAPI struct { - timestreamwriteiface.TimestreamWriteAPI + sdk.TimestreamWriteAPI } -func (o NoOpTimeStreamAPI) WriteRecordsWithContext(_ context.Context, _ *timestreamwrite.WriteRecordsInput, _ ...request.Option) (*timestreamwrite.WriteRecordsOutput, error) { +func (o NoOpTimeStreamAPI) WriteRecords(_ context.Context, _ *timestreamwrite.WriteRecordsInput, _ ...func(*timestreamwrite.Options)) (*timestreamwrite.WriteRecordsOutput, error) { return nil, nil } @@ -98,15 +97,15 @@ func (env *Environment) MeasureDurationFor(f func(), eventType EventType, dimens func (env *Environment) ExpectMetric(name string, value float64, labels map[string]string) { GinkgoHelper() - _, err := env.TimeStreamAPI.WriteRecordsWithContext(env.Context, ×treamwrite.WriteRecordsInput{ + _, err := env.TimeStreamAPI.WriteRecords(env.Context, ×treamwrite.WriteRecordsInput{ DatabaseName: aws.String(databaseName), TableName: aws.String(tableName), - Records: []*timestreamwrite.Record{ + Records: []timestreamwritetypes.Record{ { MeasureName: aws.String(name), MeasureValue: aws.String(fmt.Sprintf("%f", value)), - Dimensions: lo.MapToSlice(labels, func(k, v string) *timestreamwrite.Dimension { - return ×treamwrite.Dimension{ + Dimensions: lo.MapToSlice(labels, func(k, v string) timestreamwritetypes.Dimension { + return timestreamwritetypes.Dimension{ Name: aws.String(k), Value: aws.String(v), } diff --git a/test/suites/ami/suite_test.go b/test/suites/ami/suite_test.go index 7b62348f7579..7830c4a935c1 100644 --- a/test/suites/ami/suite_test.go +++ b/test/suites/ami/suite_test.go @@ -22,7 +22,7 @@ import ( "testing" "time" - awssdk "github.com/aws/aws-sdk-go/aws" + awssdk "github.com/aws/aws-sdk-go-v2/aws" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" @@ -31,7 +31,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/status" . "github.com/awslabs/operatorpkg/test/expectations" "github.com/samber/lo" @@ -104,8 +105,8 @@ var _ = Describe("AMI", func() { env.ExpectInstance(pod.Spec.NodeName).To(HaveField("ImageId", HaveValue(Equal(customAMI)))) }) It("should support AMI Selector Terms for Name but fail with incorrect owners", func() { - output, err := env.EC2API.DescribeImages(&ec2.DescribeImagesInput{ - ImageIds: []*string{awssdk.String(customAMI)}, + output, err := env.EC2API.DescribeImages(env.Context, &ec2.DescribeImagesInput{ + ImageIds: []string{customAMI}, }) Expect(err).To(BeNil()) Expect(output.Images).To(HaveLen(1)) @@ -123,8 +124,8 @@ var _ = Describe("AMI", func() { Expect(pod.Spec.NodeName).To(Equal("")) }) It("should support ami selector Name with default owners", func() { - output, err := env.EC2API.DescribeImages(&ec2.DescribeImagesInput{ - ImageIds: []*string{awssdk.String(customAMI)}, + output, err := env.EC2API.DescribeImages(env.Context, &ec2.DescribeImagesInput{ + ImageIds: []string{customAMI}, }) Expect(err).To(BeNil()) Expect(output.Images).To(HaveLen(1)) @@ -374,9 +375,9 @@ func getInstanceAttribute(nodeName string, attribute string) *ec2.DescribeInstan Expect(env.Client.Get(env.Context, types.NamespacedName{Name: nodeName}, &node)).To(Succeed()) providerIDSplit := strings.Split(node.Spec.ProviderID, "/") instanceID := providerIDSplit[len(providerIDSplit)-1] - instanceAttribute, err := env.EC2API.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{ + instanceAttribute, err := env.EC2API.DescribeInstanceAttribute(env.Context, &ec2.DescribeInstanceAttributeInput{ InstanceId: awssdk.String(instanceID), - Attribute: awssdk.String(attribute), + Attribute: ec2types.InstanceAttributeName(attribute), }) Expect(err).ToNot(HaveOccurred()) return instanceAttribute diff --git a/test/suites/consolidation/suite_test.go b/test/suites/consolidation/suite_test.go index b99090dd922c..19b990397a7f 100644 --- a/test/suites/consolidation/suite_test.go +++ b/test/suites/consolidation/suite_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" appsv1 "k8s.io/api/apps/v1" diff --git a/test/suites/drift/suite_test.go b/test/suites/drift/suite_test.go index 85beabe70707..53ea48599fcd 100644 --- a/test/suites/drift/suite_test.go +++ b/test/suites/drift/suite_test.go @@ -20,9 +20,10 @@ import ( "testing" "time" - awssdk "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/eks" + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/awslabs/operatorpkg/object" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -413,7 +414,7 @@ var _ = Describe("Drift", func() { }) It("should disrupt nodes that have drifted due to securitygroup", func() { By("getting the cluster vpc id") - output, err := env.EKSAPI.DescribeCluster(&eks.DescribeClusterInput{Name: awssdk.String(env.ClusterName)}) + output, err := env.EKSAPI.DescribeCluster(env.Context, &eks.DescribeClusterInput{Name: awssdk.String(env.ClusterName)}) Expect(err).To(BeNil()) By("creating new security group") @@ -421,10 +422,10 @@ var _ = Describe("Drift", func() { GroupName: awssdk.String("security-group-drift"), Description: awssdk.String("End-to-end Drift Test, should delete after drift test is completed"), VpcId: output.Cluster.ResourcesVpcConfig.VpcId, - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: awssdk.String("security-group"), - Tags: []*ec2.Tag{ + ResourceType: "security-group", + Tags: []ec2types.Tag{ { Key: awssdk.String("karpenter.sh/discovery"), Value: awssdk.String(env.ClusterName), @@ -441,7 +442,7 @@ var _ = Describe("Drift", func() { }, }, } - _, _ = env.EC2API.CreateSecurityGroup(createSecurityGroup) + _, _ = env.EC2API.CreateSecurityGroup(env.Context, createSecurityGroup) By("looking for security groups") var securitygroups []aws.SecurityGroup @@ -449,19 +450,19 @@ var _ = Describe("Drift", func() { Eventually(func(g Gomega) { securitygroups = env.GetSecurityGroups(map[string]string{"karpenter.sh/discovery": env.ClusterName}) testSecurityGroup, _ = lo.Find(securitygroups, func(sg aws.SecurityGroup) bool { - return awssdk.StringValue(sg.GroupName) == "security-group-drift" + return awssdk.ToString(sg.GroupName) == "security-group-drift" }) g.Expect(testSecurityGroup).ToNot(BeNil()) }).Should(Succeed()) By("creating a new provider with the new securitygroup") awsIDs := lo.FilterMap(securitygroups, func(sg aws.SecurityGroup, _ int) (string, bool) { - if awssdk.StringValue(sg.GroupId) != awssdk.StringValue(testSecurityGroup.GroupId) { - return awssdk.StringValue(sg.GroupId), true + if awssdk.ToString(sg.GroupId) != awssdk.ToString(testSecurityGroup.GroupId) { + return awssdk.ToString(sg.GroupId), true } return "", false }) - sgTerms := []v1.SecurityGroupSelectorTerm{{ID: awssdk.StringValue(testSecurityGroup.GroupId)}} + sgTerms := []v1.SecurityGroupSelectorTerm{{ID: awssdk.ToString(testSecurityGroup.GroupId)}} for _, id := range awsIDs { sgTerms = append(sgTerms, v1.SecurityGroupSelectorTerm{ID: id}) } @@ -473,7 +474,7 @@ var _ = Describe("Drift", func() { node := env.ExpectCreatedNodeCount("==", 1)[0] sgTerms = lo.Reject(sgTerms, func(t v1.SecurityGroupSelectorTerm, _ int) bool { - return t.ID == awssdk.StringValue(testSecurityGroup.GroupId) + return t.ID == awssdk.ToString(testSecurityGroup.GroupId) }) nodeClass.Spec.SecurityGroupSelectorTerms = sgTerms env.ExpectCreatedOrUpdated(nodeClass) diff --git a/test/suites/integration/aws_metadata_test.go b/test/suites/integration/aws_metadata_test.go index d48600206bc7..20236f339d5b 100644 --- a/test/suites/integration/aws_metadata_test.go +++ b/test/suites/integration/aws_metadata_test.go @@ -15,8 +15,8 @@ limitations under the License. package integration_test import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" coretest "sigs.k8s.io/karpenter/pkg/test" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" @@ -38,13 +38,13 @@ var _ = Describe("MetadataOptions", func() { env.ExpectCreated(pod, nodeClass, nodePool) env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) - env.ExpectInstance(pod.Spec.NodeName).To(HaveField("MetadataOptions", HaveValue(Equal(ec2.InstanceMetadataOptionsResponse{ - State: aws.String(ec2.InstanceMetadataOptionsStateApplied), - HttpEndpoint: aws.String("enabled"), - HttpProtocolIpv6: aws.String("enabled"), - HttpPutResponseHopLimit: aws.Int64(1), - HttpTokens: aws.String("required"), - InstanceMetadataTags: aws.String("disabled"), + env.ExpectInstance(pod.Spec.NodeName).To(HaveField("MetadataOptions", HaveValue(Equal(ec2types.InstanceMetadataOptionsResponse{ + State: ec2types.InstanceMetadataOptionsStateApplied, + HttpEndpoint: ec2types.InstanceMetadataEndpointStateEnabled, + HttpProtocolIpv6: ec2types.InstanceMetadataProtocolStateEnabled, + HttpPutResponseHopLimit: aws.Int32(1), + HttpTokens: ec2types.HttpTokensStateRequired, + InstanceMetadataTags: ec2types.InstanceMetadataTagsStateDisabled, })))) }) }) diff --git a/test/suites/integration/block_device_mappings_test.go b/test/suites/integration/block_device_mappings_test.go index 0006b1fa9431..2e368db26c48 100644 --- a/test/suites/integration/block_device_mappings_test.go +++ b/test/suites/integration/block_device_mappings_test.go @@ -15,7 +15,9 @@ limitations under the License. package integration_test import ( - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/samber/lo" "sigs.k8s.io/karpenter/pkg/test" "sigs.k8s.io/karpenter/pkg/utils/resources" @@ -49,10 +51,10 @@ var _ = Describe("BlockDeviceMappings", func() { Expect(instance.BlockDeviceMappings[0]).ToNot(BeNil()) Expect(instance.BlockDeviceMappings[0]).To(HaveField("DeviceName", HaveValue(Equal("/dev/xvda")))) Expect(instance.BlockDeviceMappings[0].Ebs).To(HaveField("DeleteOnTermination", HaveValue(BeTrue()))) - volume := env.GetVolume(instance.BlockDeviceMappings[0].Ebs.VolumeId) + volume := env.GetVolume(lo.FromPtr(instance.BlockDeviceMappings[0].Ebs.VolumeId)) Expect(volume).To(HaveField("Encrypted", HaveValue(BeTrue()))) - Expect(volume).To(HaveField("Size", HaveValue(Equal(int64(20))))) - Expect(volume).To(HaveField("Iops", HaveValue(Equal(int64(1000))))) - Expect(volume).To(HaveField("VolumeType", HaveValue(Equal("io2")))) + Expect(volume).To(HaveField("Size", HaveValue(Equal(int32(20))))) + Expect(volume).To(HaveField("Iops", HaveValue(Equal(int32(1000))))) + Expect(volume).To(HaveField("VolumeType", HaveValue(Equal(ec2types.VolumeType("io2"))))) }) }) diff --git a/test/suites/integration/cni_test.go b/test/suites/integration/cni_test.go index 41d65f04dae3..44e685ae8e5e 100644 --- a/test/suites/integration/cni_test.go +++ b/test/suites/integration/cni_test.go @@ -17,8 +17,8 @@ package integration_test import ( "strconv" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/samber/lo" @@ -67,17 +67,17 @@ var _ = Describe("CNITests", func() { }) func eniLimitedPodsFor(instanceType string) int64 { - instance, err := env.EC2API.DescribeInstanceTypes(&ec2.DescribeInstanceTypesInput{ - InstanceTypes: aws.StringSlice([]string{instanceType}), + instance, err := env.EC2API.DescribeInstanceTypes(env.Context, &ec2.DescribeInstanceTypesInput{ + InstanceTypes: []ec2types.InstanceType{ec2types.InstanceType(instanceType)}, }) Expect(err).ToNot(HaveOccurred()) networkInfo := *instance.InstanceTypes[0].NetworkInfo - return *networkInfo.MaximumNetworkInterfaces*(*networkInfo.Ipv4AddressesPerInterface-1) + 2 + return int64(*networkInfo.MaximumNetworkInterfaces*(*networkInfo.Ipv4AddressesPerInterface-1) + 2) } func reservedENIsFor(instanceType string) int64 { - instance, err := env.EC2API.DescribeInstanceTypes(&ec2.DescribeInstanceTypesInput{ - InstanceTypes: aws.StringSlice([]string{instanceType}), + instance, err := env.EC2API.DescribeInstanceTypes(env.Context, &ec2.DescribeInstanceTypesInput{ + InstanceTypes: []ec2types.InstanceType{ec2types.InstanceType(instanceType)}, }) Expect(err).ToNot(HaveOccurred()) networkInfo := *instance.InstanceTypes[0].NetworkInfo @@ -87,5 +87,5 @@ func reservedENIsFor(instanceType string) int64 { reservedENIs, err = strconv.Atoi(reservedENIsVar.Value) Expect(err).ToNot(HaveOccurred()) } - return (*networkInfo.MaximumNetworkInterfaces-int64(reservedENIs))*(*networkInfo.Ipv4AddressesPerInterface-1) + 2 + return int64((int(*networkInfo.MaximumNetworkInterfaces)-reservedENIs)*(int(*networkInfo.Ipv4AddressesPerInterface-1)) + 2) } diff --git a/test/suites/integration/instance_profile_test.go b/test/suites/integration/instance_profile_test.go index 00dd48b60b42..7f5bc78358e7 100644 --- a/test/suites/integration/instance_profile_test.go +++ b/test/suites/integration/instance_profile_test.go @@ -66,7 +66,7 @@ var _ = Describe("InstanceProfile Generation", func() { _, err := env.IAMAPI.GetInstanceProfile(env.Context, &iam.GetInstanceProfileInput{ InstanceProfileName: lo.ToPtr(env.GetInstanceProfileName(nodeClass)), }) - g.Expect(awserrors.IsNotFoundV2(err)).To(BeTrue()) + g.Expect(awserrors.IsNotFound(err)).To(BeTrue()) }).Should(Succeed()) }) It("should use the unmanaged instance profile", func() { diff --git a/test/suites/integration/launch_template_test.go b/test/suites/integration/launch_template_test.go index c9fd58dbc84d..65c518fee356 100644 --- a/test/suites/integration/launch_template_test.go +++ b/test/suites/integration/launch_template_test.go @@ -17,8 +17,9 @@ package integration_test import ( "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" coretest "sigs.k8s.io/karpenter/pkg/test" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" @@ -36,9 +37,9 @@ var _ = Describe("Launch Template Deletion", func() { env.ExpectDeleted(nodePool, nodeClass) Eventually(func(g Gomega) { - output, _ := env.EC2API.DescribeLaunchTemplatesWithContext(env.Context, &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{ - {Name: aws.String(fmt.Sprintf("tag:%s", v1.LabelNodeClass)), Values: []*string{aws.String(nodeClass.Name)}}, + output, _ := env.EC2API.DescribeLaunchTemplates(env.Context, &ec2.DescribeLaunchTemplatesInput{ + Filters: []ec2types.Filter{ + {Name: aws.String(fmt.Sprintf("tag:%s", v1.LabelNodeClass)), Values: []string{nodeClass.Name}}, }, }) g.Expect(output.LaunchTemplates).To(HaveLen(0)) diff --git a/test/suites/integration/security_group_test.go b/test/suites/integration/security_group_test.go index c4e0476495b1..ed4015df1daa 100644 --- a/test/suites/integration/security_group_test.go +++ b/test/suites/integration/security_group_test.go @@ -17,7 +17,7 @@ package integration_test import ( "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/status" "github.com/samber/lo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -49,7 +49,7 @@ var _ = Describe("SecurityGroups", func() { env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) - env.ExpectInstance(pod.Spec.NodeName).To(HaveField("SecurityGroups", ConsistOf(&securityGroups[0].GroupIdentifier, &securityGroups[1].GroupIdentifier))) + env.ExpectInstance(pod.Spec.NodeName).To(HaveField("SecurityGroups", ConsistOf(securityGroups[0].GroupIdentifier, securityGroups[1].GroupIdentifier))) }) It("should use the security group selector with multiple tag values", func() { @@ -60,10 +60,10 @@ var _ = Describe("SecurityGroups", func() { nodeClass.Spec.SecurityGroupSelectorTerms = []v1.SecurityGroupSelectorTerm{ { - Tags: map[string]string{"Name": lo.FromPtr(lo.FindOrElse(first.Tags, &ec2.Tag{}, func(tag *ec2.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }).Value)}, + Tags: map[string]string{"Name": lo.FromPtr(lo.FindOrElse(first.Tags, ec2types.Tag{}, func(tag ec2types.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }).Value)}, }, { - Tags: map[string]string{"Name": lo.FromPtr(lo.FindOrElse(last.Tags, &ec2.Tag{}, func(tag *ec2.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }).Value)}, + Tags: map[string]string{"Name": lo.FromPtr(lo.FindOrElse(last.Tags, ec2types.Tag{}, func(tag ec2types.Tag) bool { return lo.FromPtr(tag.Key) == "Name" }).Value)}, }, } pod := test.Pod() @@ -72,7 +72,7 @@ var _ = Describe("SecurityGroups", func() { env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) - env.ExpectInstance(pod.Spec.NodeName).To(HaveField("SecurityGroups", ConsistOf(&first.GroupIdentifier, &last.GroupIdentifier))) + env.ExpectInstance(pod.Spec.NodeName).To(HaveField("SecurityGroups", ConsistOf(first.GroupIdentifier, last.GroupIdentifier))) }) It("should update the EC2NodeClass status security groups", func() { diff --git a/test/suites/integration/subnet_test.go b/test/suites/integration/subnet_test.go index d9a233390d9d..988186d30e79 100644 --- a/test/suites/integration/subnet_test.go +++ b/test/suites/integration/subnet_test.go @@ -17,7 +17,8 @@ package integration_test import ( "time" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/awslabs/operatorpkg/status" "github.com/onsi/gomega/types" "github.com/samber/lo" @@ -142,15 +143,15 @@ var _ = Describe("Subnets", func() { func ExpectResourceBasedNamingEnabled(subnetIDs ...string) { for subnetID := range subnetIDs { - _, err := env.EC2API.ModifySubnetAttribute(&ec2.ModifySubnetAttributeInput{ - EnableResourceNameDnsARecordOnLaunch: &ec2.AttributeBooleanValue{ + _, err := env.EC2API.ModifySubnetAttribute(env.Context, &ec2.ModifySubnetAttributeInput{ + EnableResourceNameDnsARecordOnLaunch: &ec2types.AttributeBooleanValue{ Value: lo.ToPtr(true), }, SubnetId: lo.ToPtr(subnetIDs[subnetID]), }) Expect(err).To(BeNil()) - _, err = env.EC2API.ModifySubnetAttribute(&ec2.ModifySubnetAttributeInput{ - PrivateDnsHostnameTypeOnLaunch: lo.ToPtr("resource-name"), + _, err = env.EC2API.ModifySubnetAttribute(env.Context, &ec2.ModifySubnetAttributeInput{ + PrivateDnsHostnameTypeOnLaunch: "resource-name", SubnetId: lo.ToPtr(subnetIDs[subnetID]), }) Expect(err).To(BeNil()) @@ -159,15 +160,15 @@ func ExpectResourceBasedNamingEnabled(subnetIDs ...string) { func ExpectResourceBasedNamingDisabled(subnetIDs ...string) { for subnetID := range subnetIDs { - _, err := env.EC2API.ModifySubnetAttribute(&ec2.ModifySubnetAttributeInput{ - EnableResourceNameDnsARecordOnLaunch: &ec2.AttributeBooleanValue{ + _, err := env.EC2API.ModifySubnetAttribute(env.Context, &ec2.ModifySubnetAttributeInput{ + EnableResourceNameDnsARecordOnLaunch: &ec2types.AttributeBooleanValue{ Value: lo.ToPtr(false), }, SubnetId: lo.ToPtr(subnetIDs[subnetID]), }) Expect(err).To(BeNil()) - _, err = env.EC2API.ModifySubnetAttribute(&ec2.ModifySubnetAttributeInput{ - PrivateDnsHostnameTypeOnLaunch: lo.ToPtr("ip-name"), + _, err = env.EC2API.ModifySubnetAttribute(env.Context, &ec2.ModifySubnetAttributeInput{ + PrivateDnsHostnameTypeOnLaunch: "ip-name", SubnetId: lo.ToPtr(subnetIDs[subnetID]), }) Expect(err).To(BeNil()) diff --git a/test/suites/integration/tags_test.go b/test/suites/integration/tags_test.go index c6da0551c0bd..7b29b084df8d 100644 --- a/test/suites/integration/tags_test.go +++ b/test/suites/integration/tags_test.go @@ -18,24 +18,23 @@ import ( "fmt" "time" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/iam" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/awslabs/operatorpkg/object" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" coretest "sigs.k8s.io/karpenter/pkg/test" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" "github.com/aws/karpenter-provider-aws/pkg/providers/instance" "github.com/aws/karpenter-provider-aws/pkg/test" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) const createdAtTag = "node.k8s.amazonaws.com/createdAt" @@ -45,26 +44,25 @@ var _ = Describe("Tags", func() { It("should tag all associated resources", func() { nodeClass.Spec.Tags["TestTag"] = "TestVal" pod := coretest.Pod() - env.ExpectCreated(pod, nodeClass, nodePool) env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) instance := env.GetInstance(pod.Spec.NodeName) - volumes := env.GetVolumes(lo.Map(instance.BlockDeviceMappings, func(bdm *ec2.InstanceBlockDeviceMapping, _ int) *string { - return bdm.Ebs.VolumeId + volumes := env.GetVolumes(lo.Map(instance.BlockDeviceMappings, func(bdm ec2types.InstanceBlockDeviceMapping, _ int) string { + return lo.FromPtr(bdm.Ebs.VolumeId) })...) - networkInterfaces := env.GetNetworkInterfaces(lo.Map(instance.NetworkInterfaces, func(ni *ec2.InstanceNetworkInterface, _ int) *string { - return ni.NetworkInterfaceId + networkInterfaces := env.GetNetworkInterfaces(lo.Map(instance.NetworkInterfaces, func(ni ec2types.InstanceNetworkInterface, _ int) string { + return lo.FromPtr(ni.NetworkInterfaceId) })...) - Expect(instance.Tags).To(ContainElement(&ec2.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) + Expect(instance.Tags).To(ContainElement(ec2types.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) for _, volume := range volumes { - Expect(volume.Tags).To(ContainElement(&ec2.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) + Expect(volume.Tags).To(ContainElement(ec2types.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) } for _, networkInterface := range networkInterfaces { // Any ENI that contains this createdAt tag was created by the VPC CNI DaemonSet - if !lo.ContainsBy(networkInterface.TagSet, func(t *ec2.Tag) bool { return lo.FromPtr(t.Key) == createdAtTag }) { - Expect(networkInterface.TagSet).To(ContainElement(&ec2.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) + if !lo.ContainsBy(networkInterface.TagSet, func(t ec2types.Tag) bool { return lo.FromPtr(t.Key) == createdAtTag }) { + Expect(networkInterface.TagSet).To(ContainElement(ec2types.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) } } }) @@ -77,14 +75,13 @@ var _ = Describe("Tags", func() { }}) nodeClass.Spec.Tags = map[string]string{"TestTag": "TestVal"} pod := coretest.Pod() - env.ExpectCreated(pod, nodeClass, nodePool) env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) instance := env.GetInstance(pod.Spec.NodeName) Expect(instance.SpotInstanceRequestId).ToNot(BeNil()) - spotInstanceRequest := env.GetSpotInstanceRequest(instance.SpotInstanceRequestId) - Expect(spotInstanceRequest.Tags).To(ContainElement(&ec2.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) + spotInstanceRequest := env.GetSpotInstance(lo.FromPtr(instance.SpotInstanceRequestId)) + Expect(spotInstanceRequest.Tags).To(ContainElement(ec2types.Tag{Key: lo.ToPtr("TestTag"), Value: lo.ToPtr("TestVal")})) }) It("should tag managed instance profiles", func() { if env.PrivateCluster { @@ -114,7 +111,6 @@ var _ = Describe("Tags", func() { }, }) Expect(err).ToNot(HaveOccurred()) - // Restart Karpenter to flush the instance profile cache and to trigger re-tagging of the instance profile env.EventuallyExpectKarpenterRestarted() @@ -127,23 +123,20 @@ var _ = Describe("Tags", func() { }).WithTimeout(time.Second * 20).Should(Succeed()) }) }) - Context("Tagging Controller", func() { It("should tag with karpenter.sh/nodeclaim and Name tag", func() { pod := coretest.Pod() - env.ExpectCreated(nodePool, nodeClass, pod) env.EventuallyExpectCreatedNodeCount("==", 1) node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] nodeClaim := env.ExpectNodeClaimCount("==", 1)[0] - Eventually(func(g Gomega) { g.Expect(env.Client.Get(env.Context, client.ObjectKeyFromObject(nodeClaim), nodeClaim)).To(Succeed()) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationInstanceTagged, "true")) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationClusterNameTaggedCompatability, "true")) }, time.Minute).Should(Succeed()) - nodeInstance := instance.NewInstance(lo.ToPtr(env.GetInstance(node.Name))) + nodeInstance := instance.NewInstance(env.GetInstance(node.Name)) Expect(nodeInstance.Tags).To(HaveKeyWithValue("Name", node.Name)) Expect(nodeInstance.Tags).To(HaveKeyWithValue("karpenter.sh/nodeclaim", nodeClaim.Name)) Expect(nodeInstance.Tags).To(HaveKeyWithValue("eks:eks-cluster-name", env.ClusterName)) @@ -170,40 +163,36 @@ var _ = Describe("Tags", func() { }, }) pod := coretest.Pod() - env.ExpectCreated(nodePool, nodeClass, pod) env.EventuallyExpectCreatedNodeCount("==", 1) node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] nodeClaim := env.ExpectNodeClaimCount("==", 1)[0] - Eventually(func(g Gomega) { g.Expect(env.Client.Get(env.Context, client.ObjectKeyFromObject(nodeClaim), nodeClaim)).To(Succeed()) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationInstanceTagged, "true")) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationClusterNameTaggedCompatability, "true")) }, time.Minute).Should(Succeed()) - nodeInstance := instance.NewInstance(lo.ToPtr(env.GetInstance(node.Name))) + nodeInstance := instance.NewInstance(env.GetInstance(node.Name)) Expect(nodeInstance.Tags).To(HaveKeyWithValue("Name", "custom-name")) Expect(nodeInstance.Tags).To(HaveKeyWithValue("karpenter.sh/nodeclaim", nodeClaim.Name)) Expect(nodeInstance.Tags).To(HaveKeyWithValue("eks:eks-cluster-name", env.ClusterName)) }) It("should tag instance with eks:eks-cluster-name tag when the tag doesn't exist", func() { pod := coretest.Pod() - env.ExpectCreated(nodePool, nodeClass, pod) env.EventuallyExpectCreatedNodeCount("==", 1) node := env.EventuallyExpectInitializedNodeCount("==", 1)[0] nodeClaim := env.ExpectNodeClaimCount("==", 1)[0] - Eventually(func(g Gomega) { g.Expect(env.Client.Get(env.Context, client.ObjectKeyFromObject(nodeClaim), nodeClaim)).To(Succeed()) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationInstanceTagged, "true")) g.Expect(nodeClaim.Annotations).To(HaveKeyWithValue(v1.AnnotationClusterNameTaggedCompatability, "true")) }, time.Minute).Should(Succeed()) - _, err := env.EC2API.DeleteTags(&ec2.DeleteTagsInput{ - Resources: []*string{lo.ToPtr(env.ExpectParsedProviderID(node.Spec.ProviderID))}, - Tags: []*ec2.Tag{{Key: lo.ToPtr(v1.EKSClusterNameTagKey)}}, + _, err := env.EC2API.DeleteTags(env.Context, &ec2.DeleteTagsInput{ + Resources: []string{env.ExpectParsedProviderID(node.Spec.ProviderID)}, + Tags: []ec2types.Tag{{Key: lo.ToPtr(v1.EKSClusterNameTagKey)}}, }) Expect(err).ToNot(HaveOccurred()) @@ -214,13 +203,13 @@ var _ = Describe("Tags", func() { By(fmt.Sprintf("Polling for the %s tag update", v1.EKSClusterNameTagKey)) Eventually(func(g Gomega) { - out, err := env.EC2API.DescribeInstances(&ec2.DescribeInstancesInput{ - InstanceIds: []*string{lo.ToPtr(env.ExpectParsedProviderID(node.Spec.ProviderID))}, + out, err := env.EC2API.DescribeInstances(env.Context, &ec2.DescribeInstancesInput{ + InstanceIds: []string{env.ExpectParsedProviderID(node.Spec.ProviderID)}, }) g.Expect(err).ToNot(HaveOccurred()) g.Expect(out.Reservations).To(HaveLen(1)) g.Expect(out.Reservations[0].Instances).To(HaveLen(1)) - g.Expect(out.Reservations[0].Instances[0].Tags).To(ContainElement(&ec2.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(env.ClusterName)})) + g.Expect(out.Reservations[0].Instances[0].Tags).To(ContainElement(ec2types.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(env.ClusterName)})) }).Should(Succeed()) }) }) diff --git a/test/suites/nodeclaim/garbage_collection_test.go b/test/suites/nodeclaim/garbage_collection_test.go index 72bc651be015..4f5b999409d5 100644 --- a/test/suites/nodeclaim/garbage_collection_test.go +++ b/test/suites/nodeclaim/garbage_collection_test.go @@ -18,9 +18,11 @@ import ( "encoding/base64" "fmt" "os" + "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -52,30 +54,30 @@ var _ = Describe("GarbageCollection", func() { instanceProfileName = fmt.Sprintf("KarpenterNodeInstanceProfile-%s", env.ClusterName) roleName = fmt.Sprintf("KarpenterNodeRole-%s", env.ClusterName) instanceInput = &ec2.RunInstancesInput{ - InstanceType: aws.String("c5.large"), - IamInstanceProfile: &ec2.IamInstanceProfileSpecification{ + InstanceType: "c5.large", + IamInstanceProfile: &ec2types.IamInstanceProfileSpecification{ Name: aws.String(instanceProfileName), }, - SecurityGroupIds: lo.Map(securityGroups, func(s environmentaws.SecurityGroup, _ int) *string { - return s.GroupIdentifier.GroupId + SecurityGroupIds: lo.Map(securityGroups, func(s environmentaws.SecurityGroup, _ int) string { + return *s.GroupIdentifier.GroupId }), SubnetId: aws.String(subnets[0].ID), - BlockDeviceMappings: []*ec2.BlockDeviceMapping{ + BlockDeviceMappings: []ec2types.BlockDeviceMapping{ { DeviceName: aws.String("/dev/xvda"), - Ebs: &ec2.EbsBlockDevice{ + Ebs: &ec2types.EbsBlockDevice{ Encrypted: aws.Bool(true), DeleteOnTermination: aws.Bool(true), - VolumeType: aws.String(ec2.VolumeTypeGp3), - VolumeSize: aws.Int64(20), + VolumeType: ec2types.VolumeTypeGp3, + VolumeSize: aws.Int32(20), }, }, }, ImageId: aws.String(customAMI), // EKS AL2-based AMI - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeInstance), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeInstance, + Tags: []ec2types.Tag{ { Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", env.ClusterName)), Value: aws.String("owned"), @@ -91,8 +93,8 @@ var _ = Describe("GarbageCollection", func() { }, }, }, - MinCount: aws.Int64(1), - MaxCount: aws.Int64(1), + MinCount: aws.Int32(1), + MaxCount: aws.Int32(1), } }) It("should succeed to garbage collect an Instance that was launched by a NodeClaim but has no Instance mapping", func() { @@ -101,7 +103,6 @@ var _ = Describe("GarbageCollection", func() { Expect(err).ToNot(HaveOccurred()) instanceInput.UserData = lo.ToPtr(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(string(rawContent), env.ClusterName, env.ClusterEndpoint, env.ExpectCABundle())))) - env.ExpectInstanceProfileCreated(instanceProfileName, roleName) DeferCleanup(func() { env.ExpectInstanceProfileDeleted(instanceProfileName, roleName) @@ -110,24 +111,20 @@ var _ = Describe("GarbageCollection", func() { out := env.EventuallyExpectRunInstances(instanceInput) Expect(out.Instances).To(HaveLen(1)) - // Always ensure that we cleanup the instance DeferCleanup(func() { - _, err := env.EC2API.TerminateInstances(&ec2.TerminateInstancesInput{ - InstanceIds: []*string{out.Instances[0].InstanceId}, + _, err := env.EC2API.TerminateInstances(env.Context, &ec2.TerminateInstancesInput{ + InstanceIds: []string{*out.Instances[0].InstanceId}, }) - if awserrors.IsNotFoundV2(err) { - return - } - Expect(err).ToNot(HaveOccurred()) + Expect(awserrors.IgnoreNotFound(err)).ToNot(HaveOccurred()) }) // Wait for the node to register with the cluster node := env.EventuallyExpectCreatedNodeCount("==", 1)[0] // Update the tags to add the EKSClusterNameTagKey tag - _, err = env.EC2API.CreateTagsWithContext(env.Context, &ec2.CreateTagsInput{ - Resources: []*string{out.Instances[0].InstanceId}, - Tags: []*ec2.Tag{ + _, err = env.EC2API.CreateTags(env.Context, &ec2.CreateTagsInput{ + Resources: []string{*out.Instances[0].InstanceId}, + Tags: []ec2types.Tag{ { Key: aws.String(v1.EKSClusterNameTagKey), Value: aws.String(env.ClusterName), @@ -135,22 +132,22 @@ var _ = Describe("GarbageCollection", func() { }, }) Expect(err).ToNot(HaveOccurred()) - - // Eventually expect the node and the instance to be removed + // Eventually expect the node and the instance to be removed (shutting-down) env.EventuallyExpectNotFound(node) - Expect(lo.FromPtr(env.GetInstanceByID(aws.StringValue(out.Instances[0].InstanceId)).State.Name)).To(BeElementOf("terminated", "shutting-down")) + Eventually(func(g Gomega) { + g.Expect(string(env.GetInstanceByID(aws.ToString(out.Instances[0].InstanceId)).State.Name)).To(BeElementOf("terminated", "shutting-down")) + }, time.Second*10).Should(Succeed()) }) It("should succeed to garbage collect an Instance that was deleted without the cluster's knowledge", func() { // Disable the interruption queue for the garbage collection coretest env.ExpectSettingsOverridden(corev1.EnvVar{Name: "INTERRUPTION_QUEUE", Value: ""}) - pod := coretest.Pod() env.ExpectCreated(nodeClass, nodePool, pod) env.EventuallyExpectHealthy(pod) node := env.ExpectCreatedNodeCount("==", 1)[0] - _, err := env.EC2API.TerminateInstances(&ec2.TerminateInstancesInput{ - InstanceIds: aws.StringSlice([]string{lo.Must(utils.ParseInstanceID(node.Spec.ProviderID))}), + _, err := env.EC2API.TerminateInstances(env.Context, &ec2.TerminateInstancesInput{ + InstanceIds: []string{lo.Must(utils.ParseInstanceID(node.Spec.ProviderID))}, }) Expect(err).ToNot(HaveOccurred()) diff --git a/test/suites/nodeclaim/nodeclaim_test.go b/test/suites/nodeclaim/nodeclaim_test.go index f2c4b65b1782..46a7a86322dc 100644 --- a/test/suites/nodeclaim/nodeclaim_test.go +++ b/test/suites/nodeclaim/nodeclaim_test.go @@ -25,6 +25,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/karpenter/pkg/utils/resources" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -32,9 +36,6 @@ import ( "sigs.k8s.io/karpenter/pkg/test" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" ) var _ = Describe("StandaloneNodeClaim", func() { @@ -194,7 +195,7 @@ var _ = Describe("StandaloneNodeClaim", func() { env.EventuallyExpectNotFound(nodeClaim, node) Eventually(func(g Gomega) { - g.Expect(lo.FromPtr(env.GetInstanceByID(instanceID).State.Name)).To(BeElementOf("terminated", "shutting-down")) + g.Expect(env.GetInstanceByID(instanceID).State.Name).To(BeElementOf(ec2types.InstanceStateNameTerminated, ec2types.InstanceStateNameShuttingDown)) }, time.Second*10).Should(Succeed()) }) It("should delete a NodeClaim from the node termination finalizer", func() { @@ -235,7 +236,7 @@ var _ = Describe("StandaloneNodeClaim", func() { env.EventuallyExpectNotFound(nodeClaim, node) Eventually(func(g Gomega) { - g.Expect(lo.FromPtr(env.GetInstanceByID(instanceID).State.Name)).To(BeElementOf("terminated", "shutting-down")) + g.Expect(env.GetInstanceByID(instanceID).State.Name).To(BeElementOf(ec2types.InstanceStateNameTerminated, ec2types.InstanceStateNameShuttingDown)) }, time.Second*10).Should(Succeed()) }) It("should create a NodeClaim with custom labels passed through the userData", func() { diff --git a/test/suites/scheduling/suite_test.go b/test/suites/scheduling/suite_test.go index 73315a3f8c2b..fb720222f842 100644 --- a/test/suites/scheduling/suite_test.go +++ b/test/suites/scheduling/suite_test.go @@ -19,6 +19,8 @@ import ( "testing" "time" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -484,7 +486,7 @@ var _ = Describe("Scheduling", Ordered, ContinueOnFailure, func() { env.ExpectCreated(pod, nodeClass, nodePoolLowPri, nodePoolHighPri) env.EventuallyExpectHealthy(pod) env.ExpectCreatedNodeCount("==", 1) - Expect(lo.FromPtr(env.GetInstance(pod.Spec.NodeName).InstanceType)).To(Equal("c5.large")) + Expect(env.GetInstance(pod.Spec.NodeName).InstanceType).To(Equal(ec2types.InstanceType("c5.large"))) Expect(env.GetNode(pod.Spec.NodeName).Labels[karpv1.NodePoolLabelKey]).To(Equal(nodePoolHighPri.Name)) }) diff --git a/test/suites/storage/suite_test.go b/test/suites/storage/suite_test.go index 92dad8df17f1..9a9b31c096a4 100644 --- a/test/suites/storage/suite_test.go +++ b/test/suites/storage/suite_test.go @@ -22,7 +22,7 @@ import ( "k8s.io/apimachinery/pkg/labels" - awssdk "github.com/aws/aws-sdk-go/aws" + awssdk "github.com/aws/aws-sdk-go-v2/aws" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" diff --git a/test/suites/termination/termination_test.go b/test/suites/termination/termination_test.go index 2790a63ae21c..a0b196249117 100644 --- a/test/suites/termination/termination_test.go +++ b/test/suites/termination/termination_test.go @@ -17,6 +17,8 @@ package termination_test import ( "time" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/samber/lo" @@ -48,7 +50,7 @@ var _ = Describe("Termination", func() { env.ExpectDeleted(nodes[0]) env.EventuallyExpectNotFound(nodes[0]) Eventually(func(g Gomega) { - g.Expect(lo.FromPtr(env.GetInstanceByID(instanceID).State.Name)).To(BeElementOf("terminated", "shutting-down")) + g.Expect(env.GetInstanceByID(instanceID).State.Name).To(BeElementOf(ec2types.InstanceStateNameTerminated, ec2types.InstanceStateNameShuttingDown)) }, time.Second*10).Should(Succeed()) }) // Pods from Karpenter nodes are expected to drain in the following order: