Compare commits

..

No commits in common. "master" and "web/v1.5.0" have entirely different histories.

247 changed files with 5868 additions and 9860 deletions

View File

@ -48,15 +48,15 @@ jobs:
type=raw,value=latest
type=match,pattern=\d+\.\d+\.\d+
type=sha
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3.10.0
- uses: docker/setup-qemu-action@v3.2.0
- uses: docker/setup-buildx-action@v3.7.1
with:
config: .github/buildkitd.toml
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_RELEASE_USER }}
password: ${{ secrets.DOCKER_RELEASE_PASS }}
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -48,15 +48,15 @@ jobs:
type=raw,value=latest
type=match,pattern=\d+\.\d+\.\d+
type=sha
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3.10.0
- uses: docker/setup-qemu-action@v3.2.0
- uses: docker/setup-buildx-action@v3.7.1
with:
config: .github/buildkitd.toml
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_RELEASE_USER }}
password: ${{ secrets.DOCKER_RELEASE_PASS }}
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -30,13 +30,13 @@ jobs:
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: azure/setup-helm@v4.3.0
- uses: azure/setup-helm@v4.2.0
with:
version: v3.12.1
- uses: helm/chart-testing-action@v2.7.0
- uses: helm/kind-action@v1.12.0
- uses: helm/chart-testing-action@v2.6.1
- uses: helm/kind-action@v1.10.0
with:
node_image: kindest/node:v1.32.0
node_image: kindest/node:v1.30.2
- run: ct install --target-branch ${{ github.event.repository.default_branch }}
release:
@ -57,7 +57,7 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Set up Helm
uses: azure/setup-helm@v4.3.0
uses: azure/setup-helm@v4.2.0
with:
version: v3.12.1
@ -69,7 +69,7 @@ jobs:
helm repo add kong https://charts.konghq.com
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.7.0
uses: helm/chart-releaser-action@v1.6.0
with:
skip_existing: true
mark_as_latest: true

View File

@ -48,15 +48,15 @@ jobs:
type=raw,value=latest
type=match,pattern=\d+\.\d+\.\d+
type=sha
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3.10.0
- uses: docker/setup-qemu-action@v3.2.0
- uses: docker/setup-buildx-action@v3.7.1
with:
config: .github/buildkitd.toml
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_RELEASE_USER }}
password: ${{ secrets.DOCKER_RELEASE_PASS }}
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -48,15 +48,15 @@ jobs:
type=raw,value=latest
type=match,pattern=\d+\.\d+\.\d+
type=sha
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3.10.0
- uses: docker/setup-qemu-action@v3.2.0
- uses: docker/setup-buildx-action@v3.7.1
with:
config: .github/buildkitd.toml
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_RELEASE_USER }}
password: ${{ secrets.DOCKER_RELEASE_PASS }}
- uses: docker/login-action@v3.4.0
- uses: docker/login-action@v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -23,31 +23,24 @@ jobs:
name: Lint and test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.1
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4.3.0
- uses: azure/setup-helm@v4.2.0
with:
version: v3.12.1
- name: Set up Chart Testing CLI
uses: helm/chart-testing-action@v2.7.0
- name: Change Detection
id: list-changed
- uses: helm/chart-testing-action@v2.6.1
- id: list-changed
run: |
changed=$(ct list-changed --config=.ct.yml --target-branch ${{ github.event.repository.default_branch }})
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Lint Chart
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --config=.ct.yml --target-branch ${{ github.event.repository.default_branch }} --check-version-increment=false
- name: Set up Kind
if: steps.list-changed.outputs.changed == 'true'
uses: helm/kind-action@v1.12.0
- if: steps.list-changed.outputs.changed == 'true'
run: ct lint --config=.ct.yml --target-branch ${{ github.event.repository.default_branch }}
- if: steps.list-changed.outputs.changed == 'true'
uses: helm/kind-action@v1.10.0
with:
node_image: kindest/node:v1.32.0
- name: Install Chart
if: steps.list-changed.outputs.changed == 'true'
node_image: kindest/node:v1.30.2
- if: steps.list-changed.outputs.changed == 'true'
run: ct install --target-branch ${{ github.event.repository.default_branch }}

View File

@ -64,7 +64,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5.5.0
- uses: actions/setup-go@v5.0.2
with:
go-version-file: modules/api/go.mod
check-latest: true
@ -81,7 +81,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5.5.0
- uses: actions/setup-go@v5.0.2
with:
go-version-file: modules/api/go.mod
check-latest: true
@ -99,7 +99,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5.5.0
- uses: actions/setup-go@v5.0.2
with:
go-version-file: modules/api/go.mod
check-latest: true
@ -116,63 +116,6 @@ jobs:
fetch-depth: 0
- run: make image
publish:
name: Build and push API container
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' # not a fork and not dependabot
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Docket meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/kubernetes/dashboard-api
docker.io/kubernetesui/dashboard-api
tags: |
type=sha
type=ref,event=pr
type=ref,event=branch
type=semver,pattern={{version}},value=${{ needs.prepare.outputs.new_release_version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.6.0
- name: set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0
with:
config: .github/buildkitd.toml
- name: Login to Docker
uses: docker/login-action@v3.4.0
with:
username: ${{ secrets.DOCKER_RELEASE_USER }}
password: ${{ secrets.DOCKER_RELEASE_PASS }}
- name: Login to GHCR
uses: docker/login-action@v3.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: push
uses: docker/build-push-action@v6
with:
context: modules
file: modules/api/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ steps.meta.outputs.version || 'latest' }}
unit-tests:
name: Unit tests with coverage
runs-on: ubuntu-latest
@ -180,7 +123,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5.5.0
- uses: actions/setup-go@v5.0.2
with:
go-version-file: modules/go.work
check-latest: true
@ -190,7 +133,7 @@ jobs:
- working-directory: modules/web
run: yarn
- run: PATH=$PATH:$GOPATH/bin make coverage
- uses: codecov/codecov-action@v5.4.2
- uses: codecov/codecov-action@v4.6.0
with:
directory: ./.tmp
token: ${{ secrets.CODECOV_TOKEN }}
@ -205,8 +148,8 @@ jobs:
# fetch-depth: 0
# - uses: helm/kind-action@v1.9.0
# with:
# node_image: kindest/node:v1.32.0
# - uses: actions/setup-go@v5.5.0
# node_image: kindest/node:v1.30.2
# - uses: actions/setup-go@v5.0.2
# with:
# go-version-file: modules/api/go.mod
# check-latest: true

View File

@ -11,41 +11,28 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
version: "2"
#
run:
modules-download-mode: readonly
allow-parallel-runners: true
timeout: 30m
issues-exit-code: 1
tests: true
allow-parallel-runners: true
linters:
default: none
disable-all: true
enable:
# default linters
- errcheck
- errorlint
- gocyclo
- gosimple
- govet
- ineffassign
- misspell
- staticcheck
- typecheck
- unused
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gocyclo
# additional linters
- errorlint
- errname
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- misspell

View File

@ -1,15 +1,15 @@
dependencies:
- name: ingress-nginx
repository: https://kubernetes.github.io/ingress-nginx
version: 4.12.0
version: 4.10.1
- name: cert-manager
repository: https://charts.jetstack.io
version: v1.16.2
version: v1.14.5
- name: metrics-server
repository: https://kubernetes-sigs.github.io/metrics-server/
version: 3.12.2
version: 3.12.1
- name: kong
repository: https://charts.konghq.com
version: 2.46.0
digest: sha256:fa0fa7ff7e237c3db88016a57704d0f2c9f7dae9ac9b041a32112b6df35f8e23
generated: "2025-01-10T12:28:39.253786876+01:00"
version: 2.38.0
digest: sha256:ef535931f6d08b3b4b242cba567d0c02a072cf7cf68431f58a0d8d283b3b72b5
generated: "2024-06-04T11:32:45.155515532+02:00"

View File

@ -14,7 +14,7 @@
apiVersion: v2
name: kubernetes-dashboard
version: 7.13.0
version: 7.7.0
description: General-purpose web UI for Kubernetes clusters
keywords:
- kubernetes
@ -32,18 +32,18 @@ kubeVersion: ">=1.21.0-0"
dependencies:
- name: ingress-nginx
alias: nginx
version: 4.12.0
version: 4.10.1
repository: https://kubernetes.github.io/ingress-nginx
condition: nginx.enabled
- name: cert-manager
version: v1.16.2
version: v1.14.5
repository: https://charts.jetstack.io
condition: cert-manager.enabled
- name: metrics-server
version: 3.12.2
version: 3.12.1
repository: https://kubernetes-sigs.github.io/metrics-server/
condition: metrics-server.enabled
- name: kong
version: 2.46.0
version: 2.38.0
repository: https://charts.konghq.com
condition: kong.enabled

View File

@ -5,7 +5,7 @@
Congratulations! You have just installed Kubernetes Dashboard in your cluster.
{{ if not (.Values.nginx.enabled) }}
To access Dashboard run:
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-kong-proxy 8443:443
kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443
NOTE: In case port-forward command does not work, make sure that kong service name is correct.
Check the services in Kubernetes Dashboard namespace using:
@ -17,7 +17,7 @@ Dashboard will be available at:
{{ if and (has "localhost" .Values.app.ingress.hosts) (eq .Values.app.ingress.ingressClassName "internal-nginx") (.Values.nginx.enabled) }}
To access Dashboard run:
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-nginx-controller 8443:443
kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-nginx-controller 8443:443
NOTE: In case port-forward command does not work, make sure that nginx service name is correct.
Check the services in Kubernetes Dashboard namespace using:

View File

@ -75,14 +75,14 @@ spec:
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"
divisor: 1
{{- end }}
{{- if .Values.api.containers.resources.limits.memory }}
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: "1"
divisor: 1
{{- end }}
{{- with .Values.api.containers.env }}

View File

@ -76,14 +76,14 @@ spec:
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"
divisor: 1
{{- end }}
{{- if .Values.auth.containers.resources.limits.memory }}
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: "1"
divisor: 1
{{- end }}
{{- with .Values.auth.containers.env }}

View File

@ -68,14 +68,14 @@ spec:
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"
divisor: 1
{{- end }}
{{- if .Values.metricsScraper.containers.resources.limits.memory }}
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: "1"
divisor: 1
{{- end }}
{{- with .Values.metricsScraper.containers.env }}

View File

@ -70,14 +70,14 @@ spec:
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"
divisor: 1
{{- end }}
{{- if .Values.web.containers.resources.limits.memory }}
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: "1"
divisor: 1
{{- end }}
{{- with .Values.web.containers.env }}

View File

@ -15,12 +15,6 @@
{{- if and .Values.app.ingress.enabled (eq .Values.app.mode "dashboard")}}
{{- include "kubernetes-dashboard.validate.ingressIssuerScope" $ }}
# Determine the service port to use for the ingress configuration
# If TLS is enabled in the ingress configuration, use the TLS service port.
# Otherwise, fall back to the HTTP service port.
{{- $servicePort := (ternary $.Values.kong.proxy.tls.servicePort $.Values.kong.proxy.http.servicePort $.Values.app.ingress.tls.enabled) }}
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
@ -79,7 +73,7 @@ spec:
service:
name: {{ template "kong.fullname" (index $.Subcharts "kong") }}-proxy
port:
number: {{ $servicePort }}
number: {{ $.Values.kong.proxy.tls.servicePort }}
{{- end }}
{{- else }}
- http:
@ -90,6 +84,6 @@ spec:
service:
name: {{ template "kong.fullname" (index $.Subcharts "kong") }}-proxy
port:
number: {{ $servicePort }}
number: {{ $.Values.kong.proxy.tls.servicePort }}
{{- end }}
{{- end }}

View File

@ -30,19 +30,11 @@ metadata:
{{- end }}
name: {{ template "kubernetes-dashboard.fullname" . }}-{{ .Values.api.role }}
spec:
type: {{ .Values.api.service.type }}
ports:
{{- range $port := .Values.api.containers.ports }}
- name: {{ $port.name }}
port: {{ $port.containerPort }}
{{ if $port.protocol }}protocol: {{ $port.protocol }}{{- end }}
{{ if $port.appProtocol }}appProtocol: {{ $port.appProtocol }}{{- end }}
{{ if $port.nodePort }}nodePort: {{ $port.nodePort }}{{- end }}
{{ if $port.targetPort }}targetPort: {{ $port.targetPort }}{{- end }}
- name: {{ .Values.api.role }}
{{- with (index .Values.api.containers.ports 0) }}
port: {{ .containerPort }}
{{- end }}
{{ with .Values.api.service.extraSpec }}
{{ . | toYaml | nindent 2 }}
{{- end }}
selector:
{{- include "kubernetes-dashboard.matchLabels" . | nindent 4 }}
app.kubernetes.io/name: {{ template "kubernetes-dashboard.name" . }}-{{ .Values.api.role }}

View File

@ -32,19 +32,11 @@ metadata:
{{- end }}
name: {{ template "kubernetes-dashboard.fullname" . }}-{{ .Values.auth.role }}
spec:
type: {{ .Values.auth.service.type }}
ports:
{{- range $port := .Values.auth.containers.ports }}
- name: {{ $port.name }}
port: {{ $port.containerPort }}
{{ if $port.protocol }}protocol: {{ $port.protocol }}{{- end }}
{{ if $port.appProtocol }}appProtocol: {{ $port.appProtocol }}{{- end }}
{{ if $port.nodePort }}nodePort: {{ $port.nodePort }}{{- end }}
{{ if $port.targetPort }}targetPort: {{ $port.targetPort }}{{- end }}
- name: {{ .Values.auth.role }}
{{- with (index .Values.auth.containers.ports 0) }}
port: {{ .containerPort }}
{{- end }}
{{ with .Values.auth.service.extraSpec }}
{{ . | toYaml | nindent 2 }}
{{- end }}
selector:
{{- include "kubernetes-dashboard.matchLabels" . | nindent 4 }}
app.kubernetes.io/name: {{ template "kubernetes-dashboard.name" . }}-{{ .Values.auth.role }}

View File

@ -32,20 +32,10 @@ metadata:
{{- end }}
name: {{ template "kubernetes-dashboard.metrics-scraper.name" . }}
spec:
type: {{ .Values.metricsScraper.service.type }}
ports:
{{- range $port := .Values.metricsScraper.containers.ports }}
# Name is intentionally not used here as it breaks the connection between API <-> Scraper
# Named ports have an issue when trying to connect through in-cluster service proxy.
- port: {{ $port.containerPort }}
{{ if $port.protocol }}protocol: {{ $port.protocol }}{{- end }}
{{ if $port.appProtocol }}appProtocol: {{ $port.appProtocol }}{{- end }}
{{ if $port.nodePort }}nodePort: {{ $port.nodePort }}{{- end }}
{{ if $port.targetPort }}targetPort: {{ $port.targetPort }}{{- end }}
{{- with (index .Values.metricsScraper.containers.ports 0) }}
- port: {{ .containerPort }}
{{- end }}
{{ with .Values.metricsScraper.service.extraSpec }}
{{ . | toYaml | nindent 2 }}
{{- end }}
selector:
{{- include "kubernetes-dashboard.matchLabels" . | nindent 4 }}
app.kubernetes.io/name: {{ template "kubernetes-dashboard.name" . }}-{{ .Values.metricsScraper.role }}

View File

@ -32,19 +32,11 @@ metadata:
{{- end }}
name: {{ template "kubernetes-dashboard.fullname" . }}-{{ .Values.web.role }}
spec:
type: {{ .Values.web.service.type }}
ports:
{{- range $port := .Values.web.containers.ports }}
- name: {{ $port.name }}
port: {{ $port.containerPort }}
{{ if $port.protocol }}protocol: {{ $port.protocol }}{{- end }}
{{ if $port.appProtocol }}appProtocol: {{ $port.appProtocol }}{{- end }}
{{ if $port.nodePort }}nodePort: {{ $port.nodePort }}{{- end }}
{{ if $port.targetPort }}targetPort: {{ $port.targetPort }}{{- end }}
- name: {{ .Values.web.role }}
{{- with (index .Values.web.containers.ports 0) }}
port: {{ .containerPort }}
{{- end }}
{{ with .Values.web.service.extraSpec }}
{{ . | toYaml | nindent 2 }}
{{- end }}
selector:
{{- include "kubernetes-dashboard.matchLabels" . | nindent 4 }}
app.kubernetes.io/name: {{ template "kubernetes-dashboard.name" . }}-{{ .Values.web.role }}

View File

@ -23,7 +23,7 @@ app:
pullSecrets: []
scheduling:
# Node labels for pod assignment
# Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
# Ref: https://kubernetes.io/docs/user-guide/node-selection/
nodeSelector: {}
security:
# Allow overriding csrfKey used by API/Auth containers.
@ -84,8 +84,6 @@ app:
# resourceAutoRefreshTimeInterval: 10
# # Hide all access denied warnings in the notification panel
# disableAccessDeniedNotifications: false
# # Hide all namespaces option in namespace selection dropdown to avoid accidental selection in large clusters thus preventing OOM errors
# hideAllNamespaces: false
# # Namespace that should be selected by default after logging in.
# defaultNamespace: default
# # List of namespaces that should be presented to user without namespace list privileges.
@ -145,13 +143,10 @@ auth:
role: auth
image:
repository: docker.io/kubernetesui/dashboard-auth
tag: 1.3.0
tag: 1.1.3
scaling:
replicas: 1
revisionHistoryLimit: 10
service:
type: ClusterIP
extraSpec: ~
containers:
ports:
- name: auth
@ -187,13 +182,10 @@ api:
role: api
image:
repository: docker.io/kubernetesui/dashboard-api
tag: 1.13.0
tag: 1.8.1
scaling:
replicas: 1
revisionHistoryLimit: 10
service:
type: ClusterIP
extraSpec: ~
containers:
ports:
- name: api
@ -247,13 +239,10 @@ web:
role: web
image:
repository: docker.io/kubernetesui/dashboard-web
tag: 1.7.0
tag: 1.4.0
scaling:
replicas: 1
revisionHistoryLimit: 10
service:
type: ClusterIP
extraSpec: ~
containers:
ports:
- name: web
@ -310,13 +299,10 @@ metricsScraper:
role: metrics-scraper
image:
repository: docker.io/kubernetesui/dashboard-metrics-scraper
tag: 1.2.2
tag: 1.1.1
scaling:
replicas: 1
revisionHistoryLimit: 10
service:
type: ClusterIP
extraSpec: ~
containers:
ports:
- containerPort: 8000

View File

@ -3,81 +3,65 @@
Dashboard containers accept multiple arguments that can be used to customize them.
## API module arguments
| Argument name | Default value | Description |
|------------------------------|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| disable-csrf-protection | false | Allows disabling CSRF protection. |
| act-as-proxy | false | Forces dashboard to work in full proxy mode, meaning that any internal in-cluster client calls are disabled. |
| openapi-enabled | false | Enables OpenAPI v2 endpoint user '/apidocs.json'. Used to autogenerate OpenAPI/GraphQL schema. |
| profiler | false | Enables pprof handler. By default it will be exposed on localhost:8070 under '/debug/pprof'. |
| prometheus-enabled | false | Enables prometheus metrics handler. By default it will be exposed on localhost:8080 under '/metrics'. |
| apiserver-skip-tls-verify | false | Enable if connection with remote Kubernetes API should skip TLS verify. |
| auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. |
| cache-enabled | true | Whether the client cache should be enabled or not. |
| cluster-context-enabled | false | Whether multi-cluster cache context support should be enabled or not. |
| cache-size | 1000 | Max number of cache entries to hold at once. |
| cache-ttl | 10m | Time to live of each cache entry. |
| cache-refresh-debounce | 5s | Minimal time between cache refreshes in background. |
| insecure-port | 8000 | The port to listen to for incoming HTTP requests. |
| port | 8001 | The secure port to listen to for incoming HTTPS requests. |
| metric-client-check-period | 30 | Time in seconds that defines how often configured metric client health check should be run. |
| insecure-port | 9000 | The port to listen to for incoming HTTP requests. |
| port | 9001 | The secure port to listen to for incoming HTTPS requests. |
| insecure-bind-address | 127.0.0.1 | The IP address on which to serve the `--insecure-port` (set to 127.0.0.1 for loopback only). |
| bind-address | 0.0.0.0 | The IP address on which to serve the `--port` (set to 0.0.0.0 for all interfaces). |
| token-exchange-endpoint | - | Endpoint used when `--cluster-context-enabled=true` to exchange auth token for the unique context identifier. |
| default-cert-dir | /certs | Directory path containing `--tls-cert-file` and `--tls-key-file` files. Used also when auto-generating certificates flag is set. Relative to the container, not the host. |
| tls-cert-file | - | File containing the default x509 Certificate for HTTPS. |
| tls-key-file | - | File containing the default x509 private key matching --tls-cert-file. |
| auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. |
| apiserver-host | - | The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted. |
| metrics-provider | sidecar | Select provider type for metrics. 'none' will not check metrics. |
| sidecar-host | - | The address of the Sidecar Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8000. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used. |
| metrics-provider | sidecar | Select provider type for metrics. 'none' will not check metrics. |
| metric-client-check-period | 30 | Time in seconds that defines how often configured metric client health check should be run. |
| kubeconfig | - | Path to kubeconfig file with control plane location information. |
| namespace | kubernetes-dashboard | Namespace to use when accessing Dashboard specific resources, i.e. metrics scraper service. |
| metrics-scraper-service-name | kubernetes-dashboard-metrics-scraper | Name of the dashboard metrics scraper service. |
| disable-csrf-protection | false | Allows disabling CSRF protection. |
| csrf-key | - | Base64 encoded random 256 bytes key. Can be loaded from 'CSRF_KEY' environment variable. |
| v | 1 | Number for the log level verbosity (default 1) | |
| act-as-proxy | false | Forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled. |
| v | 1 | Number for the log level verbosity (default 1) | |
## Auth module arguments
| Argument name | Default value | Description |
|---------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| apiserver-skip-tls-verify | false | Enable if connection with remote Kubernetes API should skip TLS verify. |
| port | 8000 | The secure port to listen to for incoming HTTPS requests. |
| address | 0.0.0.0 | The IP address on which to serve the `--port` (set to 0.0.0.0 for all interfaces). |
| kubeconfig | - | Path to `kubeconfig` file. |
| apiserver-host | - | The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted. |
| csrf-key | - | Base64 encoded random 256 bytes key. Can be loaded from 'CSRF_KEY' environment variable. |
| v | 1 | Number for the log level verbosity (default 1) | |# Metrics scraper module arguments
| Argument name | Default value | Description |
|---------------|---------------|------------------------------------------------------------------------------------------|
| port | 8000 | The secure port to listen to for incoming HTTPS requests. |
| address | 0.0.0.0 | The IP address on which to serve the `--port` (set to 0.0.0.0 for all interfaces). |
| kubeconfig | - | Path to `kubeconfig` file. |
| csrf-key | - | Base64 encoded random 256 bytes key. Can be loaded from 'CSRF_KEY' environment variable. |
| v | 1 | Number for the log level verbosity (default 1) | |# Metrics scraper module arguments
## Metrics scraper module arguments
| Argument name | Default value | Description |
|-------------------|-----------------|---------------------------------------------------------------------------|
| metric-resolution | 1m | The resolution at which dashboard-metrics-scraper will poll metrics. |
| metric-duration | 15m | The duration after which metrics are purged from the database. |
| kubeconfig | - | Path to `kubeconfig` file. |
| db-file | /tmp/metrics.db | What file to use as a SQLite3 database. |
| namespaces | - | Namespaces to use for all metric calls. When provided, skip node metrics. |
| v | 1 | Number for the log level verbosity (default 1) | |
| Argument name | Default value | Description |
|-------------------|-----------------|------------------------------------------------------------------------------|
| kubeconfig | - | Path to `kubeconfig` file. |
| db-file | /tmp/metrics.db | What file to use as a SQLite3 database. |
| metric-resolution | 1m | The resolution at which dashboard-metrics-scraper will poll metrics. |
| metric-duration | 15m | The duration after which metrics are purged from the database. |
| logtostderr | true | Log to standard error. |
| namespace | - | The namespace to use for all metric calls. When provided, skip node metrics. |
| v | 1 | Number for the log level verbosity (default 1) | |
## Web module arguments
| Argument name | Default value | Description |
|----------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. |
| insecure-port | 8000 | The port to listen to for incoming HTTP requests. |
| port | 8001 | The secure port to listen to for incoming HTTPS requests. |
| insecure-bind-address | 127.0.0.1 | The IP address on which to serve the `--insecure-port` (set to 127.0.0.1 for loopback only). |
| bind-address | 0.0.0.0 | The IP address on which to serve the `--port` (set to 0.0.0.0 for all interfaces). |
| namespace | kube-system | Namespace to use when creating Dashboard specific resources, i.e. settings config map. |
| default-cert-dir | /certs | Directory path containing `--tls-cert-file` and `--tls-key-file` files. Used also when auto-generating certificates flag is set. Relative to the container, not the host. |
| tls-cert-file | - | File containing the default x509 Certificate for HTTPS. |
| tls-key-file | - | File containing the default x509 private key matching --tls-cert-file. |
| auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. |
| locale-config | ./locale_conf.json | File containing the configuration of locales. |
| namespace | kube-system | Namespace to use when creating Dashboard specific resources, i.e. settings config map. |
| settings-config-map-name | kubernetes-dashboard-settings | Name of a config map, that stores settings. |
| system-banner | - | When non-empty displays message to Dashboard users. Accepts simple HTML tags. |
| system-banner-severity | INFO | Severity of system banner. Should be one of `INFO\|WARNING\|ERROR`. |
| locale-config | ./locale_conf.json | File containing the configuration of locales. |
| kubeconfig | - | Path to `kubeconfig` file. |
| v | 1 | Number for the log level verbosity (default 1) | |
----
_Copyright 2019 [The Kubernetes Dashboard Authors](https://github.com/kubernetes/dashboard/graphs/contributors)_

View File

@ -2,6 +2,7 @@
This guide is for anyone interested in contributing design work themselves or contributing in a way that is impacted by design.
## Resources:
* Get in touch with Dan Romlein (@danielromlein) for general Dashboard UX questions or suggestions of tasks that need design work.
* Follow the [Getting started guide](https://github.com/kubernetes/dashboard/wiki/Getting-started) to get the most recent version of Dashboard up and running.
* Dashboard is based on Googles [Material](https://material.io/guidelines/) design system. Refer to their spec for guidance.

View File

@ -1,104 +0,0 @@
# Table of Contents
- [Motivation](#motivation)
- [Goals](#goals)
- [Non-Goals](#non-goals)
- [Terminology](#terminology)
- [Proposal](#proposal)
- [Design](#design)
- [Implementation](#implementation)
## Motivation
The Kubernetes Dashboard has been around for a long time now, and one of its pain points have always been the performance and responsiveness when running in clusters with a large number of resources. Given that, we have been thinking about implementing a proper API caching solution to enhance overall responsiveness and user experience. As clusters grow in size and complexity, users often face latency issues when interacting with the Dashboard, which can lead to inefficiencies in managing and troubleshooting applications. By implementing a proper caching solution, we can significantly reduce the time it takes to retrieve resource data, decrease peak memory usage and optimize overall resource consumption, thereby minimizing delays and improving the fluidity of the user interface.
### Goals
The primary goals of implementing the API caching solution are to:
- **Reduce Latency**: Minimize the time required to retrieve data from the Dashboard API during consecutive requests, enabling faster access to information.
- **Enhance User Experience**: Provide a smoother, more responsive interface for users managing complex clusters.
- **Optimize Resource Utilization**: Decrease the pressure on the Kubernetes API server by caching frequently accessed data, thus improving overall cluster performance.
- **Support Scalability**: Ensure the solution can accommodate clusters of varying sizes and complexities without degrading performance. Cache should be running in multi-cluster as well as in the single-cluster setup.
- **Configurability**: Provide opt-out and other configuration options.
### Non-Goals
This proposal does not aim to:
- **Replace Existing API Functionality**: The caching solution will transparently work with, not replace, the existing API endpoints and interactions.
- **Introduce Complexity for Users**: The implementation should remain transparent to users, avoiding any additional steps or configurations.
- **Cover All Resource Types Equally**: While the caching solution will enhance responsiveness, it may initially focus on the most frequently accessed resource types rather than attempting to cache every possible resource.
### Terminology
- **API Caching**: The process of storing responses from API requests temporarily to reduce the need for repeated fetching of the same data.
- **Resource**: An entity within Kubernetes, such as pods, services, deployments, etc., that users manage through the Dashboard.
- **Latency**: The time delay between a user action and the corresponding response from the system.
## Proposal
The proposed solution involves implementing a caching layer within the Kubernetes Dashboard that stores a configurable number of API responses for a configurable duration. This caching layer will hook into Kubernetes client interfaces and serve cached data when available, falling back to the API server only when necessary. The solution will leverage techniques such as time and cost-based expiration and cache invalidation strategies to ensure data freshness while balancing performance.
In general, it will resemble the "cache-and-network" type of caching due to the nature of Dashboard auth layer. Since Dashboard does not require any permissions on its own, it has to rely on the user permissions and the only time when it can act as a user is the time from receiving a request to sending a response. Such an architecture requires an on-the-fly client creation as well as background cache updates.
To ensure that cached data will not be served to unauthorized entities, every time before API returns data from the cache, it will first create a Self Subject Access Review request to the API server to validate user permissions.
It is especially important in a multi-cluster scenarios where Dashboard API is used to access multiple clusters. To avoid the situation where path stored in cache could be served from the wrong cluster context, multi-cluster cache context needs to have a way to exchange user authorization token for a unique context ID and it has to be a part of the cache key.
Cache key should consist of the below fields:
- **Kind**: resource kind
- **Namespace**: optional namespace name
- **List Options**: `v1.ListOptions` should also be part of the key to ensure that filtered API requests are stored under a separate cache key
- **Context ID**: optional context (cluster) identifier, used only in multi-context caching, controlled by dedicated argument
SHA should be created based on the above key structure and used as an internal cache key.
## Design
These sequence diagrams show simplified way of how cache works.
### Standard Caching
![Cache Sequence Diagram](../images/cache-sequence-diagram.png)
1. User requests to see a cached resource view
2. API checks if unique key generated based on the request has a corresponding value in the cache
1. In case value is not cached, return the data directly from K8S API and cache it.
2. In case value is cached, create a Self Subject Access Review to check user permissions, return cached data and in background request latest data from K8S API to update cache.
### Multi-Context Caching
![Multi-Context Cache Sequence Diagram](../images/multi-context-cache-sequence-diagram.png)
The flow is very similar to the standard caching with the difference being that provided user authorization token has to be able to be exchanged for the unique context ID using configured `token-exchange-endpoint`. It is then used to create unique cache key.
## Implementation
Cache is implemented with the help of [Theine](Yiling-J/theine-go) package. It provides in-memory cache that has good performance, supports generics and keeps its API simple.
Cache is a global variable initialized during application startup. It maps internal key SHAs to the resource lists.
It can be configured via the following arguments:
- `cache-enabled` - Enables the cache. Enabled by default.
- `cache-size` - Maximum number of items in the cache. Set to 1000 by default.
- `cache-ttl` - Cache entry time-to-live. Set to 10 minutes by default.
- `cache-refresh-debounce` - Minimal time that has to pass between consecutive cache refreshes in the background. Set to 5 seconds by default.
- `cluster-context-enabled` - Enables multi-context cache. Disabled by default. Requires `token-exchange-endpoint` to be set if enabled.
- `token-exchange-endpoint` - Endpoint used when multi-context cache is enabled. It exchanges tokens for a context identifiers. It has to be HTTP(s) `GET` that returns raw string with context identifier and accepts `Authorization: Bearer <token>` header.
Cache package provides following interface:
- `Get` - fetches item from the cache.
- `Set` - stores item in the cache.
- `DefferedLoad` - updates cache in the background. Used after cache is read to refresh items.
- `SyncedLoad` - initializes the cache ensuring that there will be no concurrent calls to the Kubernetes API for the same resources.
In order to minimize the amount of code, we have created custom interfaces similar to the `client-go` interfaces where we could override only a single `List` method and still use their generic `client.Interface`. This way our internal implementation and usage of kubernetes client did not have to change at all and we were able to inject cached client globally.
The initial implementation supports caching of the following resources:
1. Core
1. Pod
2. Node
3. ConfigMap
4. Secret
5. Namespace
6. PVC
7. PV
2. Extensions
1. Custom Resource Definitions
Whole cache implementation lives under [modules/common/client/cache](../../modules/common/client/cache).
- **Generic ResourceLister**: [resourcelister.go](../../modules/common/client/cache/client/common/resourcelister.go)
- **Core Client**: [core.go](../../modules/common/client/cache/client/core/core.go)
- **Extensions Client**: [extensions.go](../../modules/common/client/cache/client/extensions/extensions.go)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@ -70,7 +70,7 @@ type: kubernetes.io/service-account-token
After Secret is created, we can execute the following command to get the token which is saved in the Secret:
```shell
kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath="{.data.token}" | base64 -d
kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath={".data.token"} | base64 -d
```
Check [Kubernetes docs](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#manually-create-a-long-lived-api-token-for-a-serviceaccount) for more information about long-lived API tokens for a ServiceAccount.

View File

@ -54,7 +54,6 @@ services:
command:
--kubeconfig=${KUBECONFIG:?}
--sidecar-host=${SIDECAR_HOST:?}
--v=4
volumes:
- ${PWD}/modules/common:/workspace/common # Required - Common dir watched by AIR
- ${PWD}/modules/api/pkg:/workspace/api/pkg # Required - Source dir watched by AIR

View File

@ -21,7 +21,7 @@ DOCKER_COMPOSE_DEV_PATH := $(DOCKER_DIRECTORY)/dev.compose.yml
TMP_DIRECTORY := $(ROOT_DIRECTORY)/.tmp
# Kind
KIND_CLUSTER_NAME := kubernetes-dashboard
KIND_CLUSTER_VERSION := 1.32.0
KIND_CLUSTER_VERSION := 1.29.0
KIND_CLUSTER_IMAGE := docker.io/kindest/node:v${KIND_CLUSTER_VERSION}
KIND_CLUSTER_INTERNAL_KUBECONFIG_PATH := $(TMP_DIRECTORY)/kubeconfig
KIND_CLUSTER_KUBECONFIG_CONTEXT := kind-$(KIND_CLUSTER_NAME)

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FROM alpine:3.21 AS user
FROM alpine:3.19 as user
ENV USER=nonroot
ENV UID=10001
@ -27,7 +27,7 @@ RUN adduser \
--uid "${UID}" \
"${USER}"
FROM golang:1.23-alpine3.21 AS builder
FROM golang:1.23-alpine3.19 as builder
ARG TARGETARCH
ARG TARGETOS
@ -58,7 +58,7 @@ RUN echo ${VERSION}
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w -X k8s.io/dashboard/api/pkg/environment.Version=${VERSION}" -o dashboard-api .
# Scratch can be used as the base image because the binary is compiled to include all dependencies.
FROM scratch AS final
FROM scratch as final
COPY --from=builder /workspace/api/dashboard-api /dashboard-api

View File

@ -14,11 +14,11 @@
# ! Context expected to be set to "modules" dir !
FROM golang:1.23-alpine3.21 AS AIR
FROM golang:1.23-alpine3.19 as AIR
RUN go install github.com/air-verse/air@latest
FROM golang:1.23-alpine3.21
FROM golang:1.23-alpine3.19
# Copy air binary
COPY --from=AIR $GOPATH/bin/air $GOPATH/bin/air

View File

@ -4,18 +4,17 @@ go 1.23.0
require (
github.com/distribution/reference v0.6.0
github.com/emicklei/go-restful-openapi/v2 v2.11.0
github.com/emicklei/go-restful-openapi/v2 v2.10.2
github.com/emicklei/go-restful/v3 v3.12.1
github.com/go-openapi/spec v0.21.0
github.com/prometheus/client_golang v1.20.5
github.com/samber/lo v1.47.0
github.com/prometheus/client_golang v1.20.4
github.com/spf13/pflag v1.0.5
golang.org/x/net v0.40.0
golang.org/x/net v0.30.0
gopkg.in/igm/sockjs-go.v2 v2.1.0
k8s.io/api v0.32.0
k8s.io/apiextensions-apiserver v0.32.0
k8s.io/apimachinery v0.32.0
k8s.io/client-go v0.32.0
k8s.io/api v0.31.1
k8s.io/apiextensions-apiserver v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
k8s.io/dashboard/certificates v0.0.0-00010101000000-000000000000
k8s.io/dashboard/client v0.0.0-00010101000000-000000000000
k8s.io/dashboard/csrf v0.0.0-00010101000000-000000000000
@ -23,100 +22,100 @@ require (
k8s.io/dashboard/helpers v0.0.0-00010101000000-000000000000
k8s.io/dashboard/types v0.0.0-00010101000000-000000000000
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.32.0
k8s.io/kubectl v0.31.1
)
require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Yiling-J/theine-go v0.6.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bytedance/sonic v1.12.7 // indirect
github.com/bytedance/sonic/loader v0.2.2 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.10.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.23.0 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/gobuffalo/flect v1.0.3 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.61.0 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.47.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.36.2 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.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/cli-runtime v0.32.0 // indirect
k8s.io/component-base v0.32.0 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
k8s.io/cli-runtime v0.31.1 // indirect
k8s.io/component-base v0.31.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.17.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

View File

@ -1,26 +1,31 @@
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Yiling-J/theine-go v0.6.0 h1:jv7V/tcD6ijL0T4kfbJDKP81TCZBkoriNTPSqwivWuY=
github.com/Yiling-J/theine-go v0.6.0/go.mod h1:mdch1vjgGWd7s3rWKvY+MF5InRLfRv/CWVI9RVNQ8wY=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
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/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.2 h1:jxAJuN9fOot/cyz5Q6dUuMJF5OqQ6+5GfA8FjjQ0R4o=
github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80=
github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
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=
@ -32,25 +37,25 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/emicklei/go-restful-openapi/v2 v2.11.0 h1:Ur+yGxoOH/7KRmcj/UoMFqC3VeNc9VOe+/XidumxTvk=
github.com/emicklei/go-restful-openapi/v2 v2.11.0/go.mod h1:4CTuOXHFg3jkvCpnXN+Wkw5prVUnP8hIACssJTYorWo=
github.com/emicklei/go-restful-openapi/v2 v2.10.2 h1:RfxWvGmASIwVoZIEncvXLi5HxYQ0S8rNBkPresDMt1c=
github.com/emicklei/go-restful-openapi/v2 v2.10.2/go.mod h1:4CTuOXHFg3jkvCpnXN+Wkw5prVUnP8hIACssJTYorWo=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -73,39 +78,57 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.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.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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -114,11 +137,11 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -136,16 +159,16 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
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=
@ -158,14 +181,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -173,16 +196,17 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
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/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
@ -205,8 +229,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@ -217,63 +241,98 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
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=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -286,39 +345,44 @@ gopkg.in/igm/sockjs-go.v2 v2.1.0/go.mod h1:9l1o9p5TJvh2l+Q0EGE8USVB69QPfcvI7fR0H
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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-20200615113413-eeeca48fe776/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.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/cli-runtime v0.32.0 h1:dP+OZqs7zHPpGQMCGAhectbHU2SNCuZtIimRKTv2T1c=
k8s.io/cli-runtime v0.32.0/go.mod h1:Mai8ht2+esoDRK5hr861KRy6z0zHsSTYttNVJXgP3YQ=
k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU=
k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40=
k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
k8s.io/kubectl v0.32.0 h1:rpxl+ng9qeG79YA4Em9tLSfX0G8W0vfaiPVrc/WR7Xw=
k8s.io/kubectl v0.32.0/go.mod h1:qIjSX+QgPQUgdy8ps6eKsYNF+YmFOAO3WygfucIqFiE=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24=
k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
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/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0=
sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ=
sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -23,8 +23,6 @@ import (
"github.com/spf13/pflag"
"k8s.io/klog/v2"
// Load client args
_ "k8s.io/dashboard/client/args"
"k8s.io/dashboard/csrf"
"k8s.io/dashboard/helpers"
)
@ -39,41 +37,26 @@ const (
LogLevelTrace = klog.Level(5)
)
const (
defaultInsecurePort = 8000
defaultPort = 8001
defaultProfilerPort = 8070
defaultPrometheusPort = 8080
defaultProfilerPath = "/debug/pprof/"
defaultPrometheusPath = "/metrics"
)
var (
argDisableCSRFProtection = pflag.Bool("disable-csrf-protection", false, "allows disabling CSRF protection")
argIsProxyEnabled = pflag.Bool("act-as-proxy", false, "forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled")
argOpenAPIEnabled = pflag.Bool("openapi-enabled", false, "enables OpenAPI v2 endpoint under '/apidocs.json'")
argProfiler = pflag.Bool("profiler", false, "Enable pprof handler. By default it will be exposed on localhost:8070 under '/debug/pprof'")
argPrometheusEnabled = pflag.Bool("prometheus-enabled", false, "Enable prometheus metrics handler. By default it will be exposed on localhost:8080 under '/metrics'")
argApiServerSkipTLSVerify = pflag.Bool("apiserver-skip-tls-verify", false, "enable if connection with remote Kubernetes API server should skip TLS verify")
argAutoGenerateCertificates = pflag.Bool("auto-generate-certificates", false, "enables automatic certificates generation used to serve HTTPS")
argInsecurePort = pflag.Int("insecure-port", defaultInsecurePort, "port to listen to for incoming HTTP requests")
argPort = pflag.Int("port", defaultPort, "secure port to listen to for incoming HTTPS requests")
argMetricClientCheckPeriod = pflag.Int("metric-client-check-period", 30, "time interval between separate metric client health checks in seconds")
argInsecureBindAddress = pflag.IP("insecure-bind-address", net.IPv4(127, 0, 0, 1), "IP address on which to serve the --insecure-port, set to 0.0.0.0 for all interfaces")
argBindAddress = pflag.IP("bind-address", net.IPv4(0, 0, 0, 0), "IP address on which to serve the --port, set to 0.0.0.0 for all interfaces")
argInsecurePort = pflag.Int("insecure-port", 8000, "port to listen to for incoming HTTP requests")
argPort = pflag.Int("port", 8001, "secure port to listen to for incoming HTTPS requests")
argInsecureBindAddress = pflag.IP("insecure-bind-address", net.IPv4(127, 0, 0, 1), "IP address on which to serve the --insecure-port, set to 127.0.0.1 for all interfaces")
argBindAddress = pflag.IP("bind-address", net.IPv4(0, 0, 0, 0), "IP address on which to serve the --port, set to 0.0.0.0 for all interfaces")
argDefaultCertDir = pflag.String("default-cert-dir", "/certs", "directory path containing files from --tls-cert-file and --tls-key-file, used also when auto-generating certificates flag is set")
argCertFile = pflag.String("tls-cert-file", "", "file containing the default x509 certificate for HTTPS")
argKeyFile = pflag.String("tls-key-file", "", "file containing the default x509 private key matching --tls-cert-file")
argApiServerHost = pflag.String("apiserver-host", "", "address of the Kubernetes API server to connect to in the format of protocol://address:port, leave it empty if the binary runs inside cluster for local discovery attempt")
argApiServerSkipTLSVerify = pflag.Bool("apiserver-skip-tls-verify", false, "enable if connection with remote Kubernetes API server should skip TLS verify")
argMetricsProvider = pflag.String("metrics-provider", "sidecar", "select provider type for metrics, 'none' will not check metrics")
argSidecarHost = pflag.String("sidecar-host", "", "address of the Sidecar API server to connect to in the format of protocol://address:port, leave it empty if the binary runs inside cluster for service proxy usage")
argKubeConfigFile = pflag.String("kubeconfig", "", "path to kubeconfig file with control plane location information")
argMetricClientCheckPeriod = pflag.Int("metric-client-check-period", 30, "time interval between separate metric client health checks in seconds")
argAutoGenerateCertificates = pflag.Bool("auto-generate-certificates", false, "enables automatic certificates generation used to serve HTTPS")
argNamespace = pflag.String("namespace", helpers.GetEnv("POD_NAMESPACE", "kubernetes-dashboard"), "Namespace to use when accessing Dashboard specific resources, i.e. metrics scraper service")
argMetricsScraperServiceName = pflag.String("metrics-scraper-service-name", "kubernetes-dashboard-metrics-scraper", "name of the dashboard metrics scraper service")
argDisableCSRFProtection = pflag.Bool("disable-csrf-protection", false, "allows disabling CSRF protection")
argIsProxyEnabled = pflag.Bool("act-as-proxy", false, "forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled")
argOpenAPIEnabled = pflag.Bool("openapi-enabled", false, "enables OpenAPI v2 endpoint under '/apidocs.json'")
)
func init() {
@ -90,14 +73,6 @@ func init() {
if IsCSRFProtectionEnabled() {
csrf.Ensure()
}
if *argProfiler {
initProfiler()
}
if *argPrometheusEnabled {
initPrometheus()
}
}
func Address() string {

View File

@ -1,35 +0,0 @@
// Copyright 2017 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package args
import (
"fmt"
"net/http"
"net/http/pprof"
"k8s.io/klog/v2"
)
func initProfiler() {
klog.V(LogLevelInfo).Info("Initializing profiler")
mux := http.NewServeMux()
mux.HandleFunc(defaultProfilerPath, pprof.Index)
go func() {
if err := http.ListenAndServe(fmt.Sprintf(":%d", defaultProfilerPort), mux); err != nil {
klog.Fatal(err)
}
}()
}

View File

@ -1,35 +0,0 @@
// Copyright 2017 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package args
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"k8s.io/klog/v2"
)
func initPrometheus() {
klog.V(LogLevelInfo).Info("Initializing prometheus metrics")
mux := http.NewServeMux()
mux.Handle(defaultPrometheusPath, promhttp.Handler())
go func() {
if err := http.ListenAndServe(fmt.Sprintf(":%d", defaultPrometheusPort), mux); err != nil {
klog.Fatal(err)
}
}()
}

File diff suppressed because it is too large Load Diff

View File

@ -77,23 +77,14 @@ func formatRequestLog(request *restful.Request) string {
content = "{ content hidden }"
}
return fmt.Sprintf(
RequestLogString,
request.Request.Proto,
request.Request.Method,
uri,
getRemoteAddr(request.Request),
content,
)
return fmt.Sprintf(RequestLogString, time.Now().Format(time.RFC3339), request.Request.Proto,
request.Request.Method, uri, getRemoteAddr(request.Request), content)
}
// formatResponseLog formats response log string.
func formatResponseLog(response *restful.Response, request *restful.Request) string {
return fmt.Sprintf(
ResponseLogString,
getRemoteAddr(request.Request),
response.StatusCode(),
)
return fmt.Sprintf(ResponseLogString, time.Now().Format(time.RFC3339),
getRemoteAddr(request.Request), response.StatusCode())
}
// checkSensitiveUrl checks if a string matches against a sensitive URL

View File

@ -19,7 +19,6 @@ import (
"strings"
"github.com/emicklei/go-restful/v3"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/dataselect"
)

View File

@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"sync"
"time"
@ -32,9 +33,6 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/args"
)
const END_OF_TRANSMISSION = "\u0004"
@ -166,7 +164,7 @@ func (sm *SessionMap) Close(sessionId string, status uint32, reason string) {
ses := sm.Sessions[sessionId]
err := ses.sockJSSession.Close(status, reason)
if err != nil {
klog.Error(err)
log.Println(err)
}
close(ses.sizeChan)
delete(sm.Sessions, sessionId)
@ -184,22 +182,22 @@ func handleTerminalSession(session sockjs.Session) {
)
if buf, err = session.Recv(); err != nil {
klog.Errorf("handleTerminalSession: can't Recv: %v", err)
log.Printf("handleTerminalSession: can't Recv: %v", err)
return
}
if err = json.Unmarshal([]byte(buf), &msg); err != nil {
klog.Errorf("handleTerminalSession: can't UnMarshal (%v): %s", err, buf)
log.Printf("handleTerminalSession: can't UnMarshal (%v): %s", err, buf)
return
}
if msg.Op != "bind" {
klog.V(args.LogLevelVerbose).Infof("handleTerminalSession: expected 'bind' message, got: %s", buf)
log.Printf("handleTerminalSession: expected 'bind' message, got: %s", buf)
return
}
if terminalSession = terminalSessions.Get(msg.SessionID); terminalSession.id == "" {
klog.V(args.LogLevelVerbose).Infof("handleTerminalSession: can't find session '%s'", msg.SessionID)
log.Printf("handleTerminalSession: can't find session '%s'", msg.SessionID)
return
}

View File

@ -33,16 +33,16 @@ type Handler struct {
//
// By default, endpoint for checking state of the integrations is installed. It allows user
// to check state of integration by accessing `<DASHBOARD_URL>/api/v1/integration/{name}/state`.
func (in Handler) Install(ws *restful.WebService) {
func (self Handler) Install(ws *restful.WebService) {
ws.Route(
ws.GET("/integration/{name}/state").
To(in.handleGetState).
To(self.handleGetState).
Writes(api.IntegrationState{}))
}
func (in Handler) handleGetState(request *restful.Request, response *restful.Response) {
func (self Handler) handleGetState(request *restful.Request, response *restful.Response) {
integrationName := request.PathParameter("name")
state, err := in.manager.GetState(api.IntegrationID(integrationName))
state, err := self.manager.GetState(api.IntegrationID(integrationName))
if err != nil {
response.AddHeader("Content-Type", "text/plain")
_ = response.WriteErrorString(http.StatusInternalServerError, err.Error()+"\n")

View File

@ -31,7 +31,7 @@ func areErrorsEqual(err1, err2 error) bool {
// Removes all quote signs that might have been added to the message.
// Might depend on dependencies version how they are constructed.
func normalize(msg string) string {
return strings.ReplaceAll(msg, "\"", "")
return strings.Replace(msg, "\"", "", -1)
}
func TestNewIntegrationManager(t *testing.T) {
@ -82,6 +82,6 @@ func TestIntegrationManager_GetState(t *testing.T) {
func TestIntegrationManager_Metric(t *testing.T) {
metricManager := integration.NewIntegrationManager().Metric()
if metricManager == nil {
t.Error("failed to get metric manager.")
t.Error("Failed to get metric manager.")
}
}

View File

@ -127,15 +127,15 @@ type MetricPoint struct {
// Label stores information about identity of resources (UIDs) described by metric.
type Label map[types.ResourceKind][]apimachinery.UID
// AddMetricLabel returns a unique combined Label of in and other resource.
// AddMetricLabel returns a unique combined Label of self and other resource.
// New label describes both resources.
func (in Label) AddMetricLabel(other Label) Label {
func (self Label) AddMetricLabel(other Label) Label {
if other == nil {
return in
return self
}
uniqueMap := map[apimachinery.UID]bool{}
for _, v := range in {
for _, v := range self {
for _, t := range v {
uniqueMap[t] = true
}
@ -144,11 +144,11 @@ func (in Label) AddMetricLabel(other Label) Label {
for k, v := range other {
for _, t := range v {
if _, exists := uniqueMap[t]; !exists {
in[k] = append(in[k], t)
self[k] = append(self[k], t)
}
}
}
return in
return self
}
// Metric is a format of data used in this module. This is also the format of data that is being sent by backend client.
@ -190,18 +190,18 @@ func (metric *SidecarMetric) AddMetricPoint(item MetricPoint) []MetricPoint {
return metric.MetricPoints
}
func (in *Metric) AddMetricPoint(item MetricPoint) []MetricPoint {
in.MetricPoints = append(in.MetricPoints, item)
return in.MetricPoints
func (metric *Metric) AddMetricPoint(item MetricPoint) []MetricPoint {
metric.MetricPoints = append(metric.MetricPoints, item)
return metric.MetricPoints
}
// String implements stringer interface to allow easy printing
func (in Metric) String() string {
return "{\nDataPoints: " + fmt.Sprintf("%v", in.DataPoints) +
"\nMetricPoints: " + fmt.Sprintf("%v", in.MetricPoints) +
"\nMetricName: " + in.MetricName +
"\nLabel: " + fmt.Sprintf("%v", in.Label) +
"\nAggregate: " + fmt.Sprintf("%v", in.Aggregate)
func (self Metric) String() string {
return "{\nDataPoints: " + fmt.Sprintf("%v", self.DataPoints) +
"\nMetricPoints: " + fmt.Sprintf("%v", self.MetricPoints) +
"\nMetricName: " + self.MetricName +
"\nLabel: " + fmt.Sprintf("%v", self.Label) +
"\nAggregate: " + fmt.Sprintf("%v", self.Aggregate)
}
// MetricPromise is used for parallel data extraction. Contains len 1 channels for Metric and Error.
@ -211,12 +211,12 @@ type MetricPromise struct {
}
// GetMetric returns pointer to received Metrics and forwarded error (if any)
func (in MetricPromise) GetMetric() (*Metric, error) {
err := <-in.Error
func (self MetricPromise) GetMetric() (*Metric, error) {
err := <-self.Error
if err != nil {
return nil, err
}
return <-in.Metric, nil
return <-self.Metric, nil
}
// NewMetricPromise creates a MetricPromise structure with both channels of length 1.
@ -231,10 +231,10 @@ type MetricPromises []MetricPromise
// GetMetrics returns all metrics from MetricPromises.
// In case of no metrics were downloaded it does not initialise []Metric and returns nil.
func (in MetricPromises) GetMetrics() ([]Metric, error) {
func (self MetricPromises) GetMetrics() ([]Metric, error) {
result := make([]Metric, 0)
for _, metricPromise := range in {
for _, metricPromise := range self {
metric, err := metricPromise.GetMetric()
if err != nil {
// Do not fail when cannot resolve one of the metrics promises and return what can be resolved.
@ -252,8 +252,8 @@ func (in MetricPromises) GetMetrics() ([]Metric, error) {
}
// PutMetrics forwards provided list of metrics to all channels. If provided err is not nil, error will be forwarded.
func (in MetricPromises) PutMetrics(metrics []Metric, err error) {
for i, metricPromise := range in {
func (self MetricPromises) PutMetrics(metrics []Metric, err error) {
for i, metricPromise := range self {
if err != nil {
metricPromise.Metric <- nil
} else {

View File

@ -52,39 +52,39 @@ type metricManager struct {
}
// AddClient implements metric manager interface. See MetricManager for more information.
func (in *metricManager) AddClient(client metricapi.MetricClient) MetricManager {
func (self *metricManager) AddClient(client metricapi.MetricClient) MetricManager {
if client != nil {
in.clients[client.ID()] = client
self.clients[client.ID()] = client
}
return in
return self
}
// Client implements metric manager interface. See MetricManager for more information.
func (in *metricManager) Client() metricapi.MetricClient {
return in.active
func (self *metricManager) Client() metricapi.MetricClient {
return self.active
}
// Enable implements metric manager interface. See MetricManager for more information.
func (in *metricManager) Enable(id integrationapi.IntegrationID) error {
metricClient, exists := in.clients[id]
func (self *metricManager) Enable(id integrationapi.IntegrationID) error {
metricClient, exists := self.clients[id]
if !exists {
return fmt.Errorf("no metric client found for integration id: %s", id)
return fmt.Errorf("No metric client found for integration id: %s", id)
}
err := metricClient.HealthCheck()
if err != nil {
return fmt.Errorf("health check failed: %s", err.Error())
return fmt.Errorf("Health check failed: %s", err.Error())
}
in.active = metricClient
self.active = metricClient
return nil
}
// EnableWithRetry implements metric manager interface. See MetricManager for more information.
func (in *metricManager) EnableWithRetry(id integrationapi.IntegrationID, period time.Duration) {
func (self *metricManager) EnableWithRetry(id integrationapi.IntegrationID, period time.Duration) {
go wait.Forever(func() {
metricClient, exists := in.clients[id]
metricClient, exists := self.clients[id]
if !exists {
klog.V(5).InfoS("Metric client does not exist", "clientID", id)
return
@ -92,22 +92,22 @@ func (in *metricManager) EnableWithRetry(id integrationapi.IntegrationID, period
err := metricClient.HealthCheck()
if err != nil {
in.active = nil
self.active = nil
klog.Errorf("Metric client health check failed: %s. Retrying in %d seconds.", err, period)
return
}
if in.active == nil {
if self.active == nil {
klog.V(1).Infof("Successful request to %s", id)
in.active = metricClient
self.active = metricClient
}
}, period*time.Second)
}
// List implements metric manager interface. See MetricManager for more information.
func (in *metricManager) List() []integrationapi.Integration {
func (self *metricManager) List() []integrationapi.Integration {
result := make([]integrationapi.Integration, 0)
for _, c := range in.clients {
for _, c := range self.clients {
result = append(result, c.(integrationapi.Integration))
}
@ -115,16 +115,16 @@ func (in *metricManager) List() []integrationapi.Integration {
}
// ConfigureSidecar implements metric manager interface. See MetricManager for more information.
func (in *metricManager) ConfigureSidecar(host string) MetricManager {
func (self *metricManager) ConfigureSidecar(host string) MetricManager {
inClusterClient := client.InClusterClient()
metricClient, err := sidecar.CreateSidecarClient(host, inClusterClient)
if err != nil {
klog.Errorf("There was an error during sidecar client creation: %s", err.Error())
return in
return self
}
in.clients[metricClient.ID()] = metricClient
return in
self.clients[metricClient.ID()] = metricClient
return self
}
// NewMetricManager creates metric manager.

View File

@ -33,25 +33,25 @@ func (FakeMetricClient) ID() integrationapi.IntegrationID {
return fakeMetricClientID
}
func (in FakeMetricClient) HealthCheck() error {
if in.healthOk {
func (self FakeMetricClient) HealthCheck() error {
if self.healthOk {
return nil
}
return errors.NewInvalid("test-error")
}
func (in FakeMetricClient) DownloadMetric(selectors []api.ResourceSelector, metricName string,
func (self FakeMetricClient) DownloadMetric(selectors []api.ResourceSelector, metricName string,
cachedResources *api.CachedResources) api.MetricPromises {
return nil
}
func (in FakeMetricClient) DownloadMetrics(selectors []api.ResourceSelector, metricNames []string,
func (self FakeMetricClient) DownloadMetrics(selectors []api.ResourceSelector, metricNames []string,
cachedResources *api.CachedResources) api.MetricPromises {
return nil
}
func (in FakeMetricClient) AggregateMetrics(metrics api.MetricPromises, metricName string,
func (self FakeMetricClient) AggregateMetrics(metrics api.MetricPromises, metricName string,
aggregations api.AggregationModes) api.MetricPromises {
return nil
}
@ -95,7 +95,7 @@ func TestMetricManager_Enable(t *testing.T) {
client api.MetricClient
expected error
}{
{&FakeMetricClient{healthOk: false}, errors.NewInvalid("health check failed: test-error")},
{&FakeMetricClient{healthOk: false}, errors.NewInvalid("Health check failed: test-error")},
{&FakeMetricClient{healthOk: true}, nil},
}

View File

@ -19,6 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"k8s.io/apimachinery/pkg/types"
@ -40,56 +41,56 @@ type sidecarClient struct {
// Implement Integration interface.
// HealthCheck implements integration app interface. See Integration interface for more information.
func (in sidecarClient) HealthCheck() error {
if in.client == nil {
return errors.New("sidecar not configured")
func (self sidecarClient) HealthCheck() error {
if self.client == nil {
return errors.New("Sidecar not configured")
}
return in.client.HealthCheck()
return self.client.HealthCheck()
}
// ID implements integration app interface. See Integration interface for more information.
func (in sidecarClient) ID() integrationapi.IntegrationID {
func (self sidecarClient) ID() integrationapi.IntegrationID {
return integrationapi.SidecarIntegrationID
}
// Implement MetricClient interface
// DownloadMetrics implements metric client interface. See MetricClient for more information.
func (in sidecarClient) DownloadMetrics(selectors []metricapi.ResourceSelector,
func (self sidecarClient) DownloadMetrics(selectors []metricapi.ResourceSelector,
metricNames []string, cachedResources *metricapi.CachedResources) metricapi.MetricPromises {
result := metricapi.MetricPromises{}
for _, metricName := range metricNames {
collectedMetrics := in.DownloadMetric(selectors, metricName, cachedResources)
collectedMetrics := self.DownloadMetric(selectors, metricName, cachedResources)
result = append(result, collectedMetrics...)
}
return result
}
// DownloadMetric implements metric client interface. See MetricClient for more information.
func (in sidecarClient) DownloadMetric(selectors []metricapi.ResourceSelector,
func (self sidecarClient) DownloadMetric(selectors []metricapi.ResourceSelector,
metricName string, cachedResources *metricapi.CachedResources) metricapi.MetricPromises {
sidecarSelectors := getSidecarSelectors(selectors, cachedResources)
// Downloads metric in the fastest possible way by first compressing SidecarSelectors and later unpacking the result to separate boxes.
compressedSelectors, reverseMapping := compress(sidecarSelectors)
return in.downloadMetric(sidecarSelectors, compressedSelectors, reverseMapping, metricName)
return self.downloadMetric(sidecarSelectors, compressedSelectors, reverseMapping, metricName)
}
// AggregateMetrics implements metric client interface. See MetricClient for more information.
func (in sidecarClient) AggregateMetrics(metrics metricapi.MetricPromises, metricName string,
func (self sidecarClient) AggregateMetrics(metrics metricapi.MetricPromises, metricName string,
aggregations metricapi.AggregationModes) metricapi.MetricPromises {
return common.AggregateMetricPromises(metrics, metricName, aggregations, nil)
}
func (in sidecarClient) downloadMetric(sidecarSelectors []sidecarSelector,
func (self sidecarClient) downloadMetric(sidecarSelectors []sidecarSelector,
compressedSelectors []sidecarSelector, reverseMapping map[string][]int,
metricName string) metricapi.MetricPromises {
// collect all the required data (as promises)
unassignedResourcePromisesList := make([]metricapi.MetricPromises, len(compressedSelectors))
for selectorId, compressedSelector := range compressedSelectors {
unassignedResourcePromisesList[selectorId] =
in.downloadMetricForEachTargetResource(compressedSelector, metricName)
self.downloadMetricForEachTargetResource(compressedSelector, metricName)
}
// prepare final result
result := metricapi.NewMetricPromises(len(sidecarSelectors))
@ -134,28 +135,28 @@ func (in sidecarClient) downloadMetric(sidecarSelectors []sidecarSelector,
}
// downloadMetricForEachTargetResource downloads requested metric for each resource present in SidecarSelector
// and returns the result as a list of promises - one promise for each resource. Order of promises returned is the same as order in in.Resources.
func (in sidecarClient) downloadMetricForEachTargetResource(selector sidecarSelector, metricName string) metricapi.MetricPromises {
// and returns the result as a list of promises - one promise for each resource. Order of promises returned is the same as order in self.Resources.
func (self sidecarClient) downloadMetricForEachTargetResource(selector sidecarSelector, metricName string) metricapi.MetricPromises {
var notAggregatedMetrics metricapi.MetricPromises
if SidecarAllInOneDownloadConfig[selector.TargetResourceType] {
notAggregatedMetrics = in.allInOneDownload(selector, metricName)
notAggregatedMetrics = self.allInOneDownload(selector, metricName)
} else {
notAggregatedMetrics = metricapi.MetricPromises{}
for i := range selector.Resources {
notAggregatedMetrics = append(notAggregatedMetrics, in.ithResourceDownload(selector, metricName, i))
notAggregatedMetrics = append(notAggregatedMetrics, self.ithResourceDownload(selector, metricName, i))
}
}
return notAggregatedMetrics
}
// ithResourceDownload downloads metric for ith resource in in.Resources. Use only in case all in 1 download is not supported
// ithResourceDownload downloads metric for ith resource in self.Resources. Use only in case all in 1 download is not supported
// for this resource type.
func (in sidecarClient) ithResourceDownload(selector sidecarSelector, metricName string,
func (self sidecarClient) ithResourceDownload(selector sidecarSelector, metricName string,
i int) metricapi.MetricPromise {
result := metricapi.NewMetricPromise()
go func() {
rawResult := metricapi.SidecarMetricResultList{}
err := in.unmarshalType(selector.Path+selector.Resources[i]+"/metrics/"+metricName, &rawResult)
err := self.unmarshalType(selector.Path+selector.Resources[i]+"/metrics/"+metricName, &rawResult)
if err != nil {
result.Metric <- nil
result.Error <- err
@ -185,9 +186,9 @@ func (in sidecarClient) ithResourceDownload(selector sidecarSelector, metricName
return result
}
// allInOneDownload downloads metrics for all resources present in in.Resources in one request.
// returns a list of metric promises - one promise for each resource. Order of in.Resources is preserved.
func (in sidecarClient) allInOneDownload(selector sidecarSelector, metricName string) metricapi.MetricPromises {
// allInOneDownload downloads metrics for all resources present in self.Resources in one request.
// returns a list of metric promises - one promise for each resource. Order of self.Resources is preserved.
func (self sidecarClient) allInOneDownload(selector sidecarSelector, metricName string) metricapi.MetricPromises {
result := metricapi.NewMetricPromises(len(selector.Resources))
go func() {
if len(selector.Resources) == 0 {
@ -195,7 +196,7 @@ func (in sidecarClient) allInOneDownload(selector sidecarSelector, metricName st
}
rawResults := metricapi.SidecarMetricResultList{}
err := in.unmarshalType(selector.Path+strings.Join(selector.Resources, ",")+"/metrics/"+metricName, &rawResults)
err := self.unmarshalType(selector.Path+strings.Join(selector.Resources, ",")+"/metrics/"+metricName, &rawResults)
if err != nil {
result.PutMetrics(nil, err)
@ -203,7 +204,7 @@ func (in sidecarClient) allInOneDownload(selector sidecarSelector, metricName st
}
if len(result) != len(rawResults.Items) {
klog.V(args.LogLevelVerbose).Infof(`received %d resources from sidecar instead of %d`, len(rawResults.Items), len(result))
log.Printf(`received %d resources from sidecar instead of %d`, len(rawResults.Items), len(result))
}
// rawResult.Items have indefinite order.
@ -248,8 +249,8 @@ func (in sidecarClient) allInOneDownload(selector sidecarSelector, metricName st
// unmarshalType performs sidecar GET request to the specifies path and transfers
// the data to the interface provided.
func (in sidecarClient) unmarshalType(path string, v interface{}) error {
rawData, err := in.client.Get("/api/v1/dashboard/" + path).DoRaw(context.TODO())
func (self sidecarClient) unmarshalType(path string, v interface{}) error {
rawData, err := self.client.Get("/api/v1/dashboard/" + path).DoRaw(context.TODO())
if err != nil {
return err
}

View File

@ -19,6 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"reflect"
"regexp"
"strings"
@ -43,7 +44,7 @@ func areErrorsEqual(err1, err2 error) bool {
// Removes all quote signs that might have been added to the message.
// Might depend on dependencies version how they are constructed.
func normalize(msg string) string {
return strings.ReplaceAll(msg, "\"", "")
return strings.Replace(msg, "\"", "", -1)
}
type GlobalCounter int32
@ -76,27 +77,28 @@ type FakeRequest struct {
type PodData map[string][]metricapi.MetricPoint
type NodeData map[string][]metricapi.MetricPoint
func (in FakeSidecar) Get(path string) RequestInterface {
return FakeRequest{in.PodData, in.NodeData, path}
func (self FakeSidecar) Get(path string) RequestInterface {
return FakeRequest{self.PodData, self.NodeData, path}
}
func (in FakeSidecar) GetNumberOfRequestsMade() int {
func (self FakeSidecar) GetNumberOfRequestsMade() int {
num := int(_NumRequests.get())
_NumRequests.set(0)
return num
}
func (in FakeSidecar) HealthCheck() error {
func (self FakeSidecar) HealthCheck() error {
return nil
}
func (in FakeSidecar) ID() integrationapi.IntegrationID {
func (self FakeSidecar) ID() integrationapi.IntegrationID {
return "fakeSidecar"
}
func (in FakeRequest) DoRaw(ctx context.Context) ([]byte, error) {
func (self FakeRequest) DoRaw(ctx context.Context) ([]byte, error) {
_NumRequests.increment()
path := in.Path
log.Println("Performing req...")
path := self.Path
time.Sleep(50 * time.Millisecond) // simulate response delay of 0.05 seconds
if strings.Contains(path, "/pod-list/") {
r, _ := regexp.Compile(`\/pod\-list\/(.+)\/metrics\/`)
@ -115,9 +117,10 @@ func (in FakeRequest) DoRaw(ctx context.Context) ([]byte, error) {
items := metricapi.SidecarMetricResultList{}
for _, pod := range requestedPods {
items.Items = append(items.Items, metricapi.SidecarMetric{MetricPoints: in.PodData[pod+"/"+namespace], UIDs: []string{pod}})
items.Items = append(items.Items, metricapi.SidecarMetric{MetricPoints: self.PodData[pod+"/"+namespace], UIDs: []string{pod}})
}
x, err := json.Marshal(items)
log.Println("Got you:", string(x))
return x, err
} else if strings.Contains(path, "/nodes/") {
@ -129,16 +132,17 @@ func (in FakeRequest) DoRaw(ctx context.Context) ([]byte, error) {
requestedNode := submatch[1]
items := metricapi.SidecarMetricResultList{}
items.Items = append(items.Items, metricapi.SidecarMetric{MetricPoints: in.NodeData[requestedNode], UIDs: []string{requestedNode}})
items.Items = append(items.Items, metricapi.SidecarMetric{MetricPoints: self.NodeData[requestedNode], UIDs: []string{requestedNode}})
x, err := json.Marshal(items)
log.Println("Got you:", string(x))
return x, err
} else {
return nil, fmt.Errorf("Invalid request url %s", path)
}
}
func (in FakeRequest) AbsPath(segments ...string) *rest.Request {
func (self FakeRequest) AbsPath(segments ...string) *rest.Request {
return &rest.Request{}
}
@ -290,6 +294,7 @@ func TestDownloadMetric(t *testing.T) {
},
}
for _, testCase := range testCases {
log.Println("-----------\n\n\n", testCase.Info, int(_NumRequests.get()))
hClient := sidecarClient{fakeSidecarClient}
promises := hClient.DownloadMetric(testCase.Selectors, "",
&metricapi.CachedResources{})

View File

@ -56,8 +56,8 @@ func (c inClusterSidecarClient) Get(path string) RequestInterface {
// HealthCheck does a health check of the application.
// Returns nil if connection to application can be established, error object otherwise.
func (in inClusterSidecarClient) HealthCheck() error {
_, err := in.client.Get().
func (self inClusterSidecarClient) HealthCheck() error {
_, err := self.client.Get().
Namespace(args.Namespace()).
Resource("services").
Name(args.MetricsScraperServiceName()).
@ -80,7 +80,7 @@ func (c remoteSidecarClient) Get(path string) RequestInterface {
// HealthCheck does a health check of the application.
// Returns nil if connection to application can be established, error object otherwise.
func (in remoteSidecarClient) HealthCheck() error {
_, err := in.Get("healthz").AbsPath("/").DoRaw(context.TODO())
func (self remoteSidecarClient) HealthCheck() error {
_, err := self.Get("healthz").AbsPath("/").DoRaw(context.TODO())
return err
}

View File

@ -17,9 +17,9 @@ package sidecar
import (
"fmt"
"github.com/emicklei/go-restful/v3/log"
v1 "k8s.io/api/core/v1"
apimachinery "k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/helpers"
@ -39,7 +39,7 @@ func getSidecarSelectors(selectors []metricapi.ResourceSelector,
for i, selector := range selectors {
sidecarSelector, err := getSidecarSelector(selector, cachedResources)
if err != nil {
klog.Errorf("There was an error during transformation to sidecar selector: %s", err.Error())
log.Printf("There was an error during transformation to sidecar selector: %s", err.Error())
continue
}
@ -67,7 +67,7 @@ func getSidecarSelector(selector metricapi.ResourceSelector,
selector.Namespace, podListToNameList(myPods), podListToUIDList(myPods))
}
// currently can only convert derived resource to pods. You can change it by implementing other methods
return sidecarSelector{}, fmt.Errorf(`internal Error: Requested summing resources not supported. Requested "%s"`, summingResource)
return sidecarSelector{}, fmt.Errorf(`Internal Error: Requested summing resources not supported. Requested "%s"`, summingResource)
}
// getMyPodsFromCache returns a full list of pods that belong to this resource.
@ -76,11 +76,11 @@ func getSidecarSelector(selector metricapi.ResourceSelector,
func getMyPodsFromCache(selector metricapi.ResourceSelector, cachedPods []v1.Pod) (matchingPods []v1.Pod, err error) {
switch {
case cachedPods == nil:
err = fmt.Errorf(`pods were not available in cache. Required for resource type: "%s"`,
err = fmt.Errorf(`Pods were not available in cache. Required for resource type: "%s"`,
selector.ResourceType)
case selector.ResourceType == types.ResourceKindDeployment:
for _, pod := range cachedPods {
if pod.Namespace == selector.Namespace && helpers.IsSelectorMatching(selector.Selector, pod.Labels) {
if pod.ObjectMeta.Namespace == selector.Namespace && helpers.IsSelectorMatching(selector.Selector, pod.Labels) {
matchingPods = append(matchingPods, pod)
}
}
@ -104,23 +104,22 @@ func getMyPodsFromCache(selector metricapi.ResourceSelector, cachedPods []v1.Pod
func newSidecarSelectorFromNativeResource(resourceType types.ResourceKind, namespace string,
resourceNames []string, resourceUIDs []apimachinery.UID) (sidecarSelector, error) {
// Here we have 2 possibilities because this module allows downloading Nodes and Pods from sidecar
switch resourceType {
case types.ResourceKindPod:
if resourceType == types.ResourceKindPod {
return sidecarSelector{
TargetResourceType: types.ResourceKindPod,
Path: `namespaces/` + namespace + `/pod-list/`,
Resources: resourceNames,
Label: metricapi.Label{resourceType: resourceUIDs},
}, nil
case types.ResourceKindNode:
} else if resourceType == types.ResourceKindNode {
return sidecarSelector{
TargetResourceType: types.ResourceKindNode,
Path: `nodes/`,
Resources: resourceNames,
Label: metricapi.Label{resourceType: resourceUIDs},
}, nil
default:
return sidecarSelector{}, fmt.Errorf(`resource "%s" is not a native sidecar resource type or is not supported`, resourceType)
} else {
return sidecarSelector{}, fmt.Errorf(`Resource "%s" is not a native sidecar resource type or is not supported`, resourceType)
}
}

View File

@ -22,14 +22,14 @@ import (
type RoleCell ClusterRole
func (in RoleCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self RoleCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.ObjectMeta.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.ObjectMeta.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.ObjectMeta.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -15,9 +15,10 @@
package clusterrole
import (
"log"
rbac "k8s.io/api/rbac/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -39,7 +40,7 @@ type ClusterRole struct {
}
func GetClusterRoleList(client kubernetes.Interface, dsQuery *dataselect.DataSelectQuery) (*ClusterRoleList, error) {
klog.V(4).Info("Getting list of RBAC roles")
log.Println("Getting list of RBAC roles")
channels := &common.ResourceChannels{
ClusterRoleList: common.GetClusterRoleListChannel(client, 1),
}

View File

@ -22,14 +22,14 @@ import (
type ClusterRoleBindingCell ClusterRoleBinding
func (in ClusterRoleBindingCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self ClusterRoleBindingCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.ObjectMeta.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.ObjectMeta.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.ObjectMeta.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -15,9 +15,10 @@
package clusterrolebinding
import (
"log"
rbac "k8s.io/api/rbac/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -43,7 +44,7 @@ type ClusterRoleBinding struct {
// GetClusterRoleBindingList returns a list of all ClusterRoleBindings in the cluster.
func GetClusterRoleBindingList(client kubernetes.Interface, dsQuery *dataselect.DataSelectQuery) (*ClusterRoleBindingList, error) {
klog.V(4).Infof("Getting list of all clusterRoleBindings in the cluster")
log.Print("Getting list of all clusterRoleBindings in the cluster")
channels := &common.ResourceChannels{
ClusterRoleBindingList: common.GetClusterRoleBindingListChannel(client, 1),
}

View File

@ -22,7 +22,6 @@ import (
batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
policyv1 "k8s.io/api/policy/v1"
rbac "k8s.io/api/rbac/v1"
storage "k8s.io/api/storage/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@ -125,8 +124,6 @@ type ResourceChannels struct {
// List and error channels to ClusterRoleBindings
ClusterRoleBindingList ClusterRoleBindingListChannel
PodDisruptionBudget PodDisruptionBudgetListChannel
}
// ServiceListChannel is a list and error channels to Services.
@ -148,7 +145,7 @@ func GetServiceListChannel(client client.Interface, nsQuery *NamespaceQuery,
list, err := client.CoreV1().Services(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []v1.Service
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -181,7 +178,7 @@ func GetIngressListChannel(client client.Interface, nsQuery *NamespaceQuery,
list, err := client.NetworkingV1().Ingresses(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []networkingv1.Ingress
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -298,7 +295,7 @@ func GetEventListChannelWithOptions(client client.Interface,
list, err := client.CoreV1().Events(nsQuery.ToRequestParam()).List(context.TODO(), options)
var filteredItems []v1.Event
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -368,7 +365,7 @@ func GetPodListChannelWithOptions(client client.Interface, nsQuery *NamespaceQue
list, err := client.CoreV1().Pods(nsQuery.ToRequestParam()).List(context.TODO(), options)
var filteredItems []v1.Pod
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -404,7 +401,7 @@ func GetReplicationControllerListChannel(client client.Interface,
List(context.TODO(), helpers.ListEverything)
var filteredItems []v1.ReplicationController
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -439,7 +436,7 @@ func GetDeploymentListChannel(client client.Interface,
List(context.TODO(), helpers.ListEverything)
var filteredItems []apps.Deployment
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -480,7 +477,7 @@ func GetReplicaSetListChannelWithOptions(client client.Interface, nsQuery *Names
List(context.TODO(), options)
var filteredItems []apps.ReplicaSet
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -512,7 +509,7 @@ func GetDaemonSetListChannel(client client.Interface, nsQuery *NamespaceQuery, n
list, err := client.AppsV1().DaemonSets(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []apps.DaemonSet
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -544,7 +541,7 @@ func GetJobListChannel(client client.Interface,
list, err := client.BatchV1().Jobs(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []batch.Job
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -575,7 +572,7 @@ func GetCronJobListChannel(client client.Interface, nsQuery *NamespaceQuery, num
list, err := client.BatchV1().CronJobs(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []batch.CronJob
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -608,7 +605,7 @@ func GetStatefulSetListChannel(client client.Interface,
statefulSets, err := client.AppsV1().StatefulSets(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []apps.StatefulSet
for _, item := range statefulSets.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -642,7 +639,7 @@ func GetConfigMapListChannel(client client.Interface, nsQuery *NamespaceQuery,
list, err := client.CoreV1().ConfigMaps(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []v1.ConfigMap
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -676,7 +673,7 @@ func GetSecretListChannel(client client.Interface, nsQuery *NamespaceQuery,
list, err := client.CoreV1().Secrets(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
var filteredItems []v1.Secret
for _, item := range list.Items {
if nsQuery.Matches(item.Namespace) {
if nsQuery.Matches(item.ObjectMeta.Namespace) {
filteredItems = append(filteredItems, item)
}
}
@ -844,30 +841,6 @@ func GetPersistentVolumeClaimListChannel(client client.Interface, nsQuery *Names
return channel
}
type PodDisruptionBudgetListChannel struct {
List chan *policyv1.PodDisruptionBudgetList
Error chan error
}
func GetPodDisruptionBudgetListChannel(client client.Interface, nsQuery *NamespaceQuery,
numReads int) PodDisruptionBudgetListChannel {
channel := PodDisruptionBudgetListChannel{
List: make(chan *policyv1.PodDisruptionBudgetList, numReads),
Error: make(chan error, numReads),
}
go func() {
list, err := client.PolicyV1().PodDisruptionBudgets(nsQuery.ToRequestParam()).List(context.TODO(), helpers.ListEverything)
for i := 0; i < numReads; i++ {
channel.List <- list
channel.Error <- err
}
}()
return channel
}
// CustomResourceDefinitionChannelV1 is a list and error channels to CustomResourceDefinition.
type CustomResourceDefinitionChannelV1 struct {
List chan *apiextensions.CustomResourceDefinitionList

View File

@ -27,7 +27,7 @@ func FilterNamespacedServicesBySelector(services []v1.Service, namespace string,
var matchingServices []v1.Service
for _, service := range services {
if service.Namespace == namespace &&
if service.ObjectMeta.Namespace == namespace &&
helpers.IsSelectorMatching(service.Spec.Selector, resourceSelector) {
matchingServices = append(matchingServices, service)
}

View File

@ -23,14 +23,14 @@ import (
type ConfigMapCell api.ConfigMap
func (in ConfigMapCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self ConfigMapCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -16,11 +16,11 @@ package configmap
import (
"context"
"log"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)
// ConfigMapDetail API resource provides mechanisms to inject containers with configuration data while keeping
@ -36,7 +36,7 @@ type ConfigMapDetail struct {
// GetConfigMapDetail returns detailed information about a config map
func GetConfigMapDetail(client kubernetes.Interface, namespace, name string) (*ConfigMapDetail, error) {
klog.V(4).Infof("Getting details of %s config map in %s namespace", name, namespace)
log.Printf("Getting details of %s config map in %s namespace", name, namespace)
rawConfigMap, err := client.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metaV1.GetOptions{})

View File

@ -15,10 +15,11 @@
package configmap
import (
"log"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -46,7 +47,7 @@ type ConfigMap struct {
// GetConfigMapList returns a list of all ConfigMaps in the cluster.
func GetConfigMapList(client kubernetes.Interface, nsQuery *common.NamespaceQuery, dsQuery *dataselect.DataSelectQuery) (*ConfigMapList, error) {
klog.V(4).Infof("Getting list config maps in the namespace %s", nsQuery.ToRequestParam())
log.Printf("Getting list config maps in the namespace %s", nsQuery.ToRequestParam())
channels := &common.ResourceChannels{
ConfigMapList: common.GetConfigMapListChannel(client, nsQuery, 1),
}

View File

@ -112,46 +112,46 @@ func NewResourceController(ref meta.OwnerReference, namespace string, client cli
type JobController batch.Job
// Get is an implementation of Get method from ResourceController interface.
func (in JobController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsForJob(batch.Job(in), allPods)
podInfo := common.GetPodInfo(in.Status.Active, in.Spec.Completions, matchingPods)
func (self JobController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsForJob(batch.Job(self), allPods)
podInfo := common.GetPodInfo(self.Status.Active, self.Spec.Completions, matchingPods)
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindJob),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetContainerImages(&in.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&in.Spec.Template.Spec),
ContainerImages: common.GetContainerImages(&self.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&self.Spec.Template.Spec),
}
}
// UID is an implementation of UID method from ResourceController interface.
func (in JobController) UID() apimachinery.UID {
return batch.Job(in).UID
func (self JobController) UID() apimachinery.UID {
return batch.Job(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in JobController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsForJob(batch.Job(in), allPods)
func (self JobController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsForJob(batch.Job(self), allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetContainerNames(&in.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&in.Spec.Template.Spec),
ContainerNames: common.GetContainerNames(&self.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&self.Spec.Template.Spec),
}
}
type PodController v1.Pod
// Get is an implementation of Get method from ResourceController interface.
func (in PodController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&in, allPods)
func (self PodController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&self, allPods)
podInfo := common.GetPodInfo(int32(len(matchingPods)), nil, matchingPods) // Pods should not desire any Pods
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindPod),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetNonduplicateContainerImages(matchingPods),
InitContainerImages: common.GetNonduplicateInitContainerImages(matchingPods),
@ -159,13 +159,13 @@ func (in PodController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwne
}
// UID is an implementation of UID method from ResourceController interface.
func (in PodController) UID() apimachinery.UID {
return v1.Pod(in).UID
func (self PodController) UID() apimachinery.UID {
return v1.Pod(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in PodController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&in, allPods)
func (self PodController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&self, allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetNonduplicateContainerNames(controlledPods),
@ -178,32 +178,32 @@ func (in PodController) GetLogSources(allPods []v1.Pod) LogSources {
type ReplicaSetController apps.ReplicaSet
// Get is an implementation of Get method from ResourceController interface.
func (in ReplicaSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&in, allPods)
podInfo := common.GetPodInfo(in.Status.Replicas, in.Spec.Replicas, matchingPods)
func (self ReplicaSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&self, allPods)
podInfo := common.GetPodInfo(self.Status.Replicas, self.Spec.Replicas, matchingPods)
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindReplicaSet),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetContainerImages(&in.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&in.Spec.Template.Spec),
ContainerImages: common.GetContainerImages(&self.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&self.Spec.Template.Spec),
}
}
// UID is an implementation of UID method from ResourceController interface.
func (in ReplicaSetController) UID() apimachinery.UID {
return apps.ReplicaSet(in).UID
func (self ReplicaSetController) UID() apimachinery.UID {
return apps.ReplicaSet(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in ReplicaSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&in, allPods)
func (self ReplicaSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&self, allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetContainerNames(&in.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&in.Spec.Template.Spec),
ContainerNames: common.GetContainerNames(&self.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&self.Spec.Template.Spec),
}
}
@ -212,33 +212,33 @@ func (in ReplicaSetController) GetLogSources(allPods []v1.Pod) LogSources {
type ReplicationControllerController v1.ReplicationController
// Get is an implementation of Get method from ResourceController interface.
func (in ReplicationControllerController) Get(allPods []v1.Pod,
func (self ReplicationControllerController) Get(allPods []v1.Pod,
allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&in, allPods)
podInfo := common.GetPodInfo(in.Status.Replicas, in.Spec.Replicas, matchingPods)
matchingPods := common.FilterPodsByControllerRef(&self, allPods)
podInfo := common.GetPodInfo(self.Status.Replicas, self.Spec.Replicas, matchingPods)
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindReplicationController),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetContainerImages(&in.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&in.Spec.Template.Spec),
ContainerImages: common.GetContainerImages(&self.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&self.Spec.Template.Spec),
}
}
// UID is an implementation of UID method from ResourceController interface.
func (in ReplicationControllerController) UID() apimachinery.UID {
return v1.ReplicationController(in).UID
func (self ReplicationControllerController) UID() apimachinery.UID {
return v1.ReplicationController(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in ReplicationControllerController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&in, allPods)
func (self ReplicationControllerController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&self, allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetContainerNames(&in.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&in.Spec.Template.Spec),
ContainerNames: common.GetContainerNames(&self.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&self.Spec.Template.Spec),
}
}
@ -247,33 +247,33 @@ func (in ReplicationControllerController) GetLogSources(allPods []v1.Pod) LogSou
type DaemonSetController apps.DaemonSet
// Get is an implementation of Get method from ResourceController interface.
func (in DaemonSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&in, allPods)
podInfo := common.GetPodInfo(in.Status.CurrentNumberScheduled,
&in.Status.DesiredNumberScheduled, matchingPods)
func (self DaemonSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&self, allPods)
podInfo := common.GetPodInfo(self.Status.CurrentNumberScheduled,
&self.Status.DesiredNumberScheduled, matchingPods)
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindDaemonSet),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetContainerImages(&in.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&in.Spec.Template.Spec),
ContainerImages: common.GetContainerImages(&self.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&self.Spec.Template.Spec),
}
}
// UID is an implementation of UID method from ResourceController interface.
func (in DaemonSetController) UID() apimachinery.UID {
return apps.DaemonSet(in).UID
func (self DaemonSetController) UID() apimachinery.UID {
return apps.DaemonSet(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in DaemonSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&in, allPods)
func (self DaemonSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&self, allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetContainerNames(&in.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&in.Spec.Template.Spec),
ContainerNames: common.GetContainerNames(&self.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&self.Spec.Template.Spec),
}
}
@ -282,32 +282,32 @@ func (in DaemonSetController) GetLogSources(allPods []v1.Pod) LogSources {
type StatefulSetController apps.StatefulSet
// Get is an implementation of Get method from ResourceController interface.
func (in StatefulSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&in, allPods)
podInfo := common.GetPodInfo(in.Status.Replicas, in.Spec.Replicas, matchingPods)
func (self StatefulSetController) Get(allPods []v1.Pod, allEvents []v1.Event) ResourceOwner {
matchingPods := common.FilterPodsByControllerRef(&self, allPods)
podInfo := common.GetPodInfo(self.Status.Replicas, self.Spec.Replicas, matchingPods)
podInfo.Warnings = event.GetPodsEventWarnings(allEvents, matchingPods)
return ResourceOwner{
TypeMeta: types.NewTypeMeta(types.ResourceKindStatefulSet),
ObjectMeta: types.NewObjectMeta(in.ObjectMeta),
ObjectMeta: types.NewObjectMeta(self.ObjectMeta),
Pods: podInfo,
ContainerImages: common.GetContainerImages(&in.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&in.Spec.Template.Spec),
ContainerImages: common.GetContainerImages(&self.Spec.Template.Spec),
InitContainerImages: common.GetInitContainerImages(&self.Spec.Template.Spec),
}
}
// UID is an implementation of UID method from ResourceController interface.
func (in StatefulSetController) UID() apimachinery.UID {
return apps.StatefulSet(in).UID
func (self StatefulSetController) UID() apimachinery.UID {
return apps.StatefulSet(self).UID
}
// GetLogSources is an implementation of the GetLogSources method from ResourceController interface.
func (in StatefulSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&in, allPods)
func (self StatefulSetController) GetLogSources(allPods []v1.Pod) LogSources {
controlledPods := common.FilterPodsByControllerRef(&self, allPods)
return LogSources{
PodNames: getPodNames(controlledPods),
ContainerNames: common.GetContainerNames(&in.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&in.Spec.Template.Spec),
ContainerNames: common.GetContainerNames(&self.Spec.Template.Spec),
InitContainerNames: common.GetInitContainerNames(&self.Spec.Template.Spec),
}
}

View File

@ -27,26 +27,26 @@ import (
type CronJobCell batch.CronJob
func (in CronJobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self CronJobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}
func (in CronJobCell) GetResourceSelector() *metricapi.ResourceSelector {
func (self CronJobCell) GetResourceSelector() *metricapi.ResourceSelector {
return &metricapi.ResourceSelector{
Namespace: in.Namespace,
Namespace: self.ObjectMeta.Namespace,
ResourceType: types.ResourceKindCronJob,
ResourceName: in.Name,
UID: in.UID,
ResourceName: self.ObjectMeta.Name,
UID: self.UID,
}
}

View File

@ -114,11 +114,16 @@ func TriggerCronJob(client client.Interface,
jobToCreate := &batch.Job{
ObjectMeta: meta.ObjectMeta{
Name: newJobName,
Namespace: namespace,
Annotations: annotations,
Labels: labels,
OwnerReferences: []meta.OwnerReference{*meta.NewControllerRef(cronJob, batch.SchemeGroupVersion.WithKind("CronJob"))},
Name: newJobName,
Namespace: namespace,
Annotations: annotations,
Labels: labels,
OwnerReferences: []meta.OwnerReference{{
APIVersion: CronJobAPIVersion,
Kind: CronJobKindName,
Name: cronJob.Name,
UID: cronJob.UID,
}},
},
Spec: cronJob.Spec.JobTemplate.Spec,
}

View File

@ -15,10 +15,11 @@
package cronjob
import (
"log"
batch "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -54,7 +55,7 @@ type CronJob struct {
// GetCronJobList returns a list of all CronJobs in the cluster.
func GetCronJobList(client client.Interface, nsQuery *common.NamespaceQuery,
dsQuery *dataselect.DataSelectQuery) (*CronJobList, error) {
klog.V(4).Infof("Getting list of all cron jobs in the cluster")
log.Print("Getting list of all cron jobs in the cluster")
channels := &common.ResourceChannels{
CronJobList: common.GetCronJobListChannel(client, nsQuery, 1),

View File

@ -48,6 +48,20 @@ func GetExtensionsAPIVersion(client apiextensionsclientset.Interface) (string, e
return "", errors.NewNotFound("supported version for extensions api not found")
}
func GetExtensionsAPIRestClient(client apiextensionsclientset.Interface) (rest.Interface, error) {
version, err := GetExtensionsAPIVersion(client)
if err != nil {
return nil, err
}
switch version {
case v1:
return client.ApiextensionsV1().RESTClient(), nil
}
return nil, errors.NewNotFound(fmt.Sprintf("unsupported extensions api version: %s", version))
}
func GetCustomResourceDefinitionList(client apiextensionsclientset.Interface, dsQuery *dataselect.DataSelectQuery) (*types.CustomResourceDefinitionList, error) {
version, err := GetExtensionsAPIVersion(client)
if err != nil {

View File

@ -82,7 +82,7 @@ func (r *CustomResourceObject) UnmarshalJSON(data []byte) error {
return err
}
r.TypeMeta = types.NewTypeMeta(types.ResourceKind(tempStruct.Kind))
r.TypeMeta = types.NewTypeMeta(types.ResourceKind(tempStruct.TypeMeta.Kind))
r.ObjectMeta = types.NewObjectMeta(tempStruct.ObjectMeta)
return nil
}

View File

@ -25,12 +25,12 @@ import (
type CustomResourceDefinitionCell apiextensions.CustomResourceDefinition
func (in CustomResourceDefinitionCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self CustomResourceDefinitionCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
@ -58,14 +58,14 @@ func fromCells(cells []dataselect.DataCell) []apiextensions.CustomResourceDefini
// The code below allows to perform complex data section on CustomResourceObject.
type CustomResourceObjectCell types.CustomResourceObject
func (in CustomResourceObjectCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self CustomResourceObjectCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.ObjectMeta.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.ObjectMeta.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.ObjectMeta.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -67,26 +67,26 @@ func GetServicesForDSDeletion(client client.Interface, labelSelector labels.Sele
type DaemonSetCell apps.DaemonSet
func (in DaemonSetCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self DaemonSetCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}
func (in DaemonSetCell) GetResourceSelector() *metricapi.ResourceSelector {
func (self DaemonSetCell) GetResourceSelector() *metricapi.ResourceSelector {
return &metricapi.ResourceSelector{
Namespace: in.Namespace,
Namespace: self.ObjectMeta.Namespace,
ResourceType: types.ResourceKindDaemonSet,
ResourceName: in.Name,
UID: in.UID,
ResourceName: self.ObjectMeta.Name,
UID: self.UID,
}
}

View File

@ -16,11 +16,10 @@ package daemonset
import (
"context"
"log"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/common"
)
@ -40,7 +39,7 @@ type DaemonSetDetail struct {
func GetDaemonSetDetail(client k8sClient.Interface, metricClient metricapi.MetricClient,
namespace, name string) (*DaemonSetDetail, error) {
klog.V(4).Infof("Getting details of %s daemon set in %s namespace", name, namespace)
log.Printf("Getting details of %s daemon set in %s namespace", name, namespace)
daemonSet, err := client.AppsV1().DaemonSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {
return nil, err

View File

@ -106,7 +106,7 @@ func TestGetDaemonSetListFromChannels(t *testing.T) {
Labels: map[string]string{"key": "value"},
CreationTimestamp: metaV1.Unix(111, 222),
},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet, Restartable: true},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet},
Pods: common.PodInfo{
Current: 0,
Failed: 0,
@ -351,7 +351,7 @@ func TestToDaemonSetList(t *testing.T) {
Namespace: "namespace-1",
UID: "uid-1",
},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet, Restartable: true},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet},
ContainerImages: []string{"my-container-image-1"},
InitContainerImages: []string{"my-init-container-image-1"},
Pods: common.PodInfo{
@ -367,7 +367,7 @@ func TestToDaemonSetList(t *testing.T) {
Name: "my-app-2",
Namespace: "namespace-2",
},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet, Restartable: true},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet},
ContainerImages: []string{"my-container-image-2"},
InitContainerImages: []string{"my-init-container-image-2"},
Pods: common.PodInfo{
@ -379,7 +379,7 @@ func TestToDaemonSetList(t *testing.T) {
Name: "my-app-3",
Namespace: "namespace-3",
},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet, Restartable: true},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindDaemonSet},
ContainerImages: []string{"my-container-image-3"},
InitContainerImages: []string{"my-init-container-image-3"},
Pods: common.PodInfo{

View File

@ -16,12 +16,11 @@ package daemonset
import (
"context"
"log"
api "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -33,7 +32,7 @@ import (
// GetDaemonSetPods return list of pods targeting daemon set.
func GetDaemonSetPods(client k8sClient.Interface, metricClient metricapi.MetricClient,
dsQuery *dataselect.DataSelectQuery, daemonSetName, namespace string) (*pod.PodList, error) {
klog.V(4).Infof("Getting replication controller %s pods in namespace %s", daemonSetName, namespace)
log.Printf("Getting replication controller %s pods in namespace %s", daemonSetName, namespace)
pods, err := getRawDaemonSetPods(client, daemonSetName, namespace)
if err != nil {

View File

@ -1,43 +0,0 @@
// Copyright 2017 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package daemonset
import (
"context"
"time"
v1 "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client "k8s.io/client-go/kubernetes"
)
const (
// RestartedAtAnnotationKey is an annotation key for rollout restart
RestartedAtAnnotationKey = "kubectl.kubernetes.io/restartedAt"
)
// RestartDaemonSet restarts a daemon set in the manner of `kubectl rollout restart`.
func RestartDaemonSet(client client.Interface, namespace, name string) (*v1.DaemonSet, error) {
daemonSet, err := client.AppsV1().DaemonSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {
return nil, err
}
if daemonSet.Spec.Template.Annotations == nil {
daemonSet.Spec.Template.Annotations = map[string]string{}
}
daemonSet.Spec.Template.Annotations[RestartedAtAnnotationKey] = time.Now().Format(time.RFC3339)
return client.AppsV1().DaemonSets(namespace).Update(context.TODO(), daemonSet, metaV1.UpdateOptions{})
}

View File

@ -19,7 +19,6 @@ import (
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/args"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/errors"
)
@ -44,9 +43,9 @@ type MetricDataCell interface {
// ComparableValue hold any value that can be compared to its own kind.
type ComparableValue interface {
// Compares in with other value. Returns 1 if other value is smaller, 0 if they are the same, -1 if other is larger.
// Compares self with other value. Returns 1 if other value is smaller, 0 if they are the same, -1 if other is larger.
Compare(ComparableValue) int
// Returns true if in value contains or is equal to other value, false otherwise.
// Returns true if self value contains or is equal to other value, false otherwise.
Contains(ComparableValue) bool
}
@ -72,18 +71,18 @@ type DataSelector struct {
// Implementation of sort.Interface so that we can use built-in sort function (sort.Sort) for sorting SelectableData
// Len returns the length of data inside SelectableData.
func (in DataSelector) Len() int { return len(in.GenericDataList) }
func (self DataSelector) Len() int { return len(self.GenericDataList) }
// Swap swaps 2 indices inside SelectableData.
func (in DataSelector) Swap(i, j int) {
in.GenericDataList[i], in.GenericDataList[j] = in.GenericDataList[j], in.GenericDataList[i]
func (self DataSelector) Swap(i, j int) {
self.GenericDataList[i], self.GenericDataList[j] = self.GenericDataList[j], self.GenericDataList[i]
}
// Less compares 2 indices inside SelectableData and returns true if first index is larger.
func (in DataSelector) Less(i, j int) bool {
for _, sortBy := range in.DataSelectQuery.SortQuery.SortByList {
a := in.GenericDataList[i].GetProperty(sortBy.Property)
b := in.GenericDataList[j].GetProperty(sortBy.Property)
func (self DataSelector) Less(i, j int) bool {
for _, sortBy := range self.DataSelectQuery.SortQuery.SortByList {
a := self.GenericDataList[i].GetProperty(sortBy.Property)
b := self.GenericDataList[j].GetProperty(sortBy.Property)
// ignore sort completely if property name not found
if a == nil || b == nil {
break
@ -98,19 +97,19 @@ func (in DataSelector) Less(i, j int) bool {
return false
}
// Sort sorts the data inside as instructed by DataSelectQuery and returns itin to allow method chaining.
func (in *DataSelector) Sort() *DataSelector {
sort.Sort(*in)
return in
// Sort sorts the data inside as instructed by DataSelectQuery and returns itself to allow method chaining.
func (self *DataSelector) Sort() *DataSelector {
sort.Sort(*self)
return self
}
// Filter the data inside as instructed by DataSelectQuery and returns itin to allow method chaining.
func (in *DataSelector) Filter() *DataSelector {
// Filter the data inside as instructed by DataSelectQuery and returns itself to allow method chaining.
func (self *DataSelector) Filter() *DataSelector {
filteredList := []DataCell{}
for _, c := range in.GenericDataList {
for _, c := range self.GenericDataList {
matches := true
for _, filterBy := range in.DataSelectQuery.FilterQuery.FilterByList {
for _, filterBy := range self.DataSelectQuery.FilterQuery.FilterByList {
v := c.GetProperty(filterBy.Property)
if v == nil || !v.Contains(filterBy.Value) {
matches = false
@ -122,11 +121,11 @@ func (in *DataSelector) Filter() *DataSelector {
}
}
in.GenericDataList = filteredList
return in
self.GenericDataList = filteredList
return self
}
func (in *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
func (self *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
[]metricapi.MetricPromises, error) {
metricPromises := make([]metricapi.MetricPromises, 0)
@ -134,13 +133,13 @@ func (in *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
return metricPromises, nil
}
metricNames := in.DataSelectQuery.MetricQuery.MetricNames
metricNames := self.DataSelectQuery.MetricQuery.MetricNames
if metricNames == nil {
return metricPromises, errors.NewInternal("No metrics specified. Skipping metrics.")
}
selectors := make([]metricapi.ResourceSelector, len(in.GenericDataList))
for i, dataCell := range in.GenericDataList {
selectors := make([]metricapi.ResourceSelector, len(self.GenericDataList))
for i, dataCell := range self.GenericDataList {
// make sure data cells support metrics
metricDataCell, ok := dataCell.(MetricDataCell)
if !ok {
@ -152,24 +151,24 @@ func (in *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
}
for _, metricName := range metricNames {
promises := metricClient.DownloadMetric(selectors, metricName, in.CachedResources)
promises := metricClient.DownloadMetric(selectors, metricName, self.CachedResources)
metricPromises = append(metricPromises, promises)
}
return metricPromises, nil
}
// GetMetrics downloads metrics for data cells currently present in in.GenericDataList as instructed
// by MetricQuery and inserts resulting MetricPromises to in.MetricsPromises.
func (in *DataSelector) GetMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := in.getMetrics(metricClient)
// GetMetrics downloads metrics for data cells currently present in self.GenericDataList as instructed
// by MetricQuery and inserts resulting MetricPromises to self.MetricsPromises.
func (self *DataSelector) GetMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := self.getMetrics(metricClient)
if err != nil {
klog.ErrorS(err, "error during getting metrics")
return in
return self
}
if len(metricPromisesList) == 0 {
return in
return self
}
metricPromises := make(metricapi.MetricPromises, 0)
@ -177,30 +176,30 @@ func (in *DataSelector) GetMetrics(metricClient metricapi.MetricClient) *DataSel
metricPromises = append(metricPromises, promises...)
}
in.MetricsPromises = metricPromises
return in
self.MetricsPromises = metricPromises
return self
}
// GetCumulativeMetrics downloads and aggregates metrics for data cells currently present in in.GenericDataList as instructed
// by MetricQuery and inserts resulting MetricPromises to in.CumulativeMetricsPromises.
func (in *DataSelector) GetCumulativeMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := in.getMetrics(metricClient)
// GetCumulativeMetrics downloads and aggregates metrics for data cells currently present in self.GenericDataList as instructed
// by MetricQuery and inserts resulting MetricPromises to self.CumulativeMetricsPromises.
func (self *DataSelector) GetCumulativeMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := self.getMetrics(metricClient)
if err != nil {
klog.ErrorS(err, "error during getting metrics")
return in
return self
}
if len(metricPromisesList) == 0 {
return in
return self
}
metricNames := in.DataSelectQuery.MetricQuery.MetricNames
metricNames := self.DataSelectQuery.MetricQuery.MetricNames
if metricNames == nil {
klog.V(args.LogLevelVerbose).Info("Metrics names not provided. Skipping.")
return in
klog.V(1).Info("Metrics names not provided. Skipping.")
return self
}
aggregations := in.DataSelectQuery.MetricQuery.Aggregations
aggregations := self.DataSelectQuery.MetricQuery.Aggregations
if aggregations == nil {
aggregations = metricapi.OnlyDefaultAggregation
}
@ -211,28 +210,28 @@ func (in *DataSelector) GetCumulativeMetrics(metricClient metricapi.MetricClient
metricPromises = append(metricPromises, promises...)
}
in.CumulativeMetricsPromises = metricPromises
return in
self.CumulativeMetricsPromises = metricPromises
return self
}
// Paginates the data inside as instructed by DataSelectQuery and returns itin to allow method chaining.
func (in *DataSelector) Paginate() *DataSelector {
pQuery := in.DataSelectQuery.PaginationQuery
dataList := in.GenericDataList
// Paginates the data inside as instructed by DataSelectQuery and returns itself to allow method chaining.
func (self *DataSelector) Paginate() *DataSelector {
pQuery := self.DataSelectQuery.PaginationQuery
dataList := self.GenericDataList
startIndex, endIndex := pQuery.GetPaginationSettings(len(dataList))
// Return all items if provided settings do not meet requirements
if !pQuery.IsValidPagination() {
return in
return self
}
// Return no items if requested page does not exist
if !pQuery.IsPageAvailable(len(in.GenericDataList), startIndex) {
in.GenericDataList = []DataCell{}
return in
if !pQuery.IsPageAvailable(len(self.GenericDataList), startIndex) {
self.GenericDataList = []DataCell{}
return self
}
in.GenericDataList = dataList[startIndex:endIndex]
return in
self.GenericDataList = dataList[startIndex:endIndex]
return self
}
// GenericDataSelect takes a list of GenericDataCells and DataSelectQuery and returns selected data as instructed by dsQuery.

View File

@ -36,12 +36,12 @@ type TestDataCell struct {
Id int
}
func (in TestDataCell) GetProperty(name PropertyName) ComparableValue {
func (self TestDataCell) GetProperty(name PropertyName) ComparableValue {
switch name {
case NameProperty:
return StdComparableString(in.Name)
return StdComparableString(self.Name)
case CreationTimestampProperty:
return StdComparableInt(in.Id)
return StdComparableInt(self.Id)
default:
return nil
}

View File

@ -119,12 +119,11 @@ func NewSortQuery(sortByListRaw []string) *SortQuery {
// parse order option
var ascending bool
orderOption := sortByListRaw[i]
switch orderOption {
case "a":
if orderOption == "a" {
ascending = true
case "d":
} else if orderOption == "d" {
ascending = false
default:
} else {
// Invalid order option. Only ascending (a), descending (d) options are supported
return NoSort
}

View File

@ -23,60 +23,60 @@ import (
// These types specify how given value should be compared
// They all implement ComparableValueInterface
// You can convert basic types to these types to support auto sorting etc.
// If you can't find your type compare here you will have to implement it yourin :)
// If you can't find your type compare here you will have to implement it yourself :)
type StdComparableInt int
func (in StdComparableInt) Compare(otherV ComparableValue) int {
func (self StdComparableInt) Compare(otherV ComparableValue) int {
other := otherV.(StdComparableInt)
return intsCompare(int(in), int(other))
return intsCompare(int(self), int(other))
}
func (in StdComparableInt) Contains(otherV ComparableValue) bool {
return in.Compare(otherV) == 0
func (self StdComparableInt) Contains(otherV ComparableValue) bool {
return self.Compare(otherV) == 0
}
type StdComparableString string
func (in StdComparableString) Compare(otherV ComparableValue) int {
func (self StdComparableString) Compare(otherV ComparableValue) int {
other := otherV.(StdComparableString)
return strings.Compare(string(in), string(other))
return strings.Compare(string(self), string(other))
}
func (in StdComparableString) Contains(otherV ComparableValue) bool {
func (self StdComparableString) Contains(otherV ComparableValue) bool {
other := otherV.(StdComparableString)
return strings.Contains(string(in), string(other))
return strings.Contains(string(self), string(other))
}
// StdComparableRFC3339Timestamp takes RFC3339 Timestamp strings and compares them as TIMES. In case of time parsing error compares values as strings.
type StdComparableRFC3339Timestamp string
func (in StdComparableRFC3339Timestamp) Compare(otherV ComparableValue) int {
func (self StdComparableRFC3339Timestamp) Compare(otherV ComparableValue) int {
other := otherV.(StdComparableRFC3339Timestamp)
// try to compare as timestamp (earlier = smaller)
inTime, err1 := time.Parse(time.RFC3339, string(in))
selfTime, err1 := time.Parse(time.RFC3339, string(self))
otherTime, err2 := time.Parse(time.RFC3339, string(other))
if err1 != nil || err2 != nil {
// in case of timestamp parsing failure just compare as strings
return strings.Compare(string(in), string(other))
return strings.Compare(string(self), string(other))
}
return ints64Compare(inTime.Unix(), otherTime.Unix())
return ints64Compare(selfTime.Unix(), otherTime.Unix())
}
func (in StdComparableRFC3339Timestamp) Contains(otherV ComparableValue) bool {
return in.Compare(otherV) == 0
func (self StdComparableRFC3339Timestamp) Contains(otherV ComparableValue) bool {
return self.Compare(otherV) == 0
}
type StdComparableTime time.Time
func (in StdComparableTime) Compare(otherV ComparableValue) int {
func (self StdComparableTime) Compare(otherV ComparableValue) int {
other := otherV.(StdComparableTime)
return ints64Compare(time.Time(in).Unix(), time.Time(other).Unix())
return ints64Compare(time.Time(self).Unix(), time.Time(other).Unix())
}
func (in StdComparableTime) Contains(otherV ComparableValue) bool {
return in.Compare(otherV) == 0
func (self StdComparableTime) Contains(otherV ComparableValue) bool {
return self.Compare(otherV) == 0
}
// Int comparison functions. Similar to strings.Compare.

View File

@ -30,27 +30,27 @@ import (
type DeploymentCell apps.Deployment
// GetProperty is used to get property of the deployment
func (in DeploymentCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self DeploymentCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}
func (in DeploymentCell) GetResourceSelector() *metricapi.ResourceSelector {
func (self DeploymentCell) GetResourceSelector() *metricapi.ResourceSelector {
return &metricapi.ResourceSelector{
Namespace: in.Namespace,
Namespace: self.ObjectMeta.Namespace,
ResourceType: types.ResourceKindDeployment,
ResourceName: in.Name,
Selector: in.Spec.Selector.MatchLabels,
UID: in.UID,
ResourceName: self.ObjectMeta.Name,
Selector: self.Spec.Selector.MatchLabels,
UID: self.UID,
}
}

View File

@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"io"
"log"
"strings"
apps "k8s.io/api/apps/v1"
@ -34,9 +35,7 @@ import (
"k8s.io/client-go/dynamic"
client "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/args"
derrors "k8s.io/dashboard/errors"
)
@ -162,7 +161,7 @@ type Protocols struct {
// client. App deployment consists of a deployment and an optional service. Both of them
// share common labels.
func DeployApp(spec *AppDeploymentSpec, client client.Interface) error {
klog.V(args.LogLevelVerbose).Infof("Deploying %s application into %s namespace", spec.Name, spec.Namespace)
log.Printf("Deploying %s application into %s namespace", spec.Name, spec.Namespace)
annotations := map[string]string{}
if spec.Description != nil {
@ -305,7 +304,7 @@ func getLabelsMap(labels []Label) map[string]string {
// DeployAppFromFile deploys an app based on the given yaml or json file.
func DeployAppFromFile(cfg *rest.Config, spec *AppDeploymentFromFileSpec) (bool, error) {
reader := strings.NewReader(spec.Content)
klog.V(args.LogLevelVerbose).Infof("Namespace for deploy from file: %s\n", spec.Namespace)
log.Printf("Namespace for deploy from file: %s\n", spec.Namespace)
d := yaml.NewYAMLOrJSONDecoder(reader, 4096)
for {
data := &unstructured.Unstructured{}

View File

@ -16,13 +16,12 @@ package deployment
import (
"context"
"log"
apps "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/errors"
)
@ -83,7 +82,7 @@ type DeploymentDetail struct {
// GetDeploymentDetail returns model object of deployment and error, if any.
func GetDeploymentDetail(client client.Interface, namespace string, deploymentName string) (*DeploymentDetail, error) {
klog.V(4).Infof("Getting details of %s deployment in %s namespace", deploymentName, namespace)
log.Printf("Getting details of %s deployment in %s namespace", deploymentName, namespace)
deployment, err := client.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metaV1.GetOptions{})
if err != nil {

View File

@ -15,10 +15,11 @@
package deployment
import (
"log"
apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/common"
@ -63,7 +64,7 @@ type Deployment struct {
// GetDeploymentList returns a list of all Deployments in the cluster.
func GetDeploymentList(client client.Interface, nsQuery *common.NamespaceQuery, dsQuery *dataselect.DataSelectQuery,
metricClient metricapi.MetricClient) (*DeploymentList, error) {
klog.V(4).Infof("Getting list of all deployments in the cluster")
log.Print("Getting list of all deployments in the cluster")
channels := &common.ResourceChannels{
DeploymentList: common.GetDeploymentListChannel(client, nsQuery, 1),

View File

@ -47,7 +47,7 @@ func RollbackDeployment(client client.Interface, rolloutSpec *RolloutSpec, names
}
currRevision := deployment.Annotations[RevisionAnnotationKey]
if currRevision == FirstRevision {
return nil, errors.New("no revision for rolling back")
return nil, errors.New("No revision for rolling back ")
}
matchRS, err := GetReplicaSetFromDeployment(client, namespace, name)
if err != nil {
@ -66,7 +66,7 @@ func RollbackDeployment(client client.Interface, rolloutSpec *RolloutSpec, names
}, nil
}
}
return nil, errors.New("there is no ReplicaSet that has the requested revision for the Deployment")
return nil, errors.New("There is no ReplicaSet that has the requested revision for the Deployment.")
}
// PauseDeployment is used to pause a deployment
@ -83,7 +83,7 @@ func PauseDeployment(client client.Interface, namespace, name string) (*v1.Deplo
}
return deployment, nil
}
return nil, errors.New("the Deployment is already paused")
return nil, errors.New("The Deployment is already paused.")
}
// ResumeDeployment is used to resume a deployment
@ -100,7 +100,7 @@ func ResumeDeployment(client client.Interface, namespace, name string) (*v1.Depl
}
return deployment, nil
}
return nil, errors.New("the deployment is already resumed")
return nil, errors.New("The deployment is already resumed.")
}
// RestartDeployment restarts a deployment in the manner of `kubectl rollout restart`.
@ -110,10 +110,10 @@ func RestartDeployment(client client.Interface, namespace, name string) (*Rollou
return nil, err
}
if deployment.Spec.Template.Annotations == nil {
deployment.Spec.Template.Annotations = map[string]string{}
if deployment.Spec.Template.ObjectMeta.Annotations == nil {
deployment.Spec.Template.ObjectMeta.Annotations = map[string]string{}
}
deployment.Spec.Template.Annotations[RestartedAtAnnotationKey] = time.Now().Format(time.RFC3339)
deployment.Spec.Template.ObjectMeta.Annotations[RestartedAtAnnotationKey] = time.Now().Format(time.RFC3339)
res, err := client.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, metaV1.UpdateOptions{})
if err != nil {
return nil, err

View File

@ -15,14 +15,14 @@
package endpoint
import (
"log"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/args"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/types"
)
@ -57,7 +57,7 @@ func GetServiceEndpoints(client k8sClient.Interface, namespace, name string) (*E
}
endpointList = toEndpointList(serviceEndpoints)
klog.V(args.LogLevelVerbose).Infof("Found %d endpoints related to %s service in %s namespace", len(endpointList.Endpoints), name, namespace)
log.Printf("Found %d endpoints related to %s service in %s namespace", len(endpointList.Endpoints), name, namespace)
return endpointList, nil
}

View File

@ -244,20 +244,20 @@ func CreateEventList(events []v1.Event, dsQuery *dataselect.DataSelectQuery) com
type EventCell v1.Event
func (in EventCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self EventCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.FirstSeenProperty:
return dataselect.StdComparableTime(in.FirstTimestamp.Time)
return dataselect.StdComparableTime(self.FirstTimestamp.Time)
case dataselect.LastSeenProperty:
return dataselect.StdComparableTime(in.LastTimestamp.Time)
return dataselect.StdComparableTime(self.LastTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
case dataselect.ReasonProperty:
return dataselect.StdComparableString(in.Reason)
return dataselect.StdComparableString(self.Reason)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -15,9 +15,9 @@
package event
import (
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"log"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
"k8s.io/dashboard/errors"
@ -25,7 +25,7 @@ import (
func GetEventList(client k8sClient.Interface, nsQuery *common.NamespaceQuery,
dsQuery *dataselect.DataSelectQuery) (*common.EventList, error) {
klog.V(4).Infof("Getting list of events in namespace: %s", nsQuery.ToRequestParam())
log.Printf("Getting list of events in namespace: %s", nsQuery.ToRequestParam())
channels := &common.ResourceChannels{
EventList: common.GetEventListChannel(client, nsQuery, 2),

View File

@ -29,14 +29,14 @@ type ScaleTargetRef struct {
type HorizontalPodAutoscalerCell autoscaling.HorizontalPodAutoscaler
func (in HorizontalPodAutoscalerCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self HorizontalPodAutoscalerCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -16,11 +16,11 @@ package horizontalpodautoscaler
import (
"context"
"log"
autoscaling "k8s.io/api/autoscaling/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)
// HorizontalPodAutoscalerDetail provides the presentation layer view of Kubernetes Horizontal Pod Autoscaler resource.
@ -35,7 +35,7 @@ type HorizontalPodAutoscalerDetail struct {
// GetHorizontalPodAutoscalerDetail returns detailed information about a horizontal pod autoscaler
func GetHorizontalPodAutoscalerDetail(client client.Interface, namespace string, name string) (*HorizontalPodAutoscalerDetail, error) {
klog.V(4).Infof("Getting details of %s horizontal pod autoscaler", name)
log.Printf("Getting details of %s horizontal pod autoscaler", name)
rawHorizontalPodAutoscaler, err := client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(context.TODO(), name, v1.GetOptions{})
if err != nil {

View File

@ -23,14 +23,14 @@ import (
type IngressCell v1.Ingress
func (in IngressCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self IngressCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -16,11 +16,11 @@ package ingress
import (
"context"
"log"
v1 "k8s.io/api/networking/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)
// IngressDetail API resource provides mechanisms to inject containers with configuration data while keeping
@ -41,7 +41,7 @@ type IngressDetail struct {
// GetIngressDetail returns detailed information about an ingress
func GetIngressDetail(client client.Interface, namespace, name string) (*IngressDetail, error) {
klog.V(4).Infof("Getting details of %s ingress in %s namespace", name, namespace)
log.Printf("Getting details of %s ingress in %s namespace", name, namespace)
rawIngress, err := client.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metaV1.GetOptions{})

View File

@ -37,10 +37,10 @@ func ingressMatchesServiceName(ingress networkingv1.Ingress, serviceName string)
}
for _, rule := range spec.Rules {
if rule.HTTP == nil {
if rule.IngressRuleValue.HTTP == nil {
continue
}
for _, path := range rule.HTTP.Paths {
for _, path := range rule.IngressRuleValue.HTTP.Paths {
if ingressBackendMatchesServiceName(&path.Backend, serviceName) {
return true
}

View File

@ -23,14 +23,14 @@ import (
type IngressClassCell networkingv1.IngressClass
func (in IngressClassCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self IngressClassCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -16,11 +16,11 @@ package ingressclass
import (
"context"
"log"
networkingv1 "k8s.io/api/networking/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)
// IngressClassDetail provides the presentation layer view of Ingress Class resource.
@ -32,7 +32,7 @@ type IngressClassDetail struct {
// GetIngressClass returns Storage Class resource.
func GetIngressClass(client kubernetes.Interface, name string) (*IngressClassDetail, error) {
klog.V(4).Infof("Getting details of %s ingress class", name)
log.Printf("Getting details of %s ingress class", name)
ic, err := client.NetworkingV1().IngressClasses().Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {

View File

@ -15,9 +15,10 @@
package ingressclass
import (
"log"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -44,7 +45,7 @@ type IngressClass struct {
// GetIngressClassList returns a list of all Ingress class objects in the cluster.
func GetIngressClassList(client kubernetes.Interface, dsQuery *dataselect.DataSelectQuery) (
*IngressClassList, error) {
klog.V(4).Infof("Getting list of ingress classes in the cluster")
log.Print("Getting list of ingress classes in the cluster")
channels := &common.ResourceChannels{
IngressClassList: common.GetIngressClassListChannel(client, 1),

View File

@ -28,26 +28,26 @@ import (
type JobCell batch.Job
func (in JobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self JobCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil
}
}
func (in JobCell) GetResourceSelector() *metricapi.ResourceSelector {
func (self JobCell) GetResourceSelector() *metricapi.ResourceSelector {
return &metricapi.ResourceSelector{
Namespace: in.Namespace,
Namespace: self.ObjectMeta.Namespace,
ResourceType: types.ResourceKindJob,
ResourceName: in.Name,
UID: in.UID,
ResourceName: self.ObjectMeta.Name,
UID: self.UID,
}
}

View File

@ -15,10 +15,11 @@
package job
import (
"log"
batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
client "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/common"
@ -88,7 +89,7 @@ type Job struct {
// GetJobList returns a list of all Jobs in the cluster.
func GetJobList(client client.Interface, nsQuery *common.NamespaceQuery,
dsQuery *dataselect.DataSelectQuery, metricClient metricapi.MetricClient) (*JobList, error) {
klog.V(4).Infof("Getting list of all jobs in the cluster")
log.Print("Getting list of all jobs in the cluster")
channels := &common.ResourceChannels{
JobList: common.GetJobListChannel(client, nsQuery, 1),

View File

@ -16,6 +16,7 @@ package job
import (
"context"
"log"
batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
@ -23,8 +24,6 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -36,7 +35,7 @@ import (
// GetJobPods return list of pods targeting job.
func GetJobPods(client k8sClient.Interface, metricClient metricapi.MetricClient,
dsQuery *dataselect.DataSelectQuery, namespace string, jobName string) (*pod.PodList, error) {
klog.V(4).Infof("Getting replication controller %s pods in namespace %s", jobName, namespace)
log.Printf("Getting replication controller %s pods in namespace %s", jobName, namespace)
pods, err := getRawJobPods(client, jobName, namespace)
if err != nil {

View File

@ -151,23 +151,23 @@ type LogTimestamp string
// SelectLogs returns selected part of LogLines as required by logSelector, moreover it returns IDs of first and last
// of returned lines and the information of the resulting logView.
func (in LogLines) SelectLogs(logSelection *Selection) (LogLines, LogTimestamp, LogTimestamp, Selection, bool) {
func (self LogLines) SelectLogs(logSelection *Selection) (LogLines, LogTimestamp, LogTimestamp, Selection, bool) {
requestedNumItems := logSelection.OffsetTo - logSelection.OffsetFrom
referenceLineIndex := in.getLineIndex(&logSelection.ReferencePoint)
if referenceLineIndex == LineIndexNotFound || requestedNumItems <= 0 || len(in) == 0 {
referenceLineIndex := self.getLineIndex(&logSelection.ReferencePoint)
if referenceLineIndex == LineIndexNotFound || requestedNumItems <= 0 || len(self) == 0 {
// Requested reference line could not be found, probably it's already gone or requested no logs. Return no logs.
return LogLines{}, "", "", Selection{}, false
}
fromIndex := referenceLineIndex + logSelection.OffsetFrom
toIndex := referenceLineIndex + logSelection.OffsetTo
lastPage := false
if requestedNumItems > len(in) {
if requestedNumItems > len(self) {
fromIndex = 0
toIndex = len(in)
toIndex = len(self)
lastPage = true
} else if toIndex > len(in) {
fromIndex -= toIndex - len(in)
toIndex = len(in)
} else if toIndex > len(self) {
fromIndex -= toIndex - len(self)
toIndex = len(self)
lastPage = logSelection.LogFilePosition == Beginning
} else if fromIndex < 0 {
toIndex += -fromIndex
@ -177,32 +177,32 @@ func (in LogLines) SelectLogs(logSelection *Selection) (LogLines, LogTimestamp,
// set the middle of log array as a reference point, this part of array should not be affected by log deletion/addition.
newSelection := Selection{
ReferencePoint: *in.createLogLineId(len(in) / 2),
OffsetFrom: fromIndex - len(in)/2,
OffsetTo: toIndex - len(in)/2,
ReferencePoint: *self.createLogLineId(len(self) / 2),
OffsetFrom: fromIndex - len(self)/2,
OffsetTo: toIndex - len(self)/2,
LogFilePosition: logSelection.LogFilePosition,
}
return in[fromIndex:toIndex], in[fromIndex].Timestamp, in[toIndex-1].Timestamp, newSelection, lastPage
return self[fromIndex:toIndex], self[fromIndex].Timestamp, self[toIndex-1].Timestamp, newSelection, lastPage
}
// getLineIndex returns the index of the line (referenced from beginning of log array) with provided logLineId.
func (in LogLines) getLineIndex(logLineId *LogLineId) int {
if logLineId == nil || logLineId.LogTimestamp == NewestTimestamp || len(in) == 0 || logLineId.LogTimestamp == "" {
func (self LogLines) getLineIndex(logLineId *LogLineId) int {
if logLineId == nil || logLineId.LogTimestamp == NewestTimestamp || len(self) == 0 || logLineId.LogTimestamp == "" {
// if no line id provided return index of last item.
return len(in) - 1
return len(self) - 1
} else if logLineId.LogTimestamp == OldestTimestamp {
return 0
}
logTimestamp := logLineId.LogTimestamp
matchingStartedAt := 0
matchingStartedAt = sort.Search(len(in), func(i int) bool {
return in[i].Timestamp >= logTimestamp
matchingStartedAt = sort.Search(len(self), func(i int) bool {
return self[i].Timestamp >= logTimestamp
})
linesMatched := 0
if matchingStartedAt < len(in) && in[matchingStartedAt].Timestamp == logTimestamp { // match found
for (matchingStartedAt+linesMatched) < len(in) && in[matchingStartedAt+linesMatched].Timestamp == logTimestamp {
if matchingStartedAt < len(self) && self[matchingStartedAt].Timestamp == logTimestamp { // match found
for (matchingStartedAt+linesMatched) < len(self) && self[matchingStartedAt+linesMatched].Timestamp == logTimestamp {
linesMatched += 1
}
}
@ -220,22 +220,22 @@ func (in LogLines) getLineIndex(logLineId *LogLineId) int {
}
// createLogLineId returns ID of the line with provided lineIndex.
func (in LogLines) createLogLineId(lineIndex int) *LogLineId {
logTimestamp := in[lineIndex].Timestamp
func (self LogLines) createLogLineId(lineIndex int) *LogLineId {
logTimestamp := self[lineIndex].Timestamp
// determine whether to use negative or positive indexing
// check whether last line has the same index as requested line. If so, we can only use positive referencing
// as more lines may appear at the end.
// negative referencing is preferred as higher indices disappear later.
var step int
if in[len(in)-1].Timestamp == logTimestamp {
if self[len(self)-1].Timestamp == logTimestamp {
// use positive referencing
step = 1
} else {
step = -1
}
offset := step
for ; 0 <= lineIndex-offset && lineIndex-offset < len(in); offset += step {
if in[lineIndex-offset].Timestamp != logTimestamp {
for ; 0 <= lineIndex-offset && lineIndex-offset < len(self); offset += step {
if self[lineIndex-offset].Timestamp != logTimestamp {
break
}
}

View File

@ -16,13 +16,11 @@ package namespace
import (
"context"
"log"
api "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/args"
"k8s.io/dashboard/api/pkg/resource/dataselect"
)
@ -34,7 +32,7 @@ type NamespaceSpec struct {
// CreateNamespace creates namespace based on given specification.
func CreateNamespace(spec *NamespaceSpec, client kubernetes.Interface) error {
klog.V(args.LogLevelVerbose).Infof("Creating namespace %s", spec.Name)
log.Printf("Creating namespace %s", spec.Name)
namespace := &api.Namespace{
ObjectMeta: metaV1.ObjectMeta{
@ -50,14 +48,14 @@ func CreateNamespace(spec *NamespaceSpec, client kubernetes.Interface) error {
type NamespaceCell api.Namespace
func (in NamespaceCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self NamespaceCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// if name is not supported then just return a constant dummy value, sort will have no effect.
return nil

View File

@ -16,11 +16,11 @@ package namespace
import (
"context"
"log"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sClient "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/limitrange"
rq "k8s.io/dashboard/api/pkg/resource/resourcequota"
@ -47,7 +47,7 @@ type NamespaceDetail struct {
// GetNamespaceDetail gets namespace details.
func GetNamespaceDetail(client k8sClient.Interface, name string) (*NamespaceDetail, error) {
klog.V(4).Infof("Getting details of %s namespace\n", name)
log.Printf("Getting details of %s namespace\n", name)
namespace, err := client.CoreV1().Namespaces().Get(context.TODO(), name, metaV1.GetOptions{})
if err != nil {

View File

@ -16,10 +16,10 @@ package namespace
import (
"context"
"log"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/dashboard/api/pkg/resource/common"
"k8s.io/dashboard/api/pkg/resource/dataselect"
@ -64,7 +64,7 @@ func GetNamespaceListFromChannels(channels *common.ResourceChannels, dsQuery *da
// GetNamespaceList returns a list of all namespaces in the cluster.
func GetNamespaceList(client kubernetes.Interface, dsQuery *dataselect.DataSelectQuery) (*NamespaceList, error) {
klog.V(4).Info("Getting list of namespaces")
log.Println("Getting list of namespaces")
namespaces, err := client.CoreV1().Namespaces().List(context.TODO(), helpers.ListEverything)
nonCriticalErrors, criticalError := errors.ExtractErrors(err)

View File

@ -16,20 +16,19 @@ package networkpolicy
import (
v1 "k8s.io/api/networking/v1"
"k8s.io/dashboard/api/pkg/resource/dataselect"
)
type ServiceAccountCell v1.NetworkPolicy
func (in ServiceAccountCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
func (self ServiceAccountCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
return dataselect.StdComparableString(in.Name)
return dataselect.StdComparableString(self.ObjectMeta.Name)
case dataselect.CreationTimestampProperty:
return dataselect.StdComparableTime(in.CreationTimestamp.Time)
return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time)
case dataselect.NamespaceProperty:
return dataselect.StdComparableString(in.Namespace)
return dataselect.StdComparableString(self.ObjectMeta.Namespace)
default:
// If name is not supported then just return a constant dummy value, sort will have no effect.
return nil

Some files were not shown because too many files have changed in this diff Show More