├── main.go ├── docs ├── uninstall.md ├── contributing.md ├── index.md ├── usage.md └── install.md ├── Makefile ├── .github ├── dependabot.yml ├── workflows │ ├── docs.yml │ └── release.yml └── FUNDING.yml ├── template.yaml ├── cmd ├── version.go ├── root.go ├── irsa.go ├── ssm.go ├── fargate.go ├── nodegroups.go ├── addons.go ├── ami-suggest.go ├── nodes.go ├── kubeconfig.go └── logs.go ├── mkdocs.yml ├── pkg ├── aws │ └── aws.go └── kube │ ├── clientset.go │ ├── labels.go │ ├── kubeconfig.go │ ├── nodes.go │ └── utils.go ├── .goreleaser.yml ├── update.sh ├── README.md ├── .krew.yaml ├── plugins └── kubectl-eks.yaml ├── go.mod ├── LICENSE └── go.sum /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/surajincloud/kubectl-eks/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /docs/uninstall.md: -------------------------------------------------------------------------------- 1 | # Uninstallation 2 | 3 | You can uninstall `kubectl-eks`as shown below, 4 | 5 | ``` 6 | $ kubectl krew uninstall eks 7 | Uninstalled plugin: eks 8 | ``` 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES := $(shell find . -name '*.go') 2 | BINARY := kubectl-eks 3 | 4 | build: kubectl-eks 5 | 6 | clean: 7 | @rm -rf $(BINARY) 8 | 9 | $(BINARY): $(SOURCES) 10 | CGO_ENABLED=0 go build -o $(BINARY) -ldflags="-s -w" main.go 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: "gomod" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you want to contribute to this project in following ways: 4 | 5 | * Try it out and raise an Github issue [here](https://github.com/surajincloud/kubectl-eks/issues) if, 6 | - you find any bug/issue 7 | - you have any feature request 8 | 9 | * Feel free to raise [Pull request](https://github.com/surajincloud/kubectl-eks/pulls) to fix the issue/bug or for new feature. 10 | 11 | Thank you :) -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: krew.googlecontainertools.github.com/v1alpha2 2 | kind: Plugin 3 | metadata: 4 | name: eks 5 | spec: 6 | version: v{{version}} 7 | homepage: https://github.com/surajincloud/kubectl-eks 8 | shortDescription: Kubectl plugin for Amazon EKS 9 | description: | 10 | kubectl-eks plugin provides a set of commands for users which will simplify operations while interacting with Amazon EKS clusters. 11 | platforms: 12 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | `kubectl-eks` is kubectl plugin for amazon EKS. 4 | 5 | you can check out announcement blog post [here](https://surajincloud.com/announcing-kubectl-eks-plugin-v0-1-0). 6 | 7 | ## Installation 8 | 9 | Check out install page for [installation](./install) instructions. 10 | 11 | ## Usage 12 | 13 | Check out Usage page for [Usage](./usage) instructions. 14 | 15 | ## Contributing 16 | 17 | Check out Contributing page for [Contributing](./contributing) instructions. 18 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // versionCmd represents the version command 10 | var versionCmd = &cobra.Command{ 11 | Use: "version", 12 | Short: "Print the version of kubectl-eks", 13 | Long: "Print the version of kubectl-eks", 14 | RunE: version, 15 | } 16 | 17 | func version(cmd *cobra.Command, args []string) error { 18 | fmt.Println("v0.4.4") 19 | return nil 20 | } 21 | 22 | func init() { 23 | rootCmd.AddCommand(versionCmd) 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: docs-site 3 | on: 4 | push: 5 | branches: 6 | - main 7 | permissions: 8 | contents: write 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v4 15 | with: 16 | python-version: 3.x 17 | - uses: actions/cache@v3 18 | with: 19 | key: ${{ github.ref }} 20 | path: .cache 21 | - run: pip install mkdocs-material 22 | - run: mkdocs gh-deploy --force 23 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: kubectl-eks 2 | site_url: https://surajincloud.github.io/kubectl-eks/ 3 | site_author: Suraj Narwade 4 | theme: 5 | name: material 6 | palette: 7 | - scheme: default 8 | primary: indigo 9 | accent: indigo 10 | toggle: 11 | icon: material/brightness-7 12 | name: Switch to dark mode 13 | - scheme: slate 14 | primary: indigo 15 | accent: indigo 16 | toggle: 17 | icon: material/brightness-4 18 | name: Switch to light mode 19 | 20 | repo_name: surajincloud/kubectl-eks 21 | repo_url: https://github.com/surajincloud/kubectl-eks -------------------------------------------------------------------------------- /pkg/aws/aws.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/aws" 7 | "github.com/aws/aws-sdk-go-v2/config" 8 | "github.com/surajincloud/kubectl-eks/pkg/kube" 9 | ) 10 | 11 | func GetAWSConfig(ctx context.Context, region string) (aws.Config, error) { 12 | cfg, err := config.LoadDefaultConfig(ctx) 13 | if err != nil { 14 | return aws.Config{}, err 15 | } 16 | if cfg.Region == "" { 17 | // get region 18 | region, err = kube.GetRegion(region) 19 | if err != nil { 20 | return aws.Config{}, err 21 | } 22 | cfg.Region = region 23 | } 24 | return cfg, nil 25 | } 26 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | release: 2 | 3 | before: 4 | hooks: 5 | - go mod tidy 6 | 7 | builds: 8 | - binary: kubectl-eks 9 | main: ./main.go 10 | goos: 11 | - linux 12 | - darwin 13 | - windows 14 | goarch: 15 | - amd64 16 | - arm64 17 | targets: 18 | - linux_amd64 19 | - linux_arm64 20 | - windows_amd64 21 | - darwin_amd64 22 | - darwin_arm64 23 | env: 24 | - CGO_ENABLED=0 25 | 26 | brews: 27 | - name: kubectl-eks 28 | homepage: https://github.com/surajincloud/homebrew-tools 29 | tap: 30 | owner: surajincloud 31 | name: homebrew-tools 32 | 33 | checksum: 34 | name_template: 'checksums.txt' 35 | -------------------------------------------------------------------------------- /pkg/kube/clientset.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "fmt" 5 | 6 | "k8s.io/cli-runtime/pkg/genericclioptions" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | // ClientSet k8s clientset 11 | func ClientSet(configFlags *genericclioptions.ConfigFlags) (*kubernetes.Clientset, string) { 12 | namespace := "" 13 | if configFlags.Namespace != nil { 14 | namespace = *configFlags.Namespace 15 | } 16 | config, err := configFlags.ToRESTConfig() 17 | if err != nil { 18 | panic("kube config load error") 19 | } 20 | clientSet, err := kubernetes.NewForConfig(config) 21 | if err != nil { 22 | fmt.Println("Error generating Kubernetes configuration", err) 23 | } 24 | return clientSet, namespace 25 | } 26 | -------------------------------------------------------------------------------- /pkg/kube/labels.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | const ( 4 | // capacity Type 5 | CapacityTypeLabel = "eks.amazonaws.com/capacityType" 6 | KarpenterCapacityTypeLabel = "karpenter.sh/capacity-type" 7 | 8 | NodeGroupLabel = "eks.amazonaws.com/nodegroup" 9 | ComputeType = "eks.amazonaws.com/compute-type" // to detect fargate nodes 10 | ArchLabel = "kubernetes.io/arch" 11 | OsLabel = "kubernetes.io/os" 12 | HostNameLabel = "kubernetes.io/hostname" 13 | 14 | InstanceTypeLabel = "node.kubernetes.io/instance-type" 15 | 16 | ZoneLabel = "topology.kubernetes.io/zone" 17 | 18 | // Ami ID 19 | NodeGroupImage = "eks.amazonaws.com/nodegroup-image" 20 | KarpenterImage = "karpenter.k8s.aws/instance-ami-id" 21 | ) 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: kubectl-eks-release 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | jobs: 8 | release_job: 9 | runs-on: ubuntu-latest 10 | name: goreleaser 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set up Go 17 | uses: actions/setup-go@v4 18 | with: 19 | go-version: '^1.18' 20 | - name: GoReleaser 21 | uses: goreleaser/goreleaser-action@v5 22 | with: 23 | distribution: goreleaser 24 | version: latest 25 | args: release --rm-dist 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GO_RELEASER }} 28 | - name: Update new version in krew-index 29 | uses: rajatjindal/krew-release-bot@v0.0.46 30 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: surajincloud # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /pkg/kube/kubeconfig.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "k8s.io/client-go/tools/clientcmd" 5 | "k8s.io/client-go/tools/clientcmd/api" 6 | ) 7 | 8 | func getClusterFromKubeconfig() (string, error) { 9 | config, err := ReadKubeconfig() 10 | if err != nil { 11 | return "", err 12 | } 13 | // Getting the current context 14 | currentContext := config.CurrentContext 15 | context := config.Contexts[currentContext] 16 | 17 | return context.Cluster, nil 18 | } 19 | 20 | func ReadKubeconfig() (api.Config, error) { 21 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 22 | configOverrides := &clientcmd.ConfigOverrides{} 23 | 24 | clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) 25 | 26 | rawConfig, err := clientConfig.RawConfig() 27 | if err != nil { 28 | return api.Config{}, err 29 | } 30 | return rawConfig, nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/kube/nodes.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/cli-runtime/pkg/genericclioptions" 9 | ) 10 | 11 | func GetNodes(configFlags *genericclioptions.ConfigFlags) ([]corev1.Node, error) { 12 | clientSet, _ := ClientSet(configFlags) 13 | nodeList, err := clientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) 14 | if err != nil { 15 | return []corev1.Node{}, err 16 | } 17 | return nodeList.Items, nil 18 | } 19 | 20 | func GetSA(configFlags *genericclioptions.ConfigFlags) ([]corev1.ServiceAccount, error) { 21 | clientSet, namespace := ClientSet(configFlags) 22 | nodeList, err := clientSet.CoreV1().ServiceAccounts(namespace).List(context.Background(), metav1.ListOptions{}) 23 | if err != nil { 24 | return []corev1.ServiceAccount{}, err 25 | } 26 | return nodeList.Items, nil 27 | } 28 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="$1" 4 | 5 | url="https://github.com/surajincloud/kubectl-eks/releases/download/v${version}/checksums.txt" 6 | 7 | echo "downloaded file" 8 | echo $url 9 | 10 | # Download the file 11 | wget -q "$url" -O checksums.txt 12 | 13 | output="plugins/kubectl-eks.yaml" 14 | 15 | template="template.yaml" 16 | 17 | sed "s/{{version}}/$version/g" "$template" > "$output" 18 | 19 | while IFS=" " read -r sha256 filename; do 20 | os_arch=$(echo "$filename" | cut -d "_" -f 3,4 | cut -d "." -f 1) 21 | os=$(echo "$os_arch" | cut -d "_" -f 1) 22 | arch=$(echo "$os_arch" | cut -d "_" -f 2) 23 | bin=$(echo "$filename" | cut -d "_" -f 1) 24 | 25 | yaml_entry="\ 26 | - selector: 27 | matchLabels: 28 | os: $os 29 | arch: $arch 30 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v${version}/${filename} 31 | sha256: $sha256 32 | bin: $bin" 33 | 34 | echo "$yaml_entry" >> "$output" 35 | done < checksums.txt 36 | 37 | rm checksums.txt 38 | 39 | echo "Generated YAML content in $output" 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubectl EKS Plugin 2 | 3 | `kubectl-eks` is kubectl plugin for amazon EKS 4 | 5 | Check out docs website for more information: https://surajincloud.github.io/kubectl-eks/ 6 | 7 | ## Installation 8 | 9 | ### Using Krew 10 | 11 | * You can add custom index as shown below and install the plugin from there. We are planning to submit this plugin to official Krew index as well, you can track the progress [here](https://github.com/surajincloud/kubectl-eks/issues/3). 12 | 13 | ``` 14 | kubectl krew index add surajincloud https://github.com/surajincloud/kubectl-eks.git 15 | kubectl krew search eks 16 | kubectl krew install surajincloud/kubectl-eks 17 | ``` 18 | 19 | ### Using Brew 20 | 21 | ``` 22 | brew tap surajincloud/tools 23 | brew install kubectl-eks 24 | ``` 25 | 26 | ### Download the binary 27 | 28 | * Download the binary from the [release pages](https://github.com/surajincloud/kubectl-eks/releases). 29 | * Place it into any of the location from the PATH. 30 | 31 | ### Build from Source 32 | 33 | ``` 34 | git clone https://github.com/surajincloud/kubectl-eks 35 | cd kubectl-eks 36 | make 37 | ``` 38 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | "k8s.io/cli-runtime/pkg/genericclioptions" 8 | ) 9 | 10 | var KubernetesConfigFlags *genericclioptions.ConfigFlags 11 | 12 | // rootCmd represents the base command when called without any subcommands 13 | var rootCmd = &cobra.Command{ 14 | Use: "kubectl-eks", 15 | Short: "A kubectl plugin for Amazon EKS", 16 | Long: `A kubectl plugin for Amazon EKS`, 17 | // Uncomment the following line if your bare application 18 | // has an action associated with it: 19 | // Run: func(cmd *cobra.Command, args []string) { }, 20 | } 21 | 22 | // Execute adds all child commands to the root command and sets flags appropriately. 23 | // This is called by main.main(). It only needs to happen once to the rootCmd. 24 | func Execute() { 25 | err := rootCmd.Execute() 26 | if err != nil { 27 | os.Exit(1) 28 | } 29 | } 30 | 31 | func init() { 32 | KubernetesConfigFlags = genericclioptions.NewConfigFlags(true) 33 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 34 | KubernetesConfigFlags.AddFlags(rootCmd.PersistentFlags()) 35 | } 36 | -------------------------------------------------------------------------------- /cmd/irsa.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "text/tabwriter" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/surajincloud/kubectl-eks/pkg/kube" 10 | ) 11 | 12 | // irsaCmd represents the irsa command 13 | var irsaCmd = &cobra.Command{ 14 | Use: "irsa", 15 | Short: "List Serviceaccounts with their IRSA information", 16 | Long: "List Serviceaccounts with their IRSA information", 17 | RunE: irsa, 18 | } 19 | 20 | func irsa(cmd *cobra.Command, args []string) error { 21 | 22 | saList, err := kube.GetSA(KubernetesConfigFlags) 23 | if err != nil { 24 | return err 25 | } 26 | w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) 27 | defer w.Flush() 28 | fmt.Fprintln(w, "NAMESPACE", "\t", "SERVICEACCOUNT", "\t", "IAM-ROLE", "\t", "TOKEN-EXPIRATION") 29 | for _, i := range saList { 30 | if i.Annotations["eks.amazonaws.com/role-arn"] != "" { 31 | fmt.Fprintln(w, i.Namespace, "\t", i.Name, "\t", i.Annotations["eks.amazonaws.com/role-arn"], "\t", i.Annotations["eks.amazonaws.com/token-expiration"]) 32 | } 33 | } 34 | return nil 35 | } 36 | func init() { 37 | rootCmd.AddCommand(irsaCmd) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/kube/utils.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "time" 8 | 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | 11 | "k8s.io/apimachinery/pkg/util/duration" 12 | ) 13 | 14 | func GetClusterName(clusterName string) (string, error) { 15 | if clusterName == "" { 16 | clusterName = os.Getenv("AWS_EKS_CLUSTER") 17 | if clusterName == "" { 18 | clusterName, err := getClusterFromKubeconfig() 19 | if err != nil { 20 | return "", err 21 | } 22 | // if clusterName is ARN 23 | if strings.HasPrefix(clusterName, "arn") { 24 | return strings.Split(clusterName, "/")[len(strings.Split(clusterName, "/"))-1], nil 25 | } 26 | return clusterName, nil 27 | } 28 | return clusterName, nil 29 | } 30 | return clusterName, nil 31 | } 32 | 33 | func GetRegion(region string) (string, error) { 34 | if region == "" { 35 | region = os.Getenv("AWS_REGION") 36 | if region == "" { 37 | return "", fmt.Errorf("please pass region name with --region or with AWS_REGION environment variable") 38 | } 39 | return region, nil 40 | } 41 | return region, nil 42 | } 43 | 44 | func GetAge(creationStamp metav1.Time) string { 45 | 46 | currentTime := time.Now() 47 | diff := currentTime.Sub(creationStamp.Time) 48 | return duration.HumanDuration(diff) 49 | } 50 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | > Note: `kubectl-eks` is able to read `region` from aws credentials file, aws profile and environment variable, optionally you can pass it via `--region` flag. it is also able to read cluster name from the kubeconfig context, optionally you can pass it via `--cluster` flag or `AWS_EKS_CLUSTER` environment variable. 4 | 5 | ## Creates Kubeconfig 6 | 7 | ``` 8 | kubectl eks kubeconfig --cluster your-cluster --region your-region --out 9 | ``` 10 | 11 | ## Updates existing Kubeconfig 12 | 13 | * fetch and update existing kubeconfig (~/.kube/config) 14 | 15 | ``` 16 | kubectl eks kubeconfig --cluster your-cluster --region your-region 17 | ``` 18 | 19 | ## List addons 20 | 21 | ``` 22 | kubectl eks addons --cluster your-cluster --region your-region 23 | ``` 24 | 25 | ## List serviceaccount with IRSA information from all namespaces 26 | 27 | ``` 28 | kubectl eks irsa 29 | ``` 30 | 31 | ## List serviceaccount with IRSA information from given namespace 32 | 33 | ``` 34 | kubectl eks irsa -n app-staging 35 | ``` 36 | 37 | ## List nodes but get more information 38 | 39 | ``` 40 | kubectl eks nodes 41 | ``` 42 | 43 | ## List nodegroups but get more information 44 | 45 | ``` 46 | kubectl eks nodegroups 47 | ``` 48 | 49 | ## Access to EKS node via SSM 50 | 51 | ``` 52 | kubectl eks ssm 53 | ``` 54 | 55 | **Note**: above command will only work if node IAM role has predefined IAM policy AmazonSSMManagedInstanceCore policy attached. Click here for more reference. 56 | 57 | ## List fargate profiles 58 | 59 | ``` 60 | kubectl eks fargate --cluster your-cluster --region your-region 61 | ``` 62 | 63 | ## Suggest an AMI 64 | 65 | ``` 66 | kubectl eks suggest-ami 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ### Using Krew 4 | 5 | * You can add custom index as shown below and install the plugin from there. We are planning to submit this plugin to official Krew index as well, you can track the progress [here](https://github.com/surajincloud/kubectl-eks/issues/3). 6 | 7 | ``` 8 | kubectl krew index add surajincloud git@github.com:surajincloud/kubectl-eks.git 9 | kubectl krew search eks 10 | kubectl krew install surajincloud/kubectl-eks 11 | ``` 12 | 13 | 14 | ## Install from Source 15 | 16 | ``` 17 | git clone https://github.com/surajincloud/kubectl-eks 18 | cd kubectl-eks 19 | make 20 | ``` 21 | 22 | ## Install from Releases 23 | 24 | * Download latest binary from [release page](https://github.com/surajincloud/kubectl-eks/releases). 25 | 26 | * make it executable 27 | 28 | ``` 29 | chmod +x kubectl-eks 30 | ``` 31 | 32 | * move it to one of the location from the PATH 33 | 34 | ``` 35 | mv kubectl-eks ~/.local/bin 36 | ``` 37 | 38 | ## Install using Brew 39 | 40 | ``` 41 | brew tap surajincloud/tools 42 | brew install kubectl-eks 43 | ``` 44 | 45 | ## Verify Installation 46 | 47 | * Verify the installation by running the following command. 48 | 49 | ``` 50 | $ kubectl eks --help 51 | A kubectl plugin for Amazon EKS 52 | 53 | Usage: 54 | kubectl-eks [command] 55 | 56 | Available Commands: 57 | addons A brief description of your command 58 | completion Generate the autocompletion script for the specified shell 59 | help Help about any command 60 | irsa A brief description of your command 61 | nodes List all EKS Nodes 62 | ssm Access given EKS node via SSM 63 | version Print the version of kubectl-eks 64 | ... 65 | ... 66 | ``` 67 | -------------------------------------------------------------------------------- /cmd/ssm.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "strings" 8 | 9 | "github.com/spf13/cobra" 10 | "github.com/surajincloud/kubectl-eks/pkg/kube" 11 | 12 | "github.com/mmmorris1975/ssm-session-client/ssmclient" 13 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 14 | ) 15 | 16 | // ssmCmd represents the ssm command 17 | var ssmCmd = &cobra.Command{ 18 | Use: "ssm [flags] node", 19 | Args: cobra.ExactArgs(1), 20 | Short: "Access given EKS node via SSM", 21 | Long: ` 22 | SSM Access to given EKS Node 23 | IAM Roles needs to be attached to given EKS Node 24 | Check docs: https://surajincloud.github.io/kubectl-eks/usage/#access-to-eks-node-via-ssm`, 25 | RunE: performSSM, 26 | } 27 | 28 | func performSSM(cmd *cobra.Command, args []string) error { 29 | ctx := context.Background() 30 | 31 | nodeList, err := kube.GetNodes(KubernetesConfigFlags) 32 | if err != nil { 33 | return err 34 | } 35 | var givenNode, region string 36 | for _, i := range nodeList { 37 | if i.Name == args[0] { 38 | // https://github.com/aws/containers-roadmap/issues/1395 39 | str := strings.Split(i.Spec.ProviderID, "/") 40 | givenNode = str[len(str)-1] 41 | region = i.Labels["topology.kubernetes.io/region"] 42 | } 43 | } 44 | fmt.Printf("SSM into node %v in region %v\n", givenNode, region) 45 | 46 | target := givenNode 47 | 48 | // aws config 49 | cfg, err := awspkg.GetAWSConfig(ctx, region) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | // A 3rd argument can be passed to specify a command to run before turning the shell over to the user 55 | log.Fatal(ssmclient.ShellPluginSession(cfg, target)) 56 | return nil 57 | 58 | } 59 | 60 | func init() { 61 | rootCmd.AddCommand(ssmCmd) 62 | } 63 | -------------------------------------------------------------------------------- /.krew.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: krew.googlecontainertools.github.com/v1alpha2 2 | kind: Plugin 3 | metadata: 4 | name: eks 5 | spec: 6 | version: {{ .TagName }} 7 | homepage: https://github.com/surajincloud/kubectl-eks 8 | shortDescription: Kubectl plugin for Amazon EKS 9 | description: | 10 | kubectl-eks plugin provides set of commands for users which will simplify operations for users while interacting with Amazon EKS clusters. 11 | platforms: 12 | - selector: 13 | matchLabels: 14 | os: darwin 15 | arch: amd64 16 | {{addURIAndSha "https://github.com/surajincloud/kubectl-eks/releases/download/{{ .TagName }}/kubectl-eks_{{ .TagName }}_darwin_amd64.tar.gz" .TagName }} 17 | bin: kubectl-eks 18 | - selector: 19 | matchLabels: 20 | os: darwin 21 | arch: arm64 22 | {{addURIAndSha "https://github.com/surajincloud/kubectl-eks/releases/download/{{ .TagName }}/kubectl-eks_{{ .TagName }}_darwin_arm64.tar.gz" .TagName }} 23 | bin: kubectl-eks 24 | - selector: 25 | matchLabels: 26 | os: linux 27 | arch: amd64 28 | {{addURIAndSha "https://github.com/surajincloud/kubectl-eks/releases/download/{{ .TagName }}/kubectl-eks_{{ .TagName }}_linux_amd64.tar.gz" .TagName }} 29 | bin: kubectl-eks 30 | - selector: 31 | matchLabels: 32 | os: linux 33 | arch: arm64 34 | {{addURIAndSha "https://github.com/surajincloud/kubectl-eks/releases/download/{{ .TagName }}/kubectl-eks_{{ .TagName }}_linux_arm64.tar.gz" .TagName }} 35 | bin: kubectl-eks 36 | - selector: 37 | matchLabels: 38 | os: windows 39 | arch: amd64 40 | {{addURIAndSha "https://github.com/surajincloud/kubectl-eks/releases/download/{{ .TagName }}/kubectl-eks_{{ .TagName }}_windows_amd64.tar.gz" .TagName }} 41 | bin: kubectl-eks.exe 42 | -------------------------------------------------------------------------------- /plugins/kubectl-eks.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: krew.googlecontainertools.github.com/v1alpha2 2 | kind: Plugin 3 | metadata: 4 | name: eks 5 | spec: 6 | version: v0.4.5 7 | homepage: https://github.com/surajincloud/kubectl-eks 8 | shortDescription: Kubectl plugin for Amazon EKS 9 | description: | 10 | kubectl-eks plugin provides a set of commands for users which will simplify operations while interacting with Amazon EKS clusters. 11 | platforms: 12 | - selector: 13 | matchLabels: 14 | os: darwin 15 | arch: amd64 16 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v0.4.5/kubectl-eks_0.4.5_darwin_amd64.tar.gz 17 | sha256: 9e6b6768c3f507c0a0090bf27251b3bcf097a6be068825c4b5464a9b0dca040f 18 | bin: kubectl-eks 19 | - selector: 20 | matchLabels: 21 | os: darwin 22 | arch: arm64 23 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v0.4.5/kubectl-eks_0.4.5_darwin_arm64.tar.gz 24 | sha256: 87b3ba7768f40886aae3d86a41287e66aa163083c20dce6cff196b1c2defe0c7 25 | bin: kubectl-eks 26 | - selector: 27 | matchLabels: 28 | os: linux 29 | arch: amd64 30 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v0.4.5/kubectl-eks_0.4.5_linux_amd64.tar.gz 31 | sha256: 9ada15b93704e873aa8abe6bdc642e89ea7c327cd5aab8297678c614b001a003 32 | bin: kubectl-eks 33 | - selector: 34 | matchLabels: 35 | os: linux 36 | arch: arm64 37 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v0.4.5/kubectl-eks_0.4.5_linux_arm64.tar.gz 38 | sha256: 0102f8226f73c23fe3335b6faf2d32135a4c8b56279144a029d7bde86d604b02 39 | bin: kubectl-eks 40 | - selector: 41 | matchLabels: 42 | os: windows 43 | arch: amd64 44 | uri: https://github.com/surajincloud/kubectl-eks/releases/download/v0.4.5/kubectl-eks_0.4.5_windows_amd64.tar.gz 45 | sha256: b8ce0e46f623f3f8b9969eedeee707a4f5db1678cf31bcffb91446a71860648d 46 | bin: kubectl-eks 47 | -------------------------------------------------------------------------------- /cmd/fargate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "os" 11 | "text/tabwriter" 12 | 13 | "github.com/aws/aws-sdk-go-v2/aws" 14 | "github.com/aws/aws-sdk-go-v2/service/eks" 15 | "github.com/spf13/cobra" 16 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 17 | "github.com/surajincloud/kubectl-eks/pkg/kube" 18 | ) 19 | 20 | // fargateCmd represents the fargate command 21 | var fargateCmd = &cobra.Command{ 22 | Use: "fargate", 23 | Short: "List fargate profiles", 24 | Long: "List fargate profiles", 25 | RunE: fargate, 26 | } 27 | 28 | func fargate(cmd *cobra.Command, args []string) error { 29 | ctx := context.Background() 30 | 31 | // read flag values 32 | region, _ := cmd.Flags().GetString("region") 33 | 34 | // get Clustername 35 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | // aws config 41 | cfg, err := awspkg.GetAWSConfig(ctx, region) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | // Create an EKS client using the loaded configuration 47 | client := eks.NewFromConfig(cfg) 48 | 49 | // Retrieve a list of Fargate profiles 50 | input := &eks.ListFargateProfilesInput{ 51 | ClusterName: &clusterName, 52 | } 53 | output, err := client.ListFargateProfiles(ctx, input) 54 | if err != nil { 55 | fmt.Println("Failed to list EKS Fargate profiles:", err) 56 | os.Exit(1) 57 | } 58 | 59 | w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) 60 | defer w.Flush() 61 | fmt.Fprintln(w, "NAME", "\t", "PROFILE_ARN", "\t", "STATUS") 62 | for _, profile := range output.FargateProfileNames { 63 | out, err := client.DescribeFargateProfile(ctx, &eks.DescribeFargateProfileInput{ 64 | ClusterName: aws.String(clusterName), 65 | FargateProfileName: aws.String(profile), 66 | }) 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | fmt.Fprintln(w, profile, "\t", aws.ToString(out.FargateProfile.FargateProfileArn), "\t", aws.ToString(out.FargateProfile.FargateProfileArn)) 71 | 72 | } 73 | return nil 74 | } 75 | 76 | func init() { 77 | rootCmd.AddCommand(fargateCmd) 78 | fargateCmd.PersistentFlags().String("region", "", "region") 79 | } 80 | -------------------------------------------------------------------------------- /cmd/nodegroups.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "os" 11 | "strings" 12 | "text/tabwriter" 13 | 14 | "github.com/aws/aws-sdk-go-v2/aws" 15 | "github.com/aws/aws-sdk-go-v2/service/eks" 16 | "github.com/spf13/cobra" 17 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 18 | "github.com/surajincloud/kubectl-eks/pkg/kube" 19 | ) 20 | 21 | // nodegroupsCmd represents the nodegroups command 22 | var nodegroupsCmd = &cobra.Command{ 23 | Use: "nodegroups", 24 | Short: "List EKS Nodegroups", 25 | Long: "List EKS Nodegroups", 26 | RunE: nodegroups, 27 | } 28 | 29 | func nodegroups(cmd *cobra.Command, args []string) error { 30 | 31 | // AmiTypesMap := map[string]string{ 32 | // "AL2_x86_64": "Amazon Linux", 33 | // } 34 | 35 | ctx := context.Background() 36 | 37 | // read flag values 38 | region, _ := cmd.Flags().GetString("region") 39 | 40 | // get Clustername 41 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | // aws config 47 | cfg, err := awspkg.GetAWSConfig(ctx, region) 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | client := eks.NewFromConfig(cfg) 53 | nodegroupsList, err := client.ListNodegroups(ctx, &eks.ListNodegroupsInput{ClusterName: aws.String(clusterName)}) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) 58 | defer w.Flush() 59 | fmt.Fprintln(w, "NAME", "\t", "RELEASE", "\t", "AMI_TYPE", "\t", "INSTANCE_TYPES", "\t", "STATUS") 60 | for _, i := range nodegroupsList.Nodegroups { 61 | name := i 62 | dngp, err := client.DescribeNodegroup(ctx, &eks.DescribeNodegroupInput{ClusterName: aws.String(clusterName), NodegroupName: aws.String(i)}) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | 67 | rv := aws.ToString(dngp.Nodegroup.ReleaseVersion) 68 | 69 | status := dngp.Nodegroup.Status 70 | 71 | amiType := dngp.Nodegroup.AmiType 72 | 73 | instanceTypes := strings.Join(dngp.Nodegroup.InstanceTypes, ",") 74 | fmt.Fprintln(w, name, "\t", rv, "\t", amiType, "\t", instanceTypes, "\t", status) 75 | } 76 | return nil 77 | } 78 | 79 | func init() { 80 | rootCmd.AddCommand(nodegroupsCmd) 81 | nodegroupsCmd.PersistentFlags().String("region", "", "region") 82 | } 83 | -------------------------------------------------------------------------------- /cmd/addons.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "os" 11 | "text/tabwriter" 12 | 13 | "github.com/aws/aws-sdk-go-v2/aws" 14 | "github.com/aws/aws-sdk-go-v2/service/eks" 15 | "github.com/spf13/cobra" 16 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 17 | "github.com/surajincloud/kubectl-eks/pkg/kube" 18 | ) 19 | 20 | // addonsCmd represents the addons command 21 | var addonsCmd = &cobra.Command{ 22 | Use: "addons", 23 | Short: "List Addons with current and recommended versions", 24 | Long: "List Addons with current and recommended versions", 25 | RunE: addons, 26 | } 27 | 28 | type Addons struct { 29 | Name string 30 | CurrentVersion string 31 | LatestVersion string 32 | } 33 | 34 | func addons(cmd *cobra.Command, args []string) error { 35 | 36 | ctx := context.Background() 37 | // read flag values 38 | region, _ := cmd.Flags().GetString("region") 39 | 40 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | // aws config 46 | cfg, err := awspkg.GetAWSConfig(ctx, region) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | // define eks service 52 | eksSvc := eks.NewFromConfig(cfg) 53 | descluster, err := eksSvc.DescribeCluster(ctx, &eks.DescribeClusterInput{ 54 | Name: aws.String(clusterName), 55 | }) 56 | if err != nil { 57 | fmt.Println(err) 58 | os.Exit(1) 59 | } 60 | clusterVersion := aws.ToString(descluster.Cluster.Version) 61 | // List addons 62 | resp, err := eksSvc.ListAddons(context.TODO(), &eks.ListAddonsInput{ 63 | ClusterName: &clusterName, 64 | }) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) 70 | defer w.Flush() 71 | fmt.Fprintln(w, "NAME", "\t", "CURRENT-VERSION", "\t", "LATEST") 72 | for _, i := range resp.Addons { 73 | re, _ := eksSvc.DescribeAddon(ctx, &eks.DescribeAddonInput{ 74 | AddonName: &i, 75 | ClusterName: &clusterName, 76 | }) 77 | currentVersion := aws.ToString(re.Addon.AddonVersion) 78 | 79 | resp, err := eksSvc.DescribeAddonVersions(context.TODO(), &eks.DescribeAddonVersionsInput{ 80 | AddonName: &i, 81 | KubernetesVersion: &clusterVersion, 82 | }) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | 87 | fmt.Fprintln(w, i, "\t", currentVersion, "\t", aws.ToString(resp.Addons[0].AddonVersions[0].AddonVersion)) 88 | 89 | } 90 | return nil 91 | } 92 | 93 | func init() { 94 | rootCmd.AddCommand(addonsCmd) 95 | addonsCmd.PersistentFlags().String("region", "", "region") 96 | } 97 | -------------------------------------------------------------------------------- /cmd/ami-suggest.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | 11 | "github.com/aws/aws-sdk-go-v2/aws" 12 | "github.com/aws/aws-sdk-go-v2/service/eks" 13 | "github.com/aws/aws-sdk-go-v2/service/ssm" 14 | "github.com/spf13/cobra" 15 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 16 | "github.com/surajincloud/kubectl-eks/pkg/kube" 17 | ) 18 | 19 | // suggestionCmd represents the suggestion command 20 | var suggestionCmd = &cobra.Command{ 21 | Use: "suggest-ami", 22 | Short: "Suggest recommended version of AMI", 23 | Long: "Suggest recommended version of AMI", 24 | RunE: suggestion, 25 | } 26 | 27 | func suggestion(cmd *cobra.Command, args []string) error { 28 | 29 | ctx := context.Background() 30 | 31 | // read flag values 32 | region, _ := cmd.Flags().GetString("region") 33 | 34 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | // aws config 40 | cfg, err := awspkg.GetAWSConfig(ctx, region) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | // getcluser info 45 | // Grab the cluster version 46 | client2 := eks.NewFromConfig(cfg) 47 | descluster, _ := client2.DescribeCluster(ctx, &eks.DescribeClusterInput{ 48 | Name: aws.String(clusterName), 49 | }) 50 | clusterVersion := aws.ToString(descluster.Cluster.Version) 51 | client3 := ssm.NewFromConfig(cfg) 52 | fmt.Println("Recommended versions for:") 53 | fmt.Println("Amazon Linux 2:") 54 | out, err := client3.GetParameter(ctx, &ssm.GetParameterInput{ 55 | Name: aws.String(fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/recommended/release_version", clusterVersion))}) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | fmt.Printf("Release version: %v\n", aws.ToString(out.Parameter.Value)) 60 | 61 | out, err = client3.GetParameter(ctx, &ssm.GetParameterInput{ 62 | Name: aws.String(fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/recommended/image_id", clusterVersion))}) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Printf("Image ID: %v\n", aws.ToString(out.Parameter.Value)) 67 | fmt.Println("Bottlerocket:") 68 | out, err = client3.GetParameter(ctx, &ssm.GetParameterInput{ 69 | Name: aws.String(fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/x86_64/latest/image_id", clusterVersion))}) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | fmt.Printf("Image ID: %v\n", aws.ToString(out.Parameter.Value)) 74 | 75 | return nil 76 | 77 | } 78 | func init() { 79 | rootCmd.AddCommand(suggestionCmd) 80 | suggestionCmd.PersistentFlags().String("region", "", "region") 81 | } 82 | -------------------------------------------------------------------------------- /cmd/nodes.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strings" 9 | "text/tabwriter" 10 | 11 | "github.com/aws/aws-sdk-go-v2/aws" 12 | "github.com/aws/aws-sdk-go-v2/service/ec2" 13 | "github.com/spf13/cobra" 14 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 15 | "github.com/surajincloud/kubectl-eks/pkg/kube" 16 | ) 17 | 18 | // nodesCmd represents the nodes command 19 | var nodesCmd = &cobra.Command{ 20 | Use: "nodes", 21 | Short: "List EKS Nodes", 22 | Long: `A better way to list EKS nodes`, 23 | 24 | RunE: nodes, 25 | } 26 | 27 | func nodes(cmd *cobra.Command, args []string) error { 28 | nodeList, err := kube.GetNodes(KubernetesConfigFlags) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | ctx := context.Background() 34 | 35 | // read flag values 36 | region, _ := cmd.Flags().GetString("region") 37 | 38 | // aws config 39 | cfg, err := awspkg.GetAWSConfig(ctx, region) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | ec2Client := ec2.NewFromConfig(cfg) 44 | 45 | w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent) 46 | defer w.Flush() 47 | fmt.Fprintln(w, "NAME", "\t", "INSTANCE-TYPE", "\t", "OS", "\t", "CAPACITY-TYPE", "\t", "ZONE", "\t", "AMI-ID", "\t", "AMI-NAME", "\t", "AGE") 48 | for _, i := range nodeList { 49 | var amiID, amiName, capacityType string 50 | age := kube.GetAge(i.CreationTimestamp) 51 | 52 | // AMI 53 | if i.Labels[kube.NodeGroupImage] == "" { 54 | amiID = i.Labels[kube.KarpenterImage] 55 | if amiID != "" { 56 | dis, err := ec2Client.DescribeImages(ctx, &ec2.DescribeImagesInput{ImageIds: []string{amiID}}) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | amiName = aws.ToString(dis.Images[0].Name) 61 | } 62 | 63 | } else { 64 | amiID = i.Labels[kube.NodeGroupImage] 65 | dis, err := ec2Client.DescribeImages(ctx, &ec2.DescribeImagesInput{ImageIds: []string{amiID}}) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | ami := aws.ToString(dis.Images[0].Name) 70 | amiName = strings.Split(ami, "/")[len(strings.Split(ami, "/"))-1] 71 | } 72 | 73 | if amiID == "" { 74 | instanceId := strings.Split(i.Spec.ProviderID, "/")[len(strings.Split(i.Spec.ProviderID, "/"))-1] 75 | out, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ 76 | InstanceIds: []string{instanceId}, 77 | }) 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | amiID = *out.Reservations[0].Instances[0].ImageId 82 | dis, err := ec2Client.DescribeImages(ctx, &ec2.DescribeImagesInput{ImageIds: []string{amiID}}) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | amiName = aws.ToString(dis.Images[0].Name) 87 | } 88 | 89 | // Capacity Type 90 | if i.Labels[kube.CapacityTypeLabel] != "" { 91 | capacityType = i.Labels[kube.CapacityTypeLabel] 92 | } else { 93 | capacityType = strings.ToUpper(i.Labels[kube.KarpenterCapacityTypeLabel]) 94 | 95 | } 96 | fmt.Fprintln(w, i.Name, "\t", i.Labels[kube.InstanceTypeLabel], "\t", i.Labels[kube.OsLabel], "\t", capacityType, "\t", i.Labels[kube.ZoneLabel], "\t", amiID, "\t", amiName, "\t", age) 97 | } 98 | return nil 99 | } 100 | 101 | func init() { 102 | rootCmd.AddCommand(nodesCmd) 103 | nodesCmd.PersistentFlags().String("region", "", "region") 104 | } 105 | -------------------------------------------------------------------------------- /cmd/kubeconfig.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | "log" 8 | "os" 9 | 10 | "github.com/aws/aws-sdk-go-v2/aws" 11 | "github.com/aws/aws-sdk-go-v2/service/eks" 12 | kubeconfig "github.com/siderolabs/go-kubeconfig" 13 | "github.com/spf13/cobra" 14 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 15 | "github.com/surajincloud/kubectl-eks/pkg/kube" 16 | "k8s.io/client-go/tools/clientcmd" 17 | "k8s.io/client-go/tools/clientcmd/api" 18 | ) 19 | 20 | // kubeconfigCmd represents the kubeconfig command 21 | var kubeconfigCmd = &cobra.Command{ 22 | Use: "kubeconfig", 23 | Short: "Get Kubeconfig for given cluster", 24 | Long: "Get Kubeconfig for given cluster", 25 | RunE: kubeconfigCommand, 26 | } 27 | 28 | // Constants for kubeconfig 29 | const ( 30 | kubeconfigFilePath = "kubeconfig" 31 | ) 32 | 33 | func kubeconfigCommand(cmd *cobra.Command, args []string) error { 34 | 35 | ctx := context.Background() 36 | 37 | // read flag values 38 | out, _ := cmd.Flags().GetBool("out") 39 | region, _ := cmd.Flags().GetString("region") 40 | 41 | // get Clustername 42 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | // aws config 48 | cfg, err := awspkg.GetAWSConfig(ctx, region) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | 53 | // define eks service 54 | eksSvc := eks.NewFromConfig(cfg) 55 | descluster, _ := eksSvc.DescribeCluster(ctx, &eks.DescribeClusterInput{ 56 | Name: aws.String(clusterName), 57 | }) 58 | 59 | // Extract necessary cluster details 60 | serverURL := descluster.Cluster.Endpoint 61 | 62 | // cluser certificate 63 | certData := descluster.Cluster.CertificateAuthority.Data 64 | decodedCertData, err := base64.StdEncoding.DecodeString(*certData) 65 | if err != nil { 66 | fmt.Printf("Failed to decode certificate authority data: %v\n", err) 67 | os.Exit(1) 68 | } 69 | clusterCAData := string(decodedCertData) 70 | // username 71 | userName := clusterName + "-user" 72 | // build kubeconfig 73 | config := &api.Config{ 74 | APIVersion: "v1", 75 | Kind: "Config", 76 | Clusters: map[string]*api.Cluster{ 77 | clusterName: { 78 | Server: *serverURL, 79 | CertificateAuthorityData: []byte(clusterCAData), 80 | }, 81 | }, 82 | Contexts: map[string]*api.Context{ 83 | clusterName: { 84 | Cluster: clusterName, 85 | AuthInfo: userName, 86 | Namespace: "default", 87 | }, 88 | }, 89 | AuthInfos: map[string]*api.AuthInfo{ 90 | userName: { 91 | Exec: &api.ExecConfig{ 92 | APIVersion: "client.authentication.k8s.io/v1beta1", 93 | Command: "aws", 94 | Args: []string{"eks", "get-token", "--cluster-name", clusterName, "--region", region}, 95 | InteractiveMode: api.IfAvailableExecInteractiveMode, 96 | }, 97 | }, 98 | }, 99 | CurrentContext: clusterName, 100 | } 101 | 102 | if !out { 103 | existingPath, _ := kubeconfig.DefaultPath() 104 | a, err := kubeconfig.Load(existingPath) 105 | if err != nil { 106 | fmt.Println("error") 107 | } 108 | 109 | err = a.Merge(config, kubeconfig.MergeOptions{ActivateContext: true}) 110 | if err != nil { 111 | fmt.Println("error merging the kubeconfig") 112 | } 113 | 114 | err = a.Write(existingPath) 115 | if err != nil { 116 | fmt.Println("error writing the kubeconfig") 117 | } 118 | fmt.Println("existing kubeconfig file is merged") 119 | } else { 120 | kc, err := clientcmd.Write(*config) 121 | // err = clientcmd.WriteToFile(*config, kubeconfigFilePath) 122 | if err != nil { 123 | fmt.Printf("Failed to write kubeconfig file: %v\n", err) 124 | os.Exit(1) 125 | } 126 | fmt.Println(string(kc)) 127 | // fmt.Printf("kubeconfig file created at %s\n", kubeconfigFilePath) 128 | } 129 | return nil 130 | } 131 | func init() { 132 | rootCmd.AddCommand(kubeconfigCmd) 133 | kubeconfigCmd.PersistentFlags().String("region", "", "region") 134 | kubeconfigCmd.PersistentFlags().Bool("out", false, "Print kubeconfig") 135 | } 136 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/surajincloud/kubectl-eks 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/antchfx/jsonquery v1.3.3 7 | github.com/aws/aws-sdk-go-v2 v1.22.1 8 | github.com/aws/aws-sdk-go-v2/config v1.18.39 9 | github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.26.0 10 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.118.0 11 | github.com/aws/aws-sdk-go-v2/service/eks v1.29.5 12 | github.com/aws/aws-sdk-go-v2/service/ssm v1.37.5 13 | github.com/markusmobius/go-dateparser v1.2.1 14 | github.com/mmmorris1975/ssm-session-client v0.400.1 15 | github.com/siderolabs/go-kubeconfig v0.1.0 16 | github.com/spf13/cobra v1.7.0 17 | k8s.io/api v0.28.3 18 | k8s.io/apimachinery v0.28.3 19 | k8s.io/cli-runtime v0.28.3 20 | k8s.io/client-go v0.28.3 21 | ) 22 | 23 | require ( 24 | github.com/antchfx/xpath v1.2.3 // indirect 25 | github.com/aws/aws-sdk-go v1.44.76 // indirect 26 | github.com/aws/aws-sdk-go-v2/credentials v1.13.37 // indirect 27 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect 28 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 // indirect 29 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 // indirect 30 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect 31 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect 32 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect 33 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect 34 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect 35 | github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c // indirect 36 | github.com/aws/smithy-go v1.16.0 // indirect 37 | github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect 38 | github.com/davecgh/go-spew v1.1.1 // indirect 39 | github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect 40 | github.com/elliotchance/pie/v2 v2.7.0 // indirect 41 | github.com/emicklei/go-restful/v3 v3.9.0 // indirect 42 | github.com/evanphx/json-patch v5.6.0+incompatible // indirect 43 | github.com/fsnotify/fsnotify v1.5.4 // indirect 44 | github.com/go-errors/errors v1.4.2 // indirect 45 | github.com/go-logr/logr v1.2.4 // indirect 46 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 47 | github.com/go-openapi/jsonreference v0.20.2 // indirect 48 | github.com/go-openapi/swag v0.22.3 // indirect 49 | github.com/gogo/protobuf v1.3.2 // indirect 50 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 51 | github.com/golang/protobuf v1.5.3 // indirect 52 | github.com/google/btree v1.0.1 // indirect 53 | github.com/google/gnostic-models v0.6.8 // indirect 54 | github.com/google/go-cmp v0.5.9 // indirect 55 | github.com/google/gofuzz v1.2.0 // indirect 56 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 57 | github.com/google/uuid v1.3.0 // indirect 58 | github.com/gorilla/websocket v1.4.2 // indirect 59 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect 60 | github.com/hablullah/go-hijri v1.0.2 // indirect 61 | github.com/hablullah/go-juliandays v1.0.0 // indirect 62 | github.com/imdario/mergo v0.3.6 // indirect 63 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 64 | github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect 65 | github.com/jmespath/go-jmespath v0.4.0 // indirect 66 | github.com/josharian/intern v1.0.0 // indirect 67 | github.com/json-iterator/go v1.1.12 // indirect 68 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect 69 | github.com/magefile/mage v1.15.0 // indirect 70 | github.com/mailru/easyjson v0.7.7 // indirect 71 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 72 | github.com/modern-go/reflect2 v1.0.2 // indirect 73 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect 74 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 75 | github.com/peterbourgon/diskv v2.0.1+incompatible // indirect 76 | github.com/pkg/errors v0.9.1 // indirect 77 | github.com/pmezard/go-difflib v1.0.0 // indirect 78 | github.com/spf13/pflag v1.0.5 // indirect 79 | github.com/stretchr/objx v0.5.0 // indirect 80 | github.com/stretchr/testify v1.8.4 // indirect 81 | github.com/tetratelabs/wazero v1.3.1 // indirect 82 | github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19 // indirect 83 | github.com/wasilibs/go-re2 v1.3.0 // indirect 84 | github.com/xlab/treeprint v1.2.0 // indirect 85 | github.com/xtaci/smux v1.5.16 // indirect 86 | go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect 87 | golang.org/x/crypto v0.14.0 // indirect 88 | golang.org/x/exp v0.0.0-20230724220655-d98519c11495 // indirect 89 | golang.org/x/net v0.17.0 // indirect 90 | golang.org/x/oauth2 v0.8.0 // indirect 91 | golang.org/x/sync v0.2.0 // indirect 92 | golang.org/x/sys v0.13.0 // indirect 93 | golang.org/x/term v0.13.0 // indirect 94 | golang.org/x/text v0.13.0 // indirect 95 | golang.org/x/time v0.3.0 // indirect 96 | google.golang.org/appengine v1.6.7 // indirect 97 | google.golang.org/protobuf v1.30.0 // indirect 98 | gopkg.in/inf.v0 v0.9.1 // indirect 99 | gopkg.in/yaml.v2 v2.4.0 // indirect 100 | gopkg.in/yaml.v3 v3.0.1 // indirect 101 | k8s.io/klog/v2 v2.100.1 // indirect 102 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect 103 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect 104 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 105 | sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect 106 | sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect 107 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect 108 | sigs.k8s.io/yaml v1.3.0 // indirect 109 | ) 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /cmd/logs.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "strings" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/antchfx/jsonquery" 14 | "github.com/aws/aws-sdk-go-v2/aws" 15 | "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" 16 | "github.com/aws/aws-sdk-go-v2/service/eks" 17 | "github.com/markusmobius/go-dateparser" 18 | "github.com/spf13/cobra" 19 | awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" 20 | "github.com/surajincloud/kubectl-eks/pkg/kube" 21 | ) 22 | 23 | var Follow bool 24 | var SinceTime string 25 | 26 | func init() { 27 | logsCmd.Flags().BoolVarP(&Follow, "follow", "f", false, "Follow logs (not available for node file queries)") 28 | logsCmd.Flags().StringVar(&SinceTime, "since", "1 hour ago", "What time logs should start from") 29 | } 30 | 31 | // logsCmd represents the logs command 32 | var logsCmd = &cobra.Command{ 33 | Use: "logs [flags] LOG_SOURCE", 34 | Example: ` kubectl eks logs kube-apiserver 35 | kubectl eks logs NODE [kubelet] 36 | 37 | Query multiple log sources: 38 | kubectl eks logs api audit scheduler 39 | kubectl eks logs NODE kubelet containerd`, 40 | Args: cobra.MinimumNArgs(1), 41 | Short: "Get logs from EKS control plane or nodes", 42 | Long: "Get logs from EKS control plane or nodes", 43 | RunE: logs, 44 | } 45 | 46 | // the main logs function is responsible for routing the request 47 | // to the correct log endpoint (cloudwatch or kubelet) and 48 | // printing the logs to stdout from the logs channel 49 | func logs(cmd *cobra.Command, args []string) error { 50 | 51 | // pass empty string to let fuction get name 52 | clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // we only look at the first argument to determine if it is a control plane log source or node 58 | logTarget := args[0] 59 | if logTarget == "all" { 60 | args = args[1:] 61 | args = append(args, "scheduler", "kube-apiserver-audit", "kube-controller-manager", "kube-apiserver", "authenticator", "cloud-controller-manager") 62 | } 63 | 64 | logsChan := make(chan string) 65 | logsDoneChan := make(chan bool, len(args)) 66 | 67 | cloudwatchLogStreams := []string{ 68 | "scheduler", 69 | "kube-scheduler", 70 | "audit", 71 | "kube-audit", 72 | "kube-apiserver-audit", 73 | "cm", 74 | "controller-manager", 75 | "kube-controller-manager", 76 | "api", 77 | "apiserver", 78 | "kube-apiserver", 79 | "auth", 80 | "authenticator", 81 | "ccm", 82 | "cloud-controller", 83 | "cloud-controller-manager", 84 | } 85 | 86 | // handle ctl+c interrupt without printing output 87 | c := make(chan os.Signal, 1) 88 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 89 | go func() { 90 | <-c 91 | os.Exit(0) 92 | }() 93 | 94 | // check first argument if it is a control plane log source or node 95 | if contains(cloudwatchLogStreams, logTarget) || (logTarget == "all") { 96 | 97 | var cwlStreamInput cloudwatchlogs.DescribeLogStreamsInput 98 | var streams *cloudwatchlogs.DescribeLogStreamsOutput 99 | var limit int32 = 100 100 | var cwlGroupPrefix string 101 | 102 | cwlGroupName := "/aws/eks/" + clusterName + "/cluster" 103 | cwlStreamInput.LogGroupName = &cwlGroupName 104 | 105 | // aws config 106 | ctx := context.Background() 107 | // read flag values 108 | region, _ := cmd.Flags().GetString("region") 109 | 110 | cfg, err := awspkg.GetAWSConfig(ctx, region) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | 115 | // TODO check if logging is enabled 116 | // TODO allow user to enable logging 117 | svc := eks.NewFromConfig(cfg) 118 | eksClusterInput := &eks.DescribeClusterInput{ 119 | Name: aws.String(clusterName), 120 | } 121 | result, _ := svc.DescribeCluster(ctx, eksClusterInput) 122 | // check if logging is enabled 123 | if !*result.Cluster.Logging.ClusterLogging[0].Enabled == true { 124 | fmt.Println("Logging is not enabled for this cluster. Please enable logging and try again.") 125 | os.Exit(1) 126 | } 127 | 128 | cwl := cloudwatchlogs.NewFromConfig(cfg) 129 | // verify the group exists first 130 | err = ensureLogGroupExists(cwlGroupName, ctx, cwl) 131 | //TODO prompt if user wants to enable logs 132 | if err != nil { 133 | panic(err) 134 | } 135 | 136 | var fetchStream string 137 | matchedStream := 0 138 | for _, logSource := range args { 139 | 140 | cwlGroupPrefix = getLogStreamPrefix(logSource) 141 | cwlStreamInput.LogStreamNamePrefix = &cwlGroupPrefix 142 | 143 | streams, err = cwl.DescribeLogStreams(ctx, &cwlStreamInput) 144 | if err != nil { 145 | log.Fatal(err) 146 | } 147 | 148 | if len(streams.LogStreams) == 0 { 149 | fmt.Fprintln(os.Stderr, "No log streams found for", cwlGroupPrefix) 150 | } else if cwlGroupPrefix == "kube-apiserver" { 151 | // we have to make sure kube-apiserver doesn't add kube-apiserver-audit logs 152 | for _, stream := range streams.LogStreams { 153 | if !strings.Contains(*stream.LogStreamName, "kube-apiserver-audit") { 154 | fetchStream = *stream.LogStreamName 155 | // only match the first stream 156 | break 157 | } 158 | } 159 | if fetchStream == "" { 160 | fmt.Fprintln(os.Stderr, "No log streams found for", cwlGroupPrefix) 161 | } else { 162 | go getLogEvents(&cwlGroupName, &fetchStream, &limit, logsChan, logsDoneChan, ctx, cwl, len(args)) 163 | matchedStream++ 164 | 165 | } 166 | } else { 167 | fetchStream = *streams.LogStreams[0].LogStreamName 168 | go getLogEvents(&cwlGroupName, &fetchStream, &limit, logsChan, logsDoneChan, ctx, cwl, len(args)) 169 | matchedStream++ 170 | } 171 | } 172 | 173 | // use a count to make sure we have a goroutine fetching logs 174 | if matchedStream > 0 { 175 | // Print each line from logsChan 176 | for log := range logsChan { 177 | // print the log 178 | fmt.Println(log) 179 | 180 | // once all event streams are done, close the channel 181 | if len(logsDoneChan) == len(args) { 182 | close(logsChan) 183 | } 184 | } 185 | } 186 | } else { 187 | // we assume the target is a node instead of control plane 188 | nodeList, err := kube.GetNodes(KubernetesConfigFlags) 189 | if err != nil { 190 | return err 191 | } 192 | 193 | var nodeMatched bool = false 194 | var currentNodeSlice []string 195 | logTargetSlice := strings.Split(logTarget, ".") 196 | 197 | for _, i := range nodeList { 198 | // match node based on substring eg. ip-192-168-1-1 199 | currentNodeSlice = strings.Split(i.Name, ".") 200 | if currentNodeSlice[0] == logTargetSlice[0] { 201 | nodeMatched = true 202 | var query []string 203 | 204 | // use all additional arguments as services to query 205 | if len(args) > 1 { 206 | query = args[1:] 207 | } else { 208 | query = append(query, "kubelet") 209 | } 210 | 211 | // validate kubelet settings for remote logs 212 | if validateKubeletConfig(i.Name) { 213 | // get logs and assume query is journald and can accept sinceTime 214 | go getNodeLogs(i.Name, query, false, logsChan, logsDoneChan) 215 | 216 | // print each line from logsChan 217 | for log := range logsChan { 218 | 219 | fmt.Println(log) 220 | 221 | if len(logsDoneChan) == 1 { 222 | close(logsChan) 223 | } 224 | } 225 | } 226 | } 227 | } 228 | if nodeMatched { 229 | return nil 230 | } else { 231 | fmt.Printf("Node %s not found\nTo query control plane logs please see options in --help output.\n", logTarget) 232 | } 233 | } 234 | return nil 235 | } 236 | 237 | // fetches logs from a node using the kubelet api 238 | // sends logs to the logsChan and sends a bool to logsDoneChan when done 239 | // there are additional queries that can be used on the kubelet API but 240 | // I wanted to keep the UX simple so people should rely on CLI tools 241 | // to filter logs further 242 | func getNodeLogs(node string, query []string, fileQuery bool, logsChan chan<- string, logsDoneChan chan<- bool) { 243 | 244 | dt, err := dateparser.Parse(nil, SinceTime) 245 | if err != nil { 246 | panic(err) 247 | } 248 | // create URL for log fetching 249 | rawURL := "/api/v1/nodes/" + node + "/proxy/logs/" 250 | 251 | for { 252 | 253 | clientSet, _ := kube.ClientSet(KubernetesConfigFlags) 254 | req := clientSet.RESTClient().Get(). 255 | AbsPath(rawURL) 256 | 257 | for _, q := range query { 258 | req.Param("query", q) 259 | } 260 | 261 | if !fileQuery { 262 | // sinceTime is ignored for file queries 263 | req.Param("sinceTime", dt.Time.Format(time.RFC3339)) 264 | } 265 | 266 | resp, err := req.DoRaw(context.Background()) 267 | if err != nil { 268 | log.Panicln(err, req.URL().String()) 269 | } 270 | 271 | // api returns a byte string 272 | // convert to string and split by newline to send each line to channel 273 | for _, logLine := range strings.Split(string(resp), "\n") { 274 | // fmt.Printf("%v %s", lineNumber, logLine) 275 | if strings.Contains(logLine, "options present and query resolved to log files") { 276 | // file queries cannot use sinceTime 277 | // we catch this error output and run again as file query 278 | go getNodeLogs(node, query, true, logsChan, logsDoneChan) 279 | break 280 | } else if logLine == "" || 281 | strings.Contains(logLine, "-- No entries --") || 282 | strings.Contains(logLine, "-- Logs begin at ") { 283 | // don't send log decorations 284 | } else { 285 | logsChan <- logLine 286 | } 287 | } 288 | 289 | if Follow { 290 | if fileQuery { 291 | fmt.Fprintln(os.Stderr, "Cannot follow file queries") 292 | close(logsChan) 293 | break 294 | } 295 | dt, err = dateparser.Parse(nil, "now") 296 | if err != nil { 297 | panic(err) 298 | } 299 | time.Sleep(1 * time.Second) 300 | } else { 301 | if fileQuery { 302 | close(logsChan) 303 | } else { 304 | logsDoneChan <- true 305 | } 306 | break 307 | } 308 | } 309 | } 310 | 311 | // ensureLogGroupExists first checks if the log group exists 312 | // Cluster logging can be enabled but the log group may not exist 313 | // this is because not all control plane components need logging enabled 314 | func ensureLogGroupExists(name string, ctx context.Context, cwl *cloudwatchlogs.Client) error { 315 | resp, err := cwl.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{}) 316 | if err != nil { 317 | return err 318 | } 319 | 320 | for _, logGroup := range resp.LogGroups { 321 | if *logGroup.LogGroupName == name { 322 | return nil 323 | } 324 | } 325 | 326 | return err 327 | } 328 | 329 | // fetchLogs fetches logs from a cloudwatch stream 330 | // sends logs to the logsChan and sends true to logsDoneChan when done 331 | // logs are not guaranteed to be in order because they are fetched from each source 332 | // individually and no ordering is performed on the channel before printing 333 | func getLogEvents(logGroupName *string, logStreamName *string, limit *int32, logsChan chan<- string, logsDoneChan chan<- bool, ctx context.Context, cwl *cloudwatchlogs.Client, totalStreams int) { 334 | 335 | dt, err := dateparser.Parse(nil, SinceTime) 336 | if err != nil { 337 | panic(err) 338 | } 339 | 340 | // loop forever if Follow == true 341 | for { 342 | resp, err := cwl.GetLogEvents(ctx, &cloudwatchlogs.GetLogEventsInput{ 343 | Limit: limit, 344 | LogGroupName: logGroupName, 345 | LogStreamName: logStreamName, 346 | StartTime: aws.Int64(dt.Time.UnixMilli()), 347 | }) 348 | if err != nil { 349 | panic(err) 350 | } 351 | 352 | for i, event := range resp.Events { 353 | // TODO allow for following tokens for more logs from different streams 354 | // currently we only fetch the newest stream which may not have all logs 355 | 356 | if i == len(resp.Events)-1 { 357 | if Follow { 358 | dt, err = dateparser.Parse(nil, "now") 359 | if err != nil { 360 | panic(err) 361 | } 362 | // wait 1 sec before querying again 363 | time.Sleep(1 * time.Second) 364 | } else { 365 | logsDoneChan <- true 366 | } 367 | logsChan <- *event.Message 368 | } else { 369 | logsChan <- *event.Message 370 | } 371 | 372 | } 373 | 374 | } 375 | } 376 | 377 | // initialize command and get region 378 | func init() { 379 | rootCmd.AddCommand(logsCmd) 380 | logsCmd.PersistentFlags().String("region", "", "region") 381 | } 382 | 383 | // check if a string is present in a slice 384 | func contains(s []string, str string) bool { 385 | for _, v := range s { 386 | if v == str { 387 | return true 388 | } 389 | } 390 | 391 | return false 392 | } 393 | 394 | // Convert possible log source aliases to full log stream prefix 395 | // needed because log streams have defined prefixes but we allow 396 | // multiple aliases for each log source 397 | func getLogStreamPrefix(logTarget string) string { 398 | schedulerSlice := []string{"scheduler", "kube-scheduler"} 399 | auditSlice := []string{"audit", "kube-audit", "kube-apiserver-audit"} 400 | cmSlice := []string{"cm", "controller-manager", "kube-controller-manager"} 401 | apiSlice := []string{"api", "apiserver", "kube-apiserver"} 402 | authSlice := []string{"auth", "authenticator"} 403 | ccmSlice := []string{"ccm", "cloud-controller", "cloud-controller-manager"} 404 | 405 | if contains(schedulerSlice, logTarget) { 406 | return "kube-scheduler" 407 | } else if contains(auditSlice, logTarget) { 408 | return "kube-apiserver-audit" 409 | } else if contains(cmSlice, logTarget) { 410 | return "kube-controller-manager" 411 | } else if contains(apiSlice, logTarget) { 412 | return "kube-apiserver" 413 | } else if contains(authSlice, logTarget) { 414 | return "authenticator" 415 | } else if contains(ccmSlice, logTarget) { 416 | return "cloud-controller-manager" 417 | } else { 418 | return "" 419 | } 420 | } 421 | 422 | // validates kubelet config for remote logging 423 | // fetches the full config from the configz endpoint 424 | // verifies if the settings are set correctly 425 | // there's currently no way I know to change this without restarting the kubelet 426 | func validateKubeletConfig(node string) bool { 427 | rawURL := "/api/v1/nodes/" + node + "/proxy/configz" 428 | clientSet, _ := kube.ClientSet(KubernetesConfigFlags) 429 | req := clientSet.RESTClient().Get(). 430 | AbsPath(rawURL).Timeout(20 * time.Second) 431 | 432 | resp, err := req.DoRaw(context.Background()) 433 | if err != nil { 434 | log.Panicln(err, req.URL().String()) 435 | } 436 | 437 | // read the kueblet config json 438 | kubeletConfigJson, err := jsonquery.Parse(strings.NewReader(string(resp))) 439 | if err != nil { 440 | panic(err) 441 | } 442 | 443 | // check if the node has the appropriate config for remote logging 444 | nodeLogQuery := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/featureGates/NodeLogQuery") 445 | 446 | systemLogHandler := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/enableSystemLogHandler") 447 | systemLogQuery := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/enableSystemLogQuery") 448 | if (nodeLogQuery != nil) && (systemLogHandler != nil) && (systemLogQuery != nil) { 449 | if nodeLogQuery.Value().(bool) { 450 | return true 451 | } 452 | } 453 | // if the node doesn't have the appropriate config, print a message 454 | fmt.Printf(` Node %s is not configured for remote logs. 455 | Please enable remote logging on the kubelet from the documentation here 456 | Requires Kubernetes 1.27 https://kubernetes.io/blog/2023/04/21/node-log-query-alpha/`, node) 457 | 458 | return false 459 | 460 | } 461 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/antchfx/jsonquery v1.3.3 h1:zjZpbnZhYng3uOAbIfdNq81A9mMEeuDJeYIpeKpZ4es= 4 | github.com/antchfx/jsonquery v1.3.3/go.mod h1:1JG4DqRlRCHgVYDPY1ioYFAGSXGfWHzNgrbiGQHsWck= 5 | github.com/antchfx/xpath v1.2.3 h1:CCZWOzv5bAqjVv0offZ2LVgVYFbeldKQVuLNbViZdes= 6 | github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= 7 | github.com/aws/aws-sdk-go v1.44.76 h1:5e8yGO/XeNYKckOjpBKUd5wStf0So3CrQIiOMCVLpOI= 8 | github.com/aws/aws-sdk-go v1.44.76/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= 9 | github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= 10 | github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= 11 | github.com/aws/aws-sdk-go-v2 v1.22.1 h1:sjnni/AuoTXxHitsIdT0FwmqUuNUuHtufcVDErVFT9U= 12 | github.com/aws/aws-sdk-go-v2 v1.22.1/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c= 13 | github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= 14 | github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ= 15 | github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= 16 | github.com/aws/aws-sdk-go-v2/credentials v1.12.23/go.mod h1:0awX9iRr/+UO7OwRQFpV1hNtXxOVuehpjVEzrIAYNcA= 17 | github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U= 18 | github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= 19 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVPGkwT+2+WJNQV8UXFfMTWdU6VErL8= 20 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= 21 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= 22 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= 23 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= 24 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 h1:fi1ga6WysOyYb5PAf3Exd6B5GiSNpnZim4h1rhlBqx0= 25 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1/go.mod h1:V5CY8wNurvPUibTi9mwqUqpiFZ5LnioKWIFUDtIzdI8= 26 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= 27 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= 28 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 h1:ZpaV/j48RlPc4AmOZuPv22pJliXjXq8/reL63YzyFnw= 29 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1/go.mod h1:R8aXraabD2e3qv1csxM14/X9WF4wFMIY0kH4YEtYD5M= 30 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= 31 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= 32 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= 33 | github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.26.0 h1:Vdh1365tJuj64BGW4+o04F2URR5JkH3MyRyOkMGH/F0= 34 | github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.26.0/go.mod h1:b+BEEfK7ncwheUZ/IhtuH1tcguGPZkI2TbGT2T/sOZ4= 35 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.64.0/go.mod h1:zul71QqzR4D1a90/5FloZiAnZ1CtuIjVH7R9MP997+A= 36 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.118.0 h1:ueSJS07XpOwCFhYTHh/Jjw856+U+u0Dv5LIIPOB1/Ns= 37 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.118.0/go.mod h1:0FhI2Rzcv5BNM3dNnbcCx2qa2naFZoAidJi11cQgzL0= 38 | github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.14.11/go.mod h1:E29Z9YWBhILsNzaxWab92P6Wni6pdd4NVN8D4FCyNUU= 39 | github.com/aws/aws-sdk-go-v2/service/eks v1.29.5 h1:6eSpTHOsDixcFIvPdiAAVdyCru3k2jIVRPdIQfGzfc8= 40 | github.com/aws/aws-sdk-go-v2/service/eks v1.29.5/go.mod h1:TwqefcyPlF31NTF+fH34tJ2VwMMR6c74IbiiUgA6kVY= 41 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19/go.mod h1:02CP6iuYP+IVnBX5HULVdSAku/85eHB2Y9EsFhrkEwU= 42 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= 43 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= 44 | github.com/aws/aws-sdk-go-v2/service/ssm v1.31.3/go.mod h1:rEsqsZrOp9YvSGPOrcL3pR9+i/QJaWRkAYbuxMa7yCU= 45 | github.com/aws/aws-sdk-go-v2/service/ssm v1.37.5 h1:s9QR0F1W5+11lq04OJ/mihpRpA2VDFIHmu+ktgAbNfg= 46 | github.com/aws/aws-sdk-go-v2/service/ssm v1.37.5/go.mod h1:JjBzoceyKkpQY3v1GPIdg6kHqUFHRJ7SDlwtwoH0Qh8= 47 | github.com/aws/aws-sdk-go-v2/service/sso v1.11.25/go.mod h1:IARHuzTXmj1C0KS35vboR0FeJ89OkEy1M9mWbK2ifCI= 48 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= 49 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= 50 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8/go.mod h1:er2JHN+kBY6FcMfcBBKNGCT3CarImmdFzishsqBmSRI= 51 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58= 52 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= 53 | github.com/aws/aws-sdk-go-v2/service/sts v1.17.1/go.mod h1:bXcN3koeVYiJcdDU89n3kCYILob7Y34AeLopUbZgLT4= 54 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= 55 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= 56 | github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c h1:6cCrrTmS+7B+saEBhMnNblArJpA7BNmjd9F6MUHS6sQ= 57 | github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c/go.mod h1:7n17tunRPUsniNBu5Ja9C7WwJWTdOzaLqr/H0Ns3uuI= 58 | github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 59 | github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 60 | github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik= 61 | github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= 62 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 63 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 64 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 65 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 66 | github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= 67 | github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= 68 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 69 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 70 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 71 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 72 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 73 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 74 | github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= 75 | github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= 76 | github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg= 77 | github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og= 78 | github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= 79 | github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 80 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 81 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 82 | github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= 83 | github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 84 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= 85 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 86 | github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= 87 | github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 88 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 89 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 90 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 91 | github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= 92 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 93 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 94 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 95 | github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= 96 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 97 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 98 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 99 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 100 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 101 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 102 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 103 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 104 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 105 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 106 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 107 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 108 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 109 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 110 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 111 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 112 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 113 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 114 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 115 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 116 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 117 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 118 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 119 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 120 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 121 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 122 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 123 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 124 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 125 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 126 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 127 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 128 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 129 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 130 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 131 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 132 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 133 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= 134 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 135 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 136 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 137 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 138 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 139 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 140 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= 141 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 142 | github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k= 143 | github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ= 144 | github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0= 145 | github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc= 146 | github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= 147 | github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 148 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 149 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 150 | github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE= 151 | github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958/go.mod h1:Wqfu7mjUHj9WDzSSPI5KfBclTTEnLveRUFr/ujWnTgE= 152 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 153 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 154 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 155 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 156 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 157 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 158 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 159 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 160 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 161 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 162 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 163 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 164 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 165 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 166 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 167 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 168 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= 169 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= 170 | github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= 171 | github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 172 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 173 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 174 | github.com/markusmobius/go-dateparser v1.2.1 h1:mYRRdu3TzpAeE6fSl2Gn3arfxEtoTRvFOKlumlVsUtg= 175 | github.com/markusmobius/go-dateparser v1.2.1/go.mod h1:5xYsZ1h7iB3sE1BSu8bkjYpbFST7EU1/AFxcyO3mgYg= 176 | github.com/mmmorris1975/ssm-session-client v0.400.1 h1:WrKlegOjn4geYte3i7wYn3flONGW0JmshMThjMYAKno= 177 | github.com/mmmorris1975/ssm-session-client v0.400.1/go.mod h1:Gv045ehxc1lwPCiaaYNrjdRSBctFaIfTOSb3y8MpQ8E= 178 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 179 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 180 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 181 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 182 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 183 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= 184 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 185 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 186 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 187 | github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= 188 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 189 | github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= 190 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 191 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 192 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 193 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 194 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 195 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 196 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 197 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 198 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 199 | github.com/siderolabs/go-kubeconfig v0.1.0 h1:t/2oMWkLSdWHXglKPMz8ySXnx6ZjHckeGY79NaDcBTo= 200 | github.com/siderolabs/go-kubeconfig v0.1.0/go.mod h1:eM3mO02Td6wYDvdi9zTbMrj1Q4WqEFN8XQ6pNjCUWkI= 201 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 202 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 203 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 204 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 205 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 206 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 207 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 208 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 209 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 210 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 211 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 212 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 213 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 214 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 215 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 216 | github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM= 217 | github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= 218 | github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19 h1:HlxV0XiEKMMyjS3gGtJmmFZsxQ22GsLvA7F980il+1w= 219 | github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= 220 | github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw= 221 | github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg= 222 | github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ= 223 | github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= 224 | github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= 225 | github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk= 226 | github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= 227 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 228 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 229 | go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= 230 | go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= 231 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 232 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 233 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 234 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 235 | golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= 236 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 237 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 238 | golang.org/x/exp v0.0.0-20230724220655-d98519c11495 h1:zKGKw2WlGb8oPoRGqQ2PT8g2YoCN1w/YbbQjHXCdUWE= 239 | golang.org/x/exp v0.0.0-20230724220655-d98519c11495/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 240 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 241 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 242 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 243 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 244 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 245 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 246 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 247 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 248 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 249 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 250 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 251 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 252 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 253 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 254 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 255 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 256 | golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 257 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 258 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 259 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 260 | golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= 261 | golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= 262 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 263 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 264 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 265 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 266 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 267 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 268 | golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 269 | golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 270 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 271 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 272 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 275 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 276 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 277 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 278 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 279 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 280 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 281 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 282 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 283 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 284 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 285 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 286 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 287 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 288 | golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= 289 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 290 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 291 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 292 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 293 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 294 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 295 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 296 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 297 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 298 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 299 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 300 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 301 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 302 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 303 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 304 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 305 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 306 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 307 | golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= 308 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 309 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 310 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 311 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 312 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 313 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 314 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 315 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 316 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 317 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 318 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 319 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 320 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 321 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 322 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 323 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 324 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 325 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 326 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 327 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 328 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 329 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 330 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 331 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 332 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 333 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 334 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 335 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 336 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 337 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 338 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 339 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 340 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 341 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 342 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 343 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 344 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 345 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 346 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 347 | k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= 348 | k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= 349 | k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= 350 | k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= 351 | k8s.io/cli-runtime v0.28.3 h1:lvuJYVkwCqHEvpS6KuTZsUVwPePFjBfSGvuaLl2SxzA= 352 | k8s.io/cli-runtime v0.28.3/go.mod h1:jeX37ZPjIcENVuXDDTskG3+FnVuZms5D9omDXS/2Jjc= 353 | k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= 354 | k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= 355 | k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= 356 | k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 357 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= 358 | k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= 359 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= 360 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 361 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= 362 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 363 | sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= 364 | sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= 365 | sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= 366 | sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= 367 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= 368 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= 369 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 370 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 371 | --------------------------------------------------------------------------------