From 22294fb7579b491b4ff0003b500a2864b187ddfc Mon Sep 17 00:00:00 2001 From: punkwalker <126026317+punkwalker@users.noreply.github.com> Date: Tue, 28 May 2024 13:20:30 -0700 Subject: [PATCH] Add support for BlockDeviceMapping and MetadataOption (#1) * feat: Add nodepool and nodeclass validtion * feat: Add support for BlockDeviceMapping and MetadataOption --- .golangci.yaml | 4 +- .goreleaser.yaml | 2 +- README.md | 61 ++--- cmd/root.go | 2 +- cmd/run.go | 64 +++-- go.mod | 25 +- go.sum | 98 +------- pkg/aws/eks.go | 21 -- pkg/k8s/config.go | 132 ---------- pkg/karpenter/ec2nodeclass.go | 227 ----------------- pkg/karpenter/generate.go | 56 ----- pkg/karpenteraws/ec2nodeclass.go | 257 ++++++++++++++++++++ pkg/karpenteraws/generate.go | 81 ++++++ pkg/{karpenter => karpenteraws}/nodepool.go | 12 +- pkg/options/options.go | 53 ++-- pkg/printers/printers.go | 49 ++++ 16 files changed, 531 insertions(+), 613 deletions(-) delete mode 100644 pkg/k8s/config.go delete mode 100644 pkg/karpenter/ec2nodeclass.go delete mode 100644 pkg/karpenter/generate.go create mode 100644 pkg/karpenteraws/ec2nodeclass.go create mode 100644 pkg/karpenteraws/generate.go rename pkg/{karpenter => karpenteraws}/nodepool.go (96%) create mode 100644 pkg/printers/printers.go diff --git a/.golangci.yaml b/.golangci.yaml index 9a3300e..6a4d709 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -27,6 +27,4 @@ linters-settings: issues: exclude-dirs: - - dist - # TODO: remove when linting errors in existing code have been cleared - new-from-rev: HEAD~1 + - dist \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1a37d49..22f6975 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -35,7 +35,7 @@ brews: name: homebrew-tap branch: main token: "{{ .Env.TAP_GITHUB_TOKEN }}" - folder: Formula + directory: Formula homepage: https://github.com/punkwalker/karpenter-generate description: A simple CLI tool to generate Karpenter CRDs from EKS Managed Node Groups license: MIT-0 diff --git a/README.md b/README.md index 73af27d..1738400 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,33 @@ -# `karpenter-generate` +# `karpenter-generate` This is a simple CLI tool to generate AWS Karpenter Custom Kubernetes Resources (Nodepool & EC2NodeClass) from AWS EKS Managed Nodegroup information. The generated resources can be stored in as a yaml manifest file or can be directly applied to the cluster. +> [!WARNING] +> The tool can only generate ***v1beta*** resources for [Karpenter on AWS](https://karpenter.sh/). +> Compatible with Karpenter ***v.0.32.0*** onwards + ## Example Usage ### For All Managed Nodegroups -The tool can be used without any flag and it will scan all the Managed Nodegroups in the cluster. The cluster to scan is decided based on current-context in kubeconfig -```bash -karpenter-generate ``` +karpenter-generate --cluster --karpenter-nodegroup fargate (If Karpenter is deployed on Fargate) + +OR + +karpenter-generate --cluster --karpenter-nodegroup +``` + ### For specific Managed Nodegroup To generate Karpenter Custom Resources for a specific Managed Nodegroup. -```bash -karpenter-generate --nodegroup ``` - -### For cluster other than current-context -To specifcy other cluster than current-context -```bash -karpenter-generate --cluster --region +karpenter-generate --cluster --karpenter-nodegroup fargate --nodegroup ``` ## Prerequisites -- Configured [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) -- Valid kubeconfig store at `$HOME/.kube/config` +- Propely Configured [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) ## Installation ### MacOS & Linux Use [Homebrew](https://brew.sh/) and run following command. -```bash +``` brew tap punkwalker/tap brew install karpenter-generate ``` @@ -36,15 +37,15 @@ Downloaded archive file from release artifacts. Download the archive file from r | OS | Arch | Download| | ------ | ------ | ------ | -| Linux | AMD64/x86_64 | [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.3/karpenter-generate_Linux_x86_64.tar.gz)| -| | ARM64| [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.3/karpenter-generate_Linux_arm64.tar.gz)| -| Windows | AMD64/x86_64 | [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.3/karpenter-generate_Windows_x86_64.tar.gz)| -| | ARM64| [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.3/karpenter-generate_Windows_arm64.tar.gz)| +| Linux | AMD64/x86_64 | [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.4/karpenter-generate_Linux_x86_64.tar.gz)| +| | ARM64| [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.4/karpenter-generate_Linux_arm64.tar.gz)| +| Windows | AMD64/x86_64 | [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.4/karpenter-generate_Windows_x86_64.tar.gz)| +| | ARM64| [Link](https://github.com/punkwalker/karpenter-generate/releases/download/v0.0.4/karpenter-generate_Windows_arm64.tar.gz)| After downloading the archive, extract it and copy the binary/executable to `/usr/local/bin` for Linux. For Windows, run the `karpenter-generate.exe` from extracted folder. ## Help -```bash +``` karpeter-generate --help Description: @@ -52,22 +53,26 @@ Description: Nodepools and EC2NodeClass from details of EKS Managed Nodegroup Usage: - karpenter-generate [command] - karpenter-generate [flags] + karpenter-generate --cluster --karpenter-nodegroup [flags] Available Commands: version Print the version and build information for karpenter-generate -Optional Flags: - --cluster string name of the EKS cluster - (default: from kubeconfig current-context) +Flags: + --cluster string name of the EKS cluster + --karpenter-nodegroup string name of the EKS managed nodegroup running Karpenter deployment or fargate + +Optiona Flags: --nodegroup string name of the EKS managed nodegroup - (default: all the nodegroups) - --region string the region to use, overrides config/env settings - (default: from kubeconfig current-context or AWS config) + (default: all the nodegroups expectthe one running Karpenter) + --region string region of EKS cluster, overrides AWS CLI configuration/ENV values + (default: AWS CLI configuration) --profile string use the specific profile from your credential file - (default: from kubeconfig current-context or AWS config) + (default: AWS CLI configuration) + --output string output format (yaml or json) + (default: yaml) -h, --help help for karpenter-generate + ` ``` ## Contributing diff --git a/cmd/root.go b/cmd/root.go index 5b0373d..d11f7be 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,7 +17,7 @@ var rootCmd = &cobra.Command{ Nodepools and EC2NodeClass from details of EKS Managed Nodegroup. Which will allow seamless migration to Karpenter.`, SilenceUsage: true, RunE: func(_ *cobra.Command, _ []string) error { - return run(opts) + return run() }, } diff --git a/cmd/run.go b/cmd/run.go index 2fcf1f9..27b0642 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -6,30 +6,22 @@ import ( "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/punkwalker/karpenter-generate/pkg/aws" - "github.com/punkwalker/karpenter-generate/pkg/karpenter" - "github.com/punkwalker/karpenter-generate/pkg/options" + "github.com/punkwalker/karpenter-generate/pkg/karpenteraws" + "github.com/punkwalker/karpenter-generate/pkg/printers" ) -func run(opts *options.Options) error { +func run() error { aws.Init(opts) - opts.Parse() - var nodeGroups []types.Nodegroup + if err := opts.Parse(); err != nil { + return err + } - eksClient := aws.NewEKSClient() - if opts.NodegroupName != "" { - nodeGroup, err := eksClient.DescribeNodegroup(opts.ClusterName, opts.NodegroupName) - if err != nil { - return aws.FormatErrorAsMessageOnly(err) - } - nodeGroups = append(nodeGroups, *nodeGroup) - nodePools, nodeClasses, err := karpenter.Generate(&nodeGroups) - if err != nil { - return err - } - return karpenter.Print(nodePools, nodeClasses) + printer, err := printers.NewPrinter(printers.Output(opts.Output)) + if err != nil { + return err } - nodeGroups, err := eksClient.GetAllNodeGroups(opts) + nodeGroups, err := getNodegroups() if err != nil { return aws.FormatErrorAsMessageOnly(err) } @@ -38,9 +30,41 @@ func run(opts *options.Options) error { return fmt.Errorf("no nodegroups found") } - nodePools, nodeClasses, err := karpenter.Generate(&nodeGroups) + nodePools, nodeClasses, err := karpenteraws.Generate(&nodeGroups) if err != nil { return err } - return karpenter.Print(nodePools, nodeClasses) + return printers.Print(printer, nodePools, nodeClasses) +} + +func getNodegroups() ([]types.Nodegroup, error) { + var nodegroups []types.Nodegroup + var ngList []string + var err error + + eksClient := aws.NewEKSClient() + + if opts.NodegroupName != "" { + ngList = []string{opts.NodegroupName} + } else { + ngList, err = eksClient.ListNodegroups(opts.ClusterName) + if err != nil { + return nil, err + } + } + + for _, ng := range ngList { + if opts.KarpenterNodegroupName != ng { + nodegroup, err := eksClient.DescribeNodegroup(opts.ClusterName, ng) + if err != nil { + return nil, err + } + + if nodegroup.Status != "ACTIVE" { + return nil, fmt.Errorf(`nodegroup "%s" is not active, make sure all the nodegroups are in "ACTIVE" state`, ng) + } + nodegroups = append(nodegroups, *nodegroup) + } + } + return nodegroups, nil } diff --git a/go.mod b/go.mod index f4e5b6c..4f52694 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/eks v1.42.1 github.com/aws/karpenter-provider-aws v0.36.1 github.com/aws/smithy-go v1.20.2 + github.com/samber/lo v1.39.0 github.com/spf13/cobra v1.8.0 k8s.io/api v0.30.0 k8s.io/apimachinery v0.30.0 k8s.io/cli-runtime v0.30.0 - k8s.io/client-go v0.30.0 sigs.k8s.io/karpenter v0.36.1 ) @@ -33,23 +33,21 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect + github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647 // indirect + github.com/blendle/zapdriver v1.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.7.0+incompatible // indirect - github.com/go-errors/errors v1.4.2 // indirect + github.com/evanphx/json-patch/v5 v5.8.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -61,20 +59,17 @@ require ( github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/samber/lo v1.39.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/xlab/treeprint v1.2.0 // indirect - go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -84,13 +79,15 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.30.0 // indirect + k8s.io/cloud-provider v0.29.3 // indirect + k8s.io/csi-translation-lib v0.29.3 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect knative.dev/pkg v0.0.0-20231010144348-ca8c009405dd // indirect + sigs.k8s.io/controller-runtime v0.17.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect - sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 3527b25..a61c19d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= @@ -48,13 +46,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -64,18 +57,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= @@ -88,32 +79,14 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -123,12 +96,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -163,8 +132,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= @@ -175,15 +142,13 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= @@ -197,53 +162,44 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= -github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -252,18 +208,14 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -271,13 +223,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -289,10 +239,6 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -305,24 +251,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= @@ -338,8 +268,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= @@ -370,10 +298,6 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMm sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/karpenter v0.36.1 h1:NP36T+uTTeO9fAiWttEBhXrtCiDPJkiyLNZWjFSIkb0= sigs.k8s.io/karpenter v0.36.1/go.mod h1:fieFojxOec/l0tDmFT7R+g/Y+SGQbL9VlcYO8xb3sLo= -sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= -sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= -sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= -sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/pkg/aws/eks.go b/pkg/aws/eks.go index c43154c..163dbdf 100644 --- a/pkg/aws/eks.go +++ b/pkg/aws/eks.go @@ -6,7 +6,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/eks/types" - "github.com/punkwalker/karpenter-generate/pkg/options" ) type EKSClient struct { @@ -17,26 +16,6 @@ func NewEKSClient() *EKSClient { return &EKSClient{eks.NewFromConfig(GetConfig())} } -func (c *EKSClient) GetAllNodeGroups(opts *options.Options) ([]types.Nodegroup, error) { - var nodegroups []types.Nodegroup - ngList, err := c.ListNodegroups(opts.ClusterName) - - if err != nil { - return nil, err - } - - for _, ng := range ngList { - nodegroup, err := c.DescribeNodegroup(opts.ClusterName, ng) - if err != nil { - return nil, err - } - - nodegroups = append(nodegroups, *nodegroup) - } - - return nodegroups, nil -} - func (c *EKSClient) ListNodegroups(clusterName string) ([]string, error) { nodegroupNames := []string{} pageNum := 0 diff --git a/pkg/k8s/config.go b/pkg/k8s/config.go deleted file mode 100644 index 957b367..0000000 --- a/pkg/k8s/config.go +++ /dev/null @@ -1,132 +0,0 @@ -package k8s - -import ( - "fmt" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/eks/types" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" -) - -func Client(context string) (*kubernetes.Clientset, error) { - var clientConfigOverrides *clientcmd.ConfigOverrides - if context != "" { - clientConfigOverrides = &clientcmd.ConfigOverrides{ - CurrentContext: context, - } - } - - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - clientcmd.NewDefaultClientConfigLoadingRules(), - clientConfigOverrides, - ) - - restConfig, err := clientConfig.ClientConfig() - if err != nil { - return nil, genericclioptions.ErrEmptyConfig - } - - return kubernetes.NewForConfig(restConfig) -} - -func OptionsFromConfig() (string, string, string) { - var clusterName string - var region string - var profile string - raw, err := Kubeconfig() - if err != nil { - return clusterName, region, profile - } - - if err := clientcmdapi.MinifyConfig(raw); err != nil { - return clusterName, region, profile - } - - for _, user := range raw.AuthInfos { - for idx, val := range user.Exec.Args { - switch val { - case "-i", "--cluster-name": - clusterName = user.Exec.Args[idx+1] - case "--region": - region = user.Exec.Args[idx+1] - } - } - - for _, env := range user.Exec.Env { - if env.Name == "AWS_REGION" { - region = env.Value - } - if env.Name == "AWS_PROFILE" { - profile = env.Value - } - } - } - return clusterName, region, profile -} - -func ClusterURLForCurrentContext() string { - raw, err := Kubeconfig() - if err != nil { - return "" - } - - if err := clientcmdapi.MinifyConfig(raw); err != nil { - return "" - } - - return raw.Clusters[raw.Contexts[raw.CurrentContext].Cluster].Server -} - -func DynamicClient(context string) (dynamic.Interface, error) { - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - clientcmd.NewDefaultClientConfigLoadingRules(), - &clientcmd.ConfigOverrides{ - CurrentContext: context, - }, - ) - - restConfig, err := clientConfig.ClientConfig() - if err != nil { - return nil, err - } - - return dynamic.NewForConfig(restConfig) -} - -func KubeContextForCluster(cluster *types.Cluster) (string, error) { - raw, err := Kubeconfig() - if err != nil { - return "", err - } - - found := "" - - for name, context := range raw.Contexts { - if _, ok := raw.Clusters[context.Cluster]; ok { - if raw.Clusters[context.Cluster].Server == aws.ToString(cluster.Endpoint) { - found = name - break - } - } - } - - return found, nil -} - -func Kubeconfig() (*clientcmdapi.Config, error) { - config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - clientcmd.NewDefaultClientConfigLoadingRules(), - &clientcmd.ConfigOverrides{}, - ) - - raw, err := config.RawConfig() - if err != nil { - return nil, fmt.Errorf("failed to load kubeconfig: %w", err) - } - - return &raw, nil -} diff --git a/pkg/karpenter/ec2nodeclass.go b/pkg/karpenter/ec2nodeclass.go deleted file mode 100644 index 6dabb5d..0000000 --- a/pkg/karpenter/ec2nodeclass.go +++ /dev/null @@ -1,227 +0,0 @@ -package karpenter - -import ( - "encoding/base64" - "strings" - - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" - awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/punkwalker/karpenter-generate/pkg/aws" -) - -type NodeGroup struct { - *ekstypes.Nodegroup - LT *ec2types.ResponseLaunchTemplateData // LT Generated by MNG and used by ASG - CustomLT *ec2types.ResponseLaunchTemplateData // Custom LT provided to MNG -} - -var ( - NodeClassTypeMeta = metav1.TypeMeta{ - Kind: "EC2NodeClass", - APIVersion: awskarpenter.SchemeGroupVersion.Identifier(), - } - CLUSTER_TAG map[string]string -) - -func NewNodeGroup(ng ekstypes.Nodegroup) (*NodeGroup, error) { - - newNodegroup := NodeGroup{ - Nodegroup: &ng, - } - - CLUSTER_TAG = map[string]string{ - "kubernetes.io/cluster/" + *ng.ClusterName: "owned", - } - - ec2Client := aws.NewEC2Client() - asgClient := aws.NewAutoscalingClient() - - if ng.LaunchTemplate != nil { - customLT, err := ec2Client.DescribeLaunchTemplateVersions( - *ng.LaunchTemplate.Id, - *ng.LaunchTemplate.Version) - if err != nil { - return nil, aws.FormatErrorAsMessageOnly(err) - } - newNodegroup.CustomLT = customLT[0].LaunchTemplateData - } - - asg, err := asgClient.DescribeAutoScalingGroups(*ng.Resources.AutoScalingGroups[0].Name) - if err != nil { - return nil, aws.FormatErrorAsMessageOnly(err) - } - - lt, err := ec2Client.DescribeLaunchTemplateVersions( - *asg[0].MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateId, - *asg[0].MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.Version) - if err != nil { - return nil, aws.FormatErrorAsMessageOnly(err) - } - - newNodegroup.LT = lt[0].LaunchTemplateData - - return &newNodegroup, nil -} - -func (n *NodeGroup) GetEC2NodeClass() (awskarpenter.EC2NodeClass, error) { - return awskarpenter.EC2NodeClass{ - TypeMeta: NodeClassTypeMeta, - ObjectMeta: n.NodeClassObjectMeta(), - Spec: n.NodeClassSpec(), - }, nil -} - -func (n NodeGroup) Name() string { - return *n.NodegroupName -} - -func (n NodeGroup) AmiID() string { - // TODO: implement logic if user wants to preserve AMID from MNG?? - return *n.CustomLT.ImageId -} - -func (n NodeGroup) NodeClassObjectMeta() metav1.ObjectMeta { - nodeClassannotations := map[string]string{ - "generated-by": "karpenter-migrate", - } - - return metav1.ObjectMeta{ - Name: n.Name(), - Annotations: nodeClassannotations, - } -} - -func (n NodeGroup) NodeClassSpec() awskarpenter.EC2NodeClassSpec { - amiFamily := n.AMIFamily() - if amiFamily != &awskarpenter.AMIFamilyCustom { - return awskarpenter.EC2NodeClassSpec{ - AMIFamily: amiFamily, - Role: n.Role(), - SubnetSelectorTerms: n.SubnetSelectorTerms(), - SecurityGroupSelectorTerms: n.SecurityGroupSelectorTerms(), - UserData: n.UserData(), - Tags: n.FilteredTags(), - } - } - - return awskarpenter.EC2NodeClassSpec{ - AMIFamily: amiFamily, - AMISelectorTerms: n.AMISelectorTerms(), - } - -} - -func (n NodeGroup) FilteredTags() map[string]string { - filteredTags := map[string]string{} - if n.CustomLT != nil { - for _, tagspec := range n.CustomLT.TagSpecifications { - for _, tag := range tagspec.Tags { - if !strings.HasPrefix(*tag.Key, "aws:") { - filteredTags[*tag.Key] = *tag.Value - } - } - } - } - - for key, val := range n.Tags { - if !strings.HasPrefix(key, "aws:") { - filteredTags[key] = val - } - } - return filteredTags -} - -func (n NodeGroup) AMIFamily() *string { - switch n.AmiType { - case ekstypes.AMITypesAl2X8664, ekstypes.AMITypesAl2X8664Gpu, ekstypes.AMITypesAl2Arm64: - return &awskarpenter.AMIFamilyAL2 - case ekstypes.AMITypesBottlerocketX8664, ekstypes.AMITypesBottlerocketArm64, ekstypes.AMITypesBottlerocketArm64Nvidia, ekstypes.AMITypesBottlerocketX8664Nvidia: - return &awskarpenter.AMIFamilyBottlerocket - case ekstypes.AMITypesWindowsFull2019X8664, ekstypes.AMITypesWindowsCore2019X8664: - return &awskarpenter.AMIFamilyWindows2019 - case ekstypes.AMITypesWindowsFull2022X8664, ekstypes.AMITypesWindowsCore2022X8664: - return &awskarpenter.AMIFamilyWindows2022 - case ekstypes.AMITypesAl2023X8664Standard, ekstypes.AMITypesAl2023Arm64Standard: - return &awskarpenter.AMIFamilyAL2023 - default: - return &awskarpenter.AMIFamilyCustom - } -} - -func (n NodeGroup) IsCustomAMIFamily() bool { - return n.AmiType == ekstypes.AMITypesCustom -} - -func (n NodeGroup) AMISelectorTerms() []awskarpenter.AMISelectorTerm { - amiTerms := []awskarpenter.AMISelectorTerm{} - amiTerms = append(amiTerms, awskarpenter.AMISelectorTerm{ - ID: n.AmiID(), - }) - return amiTerms -} - -func (n NodeGroup) Role() string { - roleName := *n.NodeRole - lastIdx := strings.LastIndex(*n.NodeRole, "/") - roleName = roleName[lastIdx+1:] - return roleName -} - -func (n NodeGroup) SubnetSelectorTerms() []awskarpenter.SubnetSelectorTerm { - subnetSlice := []awskarpenter.SubnetSelectorTerm{} - for _, subnet := range n.Subnets { - subnetSlice = append(subnetSlice, awskarpenter.SubnetSelectorTerm{ - ID: subnet, - }) - } - return subnetSlice -} - -func (n NodeGroup) SecurityGroupSelectorTerms() []awskarpenter.SecurityGroupSelectorTerm { - sgTerms := []awskarpenter.SecurityGroupSelectorTerm{} - - // For customLaunchTemplates - if n.CustomLT != nil { - ltSG := n.CustomLT.SecurityGroupIds - for _, sg := range ltSG { - sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ - ID: sg, - }) - } - } - - // TODO: Check how MNG handles SGs if Custom LT has Additional SGs - // For Cluster SG attached by MNG - for _, intf := range n.LT.NetworkInterfaces { - for _, sg := range intf.Groups { - sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ - ID: sg, - }) - } - } - - return sgTerms -} - -// Returns UserData for nodegroup if Custom Launch Template is used with MNG -func (n NodeGroup) UserData() *string { - if n.CustomLT != nil { - // Additional UserData for non-custom AMIs - if !n.IsCustomAMIFamily() { - decodedUserData, _ := base64.StdEncoding.DecodeString(*n.CustomLT.UserData) - userData := string(decodedUserData) - return &userData - } - // UserData for custom AMIs - decodedUserData, _ := base64.StdEncoding.DecodeString(*n.CustomLT.UserData) - userData := string(decodedUserData) - return &userData - } - return nil -} - -// TODO: Implement logic for BlockDeviceMapping -// TODO: Implement logic for MetadataOptions diff --git a/pkg/karpenter/generate.go b/pkg/karpenter/generate.go deleted file mode 100644 index fbc4000..0000000 --- a/pkg/karpenter/generate.go +++ /dev/null @@ -1,56 +0,0 @@ -package karpenter - -import ( - "fmt" - "os" - - "github.com/aws/aws-sdk-go-v2/service/eks/types" - awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" - "k8s.io/cli-runtime/pkg/printers" - sigkarpenter "sigs.k8s.io/karpenter/pkg/apis/v1beta1" -) - -func Generate(nodeGroups *[]types.Nodegroup) ([]sigkarpenter.NodePool, []awskarpenter.EC2NodeClass, error) { - nodePools := make([]sigkarpenter.NodePool, 0, len(*nodeGroups)) - nodeClasses := make([]awskarpenter.EC2NodeClass, 0, len(*nodeGroups)) - - for _, ng := range *nodeGroups { - nodegroup, err := NewNodeGroup(ng) - if err != nil { - return nil, nil, err - } - - ec2Class, err := nodegroup.GetEC2NodeClass() - if err != nil { - return nil, nil, err - } - nodeClasses = append(nodeClasses, ec2Class) - - nodePool, err := nodegroup.GetNodePool() - if err != nil { - return nil, nil, err - } - nodePools = append(nodePools, nodePool) - } - - return nodePools, nodeClasses, nil -} - -func Print(nodePools []sigkarpenter.NodePool, nodeClasses []awskarpenter.EC2NodeClass) error { - y := printers.YAMLPrinter{} - - if len(nodePools) == len(nodeClasses) { - return fmt.Errorf("no. of nodepools is not equal to no. of nodeclass") - } - for idx := range len(nodePools) { - err := y.PrintObj(&nodePools[idx], os.Stdout) - if err != nil { - return err - } - err = y.PrintObj(&nodeClasses[idx], os.Stdout) - if err != nil { - return err - } - } - return nil -} diff --git a/pkg/karpenteraws/ec2nodeclass.go b/pkg/karpenteraws/ec2nodeclass.go new file mode 100644 index 0000000..c7432bd --- /dev/null +++ b/pkg/karpenteraws/ec2nodeclass.go @@ -0,0 +1,257 @@ +package karpenteraws + +import ( + "context" + "encoding/base64" + "strings" + + ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" + awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" + awskarpenterprovider "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily" + "github.com/samber/lo" + k8sapiresource "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + GiB int64 = 1024 * 1024 * 1024 + ClusterTagKey string = "kubernetes.io/cluster/" + ALAndBottleRocketDefaultDiskSize = 20 + WindowsDefaultDiskSize = 50 +) + +var ( + NodeClassTypeMeta = metav1.TypeMeta{ + Kind: "EC2NodeClass", + APIVersion: awskarpenter.SchemeGroupVersion.Identifier(), + } + ClusterTag map[string]string +) + +func (n *NodeGroup) GetEC2NodeClass() (awskarpenter.EC2NodeClass, error) { + nc := awskarpenter.EC2NodeClass{ + TypeMeta: NodeClassTypeMeta, + ObjectMeta: n.NodeClassObjectMeta(), + Spec: n.NodeClassSpec(), + } + if err := nc.Validate(context.TODO()); err != nil { + return awskarpenter.EC2NodeClass{}, err + } + return nc, nil +} + +func (n NodeGroup) Name() string { + return strings.ToLower(*n.NodegroupName) +} + +func (n NodeGroup) AmiID() string { + // TODO: implement preserve AMIId for MNG + return *n.CustomLT.ImageId +} + +func (n NodeGroup) NodeClassObjectMeta() metav1.ObjectMeta { + nodeClassannotations := map[string]string{ + "generated-by": "karpenter-migrate", + } + + return metav1.ObjectMeta{ + Name: n.Name(), + Annotations: nodeClassannotations, + } +} + +func (n NodeGroup) NodeClassSpec() awskarpenter.EC2NodeClassSpec { + return awskarpenter.EC2NodeClassSpec{ + AMIFamily: n.AMIFamily(), + Role: n.Role(), + AMISelectorTerms: n.AMISelectorTerms(), + SubnetSelectorTerms: n.SubnetSelectorTerms(), + SecurityGroupSelectorTerms: n.SecurityGroupSelectorTerms(), + UserData: n.UserData(), + BlockDeviceMappings: n.BlockDeviceMappings(), + Tags: n.FilteredTags(), + MetadataOptions: n.MetadataOptions(), + } +} + +func (n NodeGroup) FilteredTags() map[string]string { + filteredTags := map[string]string{} + if n.CustomLT != nil { + for _, tagspec := range n.CustomLT.TagSpecifications { + for _, tag := range tagspec.Tags { + if !strings.HasPrefix(*tag.Key, "aws:") { + filteredTags[*tag.Key] = *tag.Value + } + } + } + } + + for key, val := range n.Tags { + if !strings.HasPrefix(key, "aws:") { + filteredTags[key] = val + } + } + return filteredTags +} + +func (n NodeGroup) AMIFamily() *string { + switch n.AmiType { + case ekstypes.AMITypesAl2X8664, ekstypes.AMITypesAl2X8664Gpu, ekstypes.AMITypesAl2Arm64: + return lo.ToPtr(awskarpenter.AMIFamilyAL2) + case ekstypes.AMITypesBottlerocketX8664, ekstypes.AMITypesBottlerocketArm64, ekstypes.AMITypesBottlerocketArm64Nvidia, ekstypes.AMITypesBottlerocketX8664Nvidia: + return lo.ToPtr(awskarpenter.AMIFamilyBottlerocket) + case ekstypes.AMITypesWindowsFull2019X8664, ekstypes.AMITypesWindowsCore2019X8664: + return lo.ToPtr(awskarpenter.AMIFamilyWindows2019) + case ekstypes.AMITypesWindowsFull2022X8664, ekstypes.AMITypesWindowsCore2022X8664: + return lo.ToPtr(awskarpenter.AMIFamilyWindows2022) + case ekstypes.AMITypesAl2023X8664Standard, ekstypes.AMITypesAl2023Arm64Standard: + return lo.ToPtr(awskarpenter.AMIFamilyAL2023) + default: + return lo.ToPtr(awskarpenter.AMIFamilyCustom) + } +} + +func (n NodeGroup) IsCustomAMIFamily() bool { + return n.AmiType == ekstypes.AMITypesCustom +} + +func (n NodeGroup) AMISelectorTerms() []awskarpenter.AMISelectorTerm { + amiTerms := []awskarpenter.AMISelectorTerm{} + if n.CustomLT != nil { + if n.CustomLT.ImageId != nil { + amiTerms = append(amiTerms, awskarpenter.AMISelectorTerm{ + ID: n.AmiID(), + }) + } + } + return amiTerms +} + +func (n NodeGroup) Role() string { + // TODO: Implement override for MNG nodeRole + roleName := *n.NodeRole + lastIdx := strings.LastIndex(*n.NodeRole, "/") + roleName = roleName[lastIdx+1:] + return roleName +} + +func (n NodeGroup) SubnetSelectorTerms() []awskarpenter.SubnetSelectorTerm { + subnetSlice := []awskarpenter.SubnetSelectorTerm{} + for _, subnet := range n.Subnets { + subnetSlice = append(subnetSlice, awskarpenter.SubnetSelectorTerm{ + ID: subnet, + }) + } + return subnetSlice +} + +func (n NodeGroup) SecurityGroupSelectorTerms() []awskarpenter.SecurityGroupSelectorTerm { + sgTerms := []awskarpenter.SecurityGroupSelectorTerm{} + + // For customLaunchTemplates + if n.CustomLT != nil { + if n.CustomLT.SecurityGroupIds != nil { + ltSG := n.CustomLT.SecurityGroupIds + for _, sg := range ltSG { + sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ + ID: sg, + }) + } + } + if n.CustomLT.SecurityGroups != nil { + ltSG := n.CustomLT.SecurityGroups + for _, sg := range ltSG { + sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ + Name: sg, + }) + } + } + if n.CustomLT.NetworkInterfaces != nil { + for _, eni := range n.CustomLT.NetworkInterfaces { + if eni.Groups != nil { + for _, sg := range eni.Groups { + sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ + ID: sg, + }) + } + } + } + } + } + + // Use clusterTag as SecurityGroupSelector if no SGs found + if len(sgTerms) == 0 { + sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ + Tags: ClusterTag, + }) + } + return sgTerms +} + +// Returns UserData for nodegroup if Custom Launch Template is used with MNG +func (n NodeGroup) UserData() *string { + if n.CustomLT != nil && n.CustomLT.UserData != nil { + decodedUserData, _ := base64.StdEncoding.DecodeString(*n.CustomLT.UserData) + return lo.ToPtr(string(decodedUserData)) + } + return nil +} + +// Returns AWS Karpenter BlockDeviceMappings for nodegroup if Custom Launch Template is used with MNG or Custom DiskSize is configured +func (n NodeGroup) BlockDeviceMappings() []*awskarpenter.BlockDeviceMapping { + mappings := []*awskarpenter.BlockDeviceMapping{} + var diskSize *k8sapiresource.Quantity + + if n.CustomLT != nil { + for _, mapping := range n.CustomLT.BlockDeviceMappings { + if mapping.Ebs != nil { + bMap := &awskarpenter.BlockDeviceMapping{ + DeviceName: lo.EmptyableToPtr(lo.FromPtr(mapping.DeviceName)), + EBS: &awskarpenter.BlockDevice{ + VolumeSize: k8sapiresource.NewQuantity(int64(*mapping.Ebs.VolumeSize)*GiB, k8sapiresource.BinarySI), + VolumeType: lo.ToPtr(string(mapping.Ebs.VolumeType)), + DeleteOnTermination: mapping.Ebs.DeleteOnTermination, + Encrypted: mapping.Ebs.Encrypted, + IOPS: lo.EmptyableToPtr(int64(lo.FromPtr(mapping.Ebs.Iops))), + KMSKeyID: mapping.Ebs.KmsKeyId, + SnapshotID: mapping.Ebs.SnapshotId, + Throughput: lo.EmptyableToPtr(int64(lo.FromPtr(mapping.Ebs.Throughput))), + }, + } + mappings = append(mappings, bMap) + } + } + return mappings + } + + // Managed Node Group DiskSize - https: //docs.aws.amazon.com/eks/latest/APIReference/API_CreateNodegroup.html#AmazonEKS-CreateNodegroup-request-diskSize + if *n.DiskSize != ALAndBottleRocketDefaultDiskSize && (n.AMIFamily() == &awskarpenter.AMIFamilyAL2 || n.AMIFamily() == &awskarpenter.AMIFamilyAL2023 || n.AMIFamily() == &awskarpenter.AMIFamilyBottlerocket) { + diskSize = k8sapiresource.NewQuantity(int64(*n.DiskSize)*GiB, k8sapiresource.BinarySI) + } + if *n.DiskSize != WindowsDefaultDiskSize && (n.AMIFamily() == &awskarpenter.AMIFamilyWindows2019 || n.AMIFamily() == &awskarpenter.AMIFamilyWindows2022) { + diskSize = k8sapiresource.NewQuantity(int64(*n.DiskSize)*GiB, k8sapiresource.BinarySI) + } + + // Update the diskSize in default mappings if value is Set + if diskSize != nil { + amiFamily := awskarpenterprovider.GetAMIFamily(n.AMIFamily(), &awskarpenterprovider.Options{}) + mappings = amiFamily.DefaultBlockDeviceMappings() + for _, mapping := range mappings { + mapping.EBS.VolumeSize = k8sapiresource.NewQuantity(int64(*n.DiskSize)*GiB, k8sapiresource.BinarySI) + } + } + return mappings +} + +func (n NodeGroup) MetadataOptions() *awskarpenter.MetadataOptions { + if n.CustomLT != nil && n.CustomLT.MetadataOptions != nil { + metaOptions := &awskarpenter.MetadataOptions{ + HTTPEndpoint: lo.EmptyableToPtr(string(lo.FromPtr(&n.CustomLT.MetadataOptions.HttpEndpoint))), + HTTPPutResponseHopLimit: lo.EmptyableToPtr(int64(lo.FromPtr(n.LT.MetadataOptions.HttpPutResponseHopLimit))), + HTTPTokens: lo.EmptyableToPtr(string(lo.FromPtr(&n.CustomLT.MetadataOptions.HttpTokens))), + HTTPProtocolIPv6: lo.EmptyableToPtr(string(lo.FromPtr(&n.LT.MetadataOptions.HttpProtocolIpv6))), + } + return metaOptions + } + return nil +} diff --git a/pkg/karpenteraws/generate.go b/pkg/karpenteraws/generate.go new file mode 100644 index 0000000..9c03048 --- /dev/null +++ b/pkg/karpenteraws/generate.go @@ -0,0 +1,81 @@ +package karpenteraws + +import ( + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" + awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" + "github.com/punkwalker/karpenter-generate/pkg/aws" + sigkarpenter "sigs.k8s.io/karpenter/pkg/apis/v1beta1" +) + +type NodeGroup struct { + *ekstypes.Nodegroup + LT *ec2types.ResponseLaunchTemplateData // LT Generated by MNG and used by ASG (needed for MetadataOptions) + CustomLT *ec2types.ResponseLaunchTemplateData // Custom LT provided to MNG +} + +func Generate(nodeGroups *[]ekstypes.Nodegroup) ([]sigkarpenter.NodePool, []awskarpenter.EC2NodeClass, error) { + nodePools := make([]sigkarpenter.NodePool, 0, len(*nodeGroups)) + nodeClasses := make([]awskarpenter.EC2NodeClass, 0, len(*nodeGroups)) + + for _, ng := range *nodeGroups { + nodegroup, err := NewNodeGroup(ng) + if err != nil { + return nil, nil, err + } + + ec2Class, err := nodegroup.GetEC2NodeClass() + if err != nil { + return nil, nil, err + } + nodeClasses = append(nodeClasses, ec2Class) + + nodePool, err := nodegroup.GetNodePool() + if err != nil { + return nil, nil, err + } + nodePools = append(nodePools, nodePool) + } + + return nodePools, nodeClasses, nil +} + +func NewNodeGroup(ng ekstypes.Nodegroup) (*NodeGroup, error) { + + newNodegroup := NodeGroup{ + Nodegroup: &ng, + } + + ClusterTag = map[string]string{ + ClusterTagKey + *ng.ClusterName: "owned", + } + + ec2Client := aws.NewEC2Client() + asgClient := aws.NewAutoscalingClient() + + if ng.LaunchTemplate != nil { + customLT, err := ec2Client.DescribeLaunchTemplateVersions( + *ng.LaunchTemplate.Id, + *ng.LaunchTemplate.Version) + if err != nil { + return nil, aws.FormatErrorAsMessageOnly(err) + } + newNodegroup.CustomLT = customLT[0].LaunchTemplateData + } + + asg, err := asgClient.DescribeAutoScalingGroups(*ng.Resources.AutoScalingGroups[0].Name) + if err != nil { + return nil, aws.FormatErrorAsMessageOnly(err) + } + + lt, err := ec2Client.DescribeLaunchTemplateVersions( + *asg[0].MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.LaunchTemplateId, + *asg[0].MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification.Version) + if err != nil { + return nil, aws.FormatErrorAsMessageOnly(err) + } + + newNodegroup.LT = lt[0].LaunchTemplateData + + return &newNodegroup, nil +} diff --git a/pkg/karpenter/nodepool.go b/pkg/karpenteraws/nodepool.go similarity index 96% rename from pkg/karpenter/nodepool.go rename to pkg/karpenteraws/nodepool.go index b75958a..e5e352a 100644 --- a/pkg/karpenter/nodepool.go +++ b/pkg/karpenteraws/nodepool.go @@ -1,6 +1,7 @@ -package karpenter +package karpenteraws import ( + "context" "strconv" "strings" "time" @@ -21,11 +22,16 @@ var ( ) func (n *NodeGroup) GetNodePool() (sigkarpenter.NodePool, error) { - return sigkarpenter.NodePool{ + np := sigkarpenter.NodePool{ TypeMeta: NodePoolTypeMeta, ObjectMeta: n.NodePoolObjectMeta(), Spec: n.NodePoolSpec(), - }, nil + } + + if err := np.Validate(context.TODO()); err != nil { + return sigkarpenter.NodePool{}, err + } + return np, nil } func (n NodeGroup) NodePoolObjectMeta() metav1.ObjectMeta { diff --git a/pkg/options/options.go b/pkg/options/options.go index 76c28d3..85b59ea 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -1,17 +1,19 @@ package options import ( - "github.com/spf13/cobra" + "fmt" - "github.com/punkwalker/karpenter-generate/pkg/k8s" + "github.com/spf13/cobra" ) type Options struct { - ClusterName string - NodegroupName string - Profile string - Region string - Debug bool + ClusterName string + NodegroupName string + KarpenterNodegroupName string + Profile string + Region string + Output string + Debug bool } func New(cmd *cobra.Command) *Options { @@ -19,18 +21,26 @@ func New(cmd *cobra.Command) *Options { cmd.Flags().StringVar(&opts.Profile, "profile", "", "use the specific profile from your credential file") cmd.Flags().StringVar(&opts.Region, "region", "", "the region to use, overrides config/env settings") cmd.Flags().BoolVar(&opts.Debug, "debug", opts.Debug, "") - _ = cmd.Flags().MarkHidden("debug") cmd.Flags().StringVar(&opts.ClusterName, "cluster", "", "name of the EKS cluster") cmd.Flags().StringVar(&opts.NodegroupName, "nodegroup", "", "name of the EKS managed nodegroup") + cmd.Flags().StringVar(&opts.KarpenterNodegroupName, "karpenter-nodegroup", "", "name of the EKS managed nodegroup running Karpenter deployment") + cmd.Flags().StringVarP(&opts.Output, "output", "o", "yaml", "name of the EKS managed nodegroup running Karpenter deployment") + _ = cmd.MarkFlagRequired("cluster") + _ = cmd.MarkFlagRequired("karpenter-nodegroup") + _ = cmd.Flags().MarkHidden("debug") cmd.SetHelpFunc(usage) return &opts } -func (o *Options) Parse() { - if o.ClusterName == "" && o.Region == "" { - o.ClusterName, o.Region, o.Profile = k8s.OptionsFromConfig() +func (o *Options) Parse() error { + if o.ClusterName == "" { + return fmt.Errorf(`specify value for "--cluster" flag (e.g.: karpenter-generate --cluster --karpenter-nodegroup )`) + } + if o.KarpenterNodegroupName == "" { + return fmt.Errorf(`specify value for "--karpenter-nodegroup" flag (e.g.: karpenter-generate --cluster --karpenter-nodegroup )`) } + return nil } func usage(cmd *cobra.Command, _ []string) { @@ -40,21 +50,24 @@ Description: Nodepools and EC2NodeClass from details of EKS Managed Nodegroup Usage: - karpenter-generate [command] - karpenter-generate [flags] + karpenter-generate --cluster --karpenter-nodegroup Available Commands: version Print the version and build information for karpenter-generate -Optional Flags: - --cluster string name of the EKS cluster - (default: from kubeconfig current-context) +Flags: + --cluster string name of the EKS cluster + --karpenter-nodegroup string name of the EKS managed nodegroup running Karpenter deployment or fargate + +Optiona Flags: --nodegroup string name of the EKS managed nodegroup - (default: all the nodegroups) - --region string the region to use, overrides config/env settings - (default: from kubeconfig current-context or AWS config) + (default: all the nodegroups expectthe one running Karpenter) + --region string region of EKS cluster, overrides AWS CLI configuration/ENV values + (default: AWS CLI configuration) --profile string use the specific profile from your credential file - (default: from kubeconfig current-context or AWS config) + (default: AWS CLI configuration) + --output string output format (yaml or json) + (default: yaml) -h, --help help for karpenter-generate ` cmd.Println(usageString) diff --git a/pkg/printers/printers.go b/pkg/printers/printers.go new file mode 100644 index 0000000..6dc247b --- /dev/null +++ b/pkg/printers/printers.go @@ -0,0 +1,49 @@ +package printers + +import ( + "fmt" + "os" + + awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" + "k8s.io/cli-runtime/pkg/printers" + sigkarpenter "sigs.k8s.io/karpenter/pkg/apis/v1beta1" +) + +type Output string + +type OutputPrinter struct { + printers.ResourcePrinter +} + +const ( + YAML Output = "yaml" + JSON Output = "json" +) + +func NewPrinter(format Output) (printers.ResourcePrinter, error) { + switch format { + case YAML: + return &printers.YAMLPrinter{}, nil + case JSON: + return &printers.JSONPrinter{}, nil + default: + return nil, fmt.Errorf(`invalid output type, valid values are "yaml" or "json"`) + } +} + +func Print(p printers.ResourcePrinter, nodePools []sigkarpenter.NodePool, nodeClasses []awskarpenter.EC2NodeClass) error { + if len(nodePools) != len(nodeClasses) { + return fmt.Errorf("no. of nodepools is not equal to no. of nodeclass") + } + for idx := range len(nodePools) { + err := p.PrintObj(&nodePools[idx], os.Stdout) + if err != nil { + return err + } + err = p.PrintObj(&nodeClasses[idx], os.Stdout) + if err != nil { + return err + } + } + return nil +}