mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-29 22:17:16 +00:00
Compare commits
39
Commits
10.1.4
...
js/gateway
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be3401cc9d | ||
|
|
4fb77d3b8c | ||
|
|
8f61e0c68a | ||
|
|
c2ccdb4a5d | ||
|
|
b860eb798b | ||
|
|
cdbb35f449 | ||
|
|
c0e23a233b | ||
|
|
3bfea9bf10 | ||
|
|
236ca14fca | ||
|
|
9fe4a72c77 | ||
|
|
dafa0b74fc | ||
|
|
f0a370c757 | ||
|
|
cb741e3eb1 | ||
|
|
b65142e8be | ||
|
|
31bc0fdb84 | ||
|
|
292a1c257c | ||
|
|
06ef2d3f70 | ||
|
|
4bdf5315b6 | ||
|
|
278cb9008c | ||
|
|
d6858e0fe4 | ||
|
|
78fe4cb11c | ||
|
|
387e787a40 | ||
|
|
1fdfec73a1 | ||
|
|
aee298e2e0 | ||
|
|
0444cf92bc | ||
|
|
50e4c63079 | ||
|
|
7d053a6cbd | ||
|
|
d50336387b | ||
|
|
1554c90ece | ||
|
|
9dfb4262e6 | ||
|
|
27878a4670 | ||
|
|
a46eeb28b6 | ||
|
|
fd862afadc | ||
|
|
ee9f568ebd | ||
|
|
47c7deddfd | ||
|
|
2007a2c317 | ||
|
|
a5af7bb34e | ||
|
|
893e5de589 | ||
|
|
f6eed6589d |
+141
-142
@@ -1,183 +1,182 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
rok8s: fairwinds/rok8s-scripts@14
|
||||
rok8s: fairwinds/rok8s-scripts@16.0.0
|
||||
oss-docs: fairwinds/oss-docs@0
|
||||
|
||||
executors:
|
||||
vm:
|
||||
machine:
|
||||
enabled: true
|
||||
|
||||
commands:
|
||||
install_goreleaser_dependencies:
|
||||
description: Installs dependencies for CI scripts
|
||||
steps:
|
||||
- run: apk update
|
||||
# gettext provides envsubst
|
||||
- run: apk add gettext
|
||||
# Register other docker platforms, to build arm64.
|
||||
# This shouldn't be needed, why TBD.
|
||||
- run: docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
|
||||
references:
|
||||
set_environment_variables: &set_environment_variables
|
||||
run:
|
||||
name: Set Environment Variables
|
||||
command: |
|
||||
echo 'export CI_SHA1=$CIRCLE_SHA1' >> ${BASH_ENV}
|
||||
echo 'export CI_BRANCH=$CIRCLE_BRANCH' >> ${BASH_ENV}
|
||||
echo 'export CI_BUILD_NUM=$CIRCLE_BUILD_NUM' >> ${BASH_ENV}
|
||||
echo 'export CI_TAG=$CIRCLE_TAG' >> ${BASH_ENV}
|
||||
echo 'export PUSH_ALL_VERSION_TAGS=true' >> ${BASH_ENV}
|
||||
echo 'export GOPROXY=https://proxy.golang.org' >> ${BASH_ENV}
|
||||
echo 'export GO111MODULE=on' >> ${BASH_ENV}
|
||||
echo 'export GOFLAGS=-mod=mod' >> ${BASH_ENV}
|
||||
echo 'export GORELEASER_CURRENT_TAG="${CIRCLE_TAG}"' >> $BASH_ENV
|
||||
|
||||
install_k8s: &install_k8s
|
||||
run:
|
||||
name: Install K8s
|
||||
command: |
|
||||
sudo apt-get update
|
||||
echo "Installing git and jq"
|
||||
sudo apt-get install -yqq jq git
|
||||
|
||||
echo "Installing KIND"
|
||||
curl -sLO https://github.com/kubernetes-sigs/kind/releases/download/v0.19.0/kind-linux-amd64
|
||||
chmod 0755 kind-linux-amd64
|
||||
sudo mv kind-linux-amd64 /usr/local/bin/kind
|
||||
kind version
|
||||
|
||||
echo "Installing Kubectl"
|
||||
curl -sLO https://storage.googleapis.com/kubernetes-release/release/v1.25.10/bin/linux/amd64/kubectl
|
||||
chmod 0755 kubectl
|
||||
sudo mv kubectl /usr/local/bin/
|
||||
kubectl version --client
|
||||
|
||||
|
||||
echo "Creating Kubernetes Cluster with Kind"
|
||||
kind create cluster --wait=90s --image kindest/node:v1.25.9@sha256:c08d6c52820aa42e533b70bce0c2901183326d86dcdcbedecc9343681db45161
|
||||
docker ps -a
|
||||
|
||||
kubectl version
|
||||
|
||||
echo "Installing Helm"
|
||||
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
|
||||
chmod 700 get_helm.sh
|
||||
./get_helm.sh
|
||||
|
||||
echo "Installing cert-manager"
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo update
|
||||
echo "Helm install"
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.12.1 --set "installCRDs=true" --wait --create-namespace
|
||||
echo "Install cert-manager successful"
|
||||
|
||||
test_binary_dashboard: &test_binary_dashboard
|
||||
run:
|
||||
name: Test Dashboard
|
||||
command: |
|
||||
go run main.go dashboard --port 3000 --audit-path ./pkg/config/examples &
|
||||
sleep 30
|
||||
curl -f http://localhost:3000 > /dev/null
|
||||
curl -f http://localhost:3000/health > /dev/null
|
||||
curl -f http://localhost:3000/favicon.ico > /dev/null
|
||||
curl -f http://localhost:3000/static/css/main.css > /dev/null
|
||||
curl -f http://localhost:3000/results.json > /dev/null
|
||||
curl -f http://localhost:3000/details/security > /dev/null
|
||||
test_k8s: &test_k8s
|
||||
run:
|
||||
name: Test Kubernetes Deployments
|
||||
command: |
|
||||
if [[ -z $CIRCLE_PR_NUMBER ]]; then
|
||||
./test/webhook_test.sh
|
||||
./test/kube_dashboard_test.sh
|
||||
else
|
||||
echo "Skipping Kubernetes tests for forked PR"
|
||||
fi
|
||||
|
||||
install_vault_alpine: &install_vault_alpine
|
||||
install_vault_machine: &install_vault_machine
|
||||
run:
|
||||
name: install hashicorp vault
|
||||
command: |
|
||||
apk --update add curl yq
|
||||
sudo apt-get update -y && sudo apt-get install -y curl unzip
|
||||
cd /tmp
|
||||
curl -LO https://releases.hashicorp.com/vault/1.13.2/vault_1.13.2_linux_amd64.zip
|
||||
sha256sum vault_1.13.2_linux_amd64.zip | grep f7930279de8381de7c532164b4a4408895d9606c0d24e2e9d2f9acb5dfe99b3c
|
||||
unzip vault_1.13.2_linux_amd64.zip
|
||||
mv vault /usr/bin/vault
|
||||
jobs:
|
||||
test_k8s:
|
||||
working_directory: ~/polaris
|
||||
resource_class: medium
|
||||
executor: vm
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- *install_k8s
|
||||
- *test_k8s
|
||||
curl -LO https://releases.hashicorp.com/vault/1.21.4/vault_1.21.4_linux_amd64.zip
|
||||
echo '889b681990fe221b884b7932fa9c9dd0ee9811b9349554f1aa287ab63c9f3dae vault_1.21.4_linux_amd64.zip' | sha256sum -c
|
||||
unzip -o vault_1.21.4_linux_amd64.zip
|
||||
sudo mv vault /usr/bin/vault
|
||||
setup_qemu_binfmt: &setup_qemu_binfmt
|
||||
run:
|
||||
name: Setup QEMU for multi-arch Docker builds
|
||||
command: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y qemu-user-static binfmt-support
|
||||
docker buildx create --use || true
|
||||
docker buildx inspect --bootstrap
|
||||
e2e_configuration: &e2e_configuration
|
||||
executor: golang-exec
|
||||
pre_script: e2e/pre.sh
|
||||
script: e2e/test.sh
|
||||
command_runner_image: quay.io/reactiveops/ci-images:v14.1-bullseye
|
||||
enable_docker_layer_caching: true
|
||||
store-test-results: /tmp/test-results
|
||||
attach-workspace: true
|
||||
requires:
|
||||
- test
|
||||
- snapshot
|
||||
filters:
|
||||
branches:
|
||||
only: /.*/
|
||||
tags:
|
||||
ignore: /.*/
|
||||
|
||||
executors:
|
||||
golang-exec:
|
||||
docker:
|
||||
- image: cimg/go:1.26.7
|
||||
|
||||
jobs:
|
||||
test:
|
||||
docker:
|
||||
- image: cimg/go:1.25.5
|
||||
- image: cimg/go:1.26.7
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- run: go vet ./...
|
||||
- run: go test ./... -coverprofile=coverage.txt -covermode=count
|
||||
- *test_binary_dashboard
|
||||
|
||||
build_and_push:
|
||||
working_directory: /go/src/github.com/fairwindsops/polaris/
|
||||
- run:
|
||||
name: Go Mod Download
|
||||
command: go mod download && go mod verify
|
||||
- run:
|
||||
name: golangci-lint
|
||||
command: |
|
||||
curl -fsSL -o golangci-lint.tar.gz https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz
|
||||
echo '8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553 golangci-lint.tar.gz' | sha256sum -c
|
||||
tar -xzf golangci-lint.tar.gz
|
||||
mv golangci-lint-2.12.2-linux-amd64/golangci-lint "$(go env GOPATH)/bin/golangci-lint"
|
||||
golangci-lint run --timeout 5m
|
||||
- run:
|
||||
name: test
|
||||
command: |
|
||||
go test -v -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
go vet ./...
|
||||
- run:
|
||||
name: Test Dashboard
|
||||
command: ./test/dashboard_test.sh
|
||||
snapshot:
|
||||
machine:
|
||||
image: ubuntu-2204:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- *setup_qemu_binfmt
|
||||
- run:
|
||||
name: Run GoReleaser snapshot
|
||||
command: |
|
||||
docker run --rm \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v "$(pwd):/workspace" -w /workspace \
|
||||
-e CIRCLE_SHA1 \
|
||||
-e CIRCLE_BRANCH \
|
||||
-e CIRCLE_TAG \
|
||||
goreleaser/goreleaser:v2.17.1 release --snapshot --skip=sign
|
||||
- run:
|
||||
name: Save snapshot amd64 image for e2e
|
||||
command: |
|
||||
mkdir -p /tmp/workspace/docker_save
|
||||
docker save us-docker.pkg.dev/fairwinds-ops/oss/polaris:${CIRCLE_SHA1}-amd64 > /tmp/workspace/docker_save/polaris_${CIRCLE_SHA1}-amd64.tar
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace/
|
||||
paths:
|
||||
- docker_save
|
||||
- store_artifacts:
|
||||
path: dist
|
||||
destination: snapshot
|
||||
release:
|
||||
machine:
|
||||
image: ubuntu-2204:current
|
||||
resource_class: large
|
||||
shell: /bin/bash
|
||||
docker:
|
||||
# The goreleaser image tag determins the version of Go.
|
||||
# Manually check goreleaser images for their version of Go.
|
||||
# Ref: https://hub.docker.com/r/goreleaser/goreleaser/tags
|
||||
- image: goreleaser/goreleaser:v2.13.1
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- *install_vault_alpine
|
||||
- *install_vault_machine
|
||||
- rok8s/get_vault_env:
|
||||
vault_path: repo/global/env
|
||||
- rok8s/get_vault_env:
|
||||
vault_path: repo/polaris/env
|
||||
- *set_environment_variables
|
||||
- run: docker login quay.io -u="${FAIRWINDS_QUAY_USER}" -p="${FAIRWINDS_QUAY_TOKEN}"
|
||||
- install_goreleaser_dependencies
|
||||
- run: scripts/goreleaser.sh
|
||||
- run:
|
||||
name: docker login Google Artifact Registry
|
||||
command: |
|
||||
echo "$GCP_ARTIFACTREADWRITE_JSON_KEY" | base64 -d | docker login -u _json_key --password-stdin us-docker.pkg.dev
|
||||
- *setup_qemu_binfmt
|
||||
- run:
|
||||
name: Run GoReleaser release
|
||||
command: |
|
||||
export GORELEASER_CURRENT_TAG="${CIRCLE_TAG}"
|
||||
docker run --rm \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v "$(pwd):/workspace" -w /workspace \
|
||||
-v "${HOME}/.docker:/root/.docker" \
|
||||
-e GORELEASER_CURRENT_TAG \
|
||||
-e CIRCLE_TAG \
|
||||
-e CIRCLE_SHA1 \
|
||||
-e GO111MODULE=on \
|
||||
-e GITHUB_TOKEN \
|
||||
-e VAULT_ADDR \
|
||||
-e VAULT_TOKEN \
|
||||
goreleaser/goreleaser:v2.17.1 release
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
release:
|
||||
test_and_build:
|
||||
jobs:
|
||||
- test:
|
||||
filters:
|
||||
tags:
|
||||
ignore: /.*/
|
||||
- snapshot:
|
||||
requires:
|
||||
- test
|
||||
filters:
|
||||
branches:
|
||||
only: /.*/
|
||||
- build_and_push:
|
||||
tags:
|
||||
ignore: /.*/
|
||||
- rok8s/kubernetes_e2e_tests:
|
||||
name: "kubernetes e2e"
|
||||
kind_node_image: "kindest/node:v1.34.0@sha256:7416a61b42b1662ca6ca89f02028ac133a309a2a30ba309614e8ec94d976dc5a"
|
||||
<<: *e2e_configuration
|
||||
release:
|
||||
jobs:
|
||||
- test:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /v.*/
|
||||
- release:
|
||||
requires:
|
||||
- test
|
||||
context: org-global
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
ignore: /.*/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
- test_k8s:
|
||||
requires:
|
||||
- build_and_push
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
only: /v.*/
|
||||
- oss-docs/publish-docs:
|
||||
requires:
|
||||
- release
|
||||
repository: polaris
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
only: /v.*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# The action uses an own Dockerfile on purpose because the root Dockerfile takes way too long to build for an action
|
||||
|
||||
FROM alpine:3.22
|
||||
FROM alpine:3.24
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/docs"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 0
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -7,7 +7,7 @@ jobs:
|
||||
build-int:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- name: Setup polaris
|
||||
uses: ./.github/actions/setup-polaris
|
||||
with:
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
build-ext:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- name: Setup polaris
|
||||
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
|
||||
with:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# goreleaser is run via a wrapper that creates .goreleaser.yml from .goreleaser.yml.envsubst
|
||||
.goreleaser.yml
|
||||
# dist
|
||||
# # Binaries for programs and plugins
|
||||
.go-version
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
# yaml-language-server: $$schema=https://goreleaser.com/static/schema.json
|
||||
|
||||
version: 2
|
||||
project_name: polaris
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- id: polaris
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}} -X main.Commit={{.Commit}} -s -w
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- "6"
|
||||
- "7"
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
brews:
|
||||
- name: polaris
|
||||
repository:
|
||||
owner: FairwindsOps
|
||||
name: homebrew-tap
|
||||
directory: Formula
|
||||
description: Open Source Best Practices for Kubernetes
|
||||
url_template: "https://github.com/FairwindsOps/polaris/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
test: |
|
||||
system "#{bin}/polaris version"
|
||||
release:
|
||||
disable: '{{ eq (envOrDefault "GORELEASER_SKIP_RELEASE" "false") "true" }}'
|
||||
prerelease: auto
|
||||
github:
|
||||
owner: FairwindsOps
|
||||
name: polaris
|
||||
footer: |
|
||||
You can verify the signatures of both the checksums.txt file and the published docker images using [cosign](https://github.com/sigstore/cosign).
|
||||
|
||||
```bash
|
||||
cosign verify-blob checksums.txt --bundle=checksums.txt.sigstore.json --key https://artifacts.fairwinds.com/cosign-p256.pub
|
||||
```
|
||||
|
||||
```bash
|
||||
cosign verify us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }} --key https://artifacts.fairwinds.com/cosign-p256.pub
|
||||
```
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
signs:
|
||||
- cmd: cosign
|
||||
signature: "${artifact}.sigstore.json"
|
||||
args:
|
||||
- "sign-blob"
|
||||
- "--key=hashivault://cosign-p256"
|
||||
- "--bundle=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes"
|
||||
artifacts: all
|
||||
docker_signs:
|
||||
- artifacts: all
|
||||
args: ["sign", "--key=hashivault://cosign-p256", "us-docker.pkg.dev/fairwinds-ops/oss/polaris@${digest}", "-r", "--yes"]
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-amd64"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-amd64"
|
||||
use: buildx
|
||||
dockerfile: Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-arm64v8"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-arm64v8"
|
||||
use: buildx
|
||||
goarch: arm64
|
||||
goos: linux
|
||||
dockerfile: Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64/v8"
|
||||
- image_templates:
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-armv7"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-armv7"
|
||||
use: buildx
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
goos: linux
|
||||
dockerfile: Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm/v7"
|
||||
docker_manifests:
|
||||
- name_template: us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}
|
||||
image_templates:
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-amd64"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-arm64v8"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .Tag }}-armv7"
|
||||
- name_template: us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}
|
||||
image_templates:
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-amd64"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-arm64v8"
|
||||
- "us-docker.pkg.dev/fairwinds-ops/oss/polaris:{{ .FullCommit }}-armv7"
|
||||
@@ -1,114 +0,0 @@
|
||||
version: 2
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
env:
|
||||
- GOBIN={{ .Env.TMPDIR }}/go-bin
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- id: polaris
|
||||
ldflags:
|
||||
- -X main.Version={{.Version}} -X main.Commit={{.Commit}} -s -w
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 6
|
||||
- 7
|
||||
archives:
|
||||
- id: polaris
|
||||
builds: ["polaris"]
|
||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
|
||||
signs:
|
||||
- cmd: cosign
|
||||
args: ["sign-blob", "--key=hashivault://cosign", "-output-signature=${signature}", "${artifact}"]
|
||||
artifacts: checksum
|
||||
release:
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
disable: ${skip_release}
|
||||
prerelease: auto
|
||||
footer: |
|
||||
You can verify the signature of the checksums.txt file using [cosign](https://github.com/sigstore/cosign).
|
||||
|
||||
```
|
||||
cosign verify-blob checksums.txt --signature=checksums.txt.sig --key https://artifacts.fairwinds.com/cosign.pub
|
||||
```
|
||||
brews:
|
||||
- name: polaris
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_upload: ${skip_release}
|
||||
repository:
|
||||
owner: FairwindsOps
|
||||
name: homebrew-tap
|
||||
directory: Formula
|
||||
description: Open Source Best Practices for Kubernetes
|
||||
url_template: "https://github.com/FairwindsOps/polaris/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
test: |
|
||||
system "#{bin}/polaris version"
|
||||
dockers:
|
||||
# There are multiple images to match the `--platform` docker build flag with
|
||||
# combinations of `GOOS`, `GOARCH`, and `GOARM`
|
||||
- image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
use: buildx
|
||||
goarch: arm64
|
||||
goos: linux
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
docker_manifests:
|
||||
# Create DOcker manifests that make multiple architectures available within a tag,
|
||||
# and provide partial-version tags like 2, and 2.2.
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .FullCommit }}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Env.feature_docker_tag }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_feature_docker_tags}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:latest
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Tag }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Major }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
- name_template: quay.io/fairwinds/polaris:{{ .Major }}.{{ .Minor }}
|
||||
# This is replaced using `envsubst`, depending on the git branch.
|
||||
skip_push: ${skip_release}
|
||||
image_templates:
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
|
||||
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.23
|
||||
FROM alpine:3.24.1
|
||||
|
||||
LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.vendor="FairwindsOps, Inc." \
|
||||
@@ -10,8 +10,8 @@ LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.licenses="Apache License 2.0"
|
||||
|
||||
WORKDIR /usr/local/bin
|
||||
RUN apk update && apk upgrade && \
|
||||
apk --no-cache add ca-certificates
|
||||
# Install ca-certs
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
RUN addgroup -S polaris && adduser -u 1200 -S polaris -G polaris
|
||||
USER 1200
|
||||
|
||||
@@ -29,6 +29,44 @@ Polaris can be run in three different modes:
|
||||
## Documentation
|
||||
Check out the [documentation at docs.fairwinds.com](https://polaris.docs.fairwinds.com)
|
||||
|
||||
## Notice: Registry Migration and Immutable Images (v10.1.8 → v10.2.0)
|
||||
|
||||
Starting with **v10.2.0**:
|
||||
|
||||
- Images moved to `us-docker.pkg.dev/fairwinds-ops/oss/polaris`
|
||||
- `quay.io/fairwinds/polaris` is deprecated
|
||||
|
||||
### Required action
|
||||
|
||||
```diff
|
||||
- quay.io/fairwinds/polaris:<tag>
|
||||
+ us-docker.pkg.dev/fairwinds-ops/oss/polaris:<tag>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Immutable and signed images
|
||||
|
||||
* Images are now **signed**
|
||||
* Tags are **immutable**
|
||||
* No more floating tags:
|
||||
|
||||
* `v10`
|
||||
* `v10.1`
|
||||
* `latest`
|
||||
|
||||
Use full version tags:
|
||||
|
||||
```
|
||||
us-docker.pkg.dev/fairwinds-ops/oss/polaris:v<major>.<minor>.<patch>
|
||||
```
|
||||
|
||||
Or pin by digest:
|
||||
|
||||
```
|
||||
us-docker.pkg.dev/fairwinds-ops/oss/polaris@sha256:<digest>
|
||||
```
|
||||
|
||||
<!-- Begin boilerplate -->
|
||||
## Join the Fairwinds Open Source Community
|
||||
|
||||
|
||||
+19
-10
@@ -189,17 +189,18 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
|
||||
var outputBytes []byte
|
||||
var err error
|
||||
if outputFormat == "score" {
|
||||
outputBytes = []byte(fmt.Sprintf("%d\n", auditData.GetSummary().GetScore()))
|
||||
} else if outputFormat == "yaml" {
|
||||
switch outputFormat {
|
||||
case "score":
|
||||
outputBytes = fmt.Appendf(nil, "%d\n", auditData.GetSummary().GetScore())
|
||||
case "yaml":
|
||||
var jsonBytes []byte
|
||||
jsonBytes, err = json.Marshal(auditData)
|
||||
if err == nil {
|
||||
outputBytes, err = yaml.JSONToYAML(jsonBytes)
|
||||
}
|
||||
} else if outputFormat == "pretty" {
|
||||
case "pretty":
|
||||
outputBytes = []byte(auditData.GetPrettyOutput(useColor))
|
||||
} else {
|
||||
default:
|
||||
outputBytes, err = json.MarshalIndent(auditData, "", " ")
|
||||
}
|
||||
if err != nil {
|
||||
@@ -207,7 +208,10 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
os.Exit(1)
|
||||
}
|
||||
if outputURL == "" && outputFile == "" {
|
||||
os.Stdout.Write(outputBytes)
|
||||
if _, err := os.Stdout.Write(outputBytes); err != nil {
|
||||
logrus.Errorf("Error writing audit to stdout: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
if outputURL != "" {
|
||||
req, err := http.NewRequest("POST", outputURL, bytes.NewBuffer(outputBytes))
|
||||
@@ -217,11 +221,12 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if outputFormat == "json" {
|
||||
switch outputFormat {
|
||||
case "json":
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else if outputFormat == "yaml" {
|
||||
case "yaml":
|
||||
req.Header.Set("Content-Type", "application/x-yaml")
|
||||
} else {
|
||||
default:
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
|
||||
@@ -236,7 +241,11 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
logrus.Errorf("Error closing response body: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
|
||||
|
||||
@@ -61,7 +61,9 @@ var dashboardCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
if _, err := w.Write([]byte("OK")); err != nil {
|
||||
logrus.Errorf("Error writing health response: %v", err)
|
||||
}
|
||||
})
|
||||
http.Handle("/", router)
|
||||
|
||||
|
||||
+3
-1
@@ -48,7 +48,9 @@ var fixCommand = &cobra.Command{
|
||||
if err != nil {
|
||||
if errors.Is(err, fix.ErrFilesPathRequired) {
|
||||
logrus.Error("Please specify a files-path flag")
|
||||
cmd.Help()
|
||||
if helpErr := cmd.Help(); helpErr != nil {
|
||||
logrus.Error(helpErr)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
logrus.Fatal(err)
|
||||
|
||||
+3
-2
@@ -29,7 +29,6 @@ var (
|
||||
disallowExemptions bool
|
||||
disallowConfigExemptions bool
|
||||
disallowAnnotationExemptions bool
|
||||
fixChecks bool
|
||||
logLevel string
|
||||
auditPath string
|
||||
displayName string
|
||||
@@ -88,7 +87,9 @@ var rootCmd = &cobra.Command{
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if !strings.HasPrefix(cmd.Use, "audit") {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
if _, err := os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n"); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -24,6 +24,15 @@ key | default | description
|
||||
`hostNetworkSet` | `warning` | Fails when `hostNetwork` attribute is configured.
|
||||
`hostPortSet` | `warning` | Fails when `hostPort` attribute is configured.
|
||||
`tlsSettingsMissing` | `warning` | Fails when an Ingress lacks TLS settings.
|
||||
`gatewayTLSMissing` | `warning` | Fails when an HTTPS, GRPC, or terminating TLS Gateway listener lacks certificate references.
|
||||
`gatewayAllowedRoutesAll` | `warning` | Fails when a Gateway listener allows Routes from every namespace.
|
||||
`gatewayInsecureFrontendValidation` | `warning` | Fails when Gateway frontend client certificate validation allows insecure fallback.
|
||||
`gatewayCrossNamespaceCertificateRef` | `warning` | Fails when a Gateway references a certificate in another namespace without a matching ReferenceGrant. Cluster audits only.
|
||||
`httpRouteWildcardOrEmptyHost` | `warning` | Fails when an HTTPRoute omits hostnames or uses a wildcard hostname.
|
||||
`httpRouteInsecureListener` | `warning` | Fails when an HTTPRoute serves application traffic over HTTP without a full HTTPS redirect. Cluster audits only.
|
||||
`httpRouteCrossNamespaceBackendRef` | `warning` | Fails when an HTTPRoute references a backend in another namespace without a matching ReferenceGrant. Cluster audits only.
|
||||
`httpRouteBackendTLSMissing` | `warning` | Fails when an HTTPRoute TLS backend lacks a BackendTLSPolicy or kgateway BackendConfigPolicy. Cluster audits only.
|
||||
`kgatewayBackendTLSVerificationDisabled` | `warning` | Fails when a kgateway BackendConfigPolicy disables TLS certificate verification.
|
||||
`sensitiveContainerEnvVar` | `danger` | Fails when the container sets potentially sensitive environment variables.
|
||||
`sensitiveConfigmapContent` | `danger` | Fails when potentially sensitive content is detected in the ConfigMap keys or values.
|
||||
`missingNetworkPolicy` | `warning`
|
||||
@@ -40,6 +49,14 @@ key | default | description
|
||||
|
||||
Securing workloads in Kubernetes is an important part of overall cluster security. The overall goal should be to ensure that containers are running with as minimal privileges as possible. This includes avoiding privilege escalation, not running containers with a root user, not giving excessive access to the host network, and using read only file systems wherever possible.
|
||||
|
||||
### Gateway API
|
||||
|
||||
Gateway API separates listeners, routes, and backend TLS policy across different resources. Polaris checks standard `Gateway` and `HTTPRoute` resources for listener TLS, namespace isolation, host specificity, HTTPS redirects, cross-namespace authorization, and backend TLS. These checks work with conformant implementations such as kgateway.
|
||||
|
||||
`httpRouteBackendTLSMissing` also recognizes kgateway's `Backend` and `BackendConfigPolicy` resources. It identifies TLS backends from ports 443 and 8443, Service port names and `appProtocol`, and kgateway static Backend ports. `kgatewayBackendTLSVerificationDisabled` checks the kgateway-specific `insecureSkipVerify` setting. Authentication, authorization, and rate-limiting requirements are organization-specific and should be implemented as custom checks.
|
||||
|
||||
Checks marked "Cluster audits only" need related resources that are not available when Polaris evaluates a single admission request. They pass without a resource provider rather than rejecting an object without enough context.
|
||||
|
||||
A pod running with the `hostNetwork` attribute enabled will have access to the loopback device, services listening on localhost, and could be used to snoop on network activity of other pods on the same node. There are certain examples where setting `hostNetwork` to true is required, such as deploying a networking plugin like Flannel.
|
||||
|
||||
Setting the `hostPort` attribute on a container will ensure that it is accessible on that specific port on each node it is deployed to. Unfortunately when this is specified, it limits where a pod can actually be scheduled in a cluster.
|
||||
|
||||
@@ -46,11 +46,11 @@ go test ./pkg/... -v -coverprofile cover.out
|
||||
### Webhook tests
|
||||
```bash
|
||||
kind create cluster --wait=90s --image kindest/node:v1.15.11 --name polaris-test
|
||||
docker build -t quay.io/fairwinds/polaris:debug . # or use your own registry
|
||||
docker push quay.io/fairwinds/polaris:debug
|
||||
docker build -t us-docker.pkg.dev/fairwinds-ops/oss/polaris:debug . # or use your own registry
|
||||
docker push us-docker.pkg.dev/fairwinds-ops/oss/polaris:debug
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.12.1 --set "installCRDs=true" --wait
|
||||
POLARIS_IMAGE=quay.io/fairwinds/polaris:debug ./test/webhook_test.sh
|
||||
POLARIS_IMAGE=us-docker.pkg.dev/fairwinds-ops/oss/polaris:debug ./test/webhook_test.sh
|
||||
```
|
||||
to avoid the final cleanup for debugging purposes, you can run
|
||||
```bash
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ polaris dashboard --port 8080 --audit-path=./deploy/
|
||||
### Local Docker container
|
||||
|
||||
```
|
||||
docker run -d -p8080:8080 -v ~/.kube/config:/opt/app/config:ro quay.io/fairwinds/polaris:1.2 polaris dashboard --kubeconfig /opt/app/config
|
||||
docker run -d -p8080:8080 -v ~/.kube/config:/opt/app/config:ro us-docker.pkg.dev/fairwinds-ops/oss/polaris:1.2 polaris dashboard --kubeconfig /opt/app/config
|
||||
```
|
||||
|
||||
## Using the Dashboard
|
||||
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
KIND_VERSION=v0.30.0
|
||||
|
||||
if [ -z "${CI_SHA1:-}" ]; then
|
||||
echo "CI_SHA1 not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "CI_SHA1: ${CI_SHA1}"
|
||||
|
||||
tar="/tmp/workspace/docker_save/polaris_${CI_SHA1}-amd64.tar"
|
||||
if [ ! -f "$tar" ]; then
|
||||
echo "Missing snapshot image at ${tar}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v kind > /dev/null; then
|
||||
echo "Installing kind ${KIND_VERSION}"
|
||||
bindir="$(pwd)/bin-kind"
|
||||
mkdir -p "$bindir"
|
||||
curl -fsSLo "$bindir/kind" \
|
||||
"https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-amd64"
|
||||
chmod +x "$bindir/kind"
|
||||
export PATH="$bindir:$PATH"
|
||||
fi
|
||||
kind version
|
||||
|
||||
docker load --input "$tar"
|
||||
docker tag "us-docker.pkg.dev/fairwinds-ops/oss/polaris:${CI_SHA1}-amd64" \
|
||||
"us-docker.pkg.dev/fairwinds-ops/oss/polaris:${CI_SHA1}"
|
||||
kind load docker-image --name e2e "us-docker.pkg.dev/fairwinds-ops/oss/polaris:${CI_SHA1}"
|
||||
|
||||
docker cp . e2e-command-runner:/polaris
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p /tmp/test-results
|
||||
|
||||
if [[ -n "${CIRCLE_PR_NUMBER:-}" ]]; then
|
||||
echo "Skipping Kubernetes tests for forked PR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd /polaris
|
||||
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo update
|
||||
helm install cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--version v1.12.1 \
|
||||
--set installCRDs=true \
|
||||
--wait \
|
||||
--create-namespace
|
||||
|
||||
./test/webhook_test.sh
|
||||
./test/kube_dashboard_test.sh
|
||||
@@ -4,4 +4,4 @@ options:
|
||||
|
||||
images:
|
||||
docker:
|
||||
- quay.io/fairwinds/polaris:$CI_SHA1
|
||||
- us-docker.pkg.dev/fairwinds-ops/oss/polaris:$CI_SHA1
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
module github.com/fairwindsops/polaris
|
||||
|
||||
go 1.25.5
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/fairwindsops/controller-utils v0.3.4
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fatih/color v1.19.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qri-io/jsonpointer v0.1.1
|
||||
github.com/qri-io/jsonschema v0.2.1
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/sirupsen/logrus v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.35.0
|
||||
k8s.io/apimachinery v0.35.0
|
||||
k8s.io/client-go v0.35.0
|
||||
sigs.k8s.io/controller-runtime v0.23.1
|
||||
k8s.io/api v0.36.4
|
||||
k8s.io/apimachinery v0.36.4
|
||||
k8s.io/client-go v0.36.4
|
||||
sigs.k8s.io/controller-runtime v0.24.1
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
@@ -33,23 +33,21 @@ require (
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/swag v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/swag v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/google/gnostic-models v0.7.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@@ -63,26 +61,26 @@ require (
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/samber/lo v1.53.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.39.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.35.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
|
||||
k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.36.0 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
@@ -16,8 +18,8 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/fairwindsops/controller-utils v0.3.4 h1:t1qulL2GVDVUJTIE4icpBy3KnsxFTavnNAbFnd60blc=
|
||||
github.com/fairwindsops/controller-utils v0.3.4/go.mod h1:9/hOHX70/LG40RgtFAjtXFiMWEpItqm6Scf+obRFB2Y=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
@@ -29,50 +31,42 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8=
|
||||
github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
|
||||
github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
|
||||
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
|
||||
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
||||
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
|
||||
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
|
||||
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
|
||||
github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
|
||||
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
|
||||
github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
|
||||
github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
|
||||
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
|
||||
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
|
||||
github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
|
||||
github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
|
||||
github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
|
||||
github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
|
||||
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
@@ -80,9 +74,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
||||
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-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
@@ -91,8 +84,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -113,10 +104,10 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/onsi/ginkgo/v2 v2.27.4 h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y=
|
||||
github.com/onsi/ginkgo/v2 v2.27.4/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
|
||||
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -126,113 +117,73 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
|
||||
github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
||||
github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0=
|
||||
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
|
||||
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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
|
||||
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/sirupsen/logrus v1.10.1 h1:xi4336Zh11WpU14fXR6I67V3yaTPQYwRx2WEtHbRg4Q=
|
||||
github.com/sirupsen/logrus v1.10.1/go.mod h1:vsQHnG7xzNsxk3NrwboUiWPnIC3dmbjcGPykD7+tiHk=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
|
||||
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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/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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -241,46 +192,29 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWM
|
||||
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.v3 v3.0.0-20200313102051-9f266ea9e77c/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.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
|
||||
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
|
||||
k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
|
||||
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
|
||||
k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
|
||||
k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
|
||||
k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
|
||||
k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
|
||||
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
|
||||
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
|
||||
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
|
||||
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
|
||||
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
|
||||
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
|
||||
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-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ=
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
|
||||
k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
|
||||
sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
|
||||
sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE=
|
||||
sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
|
||||
k8s.io/api v0.36.4 h1:RxrvqCL6vgH5/+UnTeu1IIFqYmGfy0hnyrod1rn35Oo=
|
||||
k8s.io/api v0.36.4/go.mod h1:S2B3orCFBDhrgyWbLeuKcT2QdHIpQesBkCYSlWtwUOw=
|
||||
k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0=
|
||||
k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug=
|
||||
k8s.io/apimachinery v0.36.4 h1:PT2UzkupGuAx/+xT5XjiMJ1WGpY3fn9/hdAvjweRet4=
|
||||
k8s.io/apimachinery v0.36.4/go.mod h1:p2I2dipt7JHG+quVwQ1d02d28O4GdDi77RByQ13MTpk=
|
||||
k8s.io/client-go v0.36.4 h1:MDvfDNvMSt0Br94SK8neviVlwL9qifw9B26hJCpD1K0=
|
||||
k8s.io/client-go v0.36.4/go.mod h1:pNK4WKELbwlEDvtbE8l22lEZL5THYF61H5EealokZmA=
|
||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
|
||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4=
|
||||
sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.3 h1:u08YRbVUi59ri4YD6cg0UqNM4Dimn0sIl+wldcx5PYw=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.3/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -57,6 +57,15 @@ var (
|
||||
"sensitiveContainerEnvVar",
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"gatewayTLSMissing",
|
||||
"gatewayAllowedRoutesAll",
|
||||
"gatewayInsecureFrontendValidation",
|
||||
"gatewayCrossNamespaceCertificateRef",
|
||||
"httpRouteWildcardOrEmptyHost",
|
||||
"httpRouteInsecureListener",
|
||||
"httpRouteCrossNamespaceBackendRef",
|
||||
"httpRouteBackendTLSMissing",
|
||||
"kgatewayBackendTLSVerificationDisabled",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
successMessage: Gateway listeners restrict route attachment by namespace
|
||||
failureMessage: Gateway listeners should not allow routes from all namespaces
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
listeners:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
allowedRoutes:
|
||||
type: object
|
||||
properties:
|
||||
namespaces:
|
||||
type: object
|
||||
properties:
|
||||
from:
|
||||
not:
|
||||
const: All
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: Gateway cross-namespace certificate references are authorized
|
||||
failureMessage: Gateway cross-namespace certificate references should have a matching ReferenceGrant
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/ReferenceGrant
|
||||
@@ -0,0 +1,40 @@
|
||||
successMessage: Gateway frontend client certificate validation fails closed
|
||||
failureMessage: Gateway frontend client certificate validation should not allow insecure fallback
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
frontend:
|
||||
type: object
|
||||
properties:
|
||||
default:
|
||||
type: object
|
||||
properties:
|
||||
validation:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: AllowInsecureFallback
|
||||
perPort:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
validation:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: AllowInsecureFallback
|
||||
@@ -0,0 +1,41 @@
|
||||
successMessage: Gateway TLS listeners have certificates configured
|
||||
failureMessage: Gateway HTTPS, GRPC, and terminating TLS listeners should configure certificateRefs
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
listeners:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
if:
|
||||
anyOf:
|
||||
- required: [protocol]
|
||||
properties:
|
||||
protocol:
|
||||
enum: [HTTPS, GRPC]
|
||||
- required: [protocol]
|
||||
properties:
|
||||
protocol:
|
||||
const: TLS
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: Passthrough
|
||||
then:
|
||||
required: [tls]
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
required: [certificateRefs]
|
||||
properties:
|
||||
certificateRefs:
|
||||
type: array
|
||||
minItems: 1
|
||||
@@ -0,0 +1,9 @@
|
||||
successMessage: HTTPRoute TLS backends have TLS origination configured
|
||||
failureMessage: HTTPRoute backends on TLS ports should have a BackendTLSPolicy or kgateway BackendConfigPolicy
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- Service
|
||||
- gateway.networking.k8s.io/BackendTLSPolicy
|
||||
- gateway.kgateway.dev/Backend
|
||||
- gateway.kgateway.dev/BackendConfigPolicy
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: HTTPRoute cross-namespace backend references are authorized
|
||||
failureMessage: HTTPRoute cross-namespace backend references should have a matching ReferenceGrant
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/ReferenceGrant
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: HTTPRoute uses secure listeners or redirects HTTP to HTTPS
|
||||
failureMessage: HTTPRoute should not serve application traffic over an HTTP listener
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/Gateway
|
||||
@@ -0,0 +1,19 @@
|
||||
successMessage: HTTPRoute uses explicit hostnames
|
||||
failureMessage: HTTPRoute should use explicit hostnames instead of matching every hostname
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [hostnames]
|
||||
properties:
|
||||
hostnames:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
pattern: '^[^*]+$'
|
||||
@@ -0,0 +1,17 @@
|
||||
successMessage: kgateway backend TLS certificate verification is enabled
|
||||
failureMessage: kgateway BackendConfigPolicy should not disable TLS certificate verification
|
||||
category: Security
|
||||
target: gateway.kgateway.dev/BackendConfigPolicy
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
insecureSkipVerify:
|
||||
not:
|
||||
const: true
|
||||
@@ -108,7 +108,7 @@ func Parse(rawBytes []byte) (Configuration, error) {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return conf, fmt.Errorf("Decoding config failed: %v", err)
|
||||
return conf, fmt.Errorf("decoding config failed: %v", err)
|
||||
}
|
||||
}
|
||||
for key, check := range conf.CustomChecks {
|
||||
@@ -127,7 +127,7 @@ func Parse(rawBytes []byte) (Configuration, error) {
|
||||
// Validate checks if a config is valid
|
||||
func (conf Configuration) Validate() error {
|
||||
if len(conf.Checks) == 0 {
|
||||
return errors.New("No checks were enabled")
|
||||
return errors.New("no checks were enabled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ customChecks:
|
||||
|
||||
func TestParseError(t *testing.T) {
|
||||
_, err := Parse([]byte(confInvalid))
|
||||
expectedErr := "Decoding config failed: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type config.Configuration"
|
||||
expectedErr := "decoding config failed: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type config.Configuration"
|
||||
assert.EqualError(t, err, expectedErr)
|
||||
}
|
||||
|
||||
@@ -115,7 +115,9 @@ func TestConfigFromURL(t *testing.T) {
|
||||
var parsedConf Configuration
|
||||
srv := &http.Server{Addr: ":8081"}
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, confValidYAML)
|
||||
if _, err := io.WriteString(w, confValidYAML); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
|
||||
go func() {
|
||||
@@ -142,19 +144,20 @@ func TestConfigNoServerError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigWithCustomChecks(t *testing.T) {
|
||||
valid := map[string]interface{}{
|
||||
"securityContext": map[string]interface{}{
|
||||
valid := map[string]any{
|
||||
"securityContext": map[string]any{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
invalid := map[string]interface{}{
|
||||
"notSecurityContext": map[string]interface{}{},
|
||||
invalid := map[string]any{
|
||||
"notSecurityContext": map[string]any{},
|
||||
}
|
||||
|
||||
parsedConf, err := Parse([]byte(confCustomChecks))
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML config")
|
||||
assert.Equal(t, 1, len(parsedConf.CustomChecks))
|
||||
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]interface{}{})
|
||||
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]any{})
|
||||
assert.NoError(t, err)
|
||||
isValid, _, err := check.CheckObject(context.TODO(), valid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, isValid)
|
||||
|
||||
@@ -38,6 +38,15 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
gatewayTLSMissing: warning
|
||||
gatewayAllowedRoutesAll: warning
|
||||
gatewayInsecureFrontendValidation: warning
|
||||
gatewayCrossNamespaceCertificateRef: warning
|
||||
httpRouteWildcardOrEmptyHost: warning
|
||||
httpRouteInsecureListener: warning
|
||||
httpRouteCrossNamespaceBackendRef: warning
|
||||
httpRouteBackendTLSMissing: warning
|
||||
kgatewayBackendTLSVerificationDisabled: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
|
||||
@@ -38,6 +38,15 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
gatewayTLSMissing: warning
|
||||
gatewayAllowedRoutesAll: warning
|
||||
gatewayInsecureFrontendValidation: warning
|
||||
gatewayCrossNamespaceCertificateRef: warning
|
||||
httpRouteWildcardOrEmptyHost: warning
|
||||
httpRouteInsecureListener: warning
|
||||
httpRouteCrossNamespaceBackendRef: warning
|
||||
httpRouteBackendTLSMissing: warning
|
||||
kgatewayBackendTLSVerificationDisabled: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
|
||||
+40
-42
@@ -21,6 +21,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
@@ -58,34 +60,35 @@ var HandledTargets = []TargetKind{
|
||||
type Mutation struct {
|
||||
Path string
|
||||
Op string
|
||||
Value interface{}
|
||||
Value any
|
||||
Comment string
|
||||
}
|
||||
|
||||
// SchemaCheck is a Polaris check that runs using JSON Schema
|
||||
type SchemaCheck struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Category string `yaml:"category" json:"category"`
|
||||
SuccessMessage string `yaml:"successMessage" json:"successMessage"`
|
||||
FailureMessage string `yaml:"failureMessage" json:"failureMessage"`
|
||||
Controllers includeExcludeList `yaml:"controllers" json:"controllers"`
|
||||
Containers includeExcludeList `yaml:"containers" json:"containers"`
|
||||
Target TargetKind `yaml:"target" json:"target"`
|
||||
SchemaTarget TargetKind `yaml:"schemaTarget" json:"schemaTarget"`
|
||||
Schema map[string]interface{} `yaml:"schema" json:"schema"`
|
||||
SchemaString string `yaml:"schemaString" json:"schemaString"`
|
||||
Validator jsonschema.Schema `yaml:"-" json:"-"`
|
||||
AdditionalSchemas map[string]map[string]interface{} `yaml:"additionalSchemas" json:"additionalSchemas"`
|
||||
AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"`
|
||||
AdditionalValidators map[string]jsonschema.Schema `yaml:"-" json:"-"`
|
||||
Mutations []Mutation `yaml:"mutations" json:"mutations"`
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Category string `yaml:"category" json:"category"`
|
||||
SuccessMessage string `yaml:"successMessage" json:"successMessage"`
|
||||
FailureMessage string `yaml:"failureMessage" json:"failureMessage"`
|
||||
Controllers includeExcludeList `yaml:"controllers" json:"controllers"`
|
||||
Containers includeExcludeList `yaml:"containers" json:"containers"`
|
||||
Target TargetKind `yaml:"target" json:"target"`
|
||||
SchemaTarget TargetKind `yaml:"schemaTarget" json:"schemaTarget"`
|
||||
Schema map[string]any `yaml:"schema" json:"schema"`
|
||||
SchemaString string `yaml:"schemaString" json:"schemaString"`
|
||||
Validator jsonschema.Schema `yaml:"-" json:"-"`
|
||||
AdditionalSchemas map[string]map[string]any `yaml:"additionalSchemas" json:"additionalSchemas"`
|
||||
AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"`
|
||||
AdditionalValidators map[string]jsonschema.Schema `yaml:"-" json:"-"`
|
||||
RelatedKinds []TargetKind `yaml:"relatedKinds" json:"relatedKinds"`
|
||||
Mutations []Mutation `yaml:"mutations" json:"mutations"`
|
||||
}
|
||||
|
||||
type resourceMinimum string
|
||||
type resourceMaximum string
|
||||
|
||||
// UnmarshalYAMLOrJSON is a helper function to unmarshal data in an arbitrary format
|
||||
func UnmarshalYAMLOrJSON(raw []byte, dest interface{}) error {
|
||||
func UnmarshalYAMLOrJSON(raw []byte, dest any) error {
|
||||
reader := bytes.NewReader(raw)
|
||||
d := k8sYaml.NewYAMLOrJSONDecoder(reader, 4096)
|
||||
for {
|
||||
@@ -93,7 +96,7 @@ func UnmarshalYAMLOrJSON(raw []byte, dest interface{}) error {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("Decoding schema check failed: %v", err)
|
||||
return fmt.Errorf("decoding schema check failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -106,7 +109,9 @@ func ParseCheck(id string, rawBytes []byte) (SchemaCheck, error) {
|
||||
if err != nil {
|
||||
return check, err
|
||||
}
|
||||
check.Initialize(id)
|
||||
if err := check.Initialize(id); err != nil {
|
||||
return check, err
|
||||
}
|
||||
return check, nil
|
||||
}
|
||||
|
||||
@@ -129,7 +134,7 @@ func newResourceMaximum() jsonschema.Keyword {
|
||||
return new(resourceMaximum)
|
||||
}
|
||||
|
||||
func (min resourceMinimum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) {
|
||||
func (min resourceMinimum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data any) {
|
||||
err := validateRange(string(min), data, true)
|
||||
if err != nil {
|
||||
errs := currentState.Errs
|
||||
@@ -137,7 +142,7 @@ func (min resourceMinimum) ValidateKeyword(ctx context.Context, currentState *js
|
||||
currentState.Errs = errs
|
||||
}
|
||||
}
|
||||
func (max resourceMaximum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) {
|
||||
func (max resourceMaximum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data any) {
|
||||
err := validateRange(string(max), data, false)
|
||||
if err != nil {
|
||||
errs := currentState.Errs
|
||||
@@ -163,7 +168,7 @@ func (max resourceMaximum) Register(uri string, registry *jsonschema.SchemaRegis
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.KeyError) {
|
||||
func parseQuantity(i any) (resource.Quantity, *[]jsonschema.KeyError) {
|
||||
if resNum, ok := i.(float64); ok {
|
||||
i = fmt.Sprintf("%f", resNum)
|
||||
}
|
||||
@@ -182,7 +187,7 @@ func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.KeyError) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func validateRange(limit interface{}, data interface{}, isMinimum bool) *[]jsonschema.KeyError {
|
||||
func validateRange(limit any, data any, isMinimum bool) *[]jsonschema.KeyError {
|
||||
limitQuantity, err := parseQuantity(limit)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -228,21 +233,19 @@ func (check *SchemaCheck) Initialize(id string) error {
|
||||
}
|
||||
check.AdditionalSchemaStrings[kind] = string(jsonBytes)
|
||||
}
|
||||
check.Schema = map[string]interface{}{}
|
||||
check.AdditionalSchemas = map[string]map[string]interface{}{}
|
||||
check.Schema = map[string]any{}
|
||||
check.AdditionalSchemas = map[string]map[string]any{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TemplateForResource fills out a check's templated fields given a particular resource
|
||||
func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, error) {
|
||||
func (check SchemaCheck) TemplateForResource(res any) (*SchemaCheck, error) {
|
||||
newCheck := check // Make a copy of the check, since we're going to modify the schema
|
||||
|
||||
templateStrings := map[string]string{
|
||||
"": newCheck.SchemaString,
|
||||
}
|
||||
for kind, schema := range newCheck.AdditionalSchemaStrings {
|
||||
templateStrings[kind] = schema
|
||||
}
|
||||
maps.Copy(templateStrings, newCheck.AdditionalSchemaStrings)
|
||||
newCheck.SchemaString = ""
|
||||
newCheck.AdditionalSchemaStrings = map[string]string{}
|
||||
|
||||
@@ -294,7 +297,7 @@ func (check SchemaCheck) CheckPodSpec(ctx context.Context, pod *corev1.PodSpec)
|
||||
}
|
||||
|
||||
// CheckPodTemplate checks a pod template against the schema
|
||||
func (check SchemaCheck) CheckPodTemplate(ctx context.Context, podTemplate interface{}) (bool, []jsonschema.KeyError, error) {
|
||||
func (check SchemaCheck) CheckPodTemplate(ctx context.Context, podTemplate any) (bool, []jsonschema.KeyError, error) {
|
||||
return check.CheckObject(ctx, podTemplate)
|
||||
}
|
||||
|
||||
@@ -310,7 +313,7 @@ func (check SchemaCheck) CheckContainer(ctx context.Context, container *corev1.C
|
||||
}
|
||||
|
||||
// CheckObject checks arbitrary data against the schema
|
||||
func (check SchemaCheck) CheckObject(ctx context.Context, obj interface{}) (bool, []jsonschema.KeyError, error) {
|
||||
func (check SchemaCheck) CheckObject(ctx context.Context, obj any) (bool, []jsonschema.KeyError, error) {
|
||||
bytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
@@ -320,10 +323,10 @@ func (check SchemaCheck) CheckObject(ctx context.Context, obj interface{}) (bool
|
||||
}
|
||||
|
||||
// CheckAdditionalObjects looks for an object that passes the specified additional schema
|
||||
func (check SchemaCheck) CheckAdditionalObjects(ctx context.Context, groupkind string, objects []interface{}) (bool, error) {
|
||||
func (check SchemaCheck) CheckAdditionalObjects(ctx context.Context, groupkind string, objects []any) (bool, error) {
|
||||
val, ok := check.AdditionalValidators[groupkind]
|
||||
if !ok {
|
||||
return false, errors.New("No validator found for " + groupkind)
|
||||
return false, errors.New("no validator found for " + groupkind)
|
||||
}
|
||||
for _, obj := range objects {
|
||||
bytes, err := json.Marshal(obj)
|
||||
@@ -356,19 +359,14 @@ func (check SchemaCheck) IsActionable(target TargetKind, kind string, isInit boo
|
||||
return false
|
||||
}
|
||||
isIncluded := len(check.Controllers.Include) == 0
|
||||
for _, inclusion := range check.Controllers.Include {
|
||||
if inclusion == kind {
|
||||
isIncluded = true
|
||||
break
|
||||
}
|
||||
if slices.Contains(check.Controllers.Include, kind) {
|
||||
isIncluded = true
|
||||
}
|
||||
if !isIncluded {
|
||||
return false
|
||||
}
|
||||
for _, exclusion := range check.Controllers.Exclude {
|
||||
if exclusion == kind {
|
||||
return false
|
||||
}
|
||||
if slices.Contains(check.Controllers.Exclude, kind) {
|
||||
return false
|
||||
}
|
||||
if check.Target == TargetContainer {
|
||||
isIncluded := len(check.Containers.Include) == 0
|
||||
|
||||
+13
-10
@@ -24,7 +24,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
@@ -111,7 +110,9 @@ func writeTemplate(tmpl *template.Template, data *templateData, w http.ResponseW
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
buf.WriteTo(w)
|
||||
if _, err := buf.WriteTo(w); err != nil {
|
||||
logrus.Errorf("Error writing template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigForQuery(base config.Configuration, query url.Values) config.Configuration {
|
||||
@@ -153,7 +154,9 @@ func GetRouter(ctx context.Context, c config.Configuration, auditPath string, po
|
||||
router.PathPrefix("/static/").Handler(http.StripPrefix(path.Join(basePath, "/static/"), fileServer))
|
||||
|
||||
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
if _, err := w.Write([]byte("OK")); err != nil {
|
||||
logrus.Errorf("Error writing health response: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
router.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -163,7 +166,9 @@ func GetRouter(ctx context.Context, c config.Configuration, auditPath string, po
|
||||
http.Error(w, "Error getting favicon", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write(favicon)
|
||||
if _, err := w.Write(favicon); err != nil {
|
||||
logrus.Errorf("Error writing favicon: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
router.HandleFunc("/results.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -188,11 +193,7 @@ func GetRouter(ctx context.Context, c config.Configuration, auditPath string, po
|
||||
JSONHandler(w, r, auditData)
|
||||
})
|
||||
|
||||
router.HandleFunc("/details/{category}", func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
category := vars["category"]
|
||||
category = strings.Replace(category, ".md", "", -1)
|
||||
})
|
||||
router.HandleFunc("/details/{category}", func(http.ResponseWriter, *http.Request) {})
|
||||
|
||||
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/" && r.URL.Path != basePath {
|
||||
@@ -265,5 +266,7 @@ func MainHandler(w http.ResponseWriter, r *http.Request, c config.Configuration,
|
||||
func JSONHandler(w http.ResponseWriter, r *http.Request, auditData *validator.AuditData) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(auditData)
|
||||
if err := json.NewEncoder(w).Encode(auditData); err != nil {
|
||||
logrus.Errorf("Error encoding audit JSON: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
@@ -128,13 +128,13 @@ func getCategoryLink(category string) string {
|
||||
func getCategoryInfo(category string) string {
|
||||
switch category {
|
||||
case "Reliability":
|
||||
return fmt.Sprintf(`
|
||||
return `
|
||||
Kubernetes is built to reliabily run highly available applications.
|
||||
Polaris includes a number of checks to ensure that you are maximizing
|
||||
the reliability potential of Kubernetes.
|
||||
`)
|
||||
`
|
||||
case "Efficiency":
|
||||
return fmt.Sprintf(`
|
||||
return `
|
||||
Configuring resource requests and limits for workloads running in Kubernetes
|
||||
helps ensure that every container will have access to all the resources it
|
||||
needs. These are also a crucial part of cluster autoscaling logic, as new
|
||||
@@ -142,25 +142,20 @@ func getCategoryInfo(category string) string {
|
||||
infrastructure for new pod(s). By default, Polaris validates that resource
|
||||
requests and limits are set, it also includes optional functionality to ensure
|
||||
these requests and limits fall within specified ranges.
|
||||
`)
|
||||
`
|
||||
case "Security":
|
||||
return fmt.Sprintf(`
|
||||
return `
|
||||
Kubernetes provides a great deal of configurability when it comes to the
|
||||
security of your workloads. A key principle here involves limiting the level
|
||||
of access any individual workload has. Polaris has validations for a number of
|
||||
best practices, mostly focused on ensuring that unnecessary access has not
|
||||
been granted to an application workload.
|
||||
`)
|
||||
`
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(list, a)
|
||||
}
|
||||
|
||||
+13
-13
@@ -39,13 +39,13 @@ type GenericResource struct {
|
||||
ObjectMeta kubeAPIMetaV1.Object
|
||||
Resource unstructured.Unstructured
|
||||
PodSpec *kubeAPICoreV1.PodSpec
|
||||
PodTemplate interface{}
|
||||
PodTemplate any
|
||||
OriginalObjectJSON []byte
|
||||
OriginalObjectYAML []byte
|
||||
}
|
||||
|
||||
// NewGenericResourceFromUnstructured creates a workload from an unstructured.Unstructured
|
||||
func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecMap interface{}) (GenericResource, error) {
|
||||
func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecMap any) (GenericResource, error) {
|
||||
if unst.GetCreationTimestamp().Time.IsZero() {
|
||||
unstructured.RemoveNestedField(unst.Object, "metadata", "creationTimestamp")
|
||||
unstructured.RemoveNestedField(unst.Object, "status")
|
||||
@@ -69,7 +69,7 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecM
|
||||
return workload, err
|
||||
}
|
||||
workload.OriginalObjectJSON = b
|
||||
m := make(map[string]interface{})
|
||||
m := make(map[string]any)
|
||||
err = json.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
return workload, err
|
||||
@@ -93,7 +93,7 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecM
|
||||
}
|
||||
|
||||
// NewGenericResourceFromPod builds a new workload for a given Pod without looking at parents
|
||||
func NewGenericResourceFromPod(podResource kubeAPICoreV1.Pod, originalObject interface{}) (GenericResource, error) {
|
||||
func NewGenericResourceFromPod(podResource kubeAPICoreV1.Pod, originalObject any) (GenericResource, error) {
|
||||
podMap, err := SerializePod(&podResource)
|
||||
if err != nil {
|
||||
return GenericResource{}, err
|
||||
@@ -102,7 +102,7 @@ func NewGenericResourceFromPod(podResource kubeAPICoreV1.Pod, originalObject int
|
||||
Kind: "Pod",
|
||||
PodSpec: &podResource.Spec,
|
||||
PodTemplate: podMap,
|
||||
ObjectMeta: podResource.ObjectMeta.GetObjectMeta(),
|
||||
ObjectMeta: podResource.GetObjectMeta(),
|
||||
}
|
||||
if originalObject != nil {
|
||||
bytes, err := json.Marshal(originalObject)
|
||||
@@ -157,9 +157,9 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
|
||||
}
|
||||
topKind := "Pod"
|
||||
topMeta := podWorkload.ObjectMeta
|
||||
var topPodSpec interface{}
|
||||
var topPodSpec any
|
||||
topPodSpec = podWorkload.Resource.Object
|
||||
owners := podResource.ObjectMeta.GetOwnerReferences()
|
||||
owners := podResource.GetOwnerReferences()
|
||||
lastKey := ""
|
||||
for len(owners) > 0 {
|
||||
if len(owners) > 1 {
|
||||
@@ -263,10 +263,10 @@ func GetObject(ctx context.Context, namespace, kind, version, name string, dynam
|
||||
}
|
||||
|
||||
// GetPodSpec looks inside arbitrary YAML for a PodSpec
|
||||
func GetPodSpec(yaml map[string]interface{}) interface{} {
|
||||
func GetPodSpec(yaml map[string]any) any {
|
||||
for _, child := range podSpecFields {
|
||||
if childYaml, ok := yaml[child]; ok {
|
||||
return GetPodSpec(childYaml.(map[string]interface{}))
|
||||
return GetPodSpec(childYaml.(map[string]any))
|
||||
}
|
||||
}
|
||||
if _, ok := yaml["containers"]; ok {
|
||||
@@ -278,9 +278,9 @@ func GetPodSpec(yaml map[string]interface{}) interface{} {
|
||||
// GetPodTemplate looks inside arbitrary YAML for a Pod template, containing
|
||||
// fields `spec.containers`.
|
||||
// For example, it returns the `spec.template` level of a Kubernetes Deployment yaml.
|
||||
func GetPodTemplate(yaml map[string]interface{}) (podTemplate interface{}, err error) {
|
||||
func GetPodTemplate(yaml map[string]any) (podTemplate any, err error) {
|
||||
if yamlSpec, ok := yaml["spec"]; ok {
|
||||
if yamlSpecMap, ok := yamlSpec.(map[string]interface{}); ok {
|
||||
if yamlSpecMap, ok := yamlSpec.(map[string]any); ok {
|
||||
if _, ok := yamlSpecMap["containers"]; ok {
|
||||
// This is a hack around unstructured.SetNestedField using DeepCopy which does
|
||||
// not support the type int, and panics.
|
||||
@@ -289,7 +289,7 @@ func GetPodTemplate(yaml map[string]interface{}) (podTemplate interface{}, err e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podTemplateMap := make(map[string]interface{})
|
||||
podTemplateMap := make(map[string]any)
|
||||
err = json.Unmarshal(podTemplateJSON, &podTemplateMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -300,7 +300,7 @@ func GetPodTemplate(yaml map[string]interface{}) (podTemplate interface{}, err e
|
||||
}
|
||||
for _, podSpecField := range podSpecFields {
|
||||
if childYaml, ok := yaml[podSpecField]; ok {
|
||||
return GetPodTemplate(childYaml.(map[string]interface{}))
|
||||
return GetPodTemplate(childYaml.(map[string]any))
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
+48
-23
@@ -52,6 +52,7 @@ type ResourceProvider struct {
|
||||
SourceType string
|
||||
Nodes []corev1.Node
|
||||
Namespaces []corev1.Namespace
|
||||
Pods []corev1.Pod
|
||||
Resources resourceKindMap
|
||||
}
|
||||
|
||||
@@ -109,13 +110,13 @@ func maybeTransformKindIntoGroupKind(k string) string {
|
||||
}
|
||||
|
||||
func parseGroupKind(gk string) schema.GroupKind {
|
||||
i := strings.Index(gk, "/")
|
||||
if i == -1 {
|
||||
before, after, ok := strings.Cut(gk, "/")
|
||||
if !ok {
|
||||
return schema.GroupKind{Kind: gk}
|
||||
}
|
||||
|
||||
group := gk[:i]
|
||||
kind := gk[i+1:]
|
||||
group := before
|
||||
kind := after
|
||||
return schema.GroupKind{Group: group, Kind: kind}
|
||||
}
|
||||
|
||||
@@ -127,6 +128,7 @@ func newResourceProvider(version, sourceType, sourceName string) ResourceProvide
|
||||
CreationTime: time.Now(),
|
||||
Nodes: make([]corev1.Node, 0),
|
||||
Namespaces: make([]corev1.Namespace, 0),
|
||||
Pods: make([]corev1.Pod, 0),
|
||||
Resources: make(map[string][]GenericResource),
|
||||
}
|
||||
}
|
||||
@@ -156,13 +158,13 @@ func CreateResourceProviderFromResource(ctx context.Context, workload string) (*
|
||||
}
|
||||
serverVersion, err := clientSet.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error fetching Cluster API version: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Cluster API version: %w", err)
|
||||
}
|
||||
resources := newResourceProvider(serverVersion.Major+"."+serverVersion.Minor, "Resource", workload)
|
||||
|
||||
parts := strings.Split(workload, "/")
|
||||
if len(parts) != 4 {
|
||||
return nil, fmt.Errorf("Invalid workload identifier %s. Should be in format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend", workload)
|
||||
return nil, fmt.Errorf("invalid workload identifier %s. Should be in format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend", workload)
|
||||
}
|
||||
namespace := parts[0]
|
||||
kind := parts[1]
|
||||
@@ -171,11 +173,11 @@ func CreateResourceProviderFromResource(ctx context.Context, workload string) (*
|
||||
|
||||
obj, err := GetObject(ctx, namespace, kind, version, name, dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not find workload %s: %w", workload, err)
|
||||
return nil, fmt.Errorf("could not find workload %s: %w", workload, err)
|
||||
}
|
||||
workloadObj, err := NewGenericResourceFromUnstructured(*obj, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse workload %s: %w", workload, err)
|
||||
return nil, fmt.Errorf("could not parse workload %s: %w", workload, err)
|
||||
}
|
||||
resources.Resources.addResource(workloadObj)
|
||||
return &resources, nil
|
||||
@@ -196,6 +198,9 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
|
||||
}
|
||||
|
||||
visitFile := func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasSuffix(path, ".yml") && !strings.HasSuffix(path, ".yaml") {
|
||||
return nil
|
||||
}
|
||||
@@ -246,19 +251,19 @@ func GetKubeClient(ctx context.Context, kubeContext string) (dynamic.Interface,
|
||||
kubeConf, err = config.GetConfig()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error fetching KubeConfig: %v", err)
|
||||
return nil, nil, nil, "", fmt.Errorf("error fetching KubeConfig: %v", err)
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfig(kubeConf)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error creating Kubernetes client: %v", err)
|
||||
return nil, nil, nil, "", fmt.Errorf("error creating Kubernetes client: %v", err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(kubeConf)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error connecting to dynamic interface: %v", err)
|
||||
return nil, nil, nil, "", fmt.Errorf("error connecting to dynamic interface: %v", err)
|
||||
}
|
||||
resources, err := restmapper.GetAPIGroupResources(clientSet.Discovery())
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error getting API Group resources: %v", err)
|
||||
return nil, nil, nil, "", fmt.Errorf("error getting API Group resources: %v", err)
|
||||
}
|
||||
return dynamicClient, restmapper.NewDiscoveryRESTMapper(resources), clientSet, kubeConf.Host, nil
|
||||
}
|
||||
@@ -304,6 +309,14 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
}
|
||||
namespaces = nsList
|
||||
}
|
||||
|
||||
logrus.Info("Loading pods")
|
||||
pods, err := kube.CoreV1().Pods(c.Namespace).List(ctx, listOpts)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Pods: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logrus.Info("Setting up restmapper")
|
||||
resources, err := restmapper.GetAPIGroupResources(kube.Discovery())
|
||||
if err != nil {
|
||||
@@ -312,16 +325,22 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
}
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(resources)
|
||||
allChecks := []conf.SchemaCheck{}
|
||||
for _, check := range c.CustomChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
for checkID, check := range c.CustomChecks {
|
||||
severity, enabled := c.Checks[checkID]
|
||||
if enabled && severity.IsActionable() {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
}
|
||||
for _, check := range conf.BuiltInChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
for checkID, check := range conf.BuiltInChecks {
|
||||
severity, enabled := c.Checks[checkID]
|
||||
if enabled && severity.IsActionable() {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
}
|
||||
|
||||
var additionalKinds []conf.TargetKind
|
||||
for _, check := range allChecks {
|
||||
neededKinds := []conf.TargetKind{check.Target}
|
||||
neededKinds := append([]conf.TargetKind{check.Target}, check.RelatedKinds...)
|
||||
for key := range check.AdditionalSchemas {
|
||||
neededKinds = append(neededKinds, conf.TargetKind(key))
|
||||
}
|
||||
@@ -340,6 +359,10 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
groupKind := parseGroupKind(maybeTransformKindIntoGroupKind(string(kind)))
|
||||
mapping, err := restMapper.RESTMapping(groupKind)
|
||||
if err != nil {
|
||||
if meta.IsNoMatchError(err) {
|
||||
logrus.Infof("Skipping unavailable Kind %s", kind)
|
||||
continue
|
||||
}
|
||||
logrus.Warnf("error retrieving mapping of Kind %s because of error: %v", kind, err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -383,6 +406,7 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
|
||||
provider.Nodes = nodes.Items
|
||||
provider.Namespaces = namespaces.Items
|
||||
provider.Pods = pods.Items
|
||||
provider.Resources.addResources(kubernetesResources)
|
||||
logrus.Info("Done loading Kubernetes resources")
|
||||
return &provider, nil
|
||||
@@ -442,6 +466,7 @@ func (resources *ResourceProvider) addResourceFromString(contents string) error
|
||||
return err
|
||||
}
|
||||
workload.OriginalObjectYAML = contentBytes
|
||||
resources.Pods = append(resources.Pods, pod)
|
||||
resources.Resources.addResource(workload)
|
||||
} else {
|
||||
newResource, err := NewGenericResourceFromBytes(contentBytes)
|
||||
@@ -454,12 +479,12 @@ func (resources *ResourceProvider) addResourceFromString(contents string) error
|
||||
}
|
||||
|
||||
// SerializePodSpec converts a typed PodSpec into a map[string]interface{}
|
||||
func SerializePodSpec(pod *corev1.PodSpec) (map[string]interface{}, error) {
|
||||
func SerializePodSpec(pod *corev1.PodSpec) (map[string]any, error) {
|
||||
podJSON, err := json.Marshal(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podMap := make(map[string]interface{})
|
||||
podMap := make(map[string]any)
|
||||
err = json.Unmarshal(podJSON, &podMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -468,12 +493,12 @@ func SerializePodSpec(pod *corev1.PodSpec) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
// SerializePod converts a typed Pod into a map[string]interface{}
|
||||
func SerializePod(pod *corev1.Pod) (map[string]interface{}, error) {
|
||||
func SerializePod(pod *corev1.Pod) (map[string]any, error) {
|
||||
podJSON, err := json.Marshal(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podMap := make(map[string]interface{})
|
||||
podMap := make(map[string]any)
|
||||
err = json.Unmarshal(podJSON, &podMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -482,12 +507,12 @@ func SerializePod(pod *corev1.Pod) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
// SerializeContainer converts a typed Container into a map[string]interface{}
|
||||
func SerializeContainer(container *corev1.Container) (map[string]interface{}, error) {
|
||||
func SerializeContainer(container *corev1.Container) (map[string]any, error) {
|
||||
containerJSON, err := json.Marshal(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerMap := make(map[string]interface{})
|
||||
containerMap := make(map[string]any)
|
||||
err = json.Unmarshal(containerJSON, &containerMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestGetResourcesFromPath(t *testing.T) {
|
||||
assert.Equal(t, 0, len(provider.Nodes), "Should not have any nodes")
|
||||
|
||||
assert.Equal(t, 1, len(provider.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "two", provider.Namespaces[0].ObjectMeta.Name)
|
||||
assert.Equal(t, "two", provider.Namespaces[0].Name)
|
||||
|
||||
namespaceCount := map[string]int{}
|
||||
for _, resources := range provider.Resources {
|
||||
@@ -69,8 +69,8 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {
|
||||
assert.Equal(t, "dashboard", resources.Resources["apps/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
assert.Equal(t, "polaris-2", resources.Namespaces[1].ObjectMeta.Name)
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].Name)
|
||||
assert.Equal(t, "polaris-2", resources.Namespaces[1].Name)
|
||||
}
|
||||
|
||||
func TestGetMultipleResourceFromBadFile(t *testing.T) {
|
||||
@@ -92,8 +92,8 @@ func TestAddResourcesFromReader(t *testing.T) {
|
||||
assert.Equal(t, "dashboard", resources.Resources["apps/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
assert.Equal(t, "polaris-2", resources.Namespaces[1].ObjectMeta.Name)
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].Name)
|
||||
assert.Equal(t, "polaris-2", resources.Namespaces[1].Name)
|
||||
}
|
||||
|
||||
func TestGetResourceFromAPI(t *testing.T) {
|
||||
@@ -157,6 +157,7 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
assert.Equal(t, tt.want.SourceName, resources.SourceName)
|
||||
assert.IsType(t, tt.want.CreationTime, resources.CreationTime)
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
assert.Equal(t, 5, len(resources.Pods), "Should have 5 pods")
|
||||
for k, v := range resources.Resources {
|
||||
fmt.Println("cont", k, v)
|
||||
}
|
||||
@@ -175,3 +176,45 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdditionalKindLoading(t *testing.T) {
|
||||
ingress := test.MockIngress()
|
||||
k8s, dynamicInterface := test.SetupTestAPI(append(test.GetMockControllers("test"), &ingress)...)
|
||||
|
||||
enabled := conf.Configuration{
|
||||
Checks: map[string]conf.Severity{
|
||||
"customIngress": conf.SeverityWarning,
|
||||
},
|
||||
CustomChecks: map[string]conf.SchemaCheck{
|
||||
"customIngress": {Target: "networking.k8s.io/Ingress"},
|
||||
},
|
||||
}
|
||||
resources, err := CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, enabled)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, resources.Resources["networking.k8s.io/Ingress"], 1)
|
||||
}
|
||||
|
||||
ignored := enabled
|
||||
ignored.Checks = map[string]conf.Severity{
|
||||
"customIngress": conf.SeverityIgnore,
|
||||
}
|
||||
resources, err = CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, ignored)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Empty(t, resources.Resources["networking.k8s.io/Ingress"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnavailableAdditionalKindDoesNotFailAudit(t *testing.T) {
|
||||
k8s, dynamicInterface := test.SetupTestAPI(test.GetMockControllers("test")...)
|
||||
config := conf.Configuration{
|
||||
Checks: map[string]conf.Severity{
|
||||
"optionalCRD": conf.SeverityWarning,
|
||||
},
|
||||
CustomChecks: map[string]conf.SchemaCheck{
|
||||
"optionalCRD": {Target: "example.com/OptionalResource"},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, config)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func createPathAndFindNodes(node *yaml.Node, selectors []string, create bool) ([
|
||||
|
||||
func addOrReplaceValue(node *yaml.Node, splits []string, value *yaml.Node) error {
|
||||
if len(node.Content) == 0 {
|
||||
return errors.New("No content in node")
|
||||
return errors.New("no content in node")
|
||||
}
|
||||
nodes, err := createPathAndFindNodes(node.Content[0], splits, true)
|
||||
if err != nil {
|
||||
@@ -224,7 +224,7 @@ func addOrReplaceValue(node *yaml.Node, splits []string, value *yaml.Node) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNodeFromValue(value interface{}, comment string) (*yaml.Node, error) {
|
||||
func getNodeFromValue(value any, comment string) (*yaml.Node, error) {
|
||||
bytes, err := yaml.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -235,7 +235,7 @@ func getNodeFromValue(value interface{}, comment string) (*yaml.Node, error) {
|
||||
return nil, err
|
||||
}
|
||||
if len(doc.Content) == 0 {
|
||||
return nil, errors.New("Generated an empty YAML document")
|
||||
return nil, errors.New("generated an empty YAML document")
|
||||
}
|
||||
if doc.Content[0].Kind == yaml.MappingNode {
|
||||
doc.Content[0].Content[0].HeadComment = comment
|
||||
@@ -293,7 +293,8 @@ func removeMatchingNode(node *yaml.Node, selectors []string) error {
|
||||
for _, node := range visitArrayNodes {
|
||||
lastSelector := len(selectors) == 1
|
||||
if !lastSelector {
|
||||
removeMatchingNode(node, selectors[1:])
|
||||
// Missing nested keys are expected for * wildcards (e.g. owners/*/aliases).
|
||||
_ = removeMatchingNode(node, selectors[1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ obj:
|
||||
`,
|
||||
patch: config.Mutation{
|
||||
Op: "replace",
|
||||
Value: map[string]interface{}{
|
||||
Value: map[string]any{
|
||||
"bar": []string{"c", "d"},
|
||||
},
|
||||
Path: "/obj/foo",
|
||||
@@ -129,7 +129,7 @@ obj:
|
||||
original: `foo: bar`,
|
||||
patch: config.Mutation{
|
||||
Op: "add",
|
||||
Value: map[string]interface{}{
|
||||
Value: map[string]any{
|
||||
"baz": "quux",
|
||||
},
|
||||
Path: "/extra",
|
||||
@@ -149,7 +149,7 @@ foo: bar
|
||||
`,
|
||||
patch: config.Mutation{
|
||||
Op: "replace",
|
||||
Value: map[string]interface{}{
|
||||
Value: map[string]any{
|
||||
"baz": "quux",
|
||||
},
|
||||
Path: "/extra",
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestValidatePDB(t *testing.T) {
|
||||
}
|
||||
pdb := unstructured.Unstructured{}
|
||||
res, err := kube.NewGenericResourceFromUnstructured(pdb, nil)
|
||||
assert.NoError(t, err)
|
||||
res.Kind = "PodDisruptionBudget"
|
||||
|
||||
actualResult, err := applyNonControllerSchemaChecks(context.Background(), &c, nil, res)
|
||||
|
||||
@@ -99,10 +99,11 @@ func TestControllerLevelChecks(t *testing.T) {
|
||||
assert.Equal(t, 10, res.Resources.GetLength())
|
||||
testResources(res)
|
||||
|
||||
replicaSpec := map[string]interface{}{"replicas": 2}
|
||||
replicaSpec := map[string]any{"replicas": 2}
|
||||
b, err := json.Marshal(replicaSpec)
|
||||
assert.NoError(t, err)
|
||||
err = json.Unmarshal(b, &replicaSpec)
|
||||
assert.NoError(t, err)
|
||||
|
||||
d1, p1 := test.MockDeploy("test", "test-deployment")
|
||||
d2, p2 := test.MockDeploy("test", "test-deployment-2")
|
||||
@@ -203,7 +204,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
}
|
||||
|
||||
pod := test.MockPod()
|
||||
pod.ObjectMeta.Namespace = "foo"
|
||||
pod.Namespace = "foo"
|
||||
workload, err := kube.NewGenericResourceFromPod(pod, nil)
|
||||
assert.NoError(t, err)
|
||||
workload.Kind = "Deployment"
|
||||
|
||||
@@ -50,6 +50,7 @@ func RunAudit(ctx context.Context, config conf.Configuration, kubeResources *kub
|
||||
ClusterInfo: ClusterInfo{
|
||||
Version: kubeResources.ServerVersion,
|
||||
Nodes: len(kubeResources.Nodes),
|
||||
Pods: len(kubeResources.Pods),
|
||||
Namespaces: len(kubeResources.Namespaces),
|
||||
Controllers: kubeResources.Resources.GetNumberOfControllers(),
|
||||
},
|
||||
@@ -61,13 +62,12 @@ func RunAudit(ctx context.Context, config conf.Configuration, kubeResources *kub
|
||||
|
||||
// ReadAuditFromFile reads the data from a past audit stored in a JSON or YAML file.
|
||||
func ReadAuditFromFile(fileName string) AuditData {
|
||||
auditData := AuditData{}
|
||||
oldFileBytes, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to read contents of loaded file: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
auditData, err = ParseAudit(oldFileBytes)
|
||||
auditData, err := ParseAudit(oldFileBytes)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing file contents into auditData: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -85,7 +85,7 @@ func ParseAudit(oldFileBytes []byte) (AuditData, error) {
|
||||
if err == io.EOF {
|
||||
return conf, nil
|
||||
}
|
||||
return conf, fmt.Errorf("Decoding config failed: %v", err)
|
||||
return conf, fmt.Errorf("decoding config failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ func TestGetTemplateData(t *testing.T) {
|
||||
assert.EqualValues(t, sum, actualAudit.GetSummary())
|
||||
assert.Equal(t, actualAudit.SourceType, "Cluster", "should be from a cluster")
|
||||
assert.Equal(t, actualAudit.SourceName, "test", "should be from a cluster")
|
||||
assert.Equal(t, 5, actualAudit.ClusterInfo.Pods, "should report pod count from the cluster")
|
||||
assert.Equal(t, 5, actualAudit.ClusterInfo.Controllers, "should report controller count from the cluster")
|
||||
assert.Equal(t, 1, actualAudit.ClusterInfo.Namespaces, "should report namespace count from the cluster")
|
||||
|
||||
expectedResults := []struct {
|
||||
kind string
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/qri-io/jsonschema"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
const (
|
||||
gatewayAPIGroup = "gateway.networking.k8s.io"
|
||||
kgatewayAPIGroup = "gateway.kgateway.dev"
|
||||
)
|
||||
|
||||
type objectReference struct {
|
||||
group string
|
||||
kind string
|
||||
name string
|
||||
namespace string
|
||||
sectionName string
|
||||
port int64
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerCustomChecks("httpRouteInsecureListener", httpRouteInsecureListener)
|
||||
registerCustomChecks("gatewayCrossNamespaceCertificateRef", gatewayCrossNamespaceCertificateRef)
|
||||
registerCustomChecks("httpRouteCrossNamespaceBackendRef", httpRouteCrossNamespaceBackendRef)
|
||||
registerCustomChecks("httpRouteBackendTLSMissing", httpRouteBackendTLSMissing)
|
||||
}
|
||||
|
||||
func httpRouteInsecureListener(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if isFullHTTPSRedirect(test.Resource.Resource.Object) || test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
routeNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, parent := range referencesAt(test.Resource.Resource.Object, "spec", "parentRefs") {
|
||||
parent = withDefaults(parent, gatewayAPIGroup, "Gateway", routeNamespace)
|
||||
if parent.group != gatewayAPIGroup || parent.kind != "Gateway" {
|
||||
continue
|
||||
}
|
||||
|
||||
gateway := findResource(test.ResourceProvider.Resources[gatewayAPIGroup+"/Gateway"], parent.namespace, parent.name)
|
||||
if gateway == nil {
|
||||
continue
|
||||
}
|
||||
listeners := nestedSlice(gateway.Resource.Object, "spec", "listeners")
|
||||
for _, rawListener := range listeners {
|
||||
listener, ok := rawListener.(map[string]any)
|
||||
if !ok || (parent.sectionName != "" && stringValue(listener["name"]) != parent.sectionName) {
|
||||
continue
|
||||
}
|
||||
if stringValue(listener["protocol"]) == "HTTP" && listenerAcceptsHTTPRoute(listener, parent.namespace, test.Resource, test.ResourceProvider) {
|
||||
return gatewayFailure("spec.parentRefs", fmt.Sprintf("HTTPRoute references HTTP listener %q on Gateway %s/%s without a full HTTPS redirect", stringValue(listener["name"]), parent.namespace, parent.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func gatewayCrossNamespaceCertificateRef(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
sourceNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
listeners := nestedSlice(test.Resource.Resource.Object, "spec", "listeners")
|
||||
for _, rawListener := range listeners {
|
||||
listener, ok := rawListener.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, ref := range referencesAt(listener, "tls", "certificateRefs") {
|
||||
ref = withDefaults(ref, "", "Secret", sourceNamespace)
|
||||
if ref.namespace != sourceNamespace && !hasReferenceGrant(test.ResourceProvider, sourceNamespace, "Gateway", ref) {
|
||||
return gatewayFailure("spec.listeners.tls.certificateRefs", fmt.Sprintf("Gateway %s/%s references %s %s/%s without a matching ReferenceGrant", sourceNamespace, test.Resource.ObjectMeta.GetName(), ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func httpRouteCrossNamespaceBackendRef(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
sourceNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, ref := range httpRouteBackendRefs(test.Resource.Resource.Object) {
|
||||
ref = withDefaults(ref, "", "Service", sourceNamespace)
|
||||
if ref.namespace != sourceNamespace && !hasReferenceGrant(test.ResourceProvider, sourceNamespace, "HTTPRoute", ref) {
|
||||
return gatewayFailure("spec.rules.backendRefs", fmt.Sprintf("HTTPRoute %s/%s references %s %s/%s without a matching ReferenceGrant", sourceNamespace, test.Resource.ObjectMeta.GetName(), ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func httpRouteBackendTLSMissing(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
routeNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, ref := range httpRouteBackendRefs(test.Resource.Resource.Object) {
|
||||
ref = withDefaults(ref, "", "Service", routeNamespace)
|
||||
if !backendUsesTLS(test.ResourceProvider, ref) {
|
||||
continue
|
||||
}
|
||||
if hasBackendTLSPolicy(test.ResourceProvider, ref) || hasKgatewayBackendTLSPolicy(test.ResourceProvider, ref) {
|
||||
continue
|
||||
}
|
||||
return gatewayFailure("spec.rules.backendRefs", fmt.Sprintf("HTTPRoute backend %s %s/%s appears to use TLS but has no BackendTLSPolicy or kgateway BackendConfigPolicy", ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func isFullHTTPSRedirect(object map[string]any) bool {
|
||||
rules := nestedSlice(object, "spec", "rules")
|
||||
if len(rules) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, rawRule := range rules {
|
||||
rule, ok := rawRule.(map[string]any)
|
||||
if !ok || len(referencesAt(rule, "backendRefs")) > 0 || !ruleMatchesAllTraffic(rule) || !hasHTTPSRedirect(rule) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ruleMatchesAllTraffic(rule map[string]any) bool {
|
||||
matches := nestedSlice(rule, "matches")
|
||||
if len(matches) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, rawMatch := range matches {
|
||||
match, ok := rawMatch.(map[string]any)
|
||||
if !ok || len(match) != 1 {
|
||||
continue
|
||||
}
|
||||
path, ok := match["path"].(map[string]any)
|
||||
if ok && (stringValue(path["type"]) == "" || stringValue(path["type"]) == "PathPrefix") && stringValue(path["value"]) == "/" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasHTTPSRedirect(rule map[string]any) bool {
|
||||
filters := nestedSlice(rule, "filters")
|
||||
for _, rawFilter := range filters {
|
||||
filter, ok := rawFilter.(map[string]any)
|
||||
if !ok || stringValue(filter["type"]) != "RequestRedirect" {
|
||||
continue
|
||||
}
|
||||
redirect, ok := filter["requestRedirect"].(map[string]any)
|
||||
if ok && strings.EqualFold(stringValue(redirect["scheme"]), "https") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listenerAcceptsHTTPRoute(listener map[string]any, gatewayNamespace string, route kube.GenericResource, provider *kube.ResourceProvider) bool {
|
||||
if !listenerHostnameIntersectsRoute(listener, route.Resource.Object) {
|
||||
return false
|
||||
}
|
||||
|
||||
allowedRoutes, ok := listener["allowedRoutes"].(map[string]any)
|
||||
if !ok {
|
||||
return route.ObjectMeta.GetNamespace() == gatewayNamespace
|
||||
}
|
||||
if kinds := nestedSlice(allowedRoutes, "kinds"); len(kinds) > 0 {
|
||||
allowsHTTPRoute := false
|
||||
for _, rawKind := range kinds {
|
||||
kind, ok := rawKind.(map[string]any)
|
||||
if ok && withDefaultString(stringValue(kind["group"]), gatewayAPIGroup) == gatewayAPIGroup && stringValue(kind["kind"]) == "HTTPRoute" {
|
||||
allowsHTTPRoute = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowsHTTPRoute {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
namespaces, ok := allowedRoutes["namespaces"].(map[string]any)
|
||||
if !ok || stringValue(namespaces["from"]) == "" || stringValue(namespaces["from"]) == "Same" {
|
||||
return route.ObjectMeta.GetNamespace() == gatewayNamespace
|
||||
}
|
||||
if stringValue(namespaces["from"]) == "All" {
|
||||
return true
|
||||
}
|
||||
if stringValue(namespaces["from"]) != "Selector" {
|
||||
return false
|
||||
}
|
||||
selectorMap, ok := namespaces["selector"].(map[string]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
selector := &metav1.LabelSelector{}
|
||||
selectorJSON, err := json.Marshal(selectorMap)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err := json.Unmarshal(selectorJSON, selector); err != nil {
|
||||
return false
|
||||
}
|
||||
compiled, err := metav1.LabelSelectorAsSelector(selector)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, namespace := range provider.Namespaces {
|
||||
if namespace.Name == route.ObjectMeta.GetNamespace() {
|
||||
return compiled.Matches(labels.Set(namespace.Labels))
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listenerHostnameIntersectsRoute(listener, route map[string]any) bool {
|
||||
listenerHostname := stringValue(listener["hostname"])
|
||||
routeHostnames := nestedSlice(route, "spec", "hostnames")
|
||||
if len(routeHostnames) == 0 || listenerHostname == "" {
|
||||
return true
|
||||
}
|
||||
for _, routeHostname := range routeHostnames {
|
||||
if hostnamesIntersect(listenerHostname, stringValue(routeHostname)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hostnamesIntersect(left, right string) bool {
|
||||
if left == "" || right == "" || left == "*" || right == "*" || strings.EqualFold(left, right) {
|
||||
return true
|
||||
}
|
||||
leftSuffix, leftWildcard := strings.CutPrefix(strings.ToLower(left), "*.")
|
||||
rightSuffix, rightWildcard := strings.CutPrefix(strings.ToLower(right), "*.")
|
||||
switch {
|
||||
case leftWildcard && rightWildcard:
|
||||
return leftSuffix == rightSuffix || strings.HasSuffix(leftSuffix, "."+rightSuffix) || strings.HasSuffix(rightSuffix, "."+leftSuffix)
|
||||
case leftWildcard:
|
||||
return strings.HasSuffix(strings.ToLower(right), "."+leftSuffix)
|
||||
case rightWildcard:
|
||||
return strings.HasSuffix(strings.ToLower(left), "."+rightSuffix)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func httpRouteBackendRefs(object map[string]any) []objectReference {
|
||||
var refs []objectReference
|
||||
rules := nestedSlice(object, "spec", "rules")
|
||||
for _, rawRule := range rules {
|
||||
rule, ok := rawRule.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, referencesAt(rule, "backendRefs")...)
|
||||
filters := nestedSlice(rule, "filters")
|
||||
for _, rawFilter := range filters {
|
||||
filter, ok := rawFilter.(map[string]any)
|
||||
if !ok || stringValue(filter["type"]) != "RequestMirror" {
|
||||
continue
|
||||
}
|
||||
mirror, ok := filter["requestMirror"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if backend, ok := mirror["backendRef"].(map[string]any); ok {
|
||||
refs = append(refs, referenceFromMap(backend))
|
||||
}
|
||||
}
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
func referencesAt(object map[string]any, fields ...string) []objectReference {
|
||||
items := nestedSlice(object, fields...)
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
refs := make([]objectReference, 0, len(items))
|
||||
for _, item := range items {
|
||||
if ref, ok := item.(map[string]any); ok {
|
||||
refs = append(refs, referenceFromMap(ref))
|
||||
}
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
func referenceFromMap(ref map[string]any) objectReference {
|
||||
return objectReference{
|
||||
group: stringValue(ref["group"]),
|
||||
kind: stringValue(ref["kind"]),
|
||||
name: stringValue(ref["name"]),
|
||||
namespace: stringValue(ref["namespace"]),
|
||||
sectionName: stringValue(ref["sectionName"]),
|
||||
port: int64Value(ref["port"]),
|
||||
}
|
||||
}
|
||||
|
||||
func withDefaults(ref objectReference, group, kind, namespace string) objectReference {
|
||||
if ref.group == "" {
|
||||
ref.group = group
|
||||
}
|
||||
if ref.kind == "" {
|
||||
ref.kind = kind
|
||||
}
|
||||
if ref.namespace == "" {
|
||||
ref.namespace = namespace
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func withDefaultString(value, defaultValue string) string {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func hasReferenceGrant(provider *kube.ResourceProvider, sourceNamespace, sourceKind string, target objectReference) bool {
|
||||
for _, grant := range provider.Resources[gatewayAPIGroup+"/ReferenceGrant"] {
|
||||
if grant.ObjectMeta.GetNamespace() != target.namespace {
|
||||
continue
|
||||
}
|
||||
fromMatches := false
|
||||
for _, from := range referencesAt(grant.Resource.Object, "spec", "from") {
|
||||
if from.group == gatewayAPIGroup && from.kind == sourceKind && from.namespace == sourceNamespace {
|
||||
fromMatches = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !fromMatches {
|
||||
continue
|
||||
}
|
||||
for _, to := range referencesAt(grant.Resource.Object, "spec", "to") {
|
||||
if to.group == target.group && to.kind == target.kind && (to.name == "" || to.name == target.name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func backendUsesTLS(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
// ponytail: infer TLS from conventional ports and backend metadata; replace
|
||||
// this with controller status or an implementation graph when Polaris has one.
|
||||
if ref.port == 443 || ref.port == 8443 {
|
||||
return true
|
||||
}
|
||||
groupKind := ref.kind
|
||||
if ref.group != "" {
|
||||
groupKind = ref.group + "/" + ref.kind
|
||||
}
|
||||
backend := findResource(provider.Resources[groupKind], ref.namespace, ref.name)
|
||||
if backend == nil {
|
||||
return false
|
||||
}
|
||||
if ref.group == "" && ref.kind == "Service" {
|
||||
ports := nestedSlice(backend.Resource.Object, "spec", "ports")
|
||||
for _, rawPort := range ports {
|
||||
port, ok := rawPort.(map[string]any)
|
||||
if !ok || (ref.port != 0 && int64Value(port["port"]) != ref.port) {
|
||||
continue
|
||||
}
|
||||
name := strings.ToLower(stringValue(port["name"]))
|
||||
appProtocol := strings.ToLower(stringValue(port["appProtocol"]))
|
||||
if name == "https" || strings.HasPrefix(name, "https-") || appProtocol == "https" || strings.HasSuffix(appProtocol, "/https") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ref.group == kgatewayAPIGroup && ref.kind == "Backend" {
|
||||
hosts := nestedSlice(backend.Resource.Object, "spec", "static", "hosts")
|
||||
for _, rawHost := range hosts {
|
||||
host, ok := rawHost.(map[string]any)
|
||||
if ok && (int64Value(host["port"]) == 443 || int64Value(host["port"]) == 8443) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasBackendTLSPolicy(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
if ref.group != "" || ref.kind != "Service" {
|
||||
return false
|
||||
}
|
||||
for _, policy := range provider.Resources[gatewayAPIGroup+"/BackendTLSPolicy"] {
|
||||
if policy.ObjectMeta.GetNamespace() == ref.namespace && policyTargets(policy, ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasKgatewayBackendTLSPolicy(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
for _, policy := range provider.Resources[kgatewayAPIGroup+"/BackendConfigPolicy"] {
|
||||
if policy.ObjectMeta.GetNamespace() != ref.namespace {
|
||||
continue
|
||||
}
|
||||
if _, found := nestedValue(policy.Resource.Object, "spec", "tls"); found && policyTargets(policy, ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func policyTargets(policy kube.GenericResource, target objectReference) bool {
|
||||
for _, ref := range referencesAt(policy.Resource.Object, "spec", "targetRefs") {
|
||||
ref = withDefaults(ref, "", "Service", policy.ObjectMeta.GetNamespace())
|
||||
if ref.group == target.group && ref.kind == target.kind && ref.name == target.name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func findResource(resources []kube.GenericResource, namespace, name string) *kube.GenericResource {
|
||||
for i := range resources {
|
||||
if resources[i].ObjectMeta.GetNamespace() == namespace && resources[i].ObjectMeta.GetName() == name {
|
||||
return &resources[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringValue(value any) string {
|
||||
valueString, _ := value.(string)
|
||||
return valueString
|
||||
}
|
||||
|
||||
func int64Value(value any) int64 {
|
||||
switch number := value.(type) {
|
||||
case int:
|
||||
return int64(number)
|
||||
case int32:
|
||||
return int64(number)
|
||||
case int64:
|
||||
return number
|
||||
case float64:
|
||||
return int64(number)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func nestedSlice(object map[string]any, fields ...string) []any {
|
||||
value, found := nestedValue(object, fields...)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
items, _ := value.([]any)
|
||||
return items
|
||||
}
|
||||
|
||||
func nestedValue(object map[string]any, fields ...string) (any, bool) {
|
||||
var current any = object
|
||||
for _, field := range fields {
|
||||
currentMap, ok := current.(map[string]any)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
current, ok = currentMap[field]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return current, true
|
||||
}
|
||||
|
||||
func gatewayFailure(path, message string) (bool, []jsonschema.KeyError, error) {
|
||||
return false, []jsonschema.KeyError{{
|
||||
PropertyPath: path,
|
||||
Message: message,
|
||||
}}, nil
|
||||
}
|
||||
+15
-13
@@ -250,15 +250,16 @@ func fillString(id string, l int) string {
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
func (res AuditData) GetPrettyOutput(useColor bool) string {
|
||||
color.NoColor = !useColor
|
||||
str := titleColor.Sprint(fmt.Sprintf("Polaris audited %s %s at %s\n", res.SourceType, res.SourceName, res.AuditTime))
|
||||
str += color.CyanString(fmt.Sprintf(" Nodes: %d | Namespaces: %d | Controllers: %d\n", res.ClusterInfo.Nodes, res.ClusterInfo.Namespaces, res.ClusterInfo.Controllers))
|
||||
str += color.GreenString(fmt.Sprintf(" Final score: %d\n", res.Score))
|
||||
str += "\n"
|
||||
var str strings.Builder
|
||||
str.WriteString(titleColor.Sprint(fmt.Sprintf("Polaris audited %s %s at %s\n", res.SourceType, res.SourceName, res.AuditTime)))
|
||||
str.WriteString(color.CyanString(fmt.Sprintf(" Nodes: %d | Namespaces: %d | Controllers: %d\n", res.ClusterInfo.Nodes, res.ClusterInfo.Namespaces, res.ClusterInfo.Controllers)))
|
||||
str.WriteString(color.GreenString(fmt.Sprintf(" Final score: %d\n", res.Score)))
|
||||
str.WriteString("\n")
|
||||
for _, result := range res.Results {
|
||||
str += result.GetPrettyOutput() + "\n"
|
||||
str.WriteString(result.GetPrettyOutput() + "\n")
|
||||
}
|
||||
color.NoColor = false
|
||||
return str
|
||||
return str.String()
|
||||
}
|
||||
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
@@ -277,11 +278,12 @@ func (res Result) GetPrettyOutput() string {
|
||||
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
func (res PodResult) GetPrettyOutput() string {
|
||||
str := res.Results.GetPrettyOutput()
|
||||
var str strings.Builder
|
||||
str.WriteString(res.Results.GetPrettyOutput())
|
||||
for _, cont := range res.ContainerResults {
|
||||
str += cont.GetPrettyOutput()
|
||||
str.WriteString(cont.GetPrettyOutput())
|
||||
}
|
||||
return str
|
||||
return str.String()
|
||||
}
|
||||
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
@@ -296,7 +298,7 @@ const minIDLength = 40
|
||||
// GetPrettyOutput returns a human-readable string
|
||||
func (res ResultSet) GetPrettyOutput() string {
|
||||
indent := " "
|
||||
str := ""
|
||||
var str strings.Builder
|
||||
for _, msg := range res {
|
||||
status := color.GreenString(successMessage)
|
||||
if !msg.Success {
|
||||
@@ -309,8 +311,8 @@ func (res ResultSet) GetPrettyOutput() string {
|
||||
if color.NoColor {
|
||||
status = strings.Fields(status)[1] // remove emoji
|
||||
}
|
||||
str += fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status)
|
||||
str += fmt.Sprintf("%s %s - %s\n", indent, msg.Category, msg.Message)
|
||||
str.WriteString(indent + checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))) + " " + status + "\n")
|
||||
str.WriteString(indent + " " + msg.Category + " - " + msg.Message + "\n")
|
||||
}
|
||||
return str
|
||||
return str.String()
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
func TestGetTemplateInputReturnsPolarisSubKeys(t *testing.T) {
|
||||
pod := test.MockPod() // Includes a container, required by GetPodSpec
|
||||
pod.Spec.NodeName = "testNodeName"
|
||||
pod.ObjectMeta.Name = "testpod"
|
||||
pod.Name = "testpod"
|
||||
genRes, err := kube.NewGenericResourceFromPod(pod, pod)
|
||||
require.NoError(t, err, "creating new generic resource from a pod")
|
||||
schemaTest := schemaTestCase{
|
||||
|
||||
+20
-28
@@ -48,15 +48,15 @@ func (s schemaTestCase) ShortString() string {
|
||||
var msg strings.Builder
|
||||
targetStr := s.Target
|
||||
if targetStr != "" {
|
||||
msg.WriteString(fmt.Sprintf("target=%s, ", targetStr))
|
||||
msg.WriteString("target=" + string(targetStr) + ", ")
|
||||
}
|
||||
ns := s.Resource.ObjectMeta.GetNamespace()
|
||||
if ns != "" {
|
||||
msg.WriteString(fmt.Sprintf("namespace=%s, ", ns))
|
||||
msg.WriteString("namespace=" + ns + ", ")
|
||||
}
|
||||
msg.WriteString(fmt.Sprintf("resource=%s/%s", s.Resource.Kind, s.Resource.ObjectMeta.GetName()))
|
||||
msg.WriteString("resource=" + s.Resource.Kind + "/" + s.Resource.ObjectMeta.GetName())
|
||||
if s.Target == config.TargetContainer {
|
||||
msg.WriteString(fmt.Sprintf(", container=%s", s.Container.Name))
|
||||
msg.WriteString(", container=" + s.Container.Name)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
|
||||
check, ok = config.BuiltInChecks[checkID]
|
||||
}
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Check %s not found", checkID)
|
||||
return nil, fmt.Errorf("check %s not found", checkID)
|
||||
}
|
||||
|
||||
containerName := ""
|
||||
@@ -99,7 +99,7 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
|
||||
// getTemplateInput augments a schemaTestCase.Resource.Resource.Object with
|
||||
// Polaris built-in variables. The result can be used as input for
|
||||
// CheckSchema.TemplateForResource().
|
||||
func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
|
||||
func getTemplateInput(test schemaTestCase) (map[string]any, error) {
|
||||
templateInput := test.Resource.Resource.Object
|
||||
if templateInput == nil {
|
||||
return nil, nil
|
||||
@@ -113,7 +113,7 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podTemplateMap, ok := test.Resource.PodTemplate.(map[string]interface{})
|
||||
podTemplateMap, ok := test.Resource.PodTemplate.(map[string]any)
|
||||
if ok {
|
||||
err := unstructured.SetNestedMap(templateInput, podTemplateMap, "Polaris", "PodTemplate")
|
||||
if err != nil {
|
||||
@@ -136,17 +136,12 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.KeyError) ResultMessage {
|
||||
details := []string{}
|
||||
for _, issue := range issues {
|
||||
details = append(details, issue.Message)
|
||||
}
|
||||
result := ResultMessage{
|
||||
ID: check.ID,
|
||||
Severity: conf.Checks[check.ID],
|
||||
Category: check.Category,
|
||||
Success: passes,
|
||||
// FIXME: need to fix the tests before adding this back
|
||||
//Details: details,
|
||||
// FIXME: need to fix the tests before adding Details from issues
|
||||
}
|
||||
if passes {
|
||||
result.Message = check.SuccessMessage
|
||||
@@ -167,17 +162,14 @@ func hasExemptionAnnotation(objMeta metaV1.Object, checkID string) bool {
|
||||
}
|
||||
checkKey := fmt.Sprintf(exemptionAnnotationPattern, checkID)
|
||||
val = annot[checkKey]
|
||||
if strings.ToLower(val) == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return strings.ToLower(val) == "true"
|
||||
}
|
||||
|
||||
// ApplyAllSchemaChecksToResourceProvider applies all available checks to a ResourceProvider
|
||||
func ApplyAllSchemaChecksToResourceProvider(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) {
|
||||
results := []Result{}
|
||||
if resourceProvider == nil {
|
||||
return nil, errors.New("No resource provider set, cannot apply schema checks")
|
||||
return nil, errors.New("no resource provider set, cannot apply schema checks")
|
||||
}
|
||||
for _, resources := range resourceProvider.Resources {
|
||||
kindResults, err := ApplyAllSchemaChecksToAllResources(ctx, conf, resourceProvider, resources)
|
||||
@@ -241,7 +233,7 @@ func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration
|
||||
}
|
||||
for key, val := range nonControllerResults {
|
||||
if _, ok := finalResult.Results[key]; ok {
|
||||
return finalResult, errors.New("Duplicate finding for check " + key)
|
||||
return finalResult, errors.New("duplicate finding for check " + key)
|
||||
}
|
||||
finalResult.Results[key] = val
|
||||
}
|
||||
@@ -351,7 +343,7 @@ func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID s
|
||||
podCopy := *test.Resource.PodSpec
|
||||
podCopy.InitContainers = []corev1.Container{}
|
||||
podCopy.Containers = []corev1.Container{*test.Container}
|
||||
containerIndex := -1
|
||||
var containerIndex int
|
||||
if !test.IsInitContainer {
|
||||
containerIndex = funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
|
||||
return value.Name == test.Container.Name
|
||||
@@ -371,7 +363,7 @@ func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID s
|
||||
}
|
||||
passes, issues, err = check.CheckPodSpec(ctx, &podCopy)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget)
|
||||
return nil, fmt.Errorf("unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget)
|
||||
}
|
||||
} else if check.Target == config.TargetPodSpec {
|
||||
passes, issues, err = check.CheckPodSpec(ctx, test.Resource.PodSpec)
|
||||
@@ -380,7 +372,7 @@ func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID s
|
||||
passes, issues, err = check.CheckPodTemplate(ctx, test.Resource.PodTemplate)
|
||||
prefix = getJSONSchemaPrefix(test.Resource.Kind)
|
||||
} else if check.Target == config.TargetContainer {
|
||||
containerIndex := -1
|
||||
var containerIndex int
|
||||
if !test.IsInitContainer {
|
||||
containerIndex = funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
|
||||
return value.Name == test.Container.Name
|
||||
@@ -425,9 +417,9 @@ func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID s
|
||||
resources = funk.Filter(resources, func(res kube.GenericResource) bool {
|
||||
return res.ObjectMeta.GetNamespace() == "" || res.ObjectMeta.GetNamespace() == namespace
|
||||
}).([]kube.GenericResource)
|
||||
objects := funk.Map(resources, func(res kube.GenericResource) interface{} {
|
||||
objects := funk.Map(resources, func(res kube.GenericResource) any {
|
||||
return res.Resource.Object
|
||||
}).([]interface{})
|
||||
}).([]any)
|
||||
passes, err = check.CheckAdditionalObjects(ctx, groupkind, objects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -475,12 +467,12 @@ func deepCopyMutation(source config.Mutation) config.Mutation {
|
||||
}
|
||||
|
||||
func getJSONSchemaPrefix(kind string) (prefix string) {
|
||||
if kind == "CronJob" {
|
||||
switch kind {
|
||||
case "CronJob":
|
||||
prefix = "/spec/jobTemplate/spec/template/spec"
|
||||
} else if kind == "Pod" {
|
||||
case "Pod":
|
||||
prefix = "/spec"
|
||||
} else if (kind == "Deployment") || (kind == "DaemonSet") ||
|
||||
(kind == "StatefulSet") || (kind == "Job") || (kind == "ReplicationController") {
|
||||
case "Deployment", "DaemonSet", "StatefulSet", "Job", "ReplicationController":
|
||||
prefix = "/spec/template/spec"
|
||||
}
|
||||
return prefix
|
||||
|
||||
@@ -50,7 +50,7 @@ func (cs *CountSummary) AddSummary(other CountSummary) {
|
||||
|
||||
// AddResult adds a single result to the summary
|
||||
func (cs *CountSummary) AddResult(result ResultMessage) {
|
||||
if result.Success == false {
|
||||
if !result.Success {
|
||||
if result.Severity == config.SeverityWarning {
|
||||
cs.Warnings++
|
||||
} else {
|
||||
@@ -198,7 +198,7 @@ func (rs ResultSet) GetSuccesses() []ResultMessage {
|
||||
func (rs ResultSet) GetWarnings() []ResultMessage {
|
||||
warnings := []ResultMessage{}
|
||||
for _, msg := range rs {
|
||||
if msg.Success == false && msg.Severity == config.SeverityWarning {
|
||||
if !msg.Success && msg.Severity == config.SeverityWarning {
|
||||
warnings = append(warnings, msg)
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (rs ResultSet) GetWarnings() []ResultMessage {
|
||||
func (rs ResultSet) GetDangers() []ResultMessage {
|
||||
errors := []ResultMessage{}
|
||||
for _, msg := range rs {
|
||||
if msg.Success == false && msg.Severity == config.SeverityDanger {
|
||||
if !msg.Success && msg.Severity == config.SeverityDanger {
|
||||
errors = append(errors, msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewMutateWebhook(ctx context.Context, mgr manager.Manager, c config.Configu
|
||||
}
|
||||
|
||||
func (m *Mutator) mutate(ctx context.Context, req admission.Request) ([]jsonpatch.Operation, error) {
|
||||
results, kubeResources, err := GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config)
|
||||
results, kubeResources, err := GetValidatedResults(ctx, req.Kind.Kind, m.decoder, req, m.Config)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while validating resource: %v", err)
|
||||
return nil, err
|
||||
|
||||
+17
-27
@@ -17,8 +17,8 @@ package webhook
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/controller-utils/pkg/controller"
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
@@ -53,7 +53,7 @@ func NewValidateWebhook(mgr manager.Manager, c config.Configuration) {
|
||||
}
|
||||
|
||||
func (v *Validator) handleInternal(ctx context.Context, req admission.Request) (*validator.Result, kube.GenericResource, error) {
|
||||
return GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, v.decoder, req, v.Config)
|
||||
return GetValidatedResults(ctx, req.Kind.Kind, v.decoder, req, v.Config)
|
||||
}
|
||||
|
||||
// GetValidatedResults returns the validated results.
|
||||
@@ -71,31 +71,20 @@ func GetValidatedResults(ctx context.Context, kind string, decoder *admission.De
|
||||
return nil, resource, err
|
||||
}
|
||||
if ownerReferences, ok := decoded["metadata"].(map[string]any)["ownerReferences"].([]any); ok && len(ownerReferences) > 0 {
|
||||
allOwnersReferenceValid := true
|
||||
dynamicClient, restMapper, _, _, err := kube.GetKubeClient(context.Background(), "")
|
||||
if err != nil {
|
||||
logrus.Errorf("getting the kubernetes client: %v", err)
|
||||
return nil, resource, err
|
||||
}
|
||||
for _, ownerReference := range ownerReferences {
|
||||
ownerReference := ownerReference.(map[string]any)
|
||||
ctrl, err := kube.GetObject(context.Background(), req.Namespace, ownerReference["kind"].(string), ownerReference["apiVersion"].(string), ownerReference["name"].(string), dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
logrus.Infof("error retrieving owner for object %s - running checks: %v", req.Name, err)
|
||||
allOwnersReferenceValid = false
|
||||
break
|
||||
} else {
|
||||
err = controller.ValidateIfControllerMatches(decoded, ctrl.Object)
|
||||
if err != nil {
|
||||
logrus.Infof("object %s has an owner but the owner is invalid - running checks: %v", req.Name, err)
|
||||
allOwnersReferenceValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allOwnersReferenceValid {
|
||||
logrus.Infof("object %s has owner(s) and the owner(s) are valid - skipping", req.Name)
|
||||
return nil, resource, nil
|
||||
}
|
||||
ownerReference := ownerReferences[0].(map[string]any)
|
||||
ctrl, err := kube.GetObject(context.Background(), req.Namespace, ownerReference["kind"].(string), ownerReference["apiVersion"].(string), ownerReference["name"].(string), dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
logrus.Infof("error retrieving owner for object %s - running checks: %v", req.Name, err)
|
||||
} else if err := controller.ValidateIfControllerMatches(decoded, ctrl.Object); err != nil {
|
||||
logrus.Infof("object %s has an owner but the owner is invalid - running checks: %v", req.Name, err)
|
||||
} else {
|
||||
logrus.Infof("object %s has owner(s) and the owner(s) are valid - skipping", req.Name)
|
||||
return nil, resource, nil
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("Object %s has no owner - running checks", req.Name)
|
||||
@@ -135,11 +124,12 @@ func (v *Validator) Handle(ctx context.Context, req admission.Request) admission
|
||||
}
|
||||
|
||||
func getFailureReason(result validator.Result) string {
|
||||
reason := "\nPolaris prevented this deployment due to configuration problems:\n"
|
||||
var reason strings.Builder
|
||||
reason.WriteString("\nPolaris prevented this deployment due to configuration problems:\n")
|
||||
|
||||
for _, message := range result.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- %s: %s\n", result.Kind, message.Message)
|
||||
reason.WriteString("- " + result.Kind + ": " + message.Message + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,18 +137,18 @@ func getFailureReason(result validator.Result) string {
|
||||
if podResult != nil {
|
||||
for _, message := range podResult.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- Pod: %s\n", message.Message)
|
||||
reason.WriteString("- Pod: " + message.Message + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
for _, containerResult := range podResult.ContainerResults {
|
||||
for _, message := range containerResult.Results {
|
||||
if !message.Success && message.Severity == config.SeverityDanger {
|
||||
reason += fmt.Sprintf("- Container %s: %s\n", containerResult.Name, message.Message)
|
||||
reason.WriteString("- Container " + containerResult.Name + ": " + message.Message + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reason
|
||||
return reason.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// DO NOT EDIT - Managed by Terraform
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
":dependencyDashboard"
|
||||
],
|
||||
"labels": ["dependencies"],
|
||||
"prConcurrentLimit": 10,
|
||||
"branchConcurrentLimit": 10,
|
||||
"minimumReleaseAge": "7 days",
|
||||
"enabledManagers": [
|
||||
"gomod",
|
||||
"dockerfile",
|
||||
"circleci",
|
||||
"custom.regex"
|
||||
],
|
||||
"postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths"],
|
||||
"customManagers": [
|
||||
{
|
||||
"description": "HashiCorp Vault download URL in CircleCI",
|
||||
"customType": "regex",
|
||||
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
|
||||
"matchStrings": [
|
||||
"https://releases\\.hashicorp\\.com/vault/(?<currentValue>[\\d.]+)/vault_[\\d.]+_linux_amd64\\.zip"
|
||||
],
|
||||
"depNameTemplate": "hashicorp/vault",
|
||||
"datasourceTemplate": "hashicorp-releases",
|
||||
"autoReplaceStringTemplate": "https://releases.hashicorp.com/vault/{{{newValue}}}/vault_{{{newValue}}}_linux_amd64.zip"
|
||||
},
|
||||
{
|
||||
"description": "HashiCorp Vault archive name in CircleCI (sha256 line must be updated manually)",
|
||||
"customType": "regex",
|
||||
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
|
||||
"matchStrings": [
|
||||
"vault_(?<currentValue>[\\d.]+)_linux_amd64\\.zip"
|
||||
],
|
||||
"depNameTemplate": "hashicorp/vault",
|
||||
"datasourceTemplate": "hashicorp-releases",
|
||||
"autoReplaceStringTemplate": "vault_{{{newValue}}}_linux_amd64.zip"
|
||||
},
|
||||
{
|
||||
"description": "golangci-lint install.sh pin in CircleCI",
|
||||
"customType": "regex",
|
||||
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
|
||||
"matchStrings": [
|
||||
"golangci/golangci-lint/HEAD/install.sh \\| sh -s -- -b /usr/local/bin v(?<currentValue>[0-9.]+)"
|
||||
],
|
||||
"depNameTemplate": "golangci/golangci-lint",
|
||||
"datasourceTemplate": "github-releases",
|
||||
"extractVersionTemplate": "^v?(?<version>.*)$",
|
||||
"autoReplaceStringTemplate": "golangci/golangci-lint/HEAD/install.sh | sh -s -- -b /usr/local/bin v{{{newValue}}}"
|
||||
},
|
||||
{
|
||||
"description": "Kind node image for rok8s-scripts kubernetes_e2e_tests",
|
||||
"customType": "regex",
|
||||
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
|
||||
"matchStrings": [
|
||||
"kind_node_image:\\s*\"kindest/node:(?<currentValue>v[^\"\\s]+)\""
|
||||
],
|
||||
"depNameTemplate": "kindest/node",
|
||||
"datasourceTemplate": "docker",
|
||||
"autoReplaceStringTemplate": "kind_node_image: \"kindest/node:{{{newValue}}}\""
|
||||
}
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Group all non-major (minor + patch) updates into a single PR",
|
||||
"matchUpdateTypes": ["minor", "patch", "digest", "lockFileMaintenance"],
|
||||
"groupName": "all non-major dependencies",
|
||||
"groupSlug": "all-non-major",
|
||||
"minimumReleaseAge": "7 days"
|
||||
},
|
||||
{
|
||||
"description": "Keep major updates separate and also enforce minimum age",
|
||||
"matchUpdateTypes": ["major"],
|
||||
"minimumReleaseAge": "7 days"
|
||||
},
|
||||
{
|
||||
"description": "Disable replace updates",
|
||||
"matchManagers": ["gomod"],
|
||||
"matchDepTypes": ["replace"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "Every upgrade: upstream release must be at least 7 days old",
|
||||
"matchManagers": ["gomod", "dockerfile", "circleci", "custom.regex"],
|
||||
"minimumReleaseAge": "7 days"
|
||||
},
|
||||
{
|
||||
"description": "Group HashiCorp Vault version bumps (sha256 checksum still needs manual update)",
|
||||
"groupName": "HashiCorp Vault",
|
||||
"matchManagers": ["custom.regex"],
|
||||
"matchDepNames": ["hashicorp/vault"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# Wrap goreleaser by using envsubst on .goreleaser.yml,
|
||||
# and creating a temporary git tag.
|
||||
|
||||
function cleanup {
|
||||
if [ "${CIRCLE_TAG}" == "" ] ; then
|
||||
echo "${this_script} deleting git tag ${temporary_git_tag} for goreleaser"
|
||||
unset GORELEASER_CURRENT_TAG
|
||||
git tag -d ${temporary_git_tag}
|
||||
fi
|
||||
}
|
||||
|
||||
set -eE # errexit and errtrace
|
||||
trap 'cleanup' ERR
|
||||
this_script="$(basename $0)"
|
||||
hash envsubst
|
||||
hash goreleaser
|
||||
if [ "${TMPDIR}" == "" ] ; then
|
||||
export TMPDIR="/tmp"
|
||||
echo "${this_script} temporarily set the TMPDIR environment variable to ${TMPDIR}, used for a temporary GOBIN environment variable"
|
||||
fi
|
||||
|
||||
export skip_feature_docker_tags=false
|
||||
export skip_release=true
|
||||
if [ "${CIRCLE_TAG}" == "" ] ; then
|
||||
# Create a temporary tag for goreleaser, incrementing the last tag.
|
||||
last_git_tag="$(git describe --tags --abbrev=0 2>/dev/null)"
|
||||
if [ "${last_git_tag}" == "" ] ; then
|
||||
echo "${this_script} is unable to determine the last git tag so a temporary tag can be created, using: git describe --tags --abbrev=0"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(git config user.email)" == "" ] ; then
|
||||
# git will use this env var as its user.email.
|
||||
# git tag -m is used in case tags are manually pushed by accident,
|
||||
# however git tag -m requires an email.
|
||||
export EMAIL='goreleaser_ci@fairwinds.com'
|
||||
echo "${this_script} using ${EMAIL} temporarily as the git user.email"
|
||||
fi
|
||||
temporary_git_tag=$(echo "${last_git_tag}" | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')-rc
|
||||
echo "${this_script} creating temporary git tag ${temporary_git_tag} for goreleaser, the last real tag is ${last_git_tag}"
|
||||
# The -f is included to overwrite existing tags, perhaps from previous CI jobs.
|
||||
git tag -f -m "temporary local tag for goreleaser" ${temporary_git_tag}
|
||||
export GORELEASER_CURRENT_TAG=${temporary_git_tag}
|
||||
# Use an adjusted git feature branch name as a docker tag.
|
||||
export feature_docker_tag=$(echo "${CIRCLE_BRANCH:0:26}" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/-\+$//')
|
||||
echo "${this_script} also using docker tag ${feature_docker_tag} since ${CIRCLE_BRANCH} is a feature branch"
|
||||
else
|
||||
export GORELEASER_CURRENT_TAG=${CIRCLE_TAG}
|
||||
echo "${this_script} setting skip_release to false, and skip_feature_docker_tags to true, because CIRCLE_TAG is set"
|
||||
export skip_feature_docker_tags=true
|
||||
export skip_release=false
|
||||
fi
|
||||
|
||||
echo "${this_script} using git tag ${GORELEASER_CURRENT_TAG}"
|
||||
# Only substitute specific variables, as goreleaser uses shell variable syntax
|
||||
# for its `signs` section `signature` and `artifact` variables.
|
||||
cat .goreleaser.yml.envsubst |envsubst '${skip_release} ${skip_feature_docker_tags} ${feature_docker_tag}' >.goreleaser.yml
|
||||
goreleaser --skip=sign $@
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo "${this_script} removing the temporary .goreleaser.yml since goreleaser was successful"
|
||||
rm .goreleaser.yml # Keep git clean for additional goreleaser runs
|
||||
fi
|
||||
cleanup
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: unrestricted
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: restricted
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: Selector
|
||||
selector:
|
||||
matchLabels:
|
||||
gateway-access: "true"
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: wildcard
|
||||
namespace: certificates
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: wildcard
|
||||
namespace: certificates
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: ReferenceGrant
|
||||
metadata:
|
||||
name: allow-infra-gateway
|
||||
namespace: certificates
|
||||
spec:
|
||||
from:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
namespace: infra
|
||||
to:
|
||||
- group: ""
|
||||
kind: Secret
|
||||
name: wildcard
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: fail-open
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
tls:
|
||||
frontend:
|
||||
default:
|
||||
validation:
|
||||
mode: AllowInsecureFallback
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: fail-closed
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
tls:
|
||||
frontend:
|
||||
default:
|
||||
validation:
|
||||
mode: AllowValidOnly
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: missing-tls
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: insecure
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
mode: Terminate
|
||||
certificateRefs: []
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: secure
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
mode: Terminate
|
||||
certificateRefs:
|
||||
- name: example-tls
|
||||
- name: passthrough
|
||||
protocol: TLS
|
||||
port: 8443
|
||||
tls:
|
||||
mode: Passthrough
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: plaintext-to-tls-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 443
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-kgateway-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- group: gateway.kgateway.dev
|
||||
kind: Backend
|
||||
name: external-api
|
||||
---
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: Backend
|
||||
metadata:
|
||||
name: external-api
|
||||
namespace: app
|
||||
spec:
|
||||
type: Static
|
||||
static:
|
||||
hosts:
|
||||
- host: api.example.com
|
||||
port: 443
|
||||
---
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: external-api-tls
|
||||
namespace: app
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: gateway.kgateway.dev
|
||||
kind: Backend
|
||||
name: external-api
|
||||
tls:
|
||||
sni: api.example.com
|
||||
wellKnownCACertificates: System
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 443
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: BackendTLSPolicy
|
||||
metadata:
|
||||
name: api-tls
|
||||
namespace: app
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
validation:
|
||||
hostname: api.app.svc.cluster.local
|
||||
wellKnownCACertificates: System
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: shared-api
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
namespace: shared
|
||||
port: 8080
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: shared-api
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
namespace: shared
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: ReferenceGrant
|
||||
metadata:
|
||||
name: allow-app-route
|
||||
namespace: shared
|
||||
spec:
|
||||
from:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: HTTPRoute
|
||||
namespace: app
|
||||
to:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: plaintext
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
namespace: infra
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: app
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-host-only
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: app
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: app
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: unrelated-http
|
||||
hostname: other.example.com
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
- name: app-https
|
||||
hostname: app.example.com
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: redirect
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
namespace: infra
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
filters:
|
||||
- type: RequestRedirect
|
||||
requestRedirect:
|
||||
scheme: https
|
||||
statusCode: 301
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: every-host
|
||||
spec:
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: wildcard-host
|
||||
spec:
|
||||
hostnames:
|
||||
- "*.example.com"
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: explicit-host
|
||||
spec:
|
||||
hostnames:
|
||||
- api.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: unverified
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: verified
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
tls:
|
||||
sni: api.example.com
|
||||
wellKnownCACertificates: System
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
go run main.go dashboard --port 3000 --audit-path ./pkg/config/examples &
|
||||
sleep 30
|
||||
curl -f http://localhost:3000 > /dev/null
|
||||
curl -f http://localhost:3000/health > /dev/null
|
||||
curl -f http://localhost:3000/favicon.ico > /dev/null
|
||||
curl -f http://localhost:3000/static/css/main.css > /dev/null
|
||||
curl -f http://localhost:3000/results.json > /dev/null
|
||||
curl -f http://localhost:3000/details/security > /dev/null
|
||||
+12
-10
@@ -32,12 +32,12 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func newUnstructured(apiVersion, kind, namespace, name string, spec map[string]interface{}) unstructured.Unstructured {
|
||||
func newUnstructured(apiVersion, kind, namespace, name string, spec map[string]any) unstructured.Unstructured {
|
||||
return unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
Object: map[string]any{
|
||||
"apiVersion": apiVersion,
|
||||
"kind": kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"metadata": map[string]any{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
@@ -85,7 +85,7 @@ func MockIngress() networkingv1.Ingress {
|
||||
}
|
||||
|
||||
// MockController creates a mock controller and pod
|
||||
func MockController(apiVersion, kind, namespace, name string, spec map[string]interface{}, podSpec corev1.PodSpec, dest interface{}) corev1.Pod {
|
||||
func MockController(apiVersion, kind, namespace, name string, spec map[string]any, podSpec corev1.PodSpec, dest any) corev1.Pod {
|
||||
unst := newUnstructured(apiVersion, kind, namespace, name, spec)
|
||||
pod := corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -111,19 +111,19 @@ func MockController(apiVersion, kind, namespace, name string, spec map[string]in
|
||||
}
|
||||
|
||||
// MockControllerWithNormalSpec mocks a controller with podspec at spec.template.spec
|
||||
func MockControllerWithNormalSpec(apiVersion, kind, namespace, name string, dest interface{}) corev1.Pod {
|
||||
func MockControllerWithNormalSpec(apiVersion, kind, namespace, name string, dest any) corev1.Pod {
|
||||
p := MockPod()
|
||||
b, err := json.Marshal(p.Spec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pSpec := map[string]interface{}{}
|
||||
pSpec := map[string]any{}
|
||||
err = json.Unmarshal(b, &pSpec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
spec := map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
spec := map[string]any{
|
||||
"template": map[string]any{
|
||||
"spec": pSpec,
|
||||
},
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func MockJob(namespace, name string) (batchv1.Job, corev1.Pod) {
|
||||
func MockCronJob(namespace, name string) (batchv1.CronJob, corev1.Pod) {
|
||||
cj := batchv1.CronJob{}
|
||||
p := MockPod()
|
||||
spec := map[string]interface{}{}
|
||||
spec := map[string]any{}
|
||||
pod := MockController("batch/v1", "CronJob", namespace, name, spec, p.Spec, &cj)
|
||||
cj.Spec.JobTemplate.Spec.Template.Spec = pod.Spec
|
||||
|
||||
@@ -188,7 +188,9 @@ func MockNamespace(name string) corev1.Namespace {
|
||||
// SetupTestAPI creates a test kube API struct.
|
||||
func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Interface) {
|
||||
scheme := runtime.NewScheme()
|
||||
fake.AddToScheme(scheme)
|
||||
if err := fake.AddToScheme(scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dynamicClient := dynamicFake.NewSimpleDynamicClient(scheme, objects...)
|
||||
k := fake.NewSimpleClientset(objects...)
|
||||
k.Resources = []*metav1.APIResourceList{
|
||||
|
||||
@@ -22,7 +22,9 @@ function check_timeout() {
|
||||
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
helm install polaris fairwinds-stable/polaris --namespace polaris --create-namespace \
|
||||
--set image.tag=$CI_SHA1
|
||||
--set image.repository=us-docker.pkg.dev/fairwinds-ops/oss/polaris \
|
||||
--set image.tag=$CI_SHA1 \
|
||||
--set image.pullPolicy=IfNotPresent
|
||||
|
||||
check_dashboard_is_ready
|
||||
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ func initTestCases() ([]testCase, map[string]string, map[string][]testCase) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resourceFilename := strings.Replace(tc.Name(), "mutated", "failure", -1)
|
||||
resourceFilename := strings.ReplaceAll(tc.Name(), "mutated", "failure")
|
||||
|
||||
resources, err := kube.CreateResourceProviderFromPath(checkDir + "/" + resourceFilename)
|
||||
if err != nil {
|
||||
|
||||
@@ -88,7 +88,9 @@ helm install polaris fairwinds-stable/polaris --namespace polaris --create-names
|
||||
--set dashboard.enable=false \
|
||||
--set webhook.enable=true \
|
||||
--set webhook.mutate=true \
|
||||
--set image.tag=$CI_SHA1
|
||||
--set image.repository=us-docker.pkg.dev/fairwinds-ops/oss/polaris \
|
||||
--set image.tag=$CI_SHA1 \
|
||||
--set image.pullPolicy=IfNotPresent
|
||||
|
||||
echo "Waiting for the webhook to come online"
|
||||
check_webhook_is_ready
|
||||
|
||||
Reference in New Issue
Block a user