Compare commits

...

42 Commits

Author SHA1 Message Date
Yuxing Deng
18027bbaeb fix: the usage is not compatible with old version 2024-11-25 09:31:06 +08:00
Yuxing Deng
99c03af96e fix: wrong branch name 2024-10-21 21:49:53 +08:00
Yuxing Deng
618cd9abfb fix: add release-v branch to push workflow 2024-10-21 21:48:13 +08:00
Yuxing Deng
9c5a29c9cc fix: CROSS push 2024-10-21 21:45:47 +08:00
Yuxing Deng
2c0afb3776 fix: aliyun-metadata should have registry name for image 2024-10-21 21:45:47 +08:00
Yuxing Deng
ca5f23170f feat: use goreleaser to build binaries 2024-10-21 16:36:57 +08:00
Yuxing Deng
24a7324f92 feat: bump ui version 2024-10-08 11:05:54 +08:00
Yuxing Deng
71ad854ab1 feat: Switch to steve v2.8 version 2024-07-30 10:21:57 +08:00
Yuxing Deng
67923822f5 fix: Container doesn't stop with signal 2024-07-29 17:24:10 +08:00
Yuxing Deng
1540341550 docs: Add docs for path prefix deploy example 2024-07-29 15:07:34 +08:00
Yuxing Deng
a5e53f2b17 feat: Support for nginx ingress path prefix
And validate should failed if git tree is dirty
2024-07-29 15:07:34 +08:00
Yuxing Deng
8f069c3b38 feat: Full support for proxy deploy
The index page will be handled correctly if the `XFF` and `X-API-URL-PREFIX` are set properly.
2024-07-25 17:32:01 +08:00
Yuxing Deng
568eda3e52 fix: Index content-type missing problem 2024-07-25 17:32:01 +08:00
Yuxing Deng
eacc47482e refactor: UI resource logic
- Support embed api-ui resources
- The ui-path arg will be applied if provided. Also applied to api-ui resource files
2024-07-23 16:53:49 +08:00
Yuxing Deng
004e4751c8 fix: aliyun image is not built 2024-07-18 20:37:08 +08:00
Yuxing Deng
8bf22555dd fix: only release kube-explorer binary in release 2024-07-18 19:56:31 +08:00
Yuxing Deng
896e03e279 feat(ci): Added release step for tag
Skip compress process for PR build
Add commit message check
Add release-note
2024-07-18 17:19:41 +08:00
Yuxing Deng
979b4991fa feat: switch to GHA 2024-07-18 10:08:15 +08:00
Yuxing Deng
aec9926ed8 fix: Add dockerignore file to clean up each ci build 2024-07-17 15:02:39 +08:00
Yuxing Deng
2f3c1e6ab5 feat: Support specifying shell pod image and registry 2024-07-17 15:02:39 +08:00
Yuxing Deng
faa83722a0 refactor: No need to customize steve for explorer 2024-07-16 16:30:23 +08:00
Yuxing Deng
cd955243b6 dep(kube-explorer): Bump steve
Rebase to rancher v2.8.0 baseline
Upgrade ke-steve to ke/v0.4
Upgrade DASHBOARD UI version to v2.8.0
2024-01-05 15:00:39 +08:00
Yuxing Deng
2b39db9f07 fix(ci): Fix multi-arch build problem
Added build linux/arm64 binary to default ci
2023-11-15 16:30:57 +08:00
Yuxing Deng
4dc1acb1f2 feat(ci): Improve drone pipeline configuration
- Separate push and tag pipeline
- Use buildx to build and push multi-arch image
2023-11-15 16:09:58 +08:00
Yuxing Deng
989d087b99 No need to run hack_fs
As the embed fs packaged all files.
And bump upx version.
2023-11-13 16:29:48 +08:00
Yuxing Deng
c214e6ba6a Add windows amd64 support
Bump steve customized logic to fix serving embed assets problem
2023-11-13 15:46:03 +08:00
Yuxing Deng
390b11caef Bump steve
And upgrade builder image to v1.21.
2023-11-10 15:24:31 +08:00
niusmallnan
e016261c4b Bump steve and dashboard 2023-07-14 13:36:54 +08:00
niusmallnan
c43288964a Bump dashboard 2023-07-13 15:20:20 +08:00
niusmallnan
70e586976d Bump dashboard 2023-07-13 12:16:04 +08:00
niusmallnan
d0ce0e28bf Bump dashboard 2023-07-13 10:46:03 +08:00
niusmallnan
ad0a0c0cb3 Bump steve and dashboard for v2.7.5 2023-07-13 09:02:30 +08:00
niusmallnan
651d499086 Bumo bci 15.5 2023-07-11 11:35:10 +08:00
niusmallnan
8e592b1a3c Bump steve and dashboard for Rancher v2.7.2 2023-04-18 12:20:04 +08:00
niusmallnan
c1f5fda228 [CI SKIP] Update README for basic auth 2023-04-11 11:07:14 +08:00
niusmallnan
10e5323c95 [CI SKIP] Use ingress v1 for nginx-ingress basic auth demo 2023-04-10 17:18:22 +08:00
niusmallnan
ea49f9d3b4 [CI SKIP] Add basic-auth manifests for traefik v2 2023-04-10 17:09:30 +08:00
niusmallnan
b0b81ba87d [CI SKIP] switch to sslip.io for ingress demos 2023-04-10 16:20:43 +08:00
niusmallnan
e757347def Disable compress for darwin releases 2023-04-04 09:08:50 +08:00
niusmallnan
f4970b85a2 Bump steve and dashboard for Rancher v2.7.1 2023-03-20 15:13:04 +08:00
niusmallnan
bfae192748 Use --scanners instead of --security-checks 2023-03-20 11:24:18 +08:00
niusmallnan
3810cd702f Bump upx 4.0.2 2023-03-20 11:23:47 +08:00
57 changed files with 2500 additions and 496 deletions

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
/bin
/dist
/internal/ui/ui

View File

@@ -1,309 +0,0 @@
---
kind: pipeline
name: default-amd64
platform:
os: linux
arch: amd64
steps:
- name: build
pull: default
image: rancher/dapper:v0.5.8
commands:
- dapper ci
privileged: true
volumes:
- name: docker
path: /var/run/docker.sock
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- pull_request
- name: release
pull: default
image: rancher/dapper:v0.5.8
commands:
- dapper ci
privileged: true
environment:
CROSS: 1
volumes:
- name: docker
path: /var/run/docker.sock
when:
event:
- tag
- name: stage-binaries-head
image: rancher/dapper:v0.5.8
commands:
- "cp -r ./bin/kube-explorer ./package/"
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- name: stage-binaries
image: rancher/dapper:v0.5.8
commands:
- "cp -r ./bin/kube-explorer-linux-amd64 ./package/kube-explorer"
when:
event:
- tag
- name: github_binary_release
pull: default
image: plugins/github-release
settings:
api_key:
from_secret: github_token
checksum:
- sha256
files:
- "bin/*"
title: "${DRONE_TAG}"
overwrite: true
when:
event:
- tag
- name: docker-publish-head
pull: default
image: plugins/docker
settings:
dockerfile: package/Dockerfile
context: package/
password:
from_secret: docker_password
repo: cnrancher/kube-explorer
tag: head-linux-amd64
username:
from_secret: docker_username
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- name: image-scan-head
image: aquasec/trivy
commands:
- trivy image --no-progress --ignore-unfixed --severity HIGH,CRITICAL --security-checks vuln --exit-code 1 cnrancher/kube-explorer:head-linux-amd64
volumes:
- name: docker
path: /var/run/docker.sock
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- name: docker-publish
pull: default
image: plugins/docker
settings:
dockerfile: package/Dockerfile
context: package/
password:
from_secret: docker_password
repo: cnrancher/kube-explorer
tag: ${DRONE_TAG}-linux-amd64
username:
from_secret: docker_username
when:
event:
- tag
volumes:
- name: docker
host:
path: /var/run/docker.sock
node:
instance: agent-amd64
trigger:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- "refs/tags/*"
event:
exclude:
- promote
---
kind: pipeline
name: default-arm64
platform:
os: linux
arch: arm64
steps:
- name: build
pull: default
image: rancher/dapper:v0.5.8
commands:
- dapper ci
privileged: true
volumes:
- name: docker
path: /var/run/docker.sock
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- "refs/tags/*"
event:
- push
- tag
- name: stage-binaries
image: rancher/dapper:v0.5.8
commands:
- "cp -r ./bin/* ./package/"
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- "refs/tags/*"
event:
- push
- tag
- name: docker-publish-head
pull: default
image: plugins/docker
settings:
build_args:
- ARCH=arm64
dockerfile: package/Dockerfile
context: package/
password:
from_secret: docker_password
repo: cnrancher/kube-explorer
tag: head-linux-arm64
username:
from_secret: docker_username
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- name: docker-publish
pull: default
image: plugins/docker
settings:
build_args:
- ARCH=arm64
dockerfile: package/Dockerfile
context: package/
password:
from_secret: docker_password
repo: cnrancher/kube-explorer
tag: ${DRONE_TAG}-linux-arm64
username:
from_secret: docker_username
when:
event:
- tag
volumes:
- name: docker
host:
path: /var/run/docker.sock
trigger:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- "refs/tags/*"
event:
exclude:
- promote
node:
instance: agent-arm64
---
kind: pipeline
name: manifest
platform:
os: linux
arch: amd64
steps:
- name: push-manifest-head
image: plugins/manifest
settings:
ignore_missing: true
username:
from_secret: docker_username
password:
from_secret: docker_password
spec: manifest-head.tmpl
when:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
event:
- push
- name: push-manifest
image: plugins/manifest
settings:
ignore_missing: true
username:
from_secret: docker_username
password:
from_secret: docker_password
spec: manifest.tmpl
when:
event:
- tag
volumes:
- name: docker
host:
path: /var/run/docker.sock
node:
instance: agent-amd64
trigger:
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- "refs/tags/*"
event:
exclude:
- promote
depends_on:
- default-amd64
- default-arm64
...

165
.drone_backup.yml Normal file
View File

@@ -0,0 +1,165 @@
type: docker
kind: pipeline
name: push
platform:
os: linux
arch: amd64
trigger:
event:
exclude:
- promote
include:
- push
- pull_request
volumes:
- name: docker
host:
path: /var/run/docker.sock
node:
instance: agent-amd64
steps:
- name: build
image: rancher/dapper:v0.6.0
commands:
- dapper ci
environment:
CROSS: "${DRONE_BUILD_EVENT}"
privileged: true
volumes:
- name: docker
path: /var/run/docker.sock
- name: image-scan-head
image: aquasec/trivy
commands:
- trivy image --no-progress --ignore-unfixed --severity HIGH,CRITICAL --scanners vuln --exit-code 1 cnrancher/kube-explorer:${DRONE_COMMIT:0:7}
volumes:
- name: docker
path: /var/run/docker.sock
when:
event:
- push
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- name: install-buildx-support
image: tonistiigi/binfmt
privileged: true
entrypoint:
- /usr/bin/binfmt
command:
- --install
- all
when:
event:
- push
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
- name: docker-publish
image: thegeeklab/drone-docker-buildx
privileged: true
settings:
platforms: linux/amd64,linux/arm64/v8
dockerfile: package/Dockerfile
repo: cnrancher/kube-explorer
tag: latest
username:
from_secret: docker_username
password:
from_secret: docker_password
volumes:
- name: docker
path: /var/run/docker.sock
when:
event:
- push
ref:
include:
- "refs/heads/main"
- "refs/heads/v*"
---
type: docker
kind: pipeline
name: tag
platform:
os: linux
arch: amd64
trigger:
event:
exclude:
- promote
include:
- tag
ref:
include:
- "refs/tags/*"
volumes:
- name: docker
host:
path: /var/run/docker.sock
node:
instance: agent-amd64
steps:
- name: release
image: rancher/dapper:v0.6.0
commands:
- dapper ci
privileged: true
environment:
CROSS: "${DRONE_BUILD_EVENT}"
volumes:
- name: docker
path: /var/run/docker.sock
- name: install-buildx-support
image: tonistiigi/binfmt
privileged: true
entrypoint:
- /usr/bin/binfmt
command:
- --install
- all
- name: docker-publish
image: thegeeklab/drone-docker-buildx
privileged: true
settings:
platforms: linux/amd64,linux/arm64/v8
dockerfile: package/Dockerfile
repo: cnrancher/kube-explorer
tag: ${DRONE_TAG}
username:
from_secret: docker_username
password:
from_secret: docker_password
volumes:
- name: docker
path: /var/run/docker.sock
- name: github_binary_release
image: plugins/github-release
settings:
api_key:
from_secret: github_token
checksum:
- sha256
files:
- "bin/*"
title: "${DRONE_TAG}"
overwrite: true

19
.github/workflows/pr.yaml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: pull request
on:
pull_request:
types:
- opened
- reopened
- synchronize
jobs:
pr-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Commitsar check
uses: aevea/commitsar@v0.20.2
- name: Build to test
run: make ci

91
.github/workflows/push.yaml vendored Normal file
View File

@@ -0,0 +1,91 @@
name: Push to Master
on:
push:
branches:
- main
- "release/v*"
tags:
- "v*.*.*" # Matches any tag that starts with 'v' and follows semantic versioning
env:
ALIYUN_REGISTRY: ${{ vars.ALIYUN_REGISTRY || '' }}
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: CI
env:
SKIP_PACKAGE: "true"
run: make ci
- name: Prepare for packaging image
run: cp dist/* package/
# aliyun image to test the docker build is ok
- name: Login to Aliyun ACR
if: ${{ vars.ALIYUN_REGISTRY != '' }}
uses: docker/login-action@v3
with:
registry: ${{ env.ALIYUN_REGISTRY }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_TOKEN }}
- name: Aliyun image docker meta
if: ${{ vars.ALIYUN_REGISTRY != '' }}
id: aliyun-meta
uses: docker/metadata-action@v5
with:
images: ${{ env.ALIYUN_REGISTRY }}/${{ vars.REPO || 'cnrancher' }}/${{ vars.IMAGE || 'kube-explorer' }}
tags: |
type=ref,event=tag
type=ref,event=branch,suffix=-head
- name: Build to Aliyun
if: ${{ vars.ALIYUN_REGISTRY != '' }}
uses: docker/build-push-action@v6
with:
labels: ${{ steps.aliyun-meta.outputs.labels }}
tags: "${{ steps.aliyun-meta.outputs.tags }}"
context: package
push: true
# docker hub multi-arch image
- name: Login to Dockerhub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ vars.REPO || 'cnrancher' }}/${{ vars.IMAGE || 'kube-explorer' }}
tags: |
type=ref,event=tag
type=ref,event=branch,suffix=-head
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build to Dockerhub
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64/v8
labels: ${{ steps.meta.outputs.labels }}
tags: "${{ steps.meta.outputs.tags }}"
context: package
push: true
- name: Make release note
if: startsWith(github.ref, 'refs/tags/')
run: |
make release-note
- name: Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: dist/kube-explorer-*
body_path: dist/release-note
draft: true

8
.gitignore vendored
View File

@@ -19,3 +19,11 @@
/dist
/build
*.swp
/.vscode
/vendor
/internal/ui/ui/
**/Dockerfile.dapper*
!**/Dockerfile.dapper
dist/

68
.golangci.json Normal file
View File

@@ -0,0 +1,68 @@
{
"linters": {
"disable-all": true,
"enable": [
"govet",
"revive",
"goimports",
"misspell",
"ineffassign",
"gofmt"
]
},
"linters-settings": {
"govet": {
"check-shadowing": false
},
"gofmt": {
"simplify": false
}
},
"run": {
"skip-dirs": [
"vendor",
"tests",
"pkg/client",
"pkg/generated",
"scripts"
],
"tests": false,
"timeout": "10m"
},
"issues": {
"exclude-rules": [
{
"linters": "govet",
"text": "^(nilness|structtag)"
},
{
"path":"pkg/apis/management.cattle.io/v3/globaldns_types.go",
"text":".*lobalDns.*"
},
{
"path": "pkg/apis/management.cattle.io/v3/zz_generated_register.go",
"text":".*lobalDns.*"
},
{
"path":"pkg/apis/management.cattle.io/v3/zz_generated_list_types.go",
"text":".*lobalDns.*"
},
{
"linters": "revive",
"text": "should have comment"
},
{
"linters": "revive",
"text": "should be of the form"
},
{
"linters": "revive",
"text": "by other packages, and that stutters"
},
{
"linters": "typecheck",
"text": "imported but not used as apierrors"
}
]
}
}

75
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,75 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
dist: bin
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
builds:
- id: prod
env:
- CGO_ENABLED=0
targets:
- "darwin_amd64"
- "darwin_arm64"
- "linux_amd64"
- "linux_arm64"
- "linux_arm"
- "windows_amd64"
flags:
- -tags=embed
binary: '{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}'
ldflags: |
{{ if ne .Os "darwin" }}
-extldflags -static -s
{{ else }}
-s -w
{{ end }}
-X github.com/cnrancher/kube-explorer/internal/version.Version={{ .Env.VERSION }}
-X github.com/cnrancher/kube-explorer/internal/version.GitCommit={{ .Env.COMMIT }}
-X github.com/cnrancher/kube-explorer/internal/config.APIUIVersion={{ .Env.CATTLE_API_UI_VERSION }}
no_unique_dist_dir: true
- id: dev
env:
- CGO_ENABLED=0
targets:
- "linux_amd64"
- "linux_arm64"
flags:
- -tags=embed
binary: '{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}'
ldflags: |
{{ if ne .Os "darwin" }}
-extldflags -static -s
{{ else }}
-s -w
{{ end }}
-X github.com/cnrancher/kube-explorer/internal/version.Version={{ .Env.VERSION }}
-X github.com/cnrancher/kube-explorer/internal/version.GitCommit={{ .Env.COMMIT }}
-X github.com/cnrancher/kube-explorer/internal/config.APIUIVersion={{ .Env.CATTLE_API_UI_VERSION }}
no_unique_dist_dir: true
upx:
- compress: "5"
ids:
- prod
enabled: true
goos:
- linux
- windows
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"

View File

@@ -1,31 +1,30 @@
FROM golang:1.19
FROM goreleaser/goreleaser:v2.3.2 as goreleaser
FROM aevea/release-notary:0.9.2 as tools
FROM registry.suse.com/bci/golang:1.22
ARG PROXY
ARG GOPROXY
ARG DAPPER_HOST_ARCH
ENV HOST_ARCH=${DAPPER_HOST_ARCH} ARCH=${DAPPER_HOST_ARCH}
ENV https_proxy=${PROXY} \
http_proxy=${PROXY}
RUN apt-get update && \
apt-get install -y ca-certificates git wget curl xz-utils && \
rm -f /bin/sh && ln -s /bin/bash /bin/sh && \
curl -sL https://github.com/upx/upx/releases/download/v4.0.1/upx-4.0.1-${ARCH}_linux.tar.xz | tar xvJf - --strip-components=1 -C /tmp && \
RUN zypper -n install ca-certificates git-core wget curl unzip tar vim less file xz cosign docker
ENV UPX_VERSION 4.2.1
RUN curl -sL https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-${ARCH}_linux.tar.xz | tar xvJf - --strip-components=1 -C /tmp && \
mv /tmp/upx /usr/bin/
RUN if [ "${ARCH}" == "amd64" ]; then \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.49.0; \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.54.2; \
fi
COPY --from=goreleaser /usr/bin/goreleaser /usr/bin/goreleaser
COPY --from=tools /app/release-notary /usr/local/bin/
ENV CATTLE_DASHBOARD_UI_VERSION="v2.8.5-kube-explorer-ui-rc1"
ENV CATTLE_API_UI_VERSION="1.1.11"
ENV DOCKER_URL_amd64=https://get.docker.com/builds/Linux/x86_64/docker-1.10.3 \
DOCKER_URL_arm=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm \
DOCKER_URL_arm64=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm64 \
DOCKER_URL=DOCKER_URL_${ARCH}
RUN wget -O - ${!DOCKER_URL} > /usr/bin/docker && chmod +x /usr/bin/docker
ENV GIT_COMMIT="4bcc9108508cd8112a28903f76bfca2fdd0bfc9d" \
GIT_BRANCH="ke/v0.3" \
GIT_SOURCE=${GOPATH}/src/github.com/rancher/steve \
CATTLE_DASHBOARD_UI_VERSION="v2.7.0-kube-explorer-ui-rc1"
ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS
ENV DAPPER_SOURCE /opt/kube-explorer
ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS GOPROXY GITHUB_TOKEN GITHUB_REF GITHUB_REF_NAME BUILD_TARGET SKIP_PACKAGE
ENV DAPPER_SOURCE /go/src/github.com/cnrancher/kube-explorer
ENV DAPPER_OUTPUT ./bin ./dist
ENV DAPPER_DOCKER_SOCKET true
ENV DAPPER_RUN_ARGS "-v ke-pkg:/go/pkg -v ke-cache:/root/.cache/go-build --privileged"

12
deploy/kubectl/README.md Normal file
View File

@@ -0,0 +1,12 @@
## Access Control Via Basic Auth
Deploy the kube-explorer workload:
```
kubectl create -f .
```
Configure for different IngressClass:
- [Nginx Ingress](./nginx-auth)
- [Traefik Ingress](./traefik-v2-auth)

View File

@@ -13,9 +13,9 @@ htpasswd -nb username password | base64
To install this mode, just run this script:
```
kubectl apply -f ./secret.yaml
export MY_XIP_IO=$(curl -sL ipinfo.io/ip)
envsubst < ./ingress.yaml.tpl | kubectl apply -f -
kubectl create -f ./secret.yaml
export MY_IP=$(curl -sL ipinfo.io/ip)
envsubst < ./ingress.yaml.tpl | kubectl create -f -
```
For more infos: https://kubernetes.github.io/ingress-nginx/examples/auth/basic/

View File

@@ -1,8 +1,8 @@
# Note: please replace the host first
# To use xip.io: http://xip.io/
# To use sslip.io: https://sslip.io/
# To get your public IP: curl ipinfo.io/ip
apiVersion: networking.k8s.io/v1beta1
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kube-explorer
@@ -10,16 +10,18 @@ metadata:
labels:
app: kube-explorer
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: kube-explorer
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - kube-explorer'
spec:
rules:
- host: "${MY_XIP_IO}.xip.io"
- host: "${MY_IP}.sslip.io"
http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: kube-explorer
servicePort: 8989
service:
name: kube-explorer
port:
number: 8989

View File

@@ -0,0 +1,34 @@
# Deploy kube-explorer behind proxy with path prefix
> Supported since v0.5.0
The kube-explorer dashboard can be exposed behind a proxy and path prefix like `http://your-domain.com/kube-explorer`.
The deployment examples in this folder are:
- `nginx ingress`
- `traefik ingress`
## Serve with ingress
When serving with nginx/traefik ingress controller, the template ingress file needs to be modified. In the `*.tpl` file, you can spot the missing hostname like:
```yaml
spec:
rules:
- host: "${MY_IP}.sslip.io" # Replace with your actual domain
```
Replace your ip to `${MY_UP}`, this will use the [sslip.io](https://sslip.io/) dns service to resolve the hostname to the ingress ip address.
For the traefik ingress, it is using `v2` version of the traefik ingress schema which use middlewares to modify the proxy request. Both `stripPrefix` and `headers` are used.
For the nginx ingress, the annotations `nginx.ingress.kubernetes.io/x-forwarded-prefix` and `nginx.ingress.kubernetes.io/rewrite-target` are used to strip prefix and to add proxy request header.
## Serve with self-hosted proxy
If serving the kube-explorer with self-hosted proxy, following modifications are required when proxying:
- Rewrite the proxy request to strip the path prefix like `rewrite "(?i)/kube-explorer(/|$)(.*)" /$2 break;` in nginx configuration.
- Add header `X-API-URL-Prefix` or `X-Forwarded-Prefix` with the path prefix when proxying request like `proxy_set_header X-Forwarded-Prefix "/kube-explorer";` in nginx configuration.
Then kube-explorer will response the index.html with modified content with path prefix to the browser.

View File

@@ -0,0 +1,24 @@
# Note: please replace the host first
# To use sslip.io: https://sslip.io/
# To get your public IP: curl ipinfo.io/ip
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/x-forwarded-prefix: "/kube-explorer"
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: kube-explorer-ingress
namespace: kube-system
spec:
rules:
- host: "${MY_IP}.sslip.io" # Replace with your actual domain
http:
paths:
- backend:
service:
name: kube-explorer
port:
name: http
path: /kube-explorer(/|$)(.*)
pathType: ImplementationSpecific

View File

@@ -0,0 +1,42 @@
# Note: please replace the host first
# To use sslip.io: https://sslip.io/
# To get your public IP: curl ipinfo.io/ip
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kube-explorer-ingress
namespace: kube-system
annotations:
traefik.ingress.kubernetes.io/router.middlewares: kube-system-prefix@kubernetescrd,kube-system-add-header@kubernetescrd
spec:
rules:
- host: "${MY_IP}.sslip.io" # Replace with your actual domain
http:
paths:
- path: /kube-explorer
pathType: Prefix
backend:
service:
name: kube-explorer
port:
name: http
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: prefix
namespace: kube-system
spec:
stripPrefix:
prefixes:
- /kube-explorer
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-header
namespace: kube-system
spec:
headers:
customRequestHeaders:
X-Forwarded-Prefix: "/kube-explorer" # Adds

View File

@@ -13,9 +13,9 @@ htpasswd -nb username password | base64
To install this mode, just run this script:
```
kubectl apply -f ./secret.yaml
export MY_XIP_IO=$(curl -sL ipinfo.io/ip)
envsubst < ./ingress.yaml.tpl | kubectl apply -f -
kubectl create -f ./secret.yaml
export MY_IP=$(curl -sL ipinfo.io/ip)
envsubst < ./ingress.yaml.tpl | kubectl create -f -
```
For more infos: https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes/

View File

@@ -1,5 +1,5 @@
# Note: please replace the host first
# To use xip.io: http://xip.io/
# To use sslip.io: https://sslip.io/
# To get your public IP: curl ipinfo.io/ip
apiVersion: networking.k8s.io/v1beta1
@@ -16,7 +16,7 @@ metadata:
ingress.kubernetes.io/auth-remove-header: "true"
spec:
rules:
- host: "${MY_XIP_IO}.xip.io"
- host: "${MY_IP}.sslip.io"
http:
paths:
- path: /

View File

@@ -0,0 +1,21 @@
## Traefik Auth
This can be used in K3s, as K3s use traefik as the default ingress class.
We use `basic-auth` to control the access of kube-explorer. The auth token is stored in the secret.
The default user is `niusmallnan`, and password is `dagedddd`. You can replace to another value with `htpasswd` tool.
```
htpasswd -nb username password | base64
```
To install this mode, just run this script:
```
kubectl create -f ./middleware.yaml
export MY_IP=$(curl -sL ipinfo.io/ip)
envsubst < ./ingress.yaml.tpl | kubectl create -f -
```
For more infos: https://doc.traefik.io/traefik/middlewares/http/basicauth/

View File

@@ -0,0 +1,25 @@
# Note: please replace the host first
# To use sslip.io.io: https://sslip.io.io/
# To get your public IP: curl ipinfo.io/ip
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kube-explorer
namespace: kube-system
labels:
app: kube-explorer
annotations:
traefik.ingress.kubernetes.io/router.middlewares: kube-system-kube-explorer@kubernetescrd
spec:
rules:
- host: "${MY_IP}.sslip.io"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kube-explorer
port:
number: 8989

View File

@@ -0,0 +1,28 @@
# The definitions below require the definitions for the Middleware and IngressRoute kinds.
# https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: kube-explorer
namespace: kube-system
labels:
app: kube-explorer
spec:
basicAuth:
secret: kube-explorer
removeHeader: true
---
# To create an encoded user:password pair, the following command can be used:
# htpasswd -nb user password | base64
apiVersion: v1
kind: Secret
metadata:
name: kube-explorer
namespace: kube-system
labels:
app: kube-explorer
data:
auth: bml1c21hbGxuYW46JGFwcjEkbDdUZjJOdWskbmNXajYubHYvMGNkcXM0NFoyelVQLgoK
type: Opaque

114
go.mod Normal file
View File

@@ -0,0 +1,114 @@
module github.com/cnrancher/kube-explorer
go 1.22.0
replace (
github.com/rancher/steve => github.com/rancher/steve v0.0.0-20240529152548-9fb3e50aa806
k8s.io/api => k8s.io/api v0.28.6
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.28.6
k8s.io/apimachinery => k8s.io/apimachinery v0.28.6
k8s.io/apiserver => k8s.io/apiserver v0.28.6
k8s.io/client-go => github.com/rancher/client-go v1.28.6-rancher1
)
require (
github.com/gorilla/mux v1.8.1
github.com/rancher/apiserver v0.0.0-20240207153744-69b3c2b56f3f
github.com/rancher/steve v0.0.0-20240529152548-9fb3e50aa806
github.com/rancher/wrangler/v2 v2.1.4
github.com/sirupsen/logrus v1.9.3
github.com/urfave/cli v1.22.15
golang.org/x/text v0.14.0
k8s.io/api v0.30.1
k8s.io/apimachinery v0.30.1
k8s.io/apiserver v0.30.1
k8s.io/client-go v12.0.0+incompatible
)
require (
github.com/adrg/xdg v0.4.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rancher/dynamiclistener v0.4.0-rc2 // indirect
github.com/rancher/kubernetes-provider-detector v0.1.5 // indirect
github.com/rancher/lasso v0.0.0-20240705194423-b2a060d103c1 // indirect
github.com/rancher/norman v0.0.0-20240708202514-a0127673d1b9 // indirect
github.com/rancher/remotedialer v0.3.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/urfave/cli/v2 v2.27.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.30.1 // indirect
k8s.io/component-base v0.30.1 // indirect
k8s.io/klog v1.0.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-aggregator v0.30.1 // indirect
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
sigs.k8s.io/cli-utils v0.35.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

492
go.sum Normal file
View File

@@ -0,0 +1,492 @@
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rancher/apiserver v0.0.0-20240207153744-69b3c2b56f3f h1:51aGoMMuPO+JsoglWmXYgA9xLWPv/bnpT0/6zErgaIU=
github.com/rancher/apiserver v0.0.0-20240207153744-69b3c2b56f3f/go.mod h1:RBcpQs/KjClGntgKCd5XcrUX5J2Rz9sW5DGEMd7H5bw=
github.com/rancher/client-go v1.28.6-rancher1 h1:nSoGKs12BuPviZtzemO7wTX8jxABaLqfYKFLRBV8MI0=
github.com/rancher/client-go v1.28.6-rancher1/go.mod h1:+nu0Yp21Oeo/cBCsprNVXB2BfJTV51lFfe5tXl2rUL8=
github.com/rancher/dynamiclistener v0.4.0-rc2 h1:tgBtNnQ2cQIsjBvto2BYmwP8NGpyg3N4Mp6zFyoGx9E=
github.com/rancher/dynamiclistener v0.4.0-rc2/go.mod h1:Y+VdjQH9KQGE97uMwYEWqNN6puFQ17aBemIjVLYdlD8=
github.com/rancher/kubernetes-provider-detector v0.1.5 h1:hWRAsWuJOemzGjz/XrbTlM7QmfO4OedvFE3QwXiH60I=
github.com/rancher/kubernetes-provider-detector v0.1.5/go.mod h1:ypuJS7kP7rUiAn330xG46mj+Nhvym05GM8NqMVekpH0=
github.com/rancher/lasso v0.0.0-20240705194423-b2a060d103c1 h1:vv1jDlYbd4KhGbPNxmjs8CYgEHUrQm2bMtmULfXJ6iw=
github.com/rancher/lasso v0.0.0-20240705194423-b2a060d103c1/go.mod h1:A/y3BLQkxZXYD60MNDRwAG9WGxXfvd6Z6gWR/a8wPw8=
github.com/rancher/norman v0.0.0-20240708202514-a0127673d1b9 h1:AlRMRs5mHJcdiK83KKJyFVeybPMZ7dOUzC0l3k9aUa8=
github.com/rancher/norman v0.0.0-20240708202514-a0127673d1b9/go.mod h1:dyjfXBsNiroPWOdUZe7diUOUSLf6HQ/r2kEpwH/8zas=
github.com/rancher/remotedialer v0.3.2 h1:kstZbRwPS5gPWpGg8VjEHT2poHtArs+Fc317YM8JCzU=
github.com/rancher/remotedialer v0.3.2/go.mod h1:Ys004RpJuTLSm+k4aYUCoFiOOad37ubYev3TkOFg/5w=
github.com/rancher/steve v0.0.0-20240529152548-9fb3e50aa806 h1:QvB3tddPbwuloBMw/q7zSeYyLvKruQI/era5Y+t56dE=
github.com/rancher/steve v0.0.0-20240529152548-9fb3e50aa806/go.mod h1:o4vLBzMTKbHHhIiAcbgOiaN3aK1vIjL6ZTgaGxQYpsY=
github.com/rancher/wrangler/v2 v2.1.4 h1:ohov0i6A9dJHHO6sjfsH4Dqv93ZTdm5lIJVJdPzVdQc=
github.com/rancher/wrangler/v2 v2.1.4/go.mod h1:af5OaGU/COgreQh1mRbKiUI64draT2NN34uk+PALFY8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM=
github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.28.6 h1:yy6u9CuIhmg55YvF/BavPBBXB+5QicB64njJXxVnzLo=
k8s.io/api v0.28.6/go.mod h1:AM6Ys6g9MY3dl/XNaNfg/GePI0FT7WBGu8efU/lirAo=
k8s.io/apiextensions-apiserver v0.28.6 h1:myB3iG/3v3jqCg28JDbOefu4sH2/erNEXgytRzJKBOo=
k8s.io/apiextensions-apiserver v0.28.6/go.mod h1:qlp6xRKBgyRhe5AYc81TQpLx4kLNK8/sGQUOwMkVjRk=
k8s.io/apimachinery v0.28.6 h1:RsTeR4z6S07srPg6XYrwXpTJVMXsjPXn0ODakMytSW0=
k8s.io/apimachinery v0.28.6/go.mod h1:QFNX/kCl/EMT2WTSz8k4WLCv2XnkOLMaL8GAVRMdpsA=
k8s.io/apiserver v0.28.6 h1:SfS5v4I5UGvh0q/1rzvNwLFsK+r7YzcsixnUc0NwoEk=
k8s.io/apiserver v0.28.6/go.mod h1:8n0aerS3kPm9usyB8B+an6/BZ5+Fa9fNqlASFdDDVwk=
k8s.io/component-base v0.30.1 h1:bvAtlPh1UrdaZL20D9+sWxsJljMi0QZ3Lmw+kmZAaxQ=
k8s.io/component-base v0.30.1/go.mod h1:e/X9kDiOebwlI41AvBHuWdqFriSRrX50CdwA9TFaHLI=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-aggregator v0.30.1 h1:ymR2BsxDacTKwzKTuNhGZttuk009c+oZbSeD+IPX5q4=
k8s.io/kube-aggregator v0.30.1/go.mod h1:SFbqWsM6ea8dHd3mPLsZFzJHbjBOS5ykIgJh4znZ5iQ=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4=
sigs.k8s.io/cli-utils v0.35.0 h1:dfSJaF1W0frW74PtjwiyoB4cwdRygbHnC7qe7HF0g/Y=
sigs.k8s.io/cli-utils v0.35.0/go.mod h1:ITitykCJxP1vaj1Cew/FZEaVJ2YsTN9Q71m02jebkoE=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

34
internal/config/flags.go Normal file
View File

@@ -0,0 +1,34 @@
package config
import (
"github.com/urfave/cli"
)
var InsecureSkipTLSVerify bool
var SystemDefaultRegistry string
var APIUIVersion = "1.1.11"
var ShellPodImage string
func Flags() []cli.Flag {
return []cli.Flag{
cli.BoolFlag{
Name: "insecure-skip-tls-verify",
Destination: &InsecureSkipTLSVerify,
},
cli.StringFlag{
Name: "system-default-registry",
Destination: &SystemDefaultRegistry,
},
cli.StringFlag{
Name: "pod-image",
Destination: &ShellPodImage,
Value: "rancher/shell:v0.2.1-rc.7",
},
cli.StringFlag{
Name: "apiui-version",
Hidden: true,
Destination: &APIUIVersion,
Value: APIUIVersion,
},
}
}

11
internal/config/steve.go Normal file
View File

@@ -0,0 +1,11 @@
package config
import (
"github.com/rancher/steve/pkg/debug"
stevecli "github.com/rancher/steve/pkg/server/cli"
)
var (
Steve stevecli.Config
Debug debug.Config
)

View File

@@ -0,0 +1,80 @@
package cluster
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"github.com/rancher/apiserver/pkg/types"
"github.com/rancher/steve/pkg/podimpersonation"
"github.com/rancher/steve/pkg/resources/cluster"
"github.com/rancher/steve/pkg/server"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func Register(_ context.Context, server *server.Server, displayName string) error {
cg := server.ClientFactory
shell := &shell{
cg: cg,
namespace: shellPodNS,
impersonator: podimpersonation.New("shell", cg, time.Hour, getShellPodImage),
}
clusterSchema := server.BaseSchemas.LookupSchema("management.cattle.io.cluster")
if clusterSchema == nil {
return errors.New("failed to find management.cattle.io.cluster in base schema")
}
if clusterSchema.LinkHandlers == nil {
clusterSchema.LinkHandlers = make(map[string]http.Handler)
}
clusterSchema.LinkHandlers["shell"] = shell
clusterSchema.Store = func() types.Store {
return &displaynameWrapper{Store: clusterSchema.Store, displayName: displayName}
}()
return nil
}
type displaynameWrapper struct {
types.Store
displayName string
}
func (s *displaynameWrapper) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
obj, err := s.Store.ByID(apiOp, schema, id)
if err != nil {
return obj, err
}
if obj.ID != "local" {
return obj, nil
}
if c, ok := obj.Object.(*cluster.Cluster); ok {
c.Spec.DisplayName = getDisplayNameWithContext(s.displayName)
}
return obj, nil
}
func (s *displaynameWrapper) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
rtn, err := s.Store.List(apiOp, schema)
if err != nil {
return rtn, err
}
for _, obj := range rtn.Objects {
if obj.ID != "local" {
continue
}
if c, ok := obj.Object.(*cluster.Cluster); ok {
c.Spec.DisplayName = getDisplayNameWithContext(s.displayName)
}
}
return rtn, nil
}
func getDisplayNameWithContext(CurrentKubeContext string) string {
if CurrentKubeContext != "" {
return fmt.Sprintf("%s Cluster", cases.Title(language.English).String(CurrentKubeContext))
}
return "Local Cluster"
}

View File

@@ -0,0 +1,162 @@
package cluster
import (
"context"
"fmt"
"net/http"
"net/http/httputil"
"time"
"github.com/cnrancher/kube-explorer/internal/config"
"github.com/rancher/steve/pkg/podimpersonation"
"github.com/rancher/steve/pkg/stores/proxy"
"github.com/rancher/wrangler/v2/pkg/schemas/validation"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)
var (
shellPodNS = "kube-system"
)
type shell struct {
namespace string
impersonator *podimpersonation.PodImpersonation
cg proxy.ClientGetter
}
func (s *shell) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
ctx, user, client, err := s.contextAndClient(req)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
pod, err := s.impersonator.CreatePod(ctx, user, s.createPod(), &podimpersonation.PodOptions{
Wait: true,
})
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
client.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
s.impersonator.DeleteRole(ctx, *pod)
}()
s.proxyRequest(rw, req, pod, client)
}
func (s *shell) proxyRequest(rw http.ResponseWriter, req *http.Request, pod *v1.Pod, client kubernetes.Interface) {
attachURL := client.CoreV1().RESTClient().
Get().
Namespace(pod.Namespace).
Resource("pods").
Name(pod.Name).
SubResource("exec").
VersionedParams(&v1.PodExecOptions{
Stdin: true,
Stdout: true,
Stderr: true,
TTY: true,
Container: "shell",
Command: []string{"welcome"},
}, scheme.ParameterCodec).URL()
httpClient := client.CoreV1().RESTClient().(*rest.RESTClient).Client
p := httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL = attachURL
req.Host = attachURL.Host
delete(req.Header, "Impersonate-Group")
delete(req.Header, "Impersonate-User")
delete(req.Header, "Authorization")
delete(req.Header, "Cookie")
},
Transport: httpClient.Transport,
FlushInterval: time.Millisecond * 100,
}
p.ServeHTTP(rw, req)
}
func (s *shell) contextAndClient(req *http.Request) (context.Context, user.Info, kubernetes.Interface, error) {
ctx := req.Context()
client, err := s.cg.AdminK8sInterface()
if err != nil {
return ctx, nil, nil, err
}
user, ok := request.UserFrom(ctx)
if !ok {
return ctx, nil, nil, validation.Unauthorized
}
return ctx, user, client, nil
}
func (s *shell) createPod() *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "dashboard-shell-",
Namespace: s.namespace,
},
Spec: v1.PodSpec{
TerminationGracePeriodSeconds: new(int64),
RestartPolicy: v1.RestartPolicyNever,
NodeSelector: map[string]string{
"kubernetes.io/os": "linux",
},
Tolerations: []v1.Toleration{
{
Key: "cattle.io/os",
Operator: "Equal",
Value: "linux",
Effect: "NoSchedule",
},
{
Key: "node-role.kubernetes.io/controlplane",
Operator: "Equal",
Value: "true",
Effect: "NoSchedule",
},
{
Key: "node-role.kubernetes.io/etcd",
Operator: "Equal",
Value: "true",
Effect: "NoExecute",
},
},
Containers: []v1.Container{
{
Name: "shell",
TTY: true,
Stdin: true,
StdinOnce: true,
Env: []v1.EnvVar{
{
Name: "KUBECONFIG",
Value: "/home/shell/.kube/config",
},
},
Image: getShellPodImage(),
ImagePullPolicy: v1.PullIfNotPresent,
},
},
},
}
}
func getShellPodImage() string {
if config.SystemDefaultRegistry == "" {
return config.ShellPodImage
}
return fmt.Sprintf("%s/%s", config.SystemDefaultRegistry, config.ShellPodImage)
}

114
internal/server/config.go Normal file
View File

@@ -0,0 +1,114 @@
package server
import (
"context"
"net/http"
"strings"
"github.com/rancher/apiserver/pkg/types"
"github.com/rancher/apiserver/pkg/urlbuilder"
steveauth "github.com/rancher/steve/pkg/auth"
"github.com/rancher/steve/pkg/schema"
"github.com/rancher/steve/pkg/server"
"github.com/rancher/steve/pkg/server/cli"
"github.com/rancher/steve/pkg/server/router"
"github.com/rancher/wrangler/v2/pkg/kubeconfig"
"github.com/rancher/wrangler/v2/pkg/ratelimit"
"github.com/cnrancher/kube-explorer/internal/config"
"github.com/cnrancher/kube-explorer/internal/resources/cluster"
"github.com/cnrancher/kube-explorer/internal/ui"
"github.com/cnrancher/kube-explorer/internal/version"
)
func ToServer(ctx context.Context, c *cli.Config) (*server.Server, error) {
var (
auth steveauth.Middleware
)
restConfig, err := kubeconfig.GetNonInteractiveClientConfigWithContext(c.KubeConfig, c.Context).ClientConfig()
if err != nil {
return nil, err
}
restConfig.RateLimiter = ratelimit.None
restConfig.Insecure = config.InsecureSkipTLSVerify
if restConfig.Insecure {
restConfig.CAData = nil
restConfig.CAFile = ""
}
if c.WebhookConfig.WebhookAuthentication {
auth, err = c.WebhookConfig.WebhookMiddleware()
if err != nil {
return nil, err
}
}
controllers, err := server.NewController(restConfig, nil)
if err != nil {
return nil, err
}
ui, apiui := ui.New(&ui.Options{
ReleaseSetting: version.IsRelease,
Path: func() string { return c.UIPath },
})
steveServer, err := server.New(ctx, restConfig, &server.Options{
AuthMiddleware: auth,
Controllers: controllers,
Next: ui,
// router needs to hack here
Router: func(h router.Handlers) http.Handler {
return handleProxyHeader(
rewriteLocalCluster(
router.Routes(h),
),
)
},
})
if err != nil {
return nil, err
}
steveServer.APIServer.CustomAPIUIResponseWriter(apiui.CSS(), apiui.JS(), func() string { return config.APIUIVersion })
// registrer local cluster
if err := cluster.Register(ctx, steveServer, c.Context); err != nil {
return steveServer, err
}
// wrap default store
steveServer.SchemaFactory.AddTemplate(schema.Template{
Customize: func(a *types.APISchema) {
if a.Store == nil {
return
}
a.Store = &deleteOptionStore{
Store: a.Store,
}
},
})
return steveServer, controllers.Start(ctx)
}
func rewriteLocalCluster(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if strings.HasPrefix(req.URL.Path, "/k8s/clusters/local") {
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
if req.URL.Path == "" {
req.URL.Path = "/"
}
}
next.ServeHTTP(rw, req)
})
}
func handleProxyHeader(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if value := req.Header.Get("X-Forwarded-Prefix"); value != "" {
req.Header.Set(urlbuilder.PrefixHeader, value)
}
next.ServeHTTP(rw, req)
})
}

View File

@@ -0,0 +1,16 @@
package server
import (
"github.com/rancher/apiserver/pkg/types"
)
type deleteOptionStore struct {
types.Store
}
func (s *deleteOptionStore) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
query := apiOp.Request.URL.Query()
query.Add("propagationPolicy", "Background")
apiOp.Request.URL.RawQuery = query.Encode()
return s.Store.Delete(apiOp, schema, id)
}

55
internal/ui/apiui.go Normal file
View File

@@ -0,0 +1,55 @@
package ui
import "github.com/rancher/apiserver/pkg/writer"
type APIUI struct {
offline StringSetting
release BoolSetting
embed bool
}
func apiUI(opt *Options) APIUI {
var rtn = APIUI{
offline: opt.Offline,
release: opt.ReleaseSetting,
embed: true,
}
if rtn.offline == nil {
rtn.offline = StaticSetting("dynamic")
}
if rtn.release == nil {
rtn.release = StaticSetting(false)
}
for _, file := range []string{
"ui/api-ui/ui.min.css",
"ui/api-ui/ui.min.js",
} {
if _, err := staticContent.Open(file); err != nil {
rtn.embed = false
break
}
}
return rtn
}
func (a APIUI) content(name string) writer.StringGetter {
return func() (rtn string) {
switch a.offline() {
case "dynamic":
if !a.release() && !a.embed {
return ""
}
case "false":
return ""
}
return name
}
}
func (a APIUI) CSS() writer.StringGetter {
return a.content("/api-ui/ui.min.css")
}
func (a APIUI) JS() writer.StringGetter {
return a.content("/api-ui/ui.min.js")
}

View File

@@ -0,0 +1,24 @@
package content
import (
"io/fs"
"net/http"
)
type fsFunc func(name string) (fs.File, error)
func (f fsFunc) Open(name string) (fs.File, error) {
return f(name)
}
type fsContent interface {
ToFileServer(basePaths ...string) http.Handler
Open(name string) (fs.File, error)
}
type Handler interface {
ServeAssets(middleware func(http.Handler) http.Handler, hext http.Handler) http.Handler
ServeFaviconDashboard() http.Handler
GetIndex() ([]byte, error)
Refresh()
}

View File

@@ -0,0 +1,97 @@
package content
import (
"bytes"
"crypto/tls"
"errors"
"io"
"net/http"
"sync"
)
const (
defaultIndex = "https://releases.rancher.com/dashboard/latest/index.html"
)
func NewExternal(getIndex func() string) Handler {
return &externalIndexHandler{
getIndexFunc: getIndex,
}
}
var (
insecureClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
_ Handler = &externalIndexHandler{}
)
type externalIndexHandler struct {
sync.RWMutex
getIndexFunc func() string
current string
downloadSuccess *bool
}
func (u *externalIndexHandler) ServeAssets(_ func(http.Handler) http.Handler, next http.Handler) http.Handler {
return next
}
func (u *externalIndexHandler) ServeFaviconDashboard() http.Handler {
return http.NotFoundHandler()
}
func (u *externalIndexHandler) GetIndex() ([]byte, error) {
if u.canDownload() {
var buffer bytes.Buffer
if err := serveIndex(&buffer, u.current); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
return nil, errors.New("external index is not available")
}
func serveIndex(resp io.Writer, url string) error {
r, err := insecureClient.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
_, err = io.Copy(resp, r.Body)
return err
}
func (u *externalIndexHandler) canDownload() bool {
u.RLock()
rtn := u.downloadSuccess
u.RUnlock()
if rtn != nil {
return *rtn
}
return u.refresh()
}
func (u *externalIndexHandler) refresh() bool {
u.Lock()
defer u.RUnlock()
u.current = u.getIndexFunc()
if u.current == "" {
u.current = defaultIndex
}
t := serveIndex(io.Discard, u.current) == nil
u.downloadSuccess = &t
return t
}
func (u *externalIndexHandler) Refresh() {
_ = u.refresh()
}

71
internal/ui/content/fs.go Normal file
View File

@@ -0,0 +1,71 @@
package content
import (
"io"
"net/http"
"path/filepath"
"sync"
)
var _ Handler = &handler{}
func newFS(content fsContent) Handler {
return &handler{
content: content,
cacheFS: &sync.Map{},
}
}
type handler struct {
content fsContent
cacheFS *sync.Map
}
func (h *handler) pathExist(path string) bool {
_, err := h.content.Open(path)
return err == nil
}
func (h *handler) serveContent(basePaths ...string) http.Handler {
key := filepath.Join(basePaths...)
if rtn, ok := h.cacheFS.Load(key); ok {
return rtn.(http.Handler)
}
rtn := h.content.ToFileServer(basePaths...)
h.cacheFS.Store(key, rtn)
return rtn
}
func (h *handler) Refresh() {
h.cacheFS.Range(func(key, _ any) bool {
h.cacheFS.Delete(key)
return true
})
}
func (h *handler) ServeAssets(middleware func(http.Handler) http.Handler, next http.Handler) http.Handler {
assets := middleware(h.serveContent())
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if h.pathExist(r.URL.Path) {
assets.ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
func (h *handler) ServeFaviconDashboard() http.Handler {
return h.serveContent("dashboard")
}
func (h *handler) GetIndex() ([]byte, error) {
path := filepath.Join("dashboard", "index.html")
f, err := h.content.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
}

View File

@@ -0,0 +1,43 @@
package content
import (
"embed"
"io/fs"
"net/http"
"path/filepath"
)
func NewEmbedded(staticContent embed.FS, prefix string) Handler {
return newFS(&embedFS{
pathPrefix: prefix,
staticContent: staticContent,
})
}
var _ fsContent = &embedFS{}
type embedFS struct {
pathPrefix string
staticContent embed.FS
}
// Open implements fsContent.
func (e *embedFS) Open(name string) (fs.File, error) {
return e.staticContent.Open(joinEmbedFilepath(e.pathPrefix, name))
}
// ToFileServer implements fsContent.
func (e *embedFS) ToFileServer(basePaths ...string) http.Handler {
handler := fsFunc(func(name string) (fs.File, error) {
assetPath := joinEmbedFilepath(joinEmbedFilepath(basePaths...), name)
return e.Open(assetPath)
})
return http.FileServer(http.FS(handler))
}
func (e *embedFS) Refresh() error { return nil }
func joinEmbedFilepath(paths ...string) string {
return filepath.ToSlash(filepath.Join(paths...))
}

View File

@@ -0,0 +1,41 @@
package content
import (
"errors"
"io/fs"
"net/http"
"path/filepath"
)
func NewFilepath(getPath func() string) Handler {
return newFS(&filepathFS{
getPath: getPath,
})
}
var _ fsContent = &filepathFS{}
type filepathFS struct {
getPath func() string
}
func (f *filepathFS) ToFileServer(basePaths ...string) http.Handler {
root := f.getPath()
if root == "" {
return http.NotFoundHandler()
}
path := filepath.Join(append([]string{string(root)}, basePaths...)...)
return http.FileServer(http.Dir(path))
}
func (f *filepathFS) Open(name string) (fs.File, error) {
root := f.getPath()
if root == "" {
return nil, errors.New("filepath fs is not ready")
}
return http.Dir(root).Open(name)
}
func (f *filepathFS) Refresh() error {
return nil
}

7
internal/ui/dev.go Normal file
View File

@@ -0,0 +1,7 @@
//go:build !embed
package ui
import "embed"
var staticContent embed.FS

12
internal/ui/embed.go Normal file
View File

@@ -0,0 +1,12 @@
//go:build embed
package ui
import (
"embed"
)
// content holds our static web server content.
//
//go:embed all:ui/*
var staticContent embed.FS

137
internal/ui/handler.go Normal file
View File

@@ -0,0 +1,137 @@
package ui
import (
"net/http"
"github.com/cnrancher/kube-explorer/internal/ui/content"
"github.com/rancher/apiserver/pkg/middleware"
"github.com/sirupsen/logrus"
)
type StringSetting func() string
type BoolSetting func() bool
func StaticSetting[T any](input T) func() T {
return func() T {
return input
}
}
type Handler struct {
contentHandlers map[string]content.Handler
pathSetting func() string
indexSetting func() string
releaseSetting func() bool
offlineSetting func() string
middleware func(http.Handler) http.Handler
indexMiddleware func(http.Handler) http.Handler
}
type Options struct {
// The location on disk of the UI files
Path StringSetting
// The HTTP URL of the index file to download
Index StringSetting
// Whether or not to run the UI offline, should return true/false/dynamic/embed
Offline StringSetting
// Whether or not is it release, if true UI will run offline if set to dynamic
ReleaseSetting BoolSetting
}
func NewUIHandler(opts *Options) *Handler {
if opts == nil {
opts = &Options{}
}
h := &Handler{
contentHandlers: make(map[string]content.Handler),
indexSetting: opts.Index,
offlineSetting: opts.Offline,
pathSetting: opts.Path,
releaseSetting: opts.ReleaseSetting,
middleware: middleware.Chain{
middleware.Gzip,
middleware.FrameOptions,
middleware.CacheMiddleware("json", "js", "css"),
}.Handler,
indexMiddleware: middleware.Chain{
middleware.Gzip,
middleware.NoCache,
middleware.FrameOptions,
middleware.ContentType,
}.Handler,
}
if h.indexSetting == nil {
h.indexSetting = StaticSetting("")
}
if h.offlineSetting == nil {
h.offlineSetting = StaticSetting("dynamic")
}
if h.pathSetting == nil {
h.pathSetting = StaticSetting("")
}
if h.releaseSetting == nil {
h.releaseSetting = StaticSetting(false)
}
h.contentHandlers["embed"] = content.NewEmbedded(staticContent, "ui")
h.contentHandlers["false"] = content.NewExternal(h.indexSetting)
h.contentHandlers["true"] = content.NewFilepath(h.pathSetting)
return h
}
func (h *Handler) content() content.Handler {
offline := h.offlineSetting()
if handler, ok := h.contentHandlers[offline]; ok {
return handler
}
embedHandler := h.contentHandlers["embed"]
filepathHandler := h.contentHandlers["true"]
externalHandler := h.contentHandlers["false"]
// default to dynamic
switch {
case h.pathSetting() != "":
if _, err := filepathHandler.GetIndex(); err == nil {
return filepathHandler
}
fallthrough
case h.releaseSetting():
// release must use embed first
return embedHandler
default:
// try embed
if _, err := embedHandler.GetIndex(); err == nil {
return embedHandler
}
return externalHandler
}
}
func (h *Handler) ServeAssets(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.content().ServeAssets(h.middleware, next).ServeHTTP(w, r)
})
}
func (h *Handler) ServeFaviconDashboard() http.Handler {
return h.middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.content().ServeFaviconDashboard().ServeHTTP(w, r)
}))
}
func (h *Handler) IndexFile() http.Handler {
return h.indexMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rtn, err := h.content().GetIndex()
if err != nil {
logrus.Warnf("failed to serve index with error %v", err)
http.NotFoundHandler().ServeHTTP(w, r)
return
}
_, _ = w.Write(rtn)
}))
}

116
internal/ui/proxy.go Normal file
View File

@@ -0,0 +1,116 @@
package ui
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"strconv"
"github.com/rancher/apiserver/pkg/urlbuilder"
"k8s.io/apimachinery/pkg/util/proxy"
)
type RoundTripFunc func(*http.Request) (*http.Response, error)
func (r RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return r(req)
}
func proxyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
scheme := urlbuilder.GetScheme(r)
host := urlbuilder.GetHost(r, scheme)
pathPrepend := r.Header.Get(urlbuilder.PrefixHeader)
if scheme == r.URL.Scheme && host == r.URL.Host && pathPrepend == "" {
next.ServeHTTP(w, r)
return
}
proxyRoundtrip := proxy.Transport{
Scheme: scheme,
Host: host,
PathPrepend: pathPrepend,
RoundTripper: RoundTripFunc(func(r *http.Request) (*http.Response, error) {
rw := &dummyResponseWriter{
next: w,
header: make(http.Header),
}
next.ServeHTTP(rw, r)
return rw.getResponse(r), nil
}),
}
//proxyRoundtripper will write the response in RoundTrip func
resp, _ := proxyRoundtrip.RoundTrip(r)
responseToWriter(resp, w)
})
}
var _ http.ResponseWriter = &dummyResponseWriter{}
var _ http.Hijacker = &dummyResponseWriter{}
type dummyResponseWriter struct {
next http.ResponseWriter
header http.Header
body bytes.Buffer
statusCode int
}
// Hijack implements http.Hijacker.
func (drw *dummyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if h, ok := drw.next.(http.Hijacker); ok {
return h.Hijack()
}
return nil, nil, fmt.Errorf("")
}
// Header implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) Header() http.Header {
return drw.header
}
// Write implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) Write(b []byte) (int, error) {
return drw.body.Write(b)
}
// WriteHeader implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) WriteHeader(statusCode int) {
drw.statusCode = statusCode
}
// GetStatusCode returns the status code written to the response.
func (drw *dummyResponseWriter) GetStatusCode() int {
if drw.statusCode == 0 {
return 200
}
return drw.statusCode
}
func (drw *dummyResponseWriter) getResponse(req *http.Request) *http.Response {
return &http.Response{
Status: strconv.Itoa(drw.GetStatusCode()),
StatusCode: drw.GetStatusCode(),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Request: req,
Header: drw.header,
Body: io.NopCloser(&drw.body),
}
}
func responseToWriter(resp *http.Response, writer http.ResponseWriter) {
for k, v := range resp.Header {
writer.Header()[k] = v
}
if resp.StatusCode != http.StatusOK {
writer.WriteHeader(resp.StatusCode)
}
_, _ = io.Copy(writer, resp.Body)
}

31
internal/ui/routers.go Normal file
View File

@@ -0,0 +1,31 @@
package ui
import (
"net/http"
"strings"
"github.com/gorilla/mux"
)
func New(opt *Options) (http.Handler, APIUI) {
vue := NewUIHandler(opt)
router := mux.NewRouter()
router.UseEncodedPath()
router.Handle("/", http.RedirectHandler("/dashboard/", http.StatusFound))
router.Handle("/dashboard", http.RedirectHandler("/dashboard/", http.StatusFound))
router.Handle("/dashboard/", vue.IndexFile())
router.Handle("/favicon.png", vue.ServeFaviconDashboard())
router.Handle("/favicon.ico", vue.ServeFaviconDashboard())
router.PathPrefix("/dashboard/").Handler(vue.ServeAssets(vue.IndexFile()))
router.PathPrefix("/api-ui/").Handler(vue.ServeAssets(http.NotFoundHandler()))
router.PathPrefix("/k8s/clusters/local").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
url := strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
if url == "" {
url = "/"
}
http.Redirect(rw, req, url, http.StatusFound)
})
return proxyMiddleware(router), apiUI(opt)
}

View File

@@ -0,0 +1,23 @@
package version
import (
"fmt"
"regexp"
"strings"
)
var (
Version = "dev"
GitCommit = "HEAD"
// K-EXPLORER
releasePattern = regexp.MustCompile("^v[0-9]")
)
func FriendlyVersion() string {
return fmt.Sprintf("%s (%s)", Version, GitCommit)
}
func IsRelease() bool {
return !strings.Contains(Version, "dev") && releasePattern.MatchString(Version)
}

50
main.go Normal file
View File

@@ -0,0 +1,50 @@
package main
import (
"os"
"github.com/rancher/steve/pkg/debug"
stevecli "github.com/rancher/steve/pkg/server/cli"
"github.com/rancher/steve/pkg/version"
"github.com/rancher/wrangler/v2/pkg/signals"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
keconfig "github.com/cnrancher/kube-explorer/internal/config"
"github.com/cnrancher/kube-explorer/internal/server"
)
func main() {
app := cli.NewApp()
app.Name = "kube-explorer"
app.Version = version.FriendlyVersion()
app.Usage = ""
app.Flags = joinFlags(
stevecli.Flags(&keconfig.Steve),
debug.Flags(&keconfig.Debug),
keconfig.Flags(),
)
app.Action = run
if err := app.Run(os.Args); err != nil {
logrus.Fatal(err)
}
}
func run(_ *cli.Context) error {
ctx := signals.SetupSignalContext()
keconfig.Debug.MustSetupDebug()
s, err := server.ToServer(ctx, &keconfig.Steve)
if err != nil {
return err
}
return s.ListenAndServe(ctx, keconfig.Steve.HTTPSListenPort, keconfig.Steve.HTTPListenPort, nil)
}
func joinFlags(flags ...[]cli.Flag) []cli.Flag {
var rtn []cli.Flag
for _, flag := range flags {
rtn = append(rtn, flag...)
}
return rtn
}

View File

@@ -1,12 +0,0 @@
image: cnrancher/kube-explorer:latest
manifests:
-
image: cnrancher/kube-explorer:head-linux-amd64
platform:
architecture: amd64
os: linux
-
image: cnrancher/kube-explorer:head-linux-arm64
platform:
architecture: arm64
os: linux

View File

@@ -1,12 +0,0 @@
image: cnrancher/kube-explorer:{{build.tag}}
manifests:
-
image: cnrancher/kube-explorer:{{build.tag}}-linux-amd64
platform:
architecture: amd64
os: linux
-
image: cnrancher/kube-explorer:{{build.tag}}-linux-arm64
platform:
architecture: arm64
os: linux

View File

@@ -1,4 +1,7 @@
FROM registry.suse.com/bci/bci-minimal:15.4
COPY kube-explorer entrypoint.sh /usr/bin/
ENTRYPOINT ["entrypoint.sh"]
FROM registry.suse.com/bci/bci-base:15.6
ARG TARGETARCH
ARG TARGETOS
ENV ARCH=${TARGETARCH:-"amd64"} OS=${TARGETOS:-"linux"}
RUN zypper install -y catatonit
COPY kube-explorer-${OS}-${ARCH} /usr/bin/kube-explorer
ENTRYPOINT [ "/usr/bin/catatonit", "--", "kube-explorer" ]

View File

@@ -1,3 +0,0 @@
#!/bin/sh
kube-explorer "${@}"

View File

@@ -1,54 +1,38 @@
#!/bin/bash
set -e
source $(dirname $0)/version
source "$(dirname $0)/version"
cd "$(dirname $0)/.."
OS_ARCH_ARG_LINUX="amd64 arm arm64"
OS_ARCH_ARG_DARWIN="amd64 arm64"
rm -rf ./bin/* ./dist/*
LD_INJECT_VALUES="-X github.com/rancher/steve/pkg/version.Version=$VERSION
-X github.com/rancher/steve/pkg/version.GitCommit=$COMMIT"
BUILD_TARGET="${BUILD_TARGET:-dev}"
CROSS=${CROSS:-}
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
pushd $GIT_SOURCE
if [ -n "$CROSS" ]; then
for ARCH in ${OS_ARCH_ARG_LINUX}; do
OUTPUT_BIN="bin/kube-explorer-linux-$ARCH"
echo "Building binary for linux/$ARCH..."
GOARCH=$ARCH GOOS=linux CGO_ENABLED=0 go build -tags embed \
-ldflags \
"$LD_INJECT_VALUES $LINKFLAGS" \
-o ${OUTPUT_BIN}
done
for ARCH in ${OS_ARCH_ARG_DARWIN}; do
OUTPUT_BIN="bin/kube-explorer-darwin-$ARCH"
echo "Building binary for darwin/$ARCH..."
GOARCH=$ARCH GOOS=darwin CGO_ENABLED=0 go build -tags embed \
-ldflags \
"$LD_INJECT_VALUES" \
-o ${OUTPUT_BIN}
done
else
# only build one for current platform
CGO_ENABLED=0 go build -tags embed \
-ldflags \
"$LD_INJECT_VALUES $LINKFLAGS" \
-o bin/kube-explorer
if [[ ${GITHUB_REF} == refs/tags/* ]]; then
CROSS=tag
elif [ -n "${GITHUB_REF}" ]; then
CROSS=push
fi
for f in $(ls ./bin/); do
if [[ $f != *darwin-arm64 ]]; then
upx -o $DAPPER_SOURCE/bin/$f bin/$f || true
fi
if [ -f $DAPPER_SOURCE/bin/$f ]; then
echo "UPX done!"
else
echo "Copy origin file as UPX failed!!!"
cp bin/$f $DAPPER_SOURCE/bin/$f
fi
done
case "$CROSS" in
"tag")
BUILD_TARGET="prod"
;;
"push")
;;
*)
BUILD_ARG="${BUILD_ARG} --single-target"
;;
esac
popd
BUILD_ARG="${BUILD_ARG:-} --skip validate --id ${BUILD_TARGET}"
mkdir -p "./bin"
# upx is handled by goreleaser
VERSION=${VERSION} COMMIT=${COMMIT} goreleaser build $BUILD_ARG
mkdir -p "./dist"
cp -r bin/kube-explorer-* dist/

View File

@@ -6,4 +6,6 @@ cd $(dirname $0)
./download
./validate
./build
./package
if [ -z "${SKIP_PACKAGE}" ]; then
./package
fi

View File

@@ -6,13 +6,8 @@ cd $(dirname $0)
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
pushd $GIT_SOURCE
CGO_ENABLED=0 go build \
-ldflags \
"$LINKFLAGS" \
-o bin/kube-explorer
mv bin/kube-explorer $DAPPER_SOURCE/bin/
popd

View File

@@ -1,18 +1,22 @@
#!/bin/bash
mkdir -p $(dirname $GIT_SOURCE)
source $(dirname $0)/version
pushd $(dirname $GIT_SOURCE)
cd "$(dirname $0)/.." || exit 1;
git clone --depth=1 --branch ${GIT_BRANCH} https://github.com/niusmallnan/steve.git
cd steve
git reset --hard ${GIT_COMMIT}
if [[ "$(uname)" == "Darwin" ]]; then
TAR_CMD="gtar"
else
TAR_CMD="tar"
fi
mkdir -p pkg/ui/ui/dashboard
cd pkg/ui/ui/dashboard
curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.7-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | tar xvzf - --strip-components=2
rm -rf internal/ui/ui/*
mkdir -p internal/ui/ui/dashboard
cd internal/ui/ui/dashboard || exit 1;
curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.8-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=2
cp index.html ../index.html
popd
$(dirname $0)/hack_fs $GIT_SOURCE/pkg/ui/ui/
mkdir ../api-ui
cd ../api-ui || exit 1;
curl -sL https://releases.rancher.com/api-ui/${CATTLE_API_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=1

View File

@@ -2,10 +2,11 @@
set -e
mkdir -p bin dist
if [ -e ./scripts/$1 ]; then
if [ -e "./scripts/$1" ]; then
./scripts/"$@"
else
exec "$@"
fi
chown -R $DAPPER_UID:$DAPPER_GID .
chown -R "$DAPPER_UID:$DAPPER_GID" .

View File

@@ -1,42 +0,0 @@
#!/bin/bash
set -ex
#
# find . -type f -name "_*"
#
function hack_files() {
for f in $(find $1 -type f -name "_*"); do
name=$(basename $f)
updir=$(dirname $f)
new_path=$updir/${name:1}
echo "move $f $new_path"
mv $f $new_path
done
}
#
# find . -type d -name "_*"
#
function hack_dirs() {
for d in $(find $1 -mindepth 1 -maxdepth 1 -type d); do
if [[ ! -d $d ]]; then
continue
fi
name=$(basename $d)
if [[ ${name:0:1} == "_" ]]; then
updir=$(dirname $d)
new_path=$updir/${name:1}
echo "move $d $new_path"
mv $d $new_path
hack_dirs $new_path
continue
fi
hack_dirs $d
done
}
pushd $1
hack_files .
hack_dirs .
popd

View File

@@ -2,17 +2,8 @@
set -e
source $(dirname $0)/version
cd "$(dirname $0)/.."
pushd $DAPPER_SOURCE
cp dist/* package/
docker build -f package/Dockerfile -t "cnrancher/kube-explorer:$VERSION" package
if [ -f bin/kube-explorer-linux-${ARCH} ]; then
# For cross mode
cp bin/kube-explorer-linux-${ARCH} package/kube-explorer
else
# For common mode
cp bin/kube-explorer package/
fi
cd package
docker build -f Dockerfile -t cnrancher/kube-explorer:$VERSION .
popd

42
scripts/release-note Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env sh
set -e
source "$(dirname $0)/version"
cd "$(dirname $0)/.."
mkdir -p dist
TARGET_PATH="dist/release-note"
if [ -z "$(command -v release-notary)" ]; then
echo "release-notary is not found, skip generating release notes."
exit 0
fi
if [ -z "${GIT_TAG}" ]; then
echo "running this scrpit without tag, skip generating release notes."
exit 0
fi
GIT_TAG=$(echo "${GIT_TAG}" | grep -E "^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$") || true
if [ "${GIT_TAG}" = "" ]; then
echo "git GIT_TAG is not validated, skip generating release notes."
exit 0
fi
for tag in $(git tag -l --sort=-v:refname); do
if [ "${tag}" = "${GIT_TAG}" ]; then
continue
fi
filterred=$(echo "${tag}" | grep -E "^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-rc[0-9]*)$") || true
if [ "${filterred}" = "" ]; then
echo "get real release tag ${tag}, stopping untag"
break
fi
git tag -d ${tag}
done
echo "following release notes will be published..."
release-notary publish -d 2>/dev/null | sed '1d' | sed '$d' > $TARGET_PATH
cat "$TARGET_PATH"

View File

@@ -1,7 +1,8 @@
#!/bin/bash
set -e
source $(dirname $0)/version
pushd $GIT_SOURCE
cd "$(dirname $0)/.."
if ! command -v golangci-lint; then
echo Running: go fmt
@@ -9,8 +10,8 @@ if ! command -v golangci-lint; then
exit
fi
#echo Running: golangci-lint
#golangci-lint run
echo Running: golangci-lint
golangci-lint run
echo Tidying up modules
go mod tidy
@@ -18,4 +19,13 @@ go mod tidy
echo Verifying modules
go mod verify
popd
dirty_files="$(git status --porcelain --untracked-files=no)"
if [ -n "$dirty_files" ]; then
echo "Encountered dirty repo! Aborting."
echo "If you're seeing this, it means there are uncommitted changes in the repo."
echo "If you're seeing this in CI, it probably means that your Go modules aren't tidy, or more generally that running"
echo "validation would result in changes to the repo. Make sure you're up to date with the upstream branch and run"
echo "'go mod tidy' and commit the changes, if any. The offending changed files are as follows:"
echo "$dirty_files"
exit 1
fi

View File

@@ -1,11 +1,15 @@
#!/bin/bash
if [[ ${GITHUB_REF} == refs/tags/* ]]; then
GIT_TAG=${GIT_TAG:-${GITHUB_REF_NAME}}
fi
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
DIRTY="-dirty"
fi
COMMIT=$(git rev-parse --short HEAD)
GIT_TAG=${DRONE_TAG:-$(git tag -l --contains HEAD | head -n 1)}
GIT_TAG=${GIT_TAG:-$(git tag -l --contains HEAD | head -n 1)}
if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then
VERSION=$GIT_TAG