├── .gitignore ├── hack ├── compress.sh └── hashgen.sh ├── pkg └── version.go ├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── LICENSE ├── Makefile ├── .DEREK.yml ├── go.mod ├── README.md ├── main.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | /kubetrim 2 | /bin/** 3 | -------------------------------------------------------------------------------- /hack/compress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd bin 4 | 5 | for f in kubetrim*; do tar -cvzf ../uploads/$f.tgz $f; done -------------------------------------------------------------------------------- /hack/hashgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd bin 4 | 5 | for f in kubetrim*; do shasum -a 256 $f > ../uploads/$f.sha256; done 6 | 7 | -------------------------------------------------------------------------------- /pkg/version.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | var ( 4 | // Version is the version of the tool 5 | Version = "dev" 6 | 7 | // GitCommit is the git commit of the tool 8 | GitCommit = "" 9 | ) 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | concurrency: 14 | group: ${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@master 20 | with: 21 | fetch-depth: 1 22 | - name: Install Go 23 | uses: actions/setup-go@master 24 | with: 25 | go-version: 1.22.x 26 | - name: Make all 27 | run: make all 28 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | permissions: 9 | contents: write 10 | checks: write 11 | 12 | actions: read 13 | issues: read 14 | packages: write 15 | pull-requests: read 16 | repository-projects: read 17 | statuses: read 18 | 19 | jobs: 20 | publish: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@master 24 | with: 25 | fetch-depth: 1 26 | - name: Install Go 27 | uses: actions/setup-go@master 28 | with: 29 | go-version: 1.22.x 30 | - name: Make all 31 | run: make all 32 | - name: Upload release binaries 33 | uses: alexellis/upload-assets@0.4.1 34 | env: 35 | GITHUB_TOKEN: ${{ github.token }} 36 | with: 37 | asset_paths: '["./uploads/*"]' 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Alex Ellis, OpenFaaS Ltd 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | Version := $(shell git describe --tags --dirty) 2 | GitCommit := $(shell git rev-parse HEAD) 3 | LDFLAGS := "-s -w -X github.com/alexellis/kubetrim/pkg.Version=$(Version) -X github.com/alexellis/kubetrim/pkg.GitCommit=$(GitCommit)" 4 | SOURCE_DIRS = cmd pkg main.go 5 | export GO111MODULE=on 6 | 7 | .PHONY: all 8 | all: gofmt test build dist compress hash 9 | 10 | .PHONY: build 11 | build: 12 | go build 13 | 14 | .PHONY: gofmt 15 | gofmt: 16 | @test -z $(shell gofmt -l -s $(SOURCE_DIRS) ./ |grep -v vendor/| tee /dev/stderr) || (echo "[WARN] Fix formatting issues with 'make gofmt'" && exit 1) 17 | 18 | .PHONY: test 19 | test: 20 | CGO_ENABLED=0 go test $(shell go list ./... | grep -v /vendor/|xargs echo) -cover 21 | 22 | .PHONY: dist 23 | dist: 24 | 25 | mkdir -p bin/ 26 | mkdir -p uploads/ 27 | rm -rf bin/kubetrim* 28 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o bin/kubetrim 29 | CGO_ENABLED=0 GOOS=darwin go build -ldflags $(LDFLAGS) -o bin/kubetrim-darwin 30 | CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -a -ldflags $(LDFLAGS) -o bin/kubetrim-darwin-arm64 31 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags $(LDFLAGS) -o bin/kubetrim-arm64 32 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags $(LDFLAGS) -o bin/kubetrim.exe 33 | 34 | .PHONY: hash 35 | hash: 36 | rm -rf uploads/*.sha256 && ./hack/hashgen.sh 37 | 38 | .PHONY: compress 39 | compress: 40 | ./hack/compress.sh -------------------------------------------------------------------------------- /.DEREK.yml: -------------------------------------------------------------------------------- 1 | curators: 2 | - alexellis 3 | 4 | features: 5 | - dco_check 6 | - comments 7 | - pr_description_required 8 | - release_notes 9 | 10 | custom_messages: 11 | - name: template 12 | value: | 13 | This project uses Issue and PR templates and requires that all 14 | users fill out the template in detail before help can be given. 15 | 16 | To continue please edit your Issue/PR or open a new one, and 17 | please provide all the fields requested. 18 | 19 | Thank you for your contribution. 20 | 21 | - name: propose 22 | value: | 23 | This project follows a contributing guide which states that all 24 | changes must be proposed with an Issue before being worked on. 25 | 26 | Please raise an Issue and update your Pull Request to include 27 | the ID or link as part of the description. 28 | 29 | Thank you for your contribution. 30 | 31 | - name: test 32 | value: | 33 | This project follows a contributing guide which requires that 34 | all changes are tested before being merged. You should include 35 | worked examples that a maintainer can run to prove that the 36 | changes are good. 37 | 38 | Screenshots and command line output are also accepted, but 39 | must show the positive, and negative cases, not just that 40 | what was added worked as you expected. 41 | 42 | Thank you for your contribution. 43 | 44 | contributing_url: https://github.com/alexellis/arkade/blob/master/CONTRIBUTING.md 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/alexellis/kubetrim 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | k8s.io/apimachinery v0.31.0 7 | k8s.io/client-go v0.31.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 12 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 13 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 14 | github.com/go-logr/logr v1.4.2 // indirect 15 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 16 | github.com/go-openapi/jsonreference v0.20.2 // indirect 17 | github.com/go-openapi/swag v0.22.4 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/protobuf v1.5.4 // indirect 20 | github.com/google/gnostic-models v0.6.8 // indirect 21 | github.com/google/go-cmp v0.6.0 // indirect 22 | github.com/google/gofuzz v1.2.0 // indirect 23 | github.com/google/uuid v1.6.0 // indirect 24 | github.com/imdario/mergo v0.3.6 // indirect 25 | github.com/josharian/intern v1.0.0 // indirect 26 | github.com/json-iterator/go v1.1.12 // indirect 27 | github.com/mailru/easyjson v0.7.7 // indirect 28 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 29 | github.com/modern-go/reflect2 v1.0.2 // indirect 30 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 31 | github.com/spf13/pflag v1.0.5 // indirect 32 | github.com/x448/float16 v0.8.4 // indirect 33 | golang.org/x/net v0.26.0 // indirect 34 | golang.org/x/oauth2 v0.21.0 // indirect 35 | golang.org/x/sys v0.21.0 // indirect 36 | golang.org/x/term v0.21.0 // indirect 37 | golang.org/x/text v0.16.0 // indirect 38 | golang.org/x/time v0.3.0 // indirect 39 | google.golang.org/protobuf v1.34.2 // indirect 40 | gopkg.in/inf.v0 v0.9.1 // indirect 41 | gopkg.in/yaml.v2 v2.4.0 // indirect 42 | gopkg.in/yaml.v3 v3.0.1 // indirect 43 | k8s.io/api v0.31.0 // indirect 44 | k8s.io/klog/v2 v2.130.1 // indirect 45 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect 46 | k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect 47 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 48 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 49 | sigs.k8s.io/yaml v1.4.0 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | kubetrim 📏 2 | ==================================================== 3 | 4 | Trim 📏 your KUBECONFIG automatically. 5 | 6 | [![Sponsor Alex](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/alexellis)](https://github.com/sponsors/alexellis) [![build](https://github.com/alexellis/kubetrim/actions/workflows/build.yml/badge.svg)](https://github.com/alexellis/kubetrim/actions/workflows/build.yml) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | ![Downloads](https://img.shields.io/github/downloads/alexellis/kubetrim/total) 9 | 10 | kubetrim tidies up old and broken cluster and context entries from your kubeconfig file. 11 | 12 | It works like this: 13 | 14 | 1) Loads the config specified by KUBECONFIG 15 | 2) Tries to connect to each 16 | 3) Writes a new file with only the ones that it could access 17 | 18 | Q&A: 19 | 20 | * `kubetrim` is great, how can I support you? 21 | 22 | You can [sponsor me via GitHub](https://github.com/sponsors/alexellis) - the more sponsors, the more time I'll spend on tools like kubetrim, Have a [arkade](https://github.com/alexellis/arkade) and [k3sup](https://github.com/alexellis/k3sup). 23 | 24 | * What if I like doing things the long way? 25 | 26 | You can combine `kubectl config get-clusters` with `kubectl config use-context` and `kubectl get nodes`, followed by `kubectl config delete-cluster` and `kubectl config delete-context` for each. `kubectx -d` will remove a context, but leaves the cluster in your kubeconfig file, so requires additional steps. 27 | 28 | * Doesn't [my favourite tool] have an option to do this? 29 | 30 | Feel free to use your favourite tool instead. kubetrim is a memorable name, and a simple tool that just does one job, similar to `kubectx` 31 | 32 | * What if I want to keep a cluster that is unreachable? 33 | 34 | You can add each context name to the keep file at `~/.local/kubetrim/keep.txt`. You can also use comments in this file by prefixing the comment with `# Needs work VPN etc` 35 | 36 | * What if my cluster is valid, but kubetrim cannot detect it? 37 | 38 | Open an issue, and we can look at adding support for your use-case. 39 | 40 | * How can I run kubetrim daily? 41 | 42 | Create a crontab with the following expression: `0 0 * * * kubetrim` 43 | 44 | * Can I run kubetrim every time I open a terminal? 45 | 46 | Yes, you can add `kubetrim &` to your `.bashrc`, `.bash_profile` or `.zshrc` file, which will run in the background, and not slow down your terminal session from starting up. 47 | 48 | * What if my WiFi is down and I run this tool? 49 | 50 | If all clusters show as unavailable, kubetrim will not delete any clusters, in this case add `--force` to the command. 51 | 52 | ## Usage 53 | 54 | ```bash 55 | $ kubectx 56 | 57 | default 58 | do-lon1-openfaas-cluster 59 | kind-2 60 | kind-ingress 61 | 62 | $ kubetrim 63 | 64 | kubetrim (dev) by Alex Ellis 65 | 66 | Loaded: /home/alex/.kube/config. Checking.. 67 | - kind-2: ✅ 68 | - kind-ingress: ❌ - (failed to connect to cluster: Get "https://127.0.0.1:40349/api/v1/nodes": dial tcp 127.0.0.1:40349: connect: connection refused) 69 | - default: ✅ 70 | - do-lon1-openfaas-cluster: ❌ - (failed to connect to cluster: Get "https://da39a3ee5e6b4b0d3255bfef95601890afd80709.k8s.ondigitalocean.co.uk/api/v1/nodes": dial tcp: lookup da39a3ee5e6b4b0d3255bfef95601890afd80709.k8s.ondigitalocean.co.uk on 127.0.0.53:53: no such host) 71 | Updated: /home/alex/.kube/config (in 364ms). 72 | 73 | $ kubectx 74 | 75 | default 76 | kind-2 77 | ``` 78 | 79 | Try out kubetrim without writing changes to the kubeconfig file: 80 | 81 | ```bash 82 | $ kubetrim --write=false 83 | ``` 84 | 85 | Use a different kubeconfig file: 86 | 87 | ```bash 88 | $ KUBECONFIG=$HOME/.kube/config.bak kubetrim 89 | ``` 90 | 91 | What if the Internet is unavailable, and all clusters report as unavailable? 92 | 93 | ```bash 94 | # Take down WiFi/Ethernet 95 | 96 | $ kubetrim 97 | 98 | No contexts are working, the Internet may be down, use --force to delete all contexts anyway. 99 | 100 | # Force the deletion, even if all clusters are unavailable. 101 | 102 | $ kubetrim --force 103 | ``` 104 | 105 | ## Installation 106 | 107 | Getting `kubetrim` with arkade: 108 | 109 | ```bash 110 | curl -sfLS https://get.arkade.dev | sh 111 | 112 | arkade get kubetrim 113 | ``` 114 | 115 | Or download it manually via the [Releases page](https://github.com/alexellis/kubetrim/releases) 116 | 117 | ## License 118 | 119 | MIT license 120 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "github.com/alexellis/kubetrim/pkg" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | "k8s.io/client-go/kubernetes" 15 | _ "k8s.io/client-go/plugin/pkg/client/auth/azure" 16 | _ "k8s.io/client-go/plugin/pkg/client/auth/exec" 17 | 18 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // Import for GCP auth 19 | _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // Import for OIDC (often used with EKS) 20 | "k8s.io/client-go/tools/clientcmd" 21 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 22 | 23 | "k8s.io/client-go/util/homedir" 24 | ) 25 | 26 | var ( 27 | writeFile bool 28 | force bool 29 | keepFile string 30 | ) 31 | 32 | func main() { 33 | 34 | // set usage: 35 | 36 | flag.Usage = func() { 37 | 38 | fmt.Printf("kubetrim (%s %s) Copyright Alex Ellis (c) 2024\n\n", pkg.Version, pkg.GitCommit) 39 | fmt.Print("Sponsor Alex on GitHub: https://github.com/sponsors/alexellis\n\n") 40 | fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0]) 41 | fmt.Fprintf(flag.CommandLine.Output(), "kubetrim removes contexts & clusters from your kubeconfig if they are not accessible.\n\n") 42 | fmt.Fprintf(flag.CommandLine.Output(), "Set the kubeconfig file to use through the KUBECONFIG environment variable\n") 43 | fmt.Fprintf(flag.CommandLine.Output(), "or the default location will be used: ~/.kube/config\n\n") 44 | fmt.Fprintf(flag.CommandLine.Output(), "For partially available contexts, add each name on its own line in:\n~/.local/kubetrim/keep.txt\n\n") 45 | 46 | fmt.Fprintf(flag.CommandLine.Output(), "Options:\n") 47 | 48 | flag.PrintDefaults() 49 | } 50 | 51 | flag.BoolVar(&writeFile, "write", true, "Write changes to the kubeconfig file, set to false for a dry-run.") 52 | flag.BoolVar(&force, "force", false, "Force delete all contexts, even if all are unreachable") 53 | flag.StringVar(&keepFile, "keep-file", "$HOME/.local/kubetrim/keep.txt", "Path to the keep file for clusters that are partially available") 54 | flag.Parse() 55 | 56 | keepFile = os.ExpandEnv(keepFile) 57 | 58 | var keepContexts []string 59 | if _, err := os.Stat(keepFile); err == nil { 60 | contexts, err := loadKeepContexts(keepFile) 61 | if err != nil { 62 | fmt.Fprintf(os.Stderr, "Error loading %s: error: %s", keepFile, err) 63 | os.Exit(1) 64 | } 65 | keepContexts = contexts 66 | } 67 | 68 | // Load the kubeconfig file 69 | kubeconfig := filepath.Join(homedir.HomeDir(), ".kube", "config") 70 | if configPath := os.Getenv("KUBECONFIG"); configPath != "" { 71 | kubeconfig = configPath 72 | } 73 | 74 | // Load the kubeconfig 75 | config, err := clientcmd.LoadFromFile(kubeconfig) 76 | if err != nil { 77 | fmt.Printf("Error loading kubeconfig: %v\n", err) 78 | os.Exit(1) 79 | } 80 | 81 | fmt.Printf("kubetrim (%s %s) by Alex Ellis \n\nLoaded: %s. Checking..\n", pkg.Version, pkg.GitCommit, kubeconfig) 82 | 83 | st := time.Now() 84 | // List of ckeepContextsontexts to be deleted 85 | var contextsToDelete []string 86 | 87 | // Enumerate and check all contexts 88 | for contextName := range config.Contexts { 89 | fmt.Printf(" - %s: ", contextName) 90 | skip := false 91 | for _, keep := range keepContexts { 92 | if keep == contextName { 93 | skip = true 94 | break 95 | } 96 | } 97 | 98 | if skip { 99 | fmt.Printf("skipping due to keep list ⏩\n") 100 | continue 101 | } 102 | 103 | // Set the context for the current iteration 104 | clientConfig := clientcmd.NewNonInteractiveClientConfig(*config, contextName, &clientcmd.ConfigOverrides{}, nil) 105 | restConfig, err := clientConfig.ClientConfig() 106 | if err != nil { 107 | fmt.Printf("Failed to create REST config (%v)\n", err) 108 | contextsToDelete = append(contextsToDelete, contextName) 109 | continue 110 | } 111 | 112 | // Create Kubernetes client 113 | clientset, err := kubernetes.NewForConfig(restConfig) 114 | if err != nil { 115 | fmt.Printf("Failed to create clientset (%v)\n", err) 116 | contextsToDelete = append(contextsToDelete, contextName) 117 | continue 118 | } 119 | 120 | // Check if the context is working 121 | err = checkCluster(clientset) 122 | if err != nil { 123 | fmt.Printf("❌ - (%v)\n", err) 124 | contextsToDelete = append(contextsToDelete, contextName) 125 | } else { 126 | fmt.Println("✅") 127 | } 128 | } 129 | 130 | // Delete the contexts that are not working 131 | for _, contextName := range contextsToDelete { 132 | deleteContextAndCluster(config, contextName) 133 | } 134 | 135 | if writeFile { 136 | 137 | if len(contextsToDelete) == len(config.Contexts) && !force { 138 | fmt.Println("No contexts are working, the Internet may be down, use --force to delete all contexts anyway.") 139 | os.Exit(1) 140 | } 141 | if len(contextsToDelete) > 0 { 142 | // Save the modified kubeconfig 143 | if err = clientcmd.WriteToFile(*config, kubeconfig); err != nil { 144 | fmt.Printf("Error saving updated kubeconfig: %v\n", err) 145 | os.Exit(1) 146 | } 147 | } 148 | fmt.Printf("Updated: %s (in %s).\n", kubeconfig, time.Since(st).Round(time.Millisecond)) 149 | } 150 | } 151 | 152 | // checkCluster tries to list nodes in the cluster to verify if the context is working 153 | func checkCluster(clientset *kubernetes.Clientset) error { 154 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 155 | defer cancel() 156 | 157 | _, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) 158 | if err != nil { 159 | return fmt.Errorf("failed to connect to cluster: %v", err) 160 | } 161 | return nil 162 | } 163 | 164 | // deleteContextAndCluster removes the context and its associated cluster from the config 165 | func deleteContextAndCluster(config *clientcmdapi.Config, contextName string) { 166 | 167 | // Get the cluster name associated with the context 168 | clusterName := config.Contexts[contextName].Cluster 169 | 170 | // Delete the context 171 | delete(config.Contexts, contextName) 172 | 173 | // Check if any other contexts use this cluster 174 | clusterUsed := false 175 | for _, ctx := range config.Contexts { 176 | if ctx.Cluster == clusterName { 177 | clusterUsed = true 178 | break 179 | } 180 | } 181 | 182 | // If the cluster is not used by any other context, delete it 183 | if !clusterUsed { 184 | delete(config.Clusters, clusterName) 185 | } 186 | } 187 | 188 | func loadKeepContexts(path string) ([]string, error) { 189 | keep := []string{} 190 | 191 | data, err := os.ReadFile(path) 192 | if err != nil { 193 | return keep, err 194 | } 195 | lines := strings.Split(strings.TrimSpace(string(data)), "\n") 196 | 197 | for _, line := range lines { 198 | ln := strings.TrimSpace(line) 199 | if len(ln) > 0 && !strings.HasPrefix(ln, "#") { 200 | keep = append(keep, ln) 201 | } 202 | } 203 | 204 | return keep, nil 205 | } 206 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 5 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 7 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 8 | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= 9 | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= 10 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 11 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 12 | github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= 13 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 14 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 15 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 16 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 17 | github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= 18 | github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 19 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 20 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 21 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 22 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 23 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 24 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 25 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 26 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 27 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 29 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 30 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 31 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 32 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 33 | github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= 34 | github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 35 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 36 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 37 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 38 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 39 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 40 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 41 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 42 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 44 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 45 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 46 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 47 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 48 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 49 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 50 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 51 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 52 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 53 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 54 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 55 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 56 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 57 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 58 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 59 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 60 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 61 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 62 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 63 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 64 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 65 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 66 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 67 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 68 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 69 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 70 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 71 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 72 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 73 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 74 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 75 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 76 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 77 | golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= 78 | golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 79 | golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= 80 | golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 81 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 82 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 83 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 84 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 85 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 86 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 87 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 88 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 89 | golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= 90 | golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 91 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 92 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 93 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 94 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 95 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 96 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 97 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 98 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 99 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 100 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 101 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 102 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 103 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 104 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 105 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 106 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 107 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 108 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 109 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 110 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 111 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 112 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 113 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 114 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 115 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 116 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 117 | k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= 118 | k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= 119 | k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= 120 | k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= 121 | k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= 122 | k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= 123 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 124 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 125 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= 126 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= 127 | k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= 128 | k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 129 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= 130 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 131 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= 132 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= 133 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 134 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 135 | --------------------------------------------------------------------------------