├── .gitignore ├── Dockerfile ├── charts └── envoy-sidecar-helper │ ├── templates │ ├── _helpers.tpl │ └── rbac.yaml │ ├── Chart.yaml │ └── values.yaml ├── .golangci.yml ├── examples └── envoy-sidecar-helper-test │ ├── Chart.lock │ ├── Chart.yaml │ └── templates │ └── job.yaml ├── .github └── workflows │ ├── chart-test.yml │ ├── release-chart.yml │ └── release.yml ├── Makefile ├── scripts └── validate-license.sh ├── pkg ├── client │ └── client.go └── api │ └── api.go ├── .goreleaser.yml ├── go.mod ├── cmd └── main.go ├── README.md ├── LICENSE └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /envoy-sidecar-helper 3 | /examples/envoy-sidecar-helper-test/charts -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | COPY ./envoy-sidecar-helper /app/envoy-sidecar-helper 4 | 5 | RUN addgroup -g 30001 -S app \ 6 | && adduser -u 30001 -D -S -G app app 7 | 8 | USER 30001 9 | 10 | ENTRYPOINT [ "/app/envoy-sidecar-helper" ] -------------------------------------------------------------------------------- /charts/envoy-sidecar-helper/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{- define "envoy-sidecar-helper.image" -}} 2 | image: {{ printf "%s/%s:%s" (default .image.registry .global.imageRegistry) .image.repository .image.tag }} 3 | imagePullPolicy: {{ .image.pullPolicy }} 4 | {{- end -}} -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | issues: 4 | fix: true 5 | linters: 6 | enable-all: true 7 | disable: 8 | - nestif 9 | - gochecknoglobals 10 | - varnamelen 11 | - mnd 12 | - exhaustruct 13 | - depguard 14 | - tenv 15 | - intrange 16 | - copyloopvar -------------------------------------------------------------------------------- /charts/envoy-sidecar-helper/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | icon: https://helm.sh/img/helm.svg 3 | name: envoy-sidecar-helper 4 | version: 0.0.3 5 | description: sidecar helpers for envoy 6 | maintainers: 7 | - name: maksim-paskal # Maksim Paskal 8 | email: paskal.maksim@gmail.com 9 | -------------------------------------------------------------------------------- /examples/envoy-sidecar-helper-test/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: envoy-sidecar-helper 3 | repository: file://../../charts/envoy-sidecar-helper 4 | version: 0.0.1 5 | digest: sha256:fda698e3a4df026b4738511cddc5340b2e22dcdaa37abb7c8bfb1954fdfe1f55 6 | generated: "2022-08-14T15:55:03.486904+03:00" 7 | -------------------------------------------------------------------------------- /charts/envoy-sidecar-helper/values.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | imageRegistry: "" 3 | 4 | image: 5 | registry: docker.io 6 | repository: paskalmaksim/envoy-sidecar-helper 7 | tag: v0.0.6 8 | pullPolicy: IfNotPresent 9 | 10 | rbac: 11 | serviceAccountName: envoy-sidecar-helper 12 | annotations: {} 13 | # "helm.sh/hook": pre-install,pre-upgrade 14 | # "helm.sh/hook-weight": "-7" 15 | -------------------------------------------------------------------------------- /examples/envoy-sidecar-helper-test/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | icon: https://helm.sh/img/helm.svg 3 | name: envoy-sidecar-helper-test 4 | version: 0.0.1 5 | description: sidecar helpers for envoy 6 | maintainers: 7 | - name: maksim-paskal # Maksim Paskal 8 | email: paskal.maksim@gmail.com 9 | dependencies: 10 | - name: envoy-sidecar-helper 11 | version: "*" 12 | repository: "file://../../charts/envoy-sidecar-helper" -------------------------------------------------------------------------------- /.github/workflows/chart-test.yml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | 3 | jobs: 4 | lint-test: 5 | runs-on: ubuntu-22.04 6 | steps: 7 | - name: Checkout 8 | uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | 12 | - name: Set up Helm 13 | uses: azure/setup-helm@v1 14 | with: 15 | version: v3.8.1 16 | 17 | - uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.7 20 | 21 | - name: Set up chart-testing 22 | uses: helm/chart-testing-action@v2.2.1 23 | 24 | - name: Run chart-testing (lint) 25 | run: ct lint --target-branch main -------------------------------------------------------------------------------- /.github/workflows/release-chart.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | - name: Configure Git 15 | run: | 16 | git config user.name "${GITHUB_ACTOR}" 17 | git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" 18 | - name: Install Helm 19 | uses: azure/setup-helm@v1 20 | with: 21 | version: v3.8.1 22 | 23 | - name: Run chart-releaser 24 | uses: helm/chart-releaser-action@v1.1.0 25 | env: 26 | CR_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | push: 4 | tags: 5 | - v* 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | goreleaser: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | - name: Remove Git Tags with Charts 19 | run: git tag -d $(git tag -l "envoy-sidecar-helper-*") 20 | - name: Set up Go 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: "1.23" 24 | - name: Login to Docker Hub 25 | uses: docker/login-action@v1 26 | with: 27 | username: ${{ secrets.DOCKER_USERNAME }} 28 | password: ${{ secrets.DOCKER_PASSWORD }} 29 | - uses: docker/setup-qemu-action@v2 30 | - uses: docker/setup-buildx-action@v2 31 | - name: Run GoReleaser 32 | uses: goreleaser/goreleaser-action@v2 33 | with: 34 | distribution: goreleaser 35 | version: latest 36 | args: release 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /charts/envoy-sidecar-helper/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ .Values.rbac.serviceAccountName }} 5 | {{ if .Values.rbac.annotations }} 6 | annotations: 7 | {{ toYaml .Values.rbac.annotations | indent 4 }} 8 | {{ end }} 9 | --- 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: Role 12 | metadata: 13 | name: {{ .Values.rbac.serviceAccountName }}-role 14 | {{ if .Values.rbac.annotations }} 15 | annotations: 16 | {{ toYaml .Values.rbac.annotations | indent 4 }} 17 | {{ end }} 18 | rules: 19 | - apiGroups: [""] 20 | resources: ["pods"] 21 | verbs: ["get"] 22 | --- 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | kind: RoleBinding 25 | metadata: 26 | name: {{ .Values.rbac.serviceAccountName }}-binding 27 | {{ if .Values.rbac.annotations }} 28 | annotations: 29 | {{ toYaml .Values.rbac.annotations | indent 4 }} 30 | {{ end }} 31 | roleRef: 32 | kind: Role 33 | name: {{ .Values.rbac.serviceAccountName }}-role 34 | apiGroup: rbac.authorization.k8s.io 35 | subjects: 36 | - kind: ServiceAccount 37 | name: {{ .Values.rbac.serviceAccountName }} 38 | namespace: {{ .Release.Namespace }} -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KUBECONFIG=$(HOME)/.kube/dev 2 | tag=dev 3 | 4 | lint: 5 | ct lint --all 6 | test: 7 | ./scripts/validate-license.sh 8 | go mod tidy 9 | go fmt ./cmd/... ./pkg/... 10 | go vet ./cmd/... ./pkg/... 11 | go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run -v 12 | run: 13 | go run -race ./cmd \ 14 | -log.pretty \ 15 | -kubeconfig=$(KUBECONFIG) 16 | build: 17 | git tag -d `git tag -l "envoy-sidecar-helper-*"` 18 | go run github.com/goreleaser/goreleaser@latest build --rm-dist --snapshot 19 | mv ./dist/envoy-sidecar-helper_linux_amd64_v1/envoy-sidecar-helper envoy-sidecar-helper 20 | docker build --pull --push . -t paskalmaksim/envoy-sidecar-helper:$(tag) 21 | deploy: 22 | rm -rf ./examples/envoy-sidecar-helper-test/charts 23 | helm dep up ./examples/envoy-sidecar-helper-test --skip-refresh 24 | 25 | helm upgrade --install envoy-sidecar-helper-test \ 26 | --namespace envoy-sidecar-helper \ 27 | --create-namespace \ 28 | --set envoy-sidecar-helper.image.tag=$(tag) \ 29 | --set envoy-sidecar-helper.pullPolicy=Always \ 30 | ./examples/envoy-sidecar-helper-test 31 | clean: 32 | helm -n envoy-sidecar-helper delete envoy-sidecar-helper-test || true 33 | kubectl delete ns envoy-sidecar-helper -------------------------------------------------------------------------------- /scripts/validate-license.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright paskal.maksim@gmail.com 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | set -euo pipefail 17 | IFS=$'\n\t' 18 | 19 | find_files() { 20 | find . -not \( \ 21 | \( \ 22 | -wholename './vendor' \ 23 | -o -wholename '*testdata*' \ 24 | -o -wholename '*third_party*' \ 25 | -o -wholename '*node_modules*' \ 26 | \) -prune \ 27 | \) \ 28 | \( -name '*.go' -o -name '*.sh' -o -name 'LICENSE' \) 29 | } 30 | 31 | # Use "|| :" to ignore the error code when grep returns empty 32 | failed_license_header=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")' || :)) 33 | if (( ${#failed_license_header[@]} > 0 )); then 34 | echo "Some source files are missing license headers." 35 | printf '%s\n' "${failed_license_header[@]}" 36 | exit 1 37 | fi 38 | 39 | # Use "|| :" to ignore the error code when grep returns empty 40 | failed_copyright_header=($(find_files | xargs grep -L 'Copyright paskal.maksim@gmail.com' || :)) 41 | if (( ${#failed_copyright_header[@]} > 0 )); then 42 | echo "Some source files are missing the copyright header." 43 | printf '%s\n' "${failed_copyright_header[@]}" 44 | exit 1 45 | fi 46 | -------------------------------------------------------------------------------- /pkg/client/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright paskal.maksim@gmail.com 3 | Licensed under the Apache License, Version 2.0 (the "License") 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | package client 14 | 15 | import ( 16 | "flag" 17 | 18 | "github.com/pkg/errors" 19 | log "github.com/sirupsen/logrus" 20 | "k8s.io/client-go/kubernetes" 21 | "k8s.io/client-go/rest" 22 | "k8s.io/client-go/tools/clientcmd" 23 | ) 24 | 25 | var ( 26 | kubeconfig = flag.String("kubeconfig", "", "path to kubeconfig file") 27 | insecure = flag.Bool("insecure", false, "insecure connection to kubernetes API") 28 | clientset *kubernetes.Clientset 29 | restconfig *rest.Config 30 | ) 31 | 32 | func Init() error { 33 | var err error 34 | 35 | if len(*kubeconfig) > 0 { 36 | restconfig, err = clientcmd.BuildConfigFromFlags("", *kubeconfig) 37 | if err != nil { 38 | return errors.Wrap(err, "error in clientcmd.BuildConfigFromFlags") 39 | } 40 | } else { 41 | log.Info("No kubeconfig file use incluster") 42 | 43 | restconfig, err = rest.InClusterConfig() 44 | if err != nil { 45 | return errors.Wrap(err, "error in rest.InClusterConfig") 46 | } 47 | } 48 | 49 | if *insecure { 50 | restconfig.Insecure = true 51 | } 52 | 53 | clientset, err = kubernetes.NewForConfig(restconfig) 54 | if err != nil { 55 | return errors.Wrap(err, "error in kubernetes.NewForConfig") 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func KubeClient() *kubernetes.Clientset { 62 | return clientset 63 | } 64 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: envoy-sidecar-helper 2 | release: 3 | footer: | 4 | ## Docker Images 5 | - `paskalmaksim/{{.ProjectName}}:latest` 6 | - `paskalmaksim/{{.ProjectName}}:{{ .Tag }}` 7 | docker_manifests: 8 | - name_template: paskalmaksim/{{.ProjectName}}:latest 9 | image_templates: 10 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-amd64 11 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-arm64 12 | - name_template: paskalmaksim/{{.ProjectName}}:{{.Tag}} 13 | image_templates: 14 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-amd64 15 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-arm64 16 | dockers: 17 | - use: buildx 18 | goos: linux 19 | goarch: amd64 20 | image_templates: 21 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-amd64 22 | build_flag_templates: 23 | - "--platform=linux/amd64" 24 | - "--label=org.opencontainers.image.created={{.Date}}" 25 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 26 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 27 | - "--label=org.opencontainers.image.version={{.Version}}" 28 | - use: buildx 29 | goos: linux 30 | goarch: arm64 31 | image_templates: 32 | - paskalmaksim/{{.ProjectName}}:{{.Tag}}-arm64 33 | build_flag_templates: 34 | - "--platform=linux/arm64/v8" 35 | - "--label=org.opencontainers.image.created={{.Date}}" 36 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 37 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 38 | - "--label=org.opencontainers.image.version={{.Version}}" 39 | builds: 40 | - dir: ./cmd/ 41 | env: 42 | - CGO_ENABLED=0 43 | flags: 44 | - -trimpath 45 | ldflags: 46 | - -X main.gitVersion={{.Version}}-{{.ShortCommit}}-{{.Timestamp}} 47 | goos: 48 | - linux 49 | goarch: 50 | - amd64 51 | - arm64 52 | checksum: 53 | name_template: 'checksums.txt' 54 | snapshot: 55 | name_template: "{{ .Tag }}-next" 56 | changelog: 57 | sort: asc 58 | filters: 59 | exclude: 60 | - '^docs:' 61 | - '^test:' -------------------------------------------------------------------------------- /examples/envoy-sidecar-helper-test/templates/job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: envoy-sidecar-helper-job 5 | spec: 6 | backoffLimit: 0 7 | template: 8 | spec: 9 | serviceAccount: envoy-sidecar-helper 10 | restartPolicy: Never 11 | volumes: 12 | - name: envoy-sidecar-helper 13 | emptyDir: {} 14 | containers: 15 | - name: main1 16 | image: alpine:latest 17 | imagePullPolicy: Always 18 | command: 19 | - sh 20 | - -c 21 | - | 22 | set -ex 23 | 24 | while [ ! -f /envoy-sidecar-helper/envoy.ready ]; do sleep 1s; done 25 | 26 | echo envoy ready 27 | volumeMounts: 28 | - mountPath: /envoy-sidecar-helper 29 | name: envoy-sidecar-helper 30 | - name: main2 31 | image: alpine:latest 32 | imagePullPolicy: Always 33 | command: 34 | - sh 35 | - -c 36 | - | 37 | set -ex 38 | 39 | while [ ! -f /envoy-sidecar-helper/envoy.ready ]; do sleep 1s; done 40 | 41 | sleep 5s 42 | echo envoy ready 43 | volumeMounts: 44 | - mountPath: /envoy-sidecar-helper 45 | name: envoy-sidecar-helper 46 | - name: envoy 47 | image: envoyproxy/envoy-dev 48 | imagePullPolicy: Always 49 | # helpers 50 | - name: envoy-sidecar-helper 51 | {{ include "envoy-sidecar-helper.image" (index .Values "envoy-sidecar-helper") | indent 8 }} 52 | args: 53 | - -log.level=DEBUG 54 | - -container=main1,main2 55 | - -envoy.ready.check=true 56 | - -envoy.endpoint.ready=/ready 57 | - -envoy.port=9901 58 | env: 59 | - name: POD_NAME 60 | valueFrom: 61 | fieldRef: 62 | fieldPath: metadata.name 63 | - name: POD_NAMESPACE 64 | valueFrom: 65 | fieldRef: 66 | fieldPath: metadata.namespace 67 | volumeMounts: 68 | - mountPath: /envoy-sidecar-helper 69 | name: envoy-sidecar-helper -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maksim-paskal/envoy-sidecar-helper 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/pkg/errors v0.9.1 9 | github.com/sirupsen/logrus v1.9.3 10 | k8s.io/api v0.32.3 11 | k8s.io/apimachinery v0.32.3 12 | k8s.io/client-go v0.32.3 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 17 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 18 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 19 | github.com/go-logr/logr v1.4.2 // indirect 20 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 21 | github.com/go-openapi/jsonreference v0.20.2 // indirect 22 | github.com/go-openapi/swag v0.23.0 // indirect 23 | github.com/gogo/protobuf v1.3.2 // indirect 24 | github.com/golang/protobuf v1.5.4 // indirect 25 | github.com/google/gnostic-models v0.6.8 // indirect 26 | github.com/google/go-cmp v0.6.0 // indirect 27 | github.com/google/gofuzz v1.2.0 // indirect 28 | github.com/google/uuid v1.6.0 // indirect 29 | github.com/josharian/intern v1.0.0 // indirect 30 | github.com/json-iterator/go v1.1.12 // indirect 31 | github.com/mailru/easyjson v0.7.7 // indirect 32 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 33 | github.com/modern-go/reflect2 v1.0.2 // indirect 34 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 35 | github.com/spf13/pflag v1.0.5 // indirect 36 | github.com/x448/float16 v0.8.4 // indirect 37 | golang.org/x/net v0.30.0 // indirect 38 | golang.org/x/oauth2 v0.23.0 // indirect 39 | golang.org/x/sys v0.31.0 // indirect 40 | golang.org/x/term v0.25.0 // indirect 41 | golang.org/x/text v0.19.0 // indirect 42 | golang.org/x/time v0.7.0 // indirect 43 | google.golang.org/protobuf v1.35.1 // indirect 44 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 45 | gopkg.in/inf.v0 v0.9.1 // indirect 46 | gopkg.in/yaml.v3 v3.0.1 // indirect 47 | k8s.io/klog/v2 v2.130.1 // indirect 48 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect 49 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 50 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 51 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect 52 | sigs.k8s.io/yaml v1.4.0 // indirect 53 | ) 54 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright paskal.maksim@gmail.com 3 | Licensed under the Apache License, Version 2.0 (the "License") 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | package main 14 | 15 | import ( 16 | "context" 17 | "flag" 18 | "os" 19 | "os/signal" 20 | "syscall" 21 | 22 | "github.com/maksim-paskal/envoy-sidecar-helper/pkg/api" 23 | "github.com/maksim-paskal/envoy-sidecar-helper/pkg/client" 24 | log "github.com/sirupsen/logrus" 25 | ) 26 | 27 | var gitVersion = "dev" 28 | 29 | var ( 30 | logLevel = flag.String("log.level", "INFO", "log level (DEBUG, INFO, WARN, ERROR, FATAL, PANIC)") 31 | logPretty = flag.Bool("log.pretty", false, "logs in text format") 32 | logReportCaller = flag.Bool("log.reportCaller", true, "log line number and file name") 33 | ) 34 | 35 | func main() { 36 | flag.Parse() 37 | 38 | log.Infof("Staring envoy-sidecar-helper %s...", gitVersion) 39 | 40 | logLevel, err := log.ParseLevel(*logLevel) 41 | if err != nil { 42 | log.WithError(err).Fatal() 43 | } 44 | 45 | log.SetLevel(logLevel) 46 | 47 | if *logPretty { 48 | log.SetFormatter(&log.TextFormatter{}) 49 | } else { 50 | log.SetFormatter(&log.JSONFormatter{}) 51 | } 52 | 53 | if logLevel == log.DebugLevel || *logReportCaller { 54 | log.SetReportCaller(true) 55 | } 56 | 57 | if err := client.Init(); err != nil { 58 | log.WithError(err).Fatal() 59 | } 60 | 61 | ctx, cancel := context.WithCancel(context.Background()) 62 | defer cancel() 63 | 64 | signalChanInterrupt := make(chan os.Signal, 1) 65 | signal.Notify(signalChanInterrupt, syscall.SIGINT, syscall.SIGTERM) 66 | 67 | go func() { 68 | <-signalChanInterrupt 69 | log.Error("Got interruption signal...") 70 | cancel() 71 | 72 | <-signalChanInterrupt 73 | os.Exit(1) 74 | }() 75 | 76 | // wait for envoy start 77 | api.CheckEnvoyStart(ctx) 78 | 79 | // check container status 80 | api.CheckContainerStop(ctx) 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sidecar helper for service-meshes 2 | 3 | ## Motivation 4 | 5 | A service mesh is a dedicated infrastructure layer that you can add to your applications. This additional layer is based on adding a proxy "sidecar" along with every application deployed. 6 | 7 | Sometime it's hard to handle this "sidecar" with job or daemons: 8 | 9 | ### Problem #1 10 | 11 | Jobs or daemons need that "proxy" sidecar was ready before executing application 12 | 13 | ### Problem #2 14 | 15 | After executing job (success or failure), "proxy" sidecar must be stoped 16 | 17 | ## How it works 18 | 19 | `envoy-sidecar-helper` is additional sidecar container that will monitor Termination of main application container (with Kubernetes API), and will shutdown envoy "proxy" sidecar. Also it can share via `emptyDir` volume information about ready envoy container 20 | 21 | ```yaml 22 | ... 23 | serviceAccount: envoy-sidecar-helper 24 | volumes: 25 | - name: envoy-sidecar-helper 26 | emptyDir: {} 27 | containers: 28 | - name: main 29 | image: alpine:latest 30 | imagePullPolicy: Always 31 | command: 32 | - sh 33 | - -c 34 | - | 35 | set -ex 36 | 37 | while [ ! -f /envoy-sidecar-helper/envoy.ready ]; do sleep 1s; done 38 | 39 | # start your application 40 | echo envoy ready 41 | volumeMounts: 42 | - mountPath: /envoy-sidecar-helper 43 | name: envoy-sidecar-helper 44 | - name: envoy 45 | image: envoyproxy/envoy-dev 46 | imagePullPolicy: Always 47 | ########################################### 48 | # envoy helper 49 | ########################################### 50 | - name: envoy-sidecar-helper 51 | image: paskalmaksim/envoy-sidecar-helper:latest 52 | imagePullPolicy: Always 53 | args: 54 | - -envoy.ready.check=true 55 | - -envoy.endpoint.ready=/ready 56 | - -envoy.port=9901 57 | env: 58 | - name: POD_NAME 59 | valueFrom: 60 | fieldRef: 61 | fieldPath: metadata.name 62 | - name: POD_NAMESPACE 63 | valueFrom: 64 | fieldRef: 65 | fieldPath: metadata.namespace 66 | volumeMounts: 67 | - mountPath: /envoy-sidecar-helper 68 | name: envoy-sidecar-helper 69 | ... 70 | ``` 71 | 72 | `envoy-sidecar-helper` need service account with permissions to get pod information 73 | 74 | ```yaml 75 | apiVersion: v1 76 | kind: ServiceAccount 77 | metadata: 78 | name: envoy-sidecar-helper 79 | --- 80 | apiVersion: rbac.authorization.k8s.io/v1 81 | kind: Role 82 | metadata: 83 | name: envoy-sidecar-helper-role 84 | rules: 85 | - apiGroups: [""] 86 | resources: ["pods"] 87 | verbs: ["get"] 88 | --- 89 | apiVersion: rbac.authorization.k8s.io/v1 90 | kind: RoleBinding 91 | metadata: 92 | name: envoy-sidecar-helper 93 | roleRef: 94 | kind: Role 95 | name: envoy-sidecar-helper-role 96 | apiGroup: rbac.authorization.k8s.io 97 | subjects: 98 | - kind: ServiceAccount 99 | name: envoy-sidecar-helper 100 | namespace: default 101 | ``` 102 | -------------------------------------------------------------------------------- /pkg/api/api.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright paskal.maksim@gmail.com 3 | Licensed under the Apache License, Version 2.0 (the "License") 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | package api 14 | 15 | import ( 16 | "context" 17 | "flag" 18 | "fmt" 19 | "net/http" 20 | "os" 21 | "strings" 22 | "time" 23 | 24 | "github.com/maksim-paskal/envoy-sidecar-helper/pkg/client" 25 | "github.com/pkg/errors" 26 | log "github.com/sirupsen/logrus" 27 | v1 "k8s.io/api/core/v1" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | ) 30 | 31 | var ( 32 | envoyHost = flag.String("envoy.host", "http://127.0.0.1", "envoy host") 33 | envoyPort = flag.Int("envoy.port", 15020, "envoy port") 34 | exitZero = flag.Bool("exit.zero", false, "watch containers for zero exit status code") 35 | podName = flag.String("pod", os.Getenv("POD_NAME"), "pod name") 36 | namespace = flag.String("namespace", os.Getenv("POD_NAMESPACE"), "namespace") 37 | containersName = flag.String("container", "", "container or containers to watch (splited with comma)") 38 | envoyReadyPort = flag.Int("envoy.ready.port", 15021, "health port") 39 | envoyReadyCheck = flag.Bool("envoy.ready.check", true, "check envoy is ready") 40 | envoyReadyFile = flag.String("envoy.ready.file", "/envoy-sidecar-helper/envoy.ready", "") 41 | envoyReadyEndpoint = flag.String("envoy.endpoint.ready", "/healthz/ready", "endpoint to check envoy is ready") 42 | envoyQuitEndpoint = flag.String("envoy.endpoint.quit", "/quitquitquit", "endpoint to quit envoy") 43 | checkDuration = flag.Duration("check.duration", time.Second, "duration to check if container is stopped") 44 | httpTimeout = flag.Duration("http.timeout", time.Second*5, "http timeout") 45 | ) 46 | 47 | var httpClient = &http.Client{ 48 | Timeout: *httpTimeout, 49 | } 50 | 51 | var errContainerStillRunning = errors.New("containers still running") 52 | 53 | // wait for envoy sidecar to be ready. 54 | func CheckEnvoyStart(ctx context.Context) { 55 | if !*envoyReadyCheck { 56 | log.Info("envoy ready check disabled") 57 | 58 | return 59 | } 60 | 61 | log.Infof("waiting for envoy will be ready %s:%d%s", *envoyHost, *envoyReadyPort, *envoyReadyEndpoint) 62 | 63 | for { 64 | if ctx.Err() != nil { 65 | return 66 | } 67 | 68 | time.Sleep(*checkDuration) 69 | 70 | if err := makeCall(ctx, "GET", *envoyHost, *envoyReadyPort, *envoyReadyEndpoint); err != nil { 71 | log.WithError(err).Debug() 72 | } else { 73 | break 74 | } 75 | } 76 | 77 | log.Info("envoy is ready") 78 | 79 | if err := os.WriteFile(*envoyReadyFile, []byte("ok"), 0o644); err != nil { //nolint:gosec 80 | log.WithError(err).Error() 81 | } 82 | } 83 | 84 | // check if container is stoped. 85 | func IsContainerStoped(ctx context.Context) (bool, error) { 86 | pod, err := client.KubeClient().CoreV1().Pods(*namespace).Get(ctx, *podName, metav1.GetOptions{}) 87 | if err != nil { 88 | return false, errors.Wrap(err, "error getting pod") 89 | } 90 | 91 | // use first container if not specified 92 | podContainersName := []string{pod.Spec.Containers[0].Name} 93 | 94 | // if container name is specified, use it 95 | if len(*containersName) > 0 { 96 | podContainersName = strings.Split(*containersName, ",") 97 | } 98 | 99 | log.Debugf("containers to watch %v", podContainersName) 100 | 101 | states := getTermStates(podContainersName, pod) 102 | 103 | foundContainers := 0 104 | 105 | // check if containers are stopped 106 | for _, term := range states { 107 | if term != nil { 108 | switch { 109 | case *exitZero: 110 | if term.ExitCode == 0 { 111 | foundContainers++ 112 | } 113 | default: 114 | foundContainers++ 115 | } 116 | } 117 | } 118 | 119 | log.Debugf("foundContainers=%d,allPodContainers=%d", foundContainers, len(podContainersName)) 120 | 121 | // if all watched containers are stopped, return true 122 | if foundContainers == len(podContainersName) { 123 | return true, nil 124 | } 125 | 126 | containersName := strings.Join(podContainersName, ",") 127 | 128 | // if not all watched containers are stopped, return false 129 | return false, errors.Wrap(errContainerStillRunning, containersName) 130 | } 131 | 132 | type termStates map[string]*v1.ContainerStateTerminated 133 | 134 | // getTermStates will get container termination states for provided names. 135 | func getTermStates(names []string, pod *v1.Pod) termStates { 136 | states := termStates{} 137 | 138 | for _, podContainerName := range names { 139 | states[podContainerName] = nil 140 | } 141 | 142 | // get only the states for containers listed 143 | for _, containerStatus := range pod.Status.ContainerStatuses { 144 | if _, ok := states[containerStatus.Name]; ok { 145 | states[containerStatus.Name] = containerStatus.State.Terminated 146 | } 147 | } 148 | 149 | return states 150 | } 151 | 152 | // check is watched containers is stopped. 153 | func CheckContainerStop(ctx context.Context) { 154 | log.Info("waiting for container stop") 155 | 156 | for { 157 | if ctx.Err() != nil { 158 | return 159 | } 160 | 161 | time.Sleep(*checkDuration) 162 | 163 | stoped, err := IsContainerStoped(ctx) 164 | if err != nil { 165 | log.WithError(err).Error() 166 | } 167 | 168 | if stoped { 169 | break 170 | } 171 | } 172 | 173 | if err := makeCall(ctx, "POST", *envoyHost, *envoyPort, *envoyQuitEndpoint); err != nil { 174 | log.WithError(err).Error() 175 | } 176 | } 177 | 178 | // make http call to envoy sidecar. 179 | func makeCall(ctx context.Context, method string, host string, port int, path string) error { 180 | url := fmt.Sprintf("%s:%d%s", host, port, path) 181 | 182 | log.Debugf("create request %s, %s", method, url) 183 | 184 | req, err := http.NewRequestWithContext(ctx, method, url, nil) 185 | if err != nil { 186 | return errors.Wrap(err, "error in http.NewRequestWithContext") 187 | } 188 | 189 | resp, err := httpClient.Do(req) 190 | if err != nil { 191 | return errors.Wrap(err, "errors in httpClient.Do") 192 | } 193 | 194 | defer resp.Body.Close() 195 | 196 | if resp.StatusCode != http.StatusOK { 197 | err := fmt.Errorf("unexpected response code: %d", resp.StatusCode) //nolint:goerr113 198 | 199 | return errors.Wrap(err, "response status not OK") 200 | } 201 | 202 | return nil 203 | } 204 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright paskal.maksim@gmail.com 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /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/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 13 | github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 14 | github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 15 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 16 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 17 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 18 | github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= 19 | github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= 20 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 21 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 22 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 23 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 24 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 25 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 26 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 27 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 28 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 29 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 30 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 31 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 32 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 33 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 34 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= 35 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 36 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 37 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 38 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 39 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 40 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 41 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 42 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 43 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 44 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 45 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 46 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 47 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 48 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 49 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 50 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 51 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 52 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 53 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 54 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 56 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 57 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 58 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 59 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 60 | github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= 61 | github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 62 | github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= 63 | github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 64 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 65 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 66 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 67 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 68 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 69 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 70 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 71 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 72 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 73 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 74 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 75 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 76 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 77 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 78 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 79 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 80 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 81 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 82 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 83 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 84 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 85 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 86 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 87 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 88 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 89 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 90 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 91 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 92 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 93 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 94 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 95 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 96 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 97 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 98 | golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= 99 | golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 100 | golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= 101 | golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 102 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 103 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 104 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 105 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 106 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 107 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 108 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 109 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 110 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 111 | golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= 112 | golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 113 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 114 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 115 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 116 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 117 | golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= 118 | golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 119 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 121 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 122 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 123 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 124 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 125 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 126 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 127 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 128 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 129 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 130 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 131 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 132 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 133 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 134 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= 135 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= 136 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 137 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 138 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 139 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 140 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 141 | k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= 142 | k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= 143 | k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= 144 | k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= 145 | k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= 146 | k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= 147 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 148 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 149 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= 150 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= 151 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= 152 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 153 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= 154 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= 155 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= 156 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= 157 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 158 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 159 | --------------------------------------------------------------------------------