mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
517c8899d7 | ||
|
|
e2df222ded | ||
|
|
d45d67cdd2 | ||
|
|
e8a113388e | ||
|
|
21ef1f495c | ||
|
|
ec1ba2f2db | ||
|
|
39fb27c6dd | ||
|
|
e51b90ef79 | ||
|
|
4de082b443 | ||
|
|
264dc51f4c | ||
|
|
45b1f6a1a5 | ||
|
|
38e540e0cf | ||
|
|
2b17c31957 | ||
|
|
813d9c0a2a | ||
|
|
d7d30ac611 | ||
|
|
8fe7a94260 | ||
|
|
f29571e8bb | ||
|
|
a74714b5e5 | ||
|
|
ba04b7e330 | ||
|
|
d70591b1b8 | ||
|
|
80e6f7214e | ||
|
|
1d20a16cb3 | ||
|
|
37edbc7159 | ||
|
|
d451480acf | ||
|
|
705e6e4016 | ||
|
|
2d9aa54e68 | ||
|
|
4dd3a81bbd | ||
|
|
4b87bafbb9 | ||
|
|
62effa36b6 | ||
|
|
913988e44c | ||
|
|
f281cd1e33 | ||
|
|
073847559a | ||
|
|
9b5438d864 | ||
|
|
be349a885d | ||
|
|
08f2afe6c9 | ||
|
|
9f681b3ed4 | ||
|
|
74cbaefc58 | ||
|
|
bd8d4cc08b | ||
|
|
e66ef8b948 | ||
|
|
7930ede331 | ||
|
|
1625b755ac | ||
|
|
08a7863167 | ||
|
|
8d5247e501 | ||
|
|
d30865cefb | ||
|
|
cb7c10d288 | ||
|
|
bdfcb8fbb8 |
+132
-141
@@ -1,181 +1,172 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
rok8s: fairwinds/rok8s-scripts@12
|
||||
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: /.*/
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
ignore: /.*/
|
||||
|
||||
executors:
|
||||
golang-exec:
|
||||
docker:
|
||||
- image: cimg/go:1.26.6
|
||||
|
||||
jobs:
|
||||
test:
|
||||
docker:
|
||||
- image: cimg/go:1.19
|
||||
- image: cimg/go:1.26.6
|
||||
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: 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:v1.18.2
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 20.10.11
|
||||
- *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: |
|
||||
docker login -u _json_key -p "$(echo $GCP_ARTIFACTREADWRITE_JSON_KEY | base64 -d)" 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:
|
||||
ignore: /pull\/[0-9]+/
|
||||
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:
|
||||
ignore: /^testing-.*/
|
||||
- release:
|
||||
requires:
|
||||
- test
|
||||
context: org-global
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
- test_k8s:
|
||||
requires:
|
||||
- build_and_push
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
ignore: /.*/
|
||||
tags:
|
||||
ignore: /^testing-.*/
|
||||
- oss-docs/publish-docs:
|
||||
requires:
|
||||
- release
|
||||
repository: polaris
|
||||
filters:
|
||||
branches:
|
||||
|
||||
@@ -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.17
|
||||
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
-2
@@ -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
|
||||
@@ -30,3 +28,4 @@ dist
|
||||
|
||||
node_modules
|
||||
/dist
|
||||
docs/README.md
|
||||
|
||||
+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,112 +0,0 @@
|
||||
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}
|
||||
tap:
|
||||
owner: FairwindsOps
|
||||
name: homebrew-tap
|
||||
folder: Formula
|
||||
description: Open Source Best Practices for Kubernetes
|
||||
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"
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
* @sudermanjr
|
||||
* @sudermanjr @jdesouza @vitorvezani
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.19
|
||||
FROM alpine:3.24.1
|
||||
|
||||
LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.vendor="FairwindsOps, Inc." \
|
||||
@@ -10,7 +10,7 @@ LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.licenses="Apache License 2.0"
|
||||
|
||||
WORKDIR /usr/local/bin
|
||||
RUN apk -U upgrade
|
||||
# Install ca-certs
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
RUN addgroup -S polaris && adduser -u 1200 -S polaris -G polaris
|
||||
|
||||
@@ -29,19 +29,50 @@ 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
|
||||
|
||||
The goal of the Fairwinds Community is to exchange ideas, influence the open source roadmap,
|
||||
and network with fellow Kubernetes users.
|
||||
[Chat with us on Slack](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-e3c6vj4l-3lIH6dvKqzWII5fSSFDi1g)
|
||||
or
|
||||
[join the user group](https://www.fairwinds.com/open-source-software-user-group) to get involved!
|
||||
|
||||
<a href="https://insights.fairwinds.com/auth/register/">
|
||||
<img src="https://www.fairwinds.com/hubfs/Doc_Banners/Fairwinds_OSS_User_Group_740x125_v6.png"
|
||||
alt="Love Fairwinds Open Source? Automate Fairwinds Open Source for free with Fairwinds Insights. Click to learn more" />
|
||||
</a>
|
||||
[Chat with us on Slack](https://join.slack.com/t/fairwindscommunity/shared_invite/zt-2na8gtwb4-DGQ4qgmQbczQyB2NlFlYQQ)
|
||||
|
||||
## Other Projects from Fairwinds
|
||||
|
||||
@@ -56,9 +87,5 @@ Or [check out the full list](https://www.fairwinds.com/open-source-software?utm_
|
||||
If you're interested in running Polaris in multiple clusters,
|
||||
tracking the results over time, integrating with Slack, Datadog, and Jira,
|
||||
or unlocking other functionality, check out
|
||||
[Fairwinds Insights](https://fairwinds.com/pricing),
|
||||
[Fairwinds Insights](https://fairwinds.com/insights),
|
||||
a platform for auditing and enforcing policy in Kubernetes clusters.
|
||||
|
||||
<a href="https://fairwinds.com/pricing">
|
||||
<img src="https://www.fairwinds.com/hubfs/Doc_Banners/Fairwinds_Polaris_Ad.png" alt="Fairwinds Insights" />
|
||||
</a>
|
||||
|
||||
+3
-68
@@ -24,14 +24,8 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
workloads "github.com/fairwindsops/insights-plugins/plugins/workloads"
|
||||
workloadsPkg "github.com/fairwindsops/insights-plugins/plugins/workloads/pkg"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/auth"
|
||||
cfg "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/insights"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -55,9 +49,6 @@ var (
|
||||
auditNamespace string
|
||||
severityLevel string
|
||||
skipSslValidation bool
|
||||
uploadInsights bool
|
||||
clusterName string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -79,9 +70,6 @@ func init() {
|
||||
auditCmd.PersistentFlags().StringVar(&auditNamespace, "namespace", "", "Namespace to audit. Only applies to in-cluster audits")
|
||||
auditCmd.PersistentFlags().StringVar(&severityLevel, "severity", "", "Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)")
|
||||
auditCmd.PersistentFlags().BoolVar(&skipSslValidation, "skip-ssl-validation", false, "Skip https certificate verification")
|
||||
auditCmd.PersistentFlags().BoolVar(&uploadInsights, "upload-insights", false, "Upload scan results to Fairwinds Insights")
|
||||
auditCmd.PersistentFlags().StringVar(&clusterName, "cluster-name", "", "Set --cluster-name to a descriptive name for the cluster you're auditing")
|
||||
auditCmd.PersistentFlags().BoolVar(&quiet, "quiet", false, "Suppress the 'upload to Insights' prompt.")
|
||||
}
|
||||
|
||||
var auditCmd = &cobra.Command{
|
||||
@@ -120,23 +108,6 @@ var auditCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if uploadInsights && len(clusterName) == 0 {
|
||||
logrus.Error("cluster-name is required when using --upload-insights")
|
||||
os.Exit(1)
|
||||
}
|
||||
if uploadInsights {
|
||||
if auditPath != "" {
|
||||
logrus.Errorf("upload-insights and audit-path are not supported when used simultaneously")
|
||||
os.Exit(1)
|
||||
}
|
||||
if !auth.IsLoggedIn() {
|
||||
err := auth.HandleLogin(insightsHost)
|
||||
if err != nil {
|
||||
logrus.Errorf("error handling logging: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
k, err := kube.CreateResourceProvider(ctx, auditPath, resourceToAudit, config)
|
||||
@@ -145,49 +116,13 @@ var auditCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
auditData, err := validator.RunAudit(config, k)
|
||||
auditData, err := validator.RunAudit(context.Background(), config, k)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while running audit on resources: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if uploadInsights {
|
||||
auth, err := auth.GetAuth(insightsHost)
|
||||
if err != nil {
|
||||
logrus.Errorf("getting auth: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// fetch workloads using workload plugin... or should we adapt the workloads from above?
|
||||
dynamicClient, restMapper, clientSet, host, err := kube.GetKubeClient(ctx, "")
|
||||
if err != nil {
|
||||
logrus.Errorf("getting the kubernetes client: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
k8sResources, err := workloadsPkg.CreateResourceProviderFromAPI(ctx, dynamicClient, restMapper, clientSet, host)
|
||||
if err != nil {
|
||||
logrus.Errorf("creating resource provider: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
insightsClient := insights.NewHTTPClient(insightsHost, auth.Organization, auth.Token)
|
||||
insightsReporter := insights.NewInsightsReporter(insightsClient)
|
||||
wr := insights.WorkloadsReport{Version: workloads.Version, Payload: *k8sResources}
|
||||
pr := insights.PolarisReport{Version: version, Payload: auditData}
|
||||
logrus.Infof("Uploading to Fairwinds Insights organization '%s/%s'...", auth.Organization, clusterName)
|
||||
err = insightsReporter.ReportAuditToFairwindsInsights(clusterName, wr, pr)
|
||||
if err != nil {
|
||||
logrus.Errorf("reporting audit file to insights: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Stderr.WriteString("\n\nSuccess! You can see your results at:")
|
||||
os.Stderr.WriteString(fmt.Sprintf("\n\n%s/orgs/%s/clusters/%s/action-items\n\n", insightsHost, auth.Organization, clusterName))
|
||||
} else {
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests, severityLevel)
|
||||
if !quiet {
|
||||
os.Stderr.WriteString("\n\n🚀 Upload your Polaris findings to Fairwinds Insights to see remediation advice, add teammates, integrate with Slack or Jira, and more:")
|
||||
os.Stderr.WriteString("\n\n❯ polaris " + strings.Join(os.Args[1:], " ") + " --upload-insights --cluster-name=my-cluster\n\n")
|
||||
}
|
||||
}
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests, severityLevel)
|
||||
|
||||
summary := auditData.GetSummary()
|
||||
score := summary.GetScore()
|
||||
@@ -255,7 +190,7 @@ 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()))
|
||||
outputBytes = fmt.Appendf(nil, "%d\n", auditData.GetSummary().GetScore())
|
||||
} else if outputFormat == "yaml" {
|
||||
var jsonBytes []byte
|
||||
jsonBytes, err = json.Marshal(auditData)
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/auth"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(authCmd)
|
||||
authCmd.AddCommand(loginCmd)
|
||||
authCmd.AddCommand(logoutCmd)
|
||||
authCmd.AddCommand(statusCmd)
|
||||
authCmd.AddCommand(tokenCmd)
|
||||
}
|
||||
|
||||
var authCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Authenticate polaris with Fairwinds Insights",
|
||||
Long: `Authenticate polaris with Fairwinds Insights so better experience`,
|
||||
}
|
||||
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Authenticate polaris with Fairwinds Insights.",
|
||||
Long: `Authenticate polaris with Fairwinds Insights.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := auth.HandleLogin(insightsHost)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var logoutCmd = &cobra.Command{
|
||||
Use: "logout",
|
||||
Short: "Log out of a Fairwinds Insights.",
|
||||
Long: `Log out of a Fairwinds Insights.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := auth.HandleLogout()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "View authentication status.",
|
||||
Long: `View authentication status.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := auth.PrintStatus(insightsHost)
|
||||
if err != nil {
|
||||
logrus.Fatalf("printing status: %v", err)
|
||||
}
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var tokenCmd = &cobra.Command{
|
||||
Use: "token",
|
||||
Short: "Print the auth token gh is configured to use.",
|
||||
Long: `Print the auth token gh is configured to use.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := auth.PrintToken()
|
||||
if err != nil {
|
||||
logrus.Fatalf("printing token: %v", err)
|
||||
}
|
||||
},
|
||||
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -54,7 +55,7 @@ var dashboardCmd = &cobra.Command{
|
||||
auditData := validator.ReadAuditFromFile(loadAuditFile)
|
||||
auditDataPtr = &auditData
|
||||
}
|
||||
router, err := dashboard.GetRouter(config, auditPath, serverPort, basePath, auditDataPtr)
|
||||
router, err := dashboard.GetRouter(context.Background(), config, auditPath, serverPort, basePath, auditDataPtr)
|
||||
if err != nil {
|
||||
logrus.Fatalf("error creating router: %v", err)
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
@@ -43,7 +44,7 @@ var fixCommand = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logrus.Debug("Setting up controller manager")
|
||||
|
||||
err := fix.Execute(config, filesPath, isTemplate, checksToFix...)
|
||||
err := fix.Execute(context.Background(), config, filesPath, isTemplate, checksToFix...)
|
||||
if err != nil {
|
||||
if errors.Is(err, fix.ErrFilesPathRequired) {
|
||||
logrus.Error("Please specify a files-path flag")
|
||||
|
||||
+3
-1
@@ -24,6 +24,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
mergeConfig bool
|
||||
configPath string
|
||||
disallowExemptions bool
|
||||
disallowConfigExemptions bool
|
||||
@@ -42,6 +43,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// Flags
|
||||
rootCmd.PersistentFlags().BoolVarP(&mergeConfig, "merge-config", "m", false, "If true, custom configuration will be merged with default configuration instead of replacing it.")
|
||||
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Location of Polaris configuration file.")
|
||||
rootCmd.PersistentFlags().StringVarP(&kubeContext, "context", "x", "", "Set the kube context.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowExemptions, "disallow-exemptions", "", false, "Disallow any configured exemption.")
|
||||
@@ -65,7 +67,7 @@ var rootCmd = &cobra.Command{
|
||||
logrus.SetLevel(parsedLevel)
|
||||
}
|
||||
|
||||
config, err = conf.ParseFile(configPath)
|
||||
config, err = conf.MergeConfigAndParseFile(configPath, mergeConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error parsing config at %s: %v", configPath, err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -50,8 +51,6 @@ var webhookCmd = &cobra.Command{
|
||||
logrus.Debug("Setting up controller manager")
|
||||
|
||||
mgr, err := manager.New(k8sConfig.GetConfigOrDie(), manager.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
WebhookServer: webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
@@ -78,7 +77,7 @@ var webhookCmd = &cobra.Command{
|
||||
fwebhook.NewValidateWebhook(mgr, config)
|
||||
}
|
||||
if enableMutations {
|
||||
fwebhook.NewMutateWebhook(mgr, config)
|
||||
fwebhook.NewMutateWebhook(context.Background(), mgr, config)
|
||||
}
|
||||
logrus.Infof("Polaris webhook server listening on port %d", webhookPort)
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
|
||||
@@ -11,6 +11,16 @@ var sf14gv = 32793;
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(sf14g, s);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var gtag = document.createElement('script');
|
||||
gtag.src = "https://www.googletagmanager.com/gtag/js?id=G-ZR5M5SRYKY";
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gtag, s);
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-ZR5M5SRYKY');
|
||||
})();
|
||||
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
@@ -21,9 +31,3 @@ s.parentNode.insertBefore(t,s)}(window,document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '521127644762074');
|
||||
fbq('track', 'PageView');
|
||||
|
||||
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TM95WXQ');
|
||||
|
||||
-16
@@ -9,8 +9,6 @@ meta:
|
||||
# top-level commands
|
||||
audit
|
||||
Runs a one-time audit.
|
||||
auth
|
||||
Authenticate polaris with Fairwinds Insights
|
||||
dashboard
|
||||
Runs the webserver for Polaris dashboard.
|
||||
fix
|
||||
@@ -44,7 +42,6 @@ webhook
|
||||
# audit flags
|
||||
--audit-path string If specified, audits one or more YAML files instead of a cluster.
|
||||
--checks strings Optional flag to specify specific checks to check
|
||||
--cluster-name string Set --cluster-name to a descriptive name for the cluster you're auditing
|
||||
--color Whether to use color in pretty format. (default true)
|
||||
--display-name string An optional identifier for the audit.
|
||||
-f, --format string Output format for results - json, yaml, pretty, or score. (default "json")
|
||||
@@ -56,13 +53,11 @@ webhook
|
||||
--only-show-failed-tests If specified, audit output will only show failed tests.
|
||||
--output-file string Destination file for audit results.
|
||||
--output-url string Destination URL to send audit results.
|
||||
--quiet Suppress the 'upload to Insights' prompt.
|
||||
--resource string Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.
|
||||
--set-exit-code-below-score int Set an exit code of 4 when the score is below this threshold (1-100).
|
||||
--set-exit-code-on-danger Set an exit code of 3 when the audit contains danger-level issues.
|
||||
--severity string Severity level used to filter results. Behaves like log levels. 'danger' is the least verbose (warning, danger)
|
||||
--skip-ssl-validation Skip https certificate verification
|
||||
--upload-insights Upload scan results to Fairwinds Insights
|
||||
|
||||
# fix flags
|
||||
--checks strings Optional flag to specify specific checks to fix eg. checks=hostIPCSet,hostPIDSet and checks=all applies fix to all defined checks mutations
|
||||
@@ -76,15 +71,4 @@ webhook
|
||||
-h, --help help for webhook
|
||||
-p, --port int Port for the dashboard webserver. (default 9876)
|
||||
|
||||
# auth sub-commands
|
||||
login Authenticate polaris with Fairwinds Insights.
|
||||
logout Log out of a Fairwinds Insights.
|
||||
status View authentication status.
|
||||
token Print the auth token gh is configured to use.
|
||||
```
|
||||
|
||||
#### Suppressing 'upload to Insights' output
|
||||
|
||||
When running the `polaris audit` subcommand, you can suppress the following output using the `--quiet flag`:
|
||||
|
||||
> 🚀 Upload your Polaris findings to Fairwinds Insights to see remediation advice, add teammates, integrate with Slack or Jira, and more:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
@@ -73,7 +73,7 @@ customChecks:
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -120,7 +120,7 @@ successMessage: Label app.kubernetes.io/name matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/name must match metadata.name
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
@@ -193,7 +193,7 @@ controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
@@ -233,7 +233,7 @@ customChecks:
|
||||
foo:
|
||||
jsonSchema: |
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"type": "object"
|
||||
}
|
||||
```
|
||||
|
||||
+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
|
||||
|
||||
Generated
+4791
-14049
File diff suppressed because it is too large
Load Diff
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,81 +1,86 @@
|
||||
module github.com/fairwindsops/polaris
|
||||
|
||||
go 1.20
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/fairwindsops/controller-utils v0.3.0
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/fairwindsops/controller-utils v0.3.4
|
||||
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/jsonschema v0.1.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/qri-io/jsonpointer v0.1.1
|
||||
github.com/qri-io/jsonschema v0.2.1
|
||||
github.com/sirupsen/logrus v1.10.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.12.0
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.0
|
||||
k8s.io/apimachinery v0.29.0
|
||||
k8s.io/client-go v0.29.0
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
k8s.io/api v0.36.3
|
||||
k8s.io/apimachinery v0.36.3
|
||||
k8s.io/client-go v0.36.3
|
||||
sigs.k8s.io/controller-runtime v0.24.1
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
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.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/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/qri-io/jsonpointer v0.1.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
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.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.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/term v0.43.0 // indirect
|
||||
golang.org/x/text v0.37.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
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/component-base v0.27.2 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // 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.3 // indirect
|
||||
)
|
||||
|
||||
@@ -1,257 +1,219 @@
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/fairwindsops/controller-utils v0.3.0 h1:otfA6HZ7oVzF690AdGwVU4+6Q5Uie6G6oOF7MBT+L4g=
|
||||
github.com/fairwindsops/controller-utils v0.3.0/go.mod h1:Plr++8B/A+TlhbVBG0sNhToaaQKo7LoFNIfdKi58vpM=
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c h1:js6wazy/7V7cZI43affIa2FymWZ61rOp/H26HEE3ChY=
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c/go.mod h1:bNIBr22uoqvgi3JzUg7Hg9QidCsvXWAhquNQRyDGJCo=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
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.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=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-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.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/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=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
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=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
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=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
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.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
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.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
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.1.2 h1:JlI7JAlxBbxh5Y641ctf+3kxcwYM6QbKDiqiQRkIBr0=
|
||||
github.com/qri-io/jsonschema v0.1.2/go.mod h1:SiF7DGMMKfw3cPrKZErviQKaUGserd2PYMOGm0vrELU=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
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.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.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/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc=
|
||||
github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
|
||||
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
|
||||
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
|
||||
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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
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=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
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.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 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
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.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
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.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=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/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.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
|
||||
k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
|
||||
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
|
||||
k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8=
|
||||
k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38=
|
||||
k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo=
|
||||
k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU=
|
||||
sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
k8s.io/api v0.36.3 h1:NxB+05W2UGqXWFXcLO0RB5cnqnUPP5v5sVlaOH0Iz4w=
|
||||
k8s.io/api v0.36.3/go.mod h1:JzLQKqRHC5+I8RVj/lS3lCg0mg6nWI9Fo/Sk3ElxHzg=
|
||||
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.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM=
|
||||
k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE=
|
||||
k8s.io/client-go v0.36.3 h1:M4JdVzXxYcZk4fGpfDdYnxSwhLKWCFoQsHW6t+z8Hfg=
|
||||
k8s.io/client-go v0.36.3/go.mod h1:gcPwr0c87vjjG6HB6pWEqOeuYVoXSsREjzux2j6GF30=
|
||||
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.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=
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var userHomeDir string
|
||||
var polarisHostsFilepath string
|
||||
|
||||
var ErrNotLoggedIn = errors.New("not logged in")
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
userHomeDir, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
logrus.Fatalf("reading user home dir: %v", err)
|
||||
}
|
||||
polarisHostsFilepath = userHomeDir + "/.config/polaris/hosts.yaml"
|
||||
}
|
||||
|
||||
func readPolarisHostsFile() (map[string]Host, error) {
|
||||
f, err := os.Open(polarisHostsFilepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content := map[string]Host{}
|
||||
err = yaml.Unmarshal(b, &content)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func GetAuth(insightsHost string) (*Host, error) {
|
||||
hosts, err := readPolarisHostsFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hosts) == 0 {
|
||||
return nil, ErrNotLoggedIn
|
||||
}
|
||||
if h, ok := hosts[insightsHost]; ok {
|
||||
return &h, nil
|
||||
}
|
||||
return nil, ErrNotLoggedIn
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// openBrowser opens up the provided URL in a browser
|
||||
func openBrowser(url string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "openbsd":
|
||||
fallthrough
|
||||
case "linux":
|
||||
cmd = exec.Command("xdg-open", url)
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", url)
|
||||
case "windows":
|
||||
r := strings.NewReplacer("&", "^&")
|
||||
cmd = exec.Command("cmd", "/c", "start", r.Replace(url))
|
||||
}
|
||||
if cmd != nil {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
logrus.Printf("Failed to open browser due to error %v", err)
|
||||
return fmt.Errorf("Failed to open browser: " + err.Error())
|
||||
}
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
logrus.Printf("Failed to wait for open browser command to finish due to error %v", err)
|
||||
return fmt.Errorf("Failed to wait for open browser command to finish: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("unsupported platform")
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/fairwindsops/polaris/pkg/insights"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const loginPath = "/auth/login"
|
||||
const registerPath = "/auth/register"
|
||||
|
||||
const (
|
||||
loginUsingBrowser = "Login with a web browser"
|
||||
pasteAnAuthenticationToken = "Paste an authentication token"
|
||||
)
|
||||
|
||||
type paramsOrError struct {
|
||||
token string
|
||||
user string
|
||||
organization string
|
||||
err error
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
Token string `yaml:"token"`
|
||||
User string `yaml:"user"`
|
||||
Organization string `yaml:"organization"`
|
||||
}
|
||||
|
||||
var paramsOrErrorChan = make(chan paramsOrError)
|
||||
|
||||
func HandleLogin(insightsHost string) error {
|
||||
if _, err := os.Stat(polarisHostsFilepath); err == nil {
|
||||
content, err := readPolarisHostsFile()
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading polaris hosts file: %w", err)
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
if h, ok := content[insightsHost]; ok {
|
||||
c := insights.NewHTTPClient(insightsHost, h.Organization, h.Token)
|
||||
isValid, err := c.IsTokenValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isValid {
|
||||
var reAuthenticate bool
|
||||
err = survey.AskOne(&survey.Confirm{Message: fmt.Sprintf("You're already logged into %s. Do you want to re-authenticate?", insightsHost)}, &reAuthenticate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("prompting re-authenticate: %w", err)
|
||||
}
|
||||
if !reAuthenticate {
|
||||
// bail-out
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selection := &survey.Select{
|
||||
Message: "How would you like to authenticate Polaris?",
|
||||
Options: []string{loginUsingBrowser, pasteAnAuthenticationToken},
|
||||
Default: loginUsingBrowser,
|
||||
}
|
||||
|
||||
var answer string
|
||||
err := survey.AskOne(selection, &answer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("asking how to authenticate: %w", err)
|
||||
}
|
||||
|
||||
var user, token, organization string
|
||||
if answer == loginUsingBrowser {
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
localServerPort := listener.Addr().(*net.TCPAddr).Port
|
||||
url := fmt.Sprintf(insightsHost + registerPath + "?source=polaris&callbackUrl=" + fmt.Sprintf("http://localhost:%d/auth/login/callback", localServerPort))
|
||||
err = openBrowser(url)
|
||||
if err != nil {
|
||||
logrus.Warnf("could not open browser: %v", err)
|
||||
logrus.Infoln("paste the link below into your browser:")
|
||||
os.Stdout.Write([]byte(url + "\n"))
|
||||
}
|
||||
|
||||
var router *mux.Router
|
||||
go func() {
|
||||
router = mux.NewRouter()
|
||||
router.HandleFunc("/auth/login/callback", callbackHandler(insightsHost, localServerPort))
|
||||
if err := http.Serve(listener, router); err != nil {
|
||||
paramsOrErrorChan <- paramsOrError{err: fmt.Errorf("starting the local http server: %w", err)}
|
||||
}
|
||||
}()
|
||||
|
||||
// wait the browser to callback the local server
|
||||
paramOrError := <-paramsOrErrorChan
|
||||
|
||||
if paramOrError.err != nil {
|
||||
return paramOrError.err
|
||||
}
|
||||
|
||||
user = paramOrError.user
|
||||
organization = paramOrError.organization
|
||||
token = paramOrError.token
|
||||
} else {
|
||||
var answer string
|
||||
var bot bot
|
||||
err := survey.AskOne(&survey.Password{Message: "Paste your authentication token:"}, &answer, survey.WithValidator(validateToken(insightsHost, &bot)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("asking how to authenticate: %w", err)
|
||||
}
|
||||
token = answer
|
||||
user = bot.Name
|
||||
organization = bot.Organization
|
||||
}
|
||||
|
||||
polarisCfgDir := filepath.Join(userHomeDir, ".config", "polaris")
|
||||
err = os.MkdirAll(polarisCfgDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating polaris config dir: %w", err)
|
||||
}
|
||||
|
||||
content := map[string]Host{insightsHost: {Token: token, User: user, Organization: organization}}
|
||||
b, err := yaml.Marshal(content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling yaml data: %w", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(polarisHostsFilepath, b, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing data to file: %w", err)
|
||||
}
|
||||
|
||||
logrus.Debugf("hosts file has been saved")
|
||||
|
||||
fmt.Println("✓ Authentication complete.")
|
||||
fmt.Printf("✓ Logged in organization %s as %s.\n", organization, user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchAuthToken(insightsHost, organization, code string) (string, error) {
|
||||
authTokenURL := fmt.Sprintf("%s/v0/organizations/%s/auth/token", insightsHost, organization)
|
||||
body := map[string]any{"grantType": "authorization_code", "code": code}
|
||||
b, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("POST", authTokenURL, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("expected 200 OK - received %s", res.Status)
|
||||
}
|
||||
|
||||
var rBody map[string]any
|
||||
err = json.NewDecoder(res.Body).Decode(&rBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token, ok := rBody["accessToken"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to parse accessToken from response body: %v", rBody)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func callbackHandler(insightsHost string, localServerPort int) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// checks for error in the params
|
||||
errMsg := r.URL.Query().Get("error")
|
||||
if len(errMsg) > 0 {
|
||||
errDescriptionMsg := r.URL.Query().Get("error_description")
|
||||
fmt.Fprintf(w, "unable to perform integration: %s - %s", errMsg, errDescriptionMsg)
|
||||
paramsOrErrorChan <- paramsOrError{err: fmt.Errorf("%s - %s", errMsg, errDescriptionMsg)}
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
code := r.URL.Query().Get("code")
|
||||
if len(code) == 0 {
|
||||
err = errors.New("code query param is required in callback")
|
||||
}
|
||||
user := r.URL.Query().Get("user")
|
||||
if len(user) == 0 {
|
||||
err = errors.New("user query param is required in callback")
|
||||
}
|
||||
organization := r.URL.Query().Get("organization")
|
||||
if len(organization) == 0 {
|
||||
err = errors.New("organization query param is required in callback")
|
||||
}
|
||||
token, err := fetchAuthToken(insightsHost, organization, code)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("fetching auth token: %w", err)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "unable to perform integration: %v", err)
|
||||
paramsOrErrorChan <- paramsOrError{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, "Polaris and Fairwinds Insights integration has finished successfully, your credentials are set! You can safely close this tab now.")
|
||||
paramsOrErrorChan <- paramsOrError{token: token, user: user, organization: organization}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func validateToken(insightsHost string, bot *bot) func(args any) error {
|
||||
return func(args any) error {
|
||||
token, ok := args.(string)
|
||||
if !ok {
|
||||
return errors.New("casting token to string")
|
||||
}
|
||||
if len(strings.TrimSpace(token)) <= 0 {
|
||||
return errors.New("token is required")
|
||||
}
|
||||
return fetchOrganizationBot(insightsHost, token, bot)
|
||||
}
|
||||
}
|
||||
|
||||
type bot struct {
|
||||
ID int
|
||||
Organization string
|
||||
Name string
|
||||
Role string
|
||||
AuthToken string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func fetchOrganizationBot(insightsHost, authToken string, bot *bot) error {
|
||||
authTokenURL := fmt.Sprintf("%s/v0/bots/from-request", insightsHost)
|
||||
r, err := http.NewRequest("GET", authTokenURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Add("Content-Type", "application/json")
|
||||
r.Header.Add("Authorization", "Bearer "+authToken)
|
||||
|
||||
res, err := http.DefaultClient.Do(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 400 {
|
||||
return fmt.Errorf("expected 200 (OK) - received %d", res.StatusCode)
|
||||
}
|
||||
|
||||
err = json.NewDecoder(res.Body).Decode(bot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsLoggedIn() bool {
|
||||
if _, err := os.Stat(polarisHostsFilepath); err == nil {
|
||||
content, err := readPolarisHostsFile()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(content) > 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func HandleLogout() error {
|
||||
if !IsLoggedIn() {
|
||||
fmt.Println("not logged in to Fairwinds Insights")
|
||||
return nil
|
||||
}
|
||||
err := performLogout()
|
||||
if err != nil {
|
||||
return fmt.Errorf("performing logout: %v", err)
|
||||
}
|
||||
fmt.Println("✓ Logged out of Fairwinds Insights")
|
||||
return nil
|
||||
}
|
||||
|
||||
func performLogout() error {
|
||||
content, err := readPolarisHostsFile()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
err = os.WriteFile(polarisHostsFilepath, []byte("{}"), os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing data to file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/insights"
|
||||
)
|
||||
|
||||
func PrintStatus(insightsHost string) error {
|
||||
if content, err := readPolarisHostsFile(); err == nil {
|
||||
if len(content) > 0 {
|
||||
if h, ok := content[insightsHost]; ok {
|
||||
c := insights.NewHTTPClient(insightsHost, h.Organization, h.Token)
|
||||
isValid, err := c.IsTokenValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isValid {
|
||||
fmt.Println("✕ Your token is no longer valid. Run polaris auth login to authenticate.")
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("✓ Logged in to %s as %s\n", insightsHost, h.User)
|
||||
fmt.Printf("✓ Token: %s\n", hideToken(h.Token, 3))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fmt.Printf("✕ No authentication found for host %s. Run polaris auth login to authenticate.\n", insightsHost)
|
||||
return nil
|
||||
}
|
||||
fmt.Println("You are not logged into Fairwinds Insights. Run polaris auth login to authenticate.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func hideToken(token string, hideAfter int) string {
|
||||
var i int
|
||||
return strings.Map(func(r rune) rune {
|
||||
defer func() {
|
||||
i++
|
||||
}()
|
||||
if i > hideAfter {
|
||||
return []rune("*")[0]
|
||||
}
|
||||
return r
|
||||
}, token)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package auth
|
||||
|
||||
import "fmt"
|
||||
|
||||
func PrintToken() error {
|
||||
if content, err := readPolarisHostsFile(); err == nil {
|
||||
if len(content) > 0 {
|
||||
for k, h := range content {
|
||||
if len(content) == 1 {
|
||||
fmt.Println(h.Token)
|
||||
} else {
|
||||
fmt.Printf("%s: %s\n", k, h.Token)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fmt.Println("no oauth token")
|
||||
return nil
|
||||
}
|
||||
@@ -29,11 +29,14 @@ var (
|
||||
"deploymentMissingReplicas",
|
||||
// Pod checks
|
||||
"hostIPCSet",
|
||||
"hostPathSet",
|
||||
"hostProcess",
|
||||
"hostPIDSet",
|
||||
"hostNetworkSet",
|
||||
"automountServiceAccountToken",
|
||||
"topologySpreadConstraint",
|
||||
// Container checks
|
||||
"procMount",
|
||||
"memoryLimitsMissing",
|
||||
"memoryRequestsMissing",
|
||||
"cpuLimitsMissing",
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The ServiceAccount will be automounted
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
serviceAccountName:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The ClusterRole allows Pods/exec or pods/attach
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRole
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
anyOf:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The ClusterRoleBinding references the default cluster-admin Clus
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The ClusterRoleBinding references a ClusterRole that allows Pods
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/ClusterRoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Container should not have dangerous capabilities
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
|
||||
@@ -6,7 +6,7 @@ controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Host IPC should not be configured
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostIPC:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Host network should not be configured
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostNetwork:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Host PID should not be configured
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hostPID:
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
successMessage: HostPath volumes are not configured
|
||||
failureMessage: HostPath volumes must be forbidden
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
volumes:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
hostPath:
|
||||
type: string
|
||||
const: ''
|
||||
@@ -3,7 +3,7 @@ failureMessage: Host port should not be configured
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
properties:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
successMessage: Privileged access to the host check is valid
|
||||
failureMessage: Privileged access to the host is disallowed
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
windowsOptions:
|
||||
type: object
|
||||
properties:
|
||||
hostProcess:
|
||||
type: boolean
|
||||
const: false
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
windowsOptions:
|
||||
type: object
|
||||
properties:
|
||||
hostProcess:
|
||||
type: boolean
|
||||
const: false
|
||||
@@ -3,7 +3,7 @@ failureMessage: HPA maxReplicas and minReplicas should be different
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
schemaString: |
|
||||
"$schema": http://json-schema.org/draft-07/schema#
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: HPA minReplicas should be 2 or more
|
||||
category: Reliability
|
||||
target: autoscaling/HorizontalPodAutoscaler
|
||||
schema:
|
||||
"$schema": http://json-schema.org/draft-07/schema#
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Container should not have insecure capabilities
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
|
||||
@@ -3,8 +3,8 @@ FailureMessage: Use one of AppArmor, Seccomp, SELinux, or dropping Linux Capabil
|
||||
category: Security
|
||||
target: Container
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
podOrContainerSeccompProfile:
|
||||
type: object
|
||||
{{ $podSeccompProfileType := .Polaris.PodSpec.securityContext.seccompProfile.type }}
|
||||
@@ -83,7 +83,7 @@ schemaString: |
|
||||
type: object
|
||||
{{ else }}
|
||||
anyOf:
|
||||
- $ref: "#/definitions/podOrContainerSeccompProfile"
|
||||
- $ref: "#/definitions/podOrContainerSELinuxOptions"
|
||||
- $ref: "#/definitions/containerDropCapabilities"
|
||||
- $ref: "#/$defs/podOrContainerSeccompProfile"
|
||||
- $ref: "#/$defs/podOrContainerSELinuxOptions"
|
||||
- $ref: "#/$defs/containerDropCapabilities"
|
||||
{{ end}}
|
||||
|
||||
@@ -10,7 +10,7 @@ containers:
|
||||
- initContainer
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- livenessProbe
|
||||
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
|
||||
@@ -6,7 +6,7 @@ containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Label app.kubernetes.io/instance must match metadata.name
|
||||
category: Reliability
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: A NetworkPolicy should match pod labels and contain applied egre
|
||||
category: Security
|
||||
target: PodTemplate
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
|
||||
@@ -6,7 +6,7 @@ controllers:
|
||||
include:
|
||||
- Deployment
|
||||
schema:
|
||||
"$schema": http://json-schema.org/draft-07/schema#
|
||||
"$schema": https://json-schema.org/draft/2019-09/schema#
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
|
||||
@@ -4,8 +4,8 @@ category: Security
|
||||
target: Container
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -25,13 +25,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
@@ -40,7 +40,7 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/readOnlyRootFilesystem
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Voluntary evictions are not possible
|
||||
category: Reliability
|
||||
target: policy/PodDisruptionBudget
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Priority class should be set
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- priorityClassName
|
||||
|
||||
@@ -4,8 +4,8 @@ category: Security
|
||||
target: Container
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -25,13 +25,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
- properties:
|
||||
containers:
|
||||
type: array
|
||||
@@ -40,7 +40,7 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
|
||||
mutations:
|
||||
- op: add
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
successMessage: The default /proc masks are set up to reduce attack surface, and should be required
|
||||
failureMessage: Proc mount must not be changed from the default
|
||||
category: Security
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
type: object
|
||||
properties:
|
||||
procMount:
|
||||
type: string
|
||||
const: Default
|
||||
@@ -3,7 +3,7 @@ failureMessage: Image pull policy should be "Always"
|
||||
category: Reliability
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
required:
|
||||
- imagePullPolicy
|
||||
properties:
|
||||
|
||||
@@ -10,7 +10,7 @@ containers:
|
||||
- initContainer
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- readinessProbe
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The Role allows Pods/exec or pods/attach
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/Role
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
anyOf:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The RoleBinding references the default cluster-admin ClusterRole
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a Role.
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a Role with wildcard permissions
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a ClusterRole.
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a ClusterRole that allows Pods/exec,
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a Role.
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a Role that allows Pods/exec, allows
|
||||
category: Security
|
||||
target: rbac.authorization.k8s.io/RoleBinding
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
anyOf:
|
||||
# Pass RoleBindings that point to a ClusterRole.
|
||||
@@ -17,7 +17,7 @@ schemaString: |
|
||||
const: "rbac.authorization.k8s.io"
|
||||
kind:
|
||||
type: string
|
||||
const: "Role"
|
||||
const: "ClusterRole"
|
||||
# Do not alert on default RoleBindings.
|
||||
- required: ["metadata"]
|
||||
properties:
|
||||
|
||||
@@ -4,8 +4,8 @@ category: Security
|
||||
target: Container
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
notBadSecurityContext:
|
||||
type: object
|
||||
properties:
|
||||
@@ -15,13 +15,13 @@ schema:
|
||||
type: object
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/privileged
|
||||
|
||||
@@ -4,8 +4,8 @@ category: Security
|
||||
target: Container
|
||||
schemaTarget: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
definitions:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
$defs:
|
||||
goodSecurityContext:
|
||||
type: object
|
||||
anyOf:
|
||||
@@ -33,13 +33,13 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
containers:
|
||||
type: array
|
||||
items:
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/notBadSecurityContext"
|
||||
$ref: "#/$defs/notBadSecurityContext"
|
||||
# non-root specified at container level
|
||||
- properties:
|
||||
containers:
|
||||
@@ -49,7 +49,7 @@ schema:
|
||||
- securityContext
|
||||
properties:
|
||||
securityContext:
|
||||
$ref: "#/definitions/goodSecurityContext"
|
||||
$ref: "#/$defs/goodSecurityContext"
|
||||
mutations:
|
||||
- op: add
|
||||
path: /securityContext/runAsNonRoot
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Potentially sensitive content is detected in the ConfigMap keys
|
||||
category: Security
|
||||
target: /ConfigMap
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: ["metadata"]
|
||||
properties:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: The container sets potentially sensitive environment variables
|
||||
category: Security
|
||||
target: Container
|
||||
schemaString: |
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
env:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Image tag should be specified
|
||||
category: Reliability
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
required:
|
||||
- image
|
||||
allOf:
|
||||
|
||||
@@ -3,7 +3,7 @@ failureMessage: Ingress does not have TLS configured
|
||||
category: Security
|
||||
target: networking.k8s.io/Ingress
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
|
||||
@@ -7,7 +7,7 @@ controllers:
|
||||
- Job
|
||||
- CronJob
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- topologySpreadConstraints
|
||||
|
||||
+40
-17
@@ -52,29 +52,52 @@ type Exemption struct {
|
||||
//go:embed default.yaml
|
||||
var defaultConfig []byte
|
||||
|
||||
// ParseFile parses config from a file.
|
||||
func ParseFile(path string) (Configuration, error) {
|
||||
var rawBytes []byte
|
||||
var err error
|
||||
if path == "" {
|
||||
rawBytes = defaultConfig
|
||||
} else if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") {
|
||||
// path is a url
|
||||
response, err2 := http.Get(path)
|
||||
if err2 != nil {
|
||||
return Configuration{}, err2
|
||||
}
|
||||
rawBytes, err = io.ReadAll(response.Body)
|
||||
} else {
|
||||
// path is local
|
||||
rawBytes, err = os.ReadFile(path)
|
||||
}
|
||||
// MergeConfigAndParseFile parses config from a file.
|
||||
func MergeConfigAndParseFile(customConfigPath string, mergeConfig bool) (Configuration, error) {
|
||||
rawBytes, err := mergeConfigFile(customConfigPath, mergeConfig)
|
||||
if err != nil {
|
||||
return Configuration{}, err
|
||||
}
|
||||
|
||||
return Parse(rawBytes)
|
||||
}
|
||||
|
||||
func mergeConfigFile(customConfigPath string, mergeConfig bool) ([]byte, error) {
|
||||
if customConfigPath == "" {
|
||||
return defaultConfig, nil
|
||||
}
|
||||
|
||||
var customConfigContent []byte
|
||||
var err error
|
||||
if strings.HasPrefix(customConfigPath, "https://") || strings.HasPrefix(customConfigPath, "http://") {
|
||||
// path is a url
|
||||
response, err := http.Get(customConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customConfigContent, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// path is local
|
||||
customConfigContent, err = os.ReadFile(customConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if mergeConfig {
|
||||
mergedConfig, err := mergeYaml(defaultConfig, customConfigContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
return customConfigContent, nil
|
||||
}
|
||||
|
||||
// Parse parses config from a byte array.
|
||||
func Parse(rawBytes []byte) (Configuration, error) {
|
||||
reader := bytes.NewReader(rawBytes)
|
||||
|
||||
+14
-14
@@ -52,7 +52,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
@@ -69,7 +69,7 @@ customChecks:
|
||||
target: Container
|
||||
jsonSchema: >
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"type": "object",
|
||||
"required": ["securityContext"]
|
||||
}
|
||||
@@ -83,7 +83,7 @@ customChecks:
|
||||
category: Security
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- securityContext
|
||||
@@ -125,7 +125,7 @@ func TestConfigFromURL(t *testing.T) {
|
||||
}()
|
||||
time.Sleep(time.Second)
|
||||
|
||||
parsedConf, err = ParseFile("http://localhost:8081/exampleURL")
|
||||
parsedConf, err = MergeConfigAndParseFile("http://localhost:8081/exampleURL", false)
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML from URL")
|
||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||
panic(err)
|
||||
@@ -136,41 +136,41 @@ func TestConfigFromURL(t *testing.T) {
|
||||
|
||||
func TestConfigNoServerError(t *testing.T) {
|
||||
var err error
|
||||
_, err = ParseFile("http://localhost:8081/exampleURL")
|
||||
_, err = MergeConfigAndParseFile("http://localhost:8081/exampleURL", false)
|
||||
assert.Error(t, err)
|
||||
assert.Regexp(t, regexp.MustCompile("connection refused"), err.Error())
|
||||
}
|
||||
|
||||
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{}{})
|
||||
isValid, _, err := check.CheckObject(valid)
|
||||
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]any{})
|
||||
isValid, _, err := check.CheckObject(context.TODO(), valid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, isValid)
|
||||
isValid, _, err = check.CheckObject(invalid)
|
||||
isValid, _, err = check.CheckObject(context.TODO(), invalid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, isValid)
|
||||
|
||||
parsedConf, err = Parse([]byte(confCustomChecksWithJSONSchema))
|
||||
assert.NoError(t, err, "Expected no error when parsing YAML config")
|
||||
assert.Equal(t, 1, len(parsedConf.CustomChecks))
|
||||
isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(valid)
|
||||
isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(context.TODO(), valid)
|
||||
assert.NoError(t, err)
|
||||
if !assert.Equal(t, true, isValid) {
|
||||
fmt.Println(problems[0].PropertyPath, problems[0].InvalidValue, problems[0].Message)
|
||||
}
|
||||
isValid, _, err = check.CheckObject(invalid)
|
||||
isValid, _, err = check.CheckObject(context.TODO(), invalid)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, isValid)
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ checks:
|
||||
# security
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
hostPathSet: warning
|
||||
hostProcess: warning
|
||||
hostPIDSet: danger
|
||||
linuxHardening: warning
|
||||
missingNetworkPolicy: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: danger
|
||||
procMount: warning
|
||||
runAsRootAllowed: danger
|
||||
runAsPrivileged: danger
|
||||
dangerousCapabilities: danger
|
||||
|
||||
@@ -23,11 +23,14 @@ checks:
|
||||
# security
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
hostPathSet: warning
|
||||
hostProcess: warning
|
||||
hostPIDSet: danger
|
||||
linuxHardening: danger
|
||||
missingNetworkPolicy: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: danger
|
||||
procMount: warning
|
||||
runAsRootAllowed: danger
|
||||
runAsPrivileged: danger
|
||||
dangerousCapabilities: danger
|
||||
@@ -72,7 +75,7 @@ customChecks:
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
@@ -102,7 +105,7 @@ customChecks:
|
||||
category: Images
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3" // do not change the yaml import
|
||||
)
|
||||
|
||||
func mergeYaml(defaultConfig, overridesConfig []byte) ([]byte, error) {
|
||||
var defaultData, overrideConfig map[string]any
|
||||
|
||||
err := yaml.Unmarshal([]byte(defaultConfig), &defaultData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal([]byte(overridesConfig), &overrideConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergedData := mergeYAMLMaps(defaultData, overrideConfig)
|
||||
|
||||
mergedConfig, err := yaml.Marshal(mergedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
func mergeYAMLMaps(defaults, overrides map[string]any) map[string]any {
|
||||
for k, v := range overrides {
|
||||
if vMap, ok := v.(map[string]any); ok {
|
||||
// if the key exists in defaults and is a map, recursively merge
|
||||
if mv1, ok := defaults[k].(map[string]any); ok {
|
||||
defaults[k] = mergeYAMLMaps(mv1, vMap)
|
||||
} else {
|
||||
defaults[k] = vMap
|
||||
}
|
||||
} else {
|
||||
// add or overwrite the value in defaults
|
||||
defaults[k] = v
|
||||
}
|
||||
}
|
||||
return defaults
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var defaults = `
|
||||
checks:
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
tagNotSpecified: danger
|
||||
existing:
|
||||
sub:
|
||||
key: value
|
||||
`
|
||||
|
||||
var overrides = `
|
||||
checks:
|
||||
pullPolicyNotAlways: ignore
|
||||
tagNotSpecified: overrides
|
||||
existing:
|
||||
sub:
|
||||
key1: value1
|
||||
new: value
|
||||
new:
|
||||
key: value
|
||||
`
|
||||
|
||||
func TestMergeYaml(t *testing.T) {
|
||||
mergedContent, err := mergeYaml([]byte(defaults), []byte(overrides))
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedYAML := `checks:
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: warning
|
||||
pullPolicyNotAlways: ignore
|
||||
tagNotSpecified: overrides
|
||||
existing:
|
||||
new: value
|
||||
sub:
|
||||
key: value
|
||||
key1: value1
|
||||
new:
|
||||
key: value
|
||||
`
|
||||
|
||||
assert.Equal(t, expectedYAML, string(mergedContent))
|
||||
}
|
||||
+83
-67
@@ -16,13 +16,17 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/qri-io/jsonpointer"
|
||||
"github.com/qri-io/jsonschema"
|
||||
"github.com/thoas/go-funk"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -56,34 +60,34 @@ 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.RootSchema `yaml:"-" json:"-"`
|
||||
AdditionalSchemas map[string]map[string]interface{} `yaml:"additionalSchemas" json:"additionalSchemas"`
|
||||
AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"`
|
||||
AdditionalValidators map[string]jsonschema.RootSchema `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:"-"`
|
||||
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 {
|
||||
@@ -109,8 +113,9 @@ func ParseCheck(id string, rawBytes []byte) (SchemaCheck, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
jsonschema.RegisterValidator("resourceMinimum", newResourceMinimum)
|
||||
jsonschema.RegisterValidator("resourceMaximum", newResourceMaximum)
|
||||
jsonschema.RegisterKeyword("resourceMinimum", newResourceMinimum)
|
||||
jsonschema.RegisterKeyword("resourceMaximum", newResourceMaximum)
|
||||
jsonschema.LoadDraft2019_09()
|
||||
}
|
||||
|
||||
type includeExcludeList struct {
|
||||
@@ -118,50 +123,68 @@ type includeExcludeList struct {
|
||||
Exclude []string `yaml:"exclude"`
|
||||
}
|
||||
|
||||
func newResourceMinimum() jsonschema.Validator {
|
||||
func newResourceMinimum() jsonschema.Keyword {
|
||||
return new(resourceMinimum)
|
||||
}
|
||||
|
||||
func newResourceMaximum() jsonschema.Validator {
|
||||
func newResourceMaximum() jsonschema.Keyword {
|
||||
return new(resourceMaximum)
|
||||
}
|
||||
|
||||
// Validate checks that a specified quanitity is not less than the minimum
|
||||
func (min resourceMinimum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) {
|
||||
err := validateRange(path, string(min), data, true)
|
||||
func (min resourceMinimum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data any) {
|
||||
err := validateRange(string(min), data, true)
|
||||
if err != nil {
|
||||
errs := currentState.Errs
|
||||
*errs = append(*errs, *err...)
|
||||
currentState.Errs = errs
|
||||
}
|
||||
}
|
||||
func (max resourceMaximum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data any) {
|
||||
err := validateRange(string(max), data, false)
|
||||
if err != nil {
|
||||
errs := currentState.Errs
|
||||
*errs = append(*errs, *err...)
|
||||
currentState.Errs = errs
|
||||
}
|
||||
}
|
||||
|
||||
// Validate checks that a specified quanitity is not greater than the maximum
|
||||
func (max resourceMaximum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) {
|
||||
err := validateRange(path, string(max), data, false)
|
||||
if err != nil {
|
||||
*errs = append(*errs, *err...)
|
||||
}
|
||||
func (min resourceMinimum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema {
|
||||
// Not implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.ValError) {
|
||||
func (min resourceMinimum) Register(uri string, registry *jsonschema.SchemaRegistry) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
func (max resourceMaximum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema {
|
||||
// Not implemented
|
||||
return nil
|
||||
}
|
||||
func (max resourceMaximum) Register(uri string, registry *jsonschema.SchemaRegistry) {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
func parseQuantity(i any) (resource.Quantity, *[]jsonschema.KeyError) {
|
||||
if resNum, ok := i.(float64); ok {
|
||||
i = fmt.Sprintf("%f", resNum)
|
||||
}
|
||||
resStr, ok := i.(string)
|
||||
if !ok {
|
||||
return resource.Quantity{}, &[]jsonschema.ValError{
|
||||
return resource.Quantity{}, &[]jsonschema.KeyError{
|
||||
{Message: fmt.Sprintf("Resource quantity %v is not a string", i)},
|
||||
}
|
||||
}
|
||||
q, err := resource.ParseQuantity(resStr)
|
||||
if err != nil {
|
||||
return resource.Quantity{}, &[]jsonschema.ValError{
|
||||
return resource.Quantity{}, &[]jsonschema.KeyError{
|
||||
{Message: fmt.Sprintf("Could not parse resource quantity: %s", resStr)},
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func validateRange(path string, limit interface{}, data interface{}, isMinimum bool) *[]jsonschema.ValError {
|
||||
func validateRange(limit any, data any, isMinimum bool) *[]jsonschema.KeyError {
|
||||
limitQuantity, err := parseQuantity(limit)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -173,14 +196,14 @@ func validateRange(path string, limit interface{}, data interface{}, isMinimum b
|
||||
cmp := limitQuantity.Cmp(actualQuantity)
|
||||
if isMinimum {
|
||||
if cmp == 1 {
|
||||
return &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("%s quantity %v is > %v", path, actualQuantity, limitQuantity)},
|
||||
return &[]jsonschema.KeyError{
|
||||
{Message: fmt.Sprintf("quantity %v is > %v", actualQuantity, limitQuantity)},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cmp == -1 {
|
||||
return &[]jsonschema.ValError{
|
||||
{Message: fmt.Sprintf("%s quantity %v is < %v", path, actualQuantity, limitQuantity)},
|
||||
return &[]jsonschema.KeyError{
|
||||
{Message: fmt.Sprintf("quantity %v is < %v", actualQuantity, limitQuantity)},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,21 +230,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{}
|
||||
|
||||
@@ -251,9 +272,9 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err
|
||||
}
|
||||
}
|
||||
|
||||
newCheck.AdditionalValidators = map[string]jsonschema.RootSchema{}
|
||||
newCheck.AdditionalValidators = map[string]jsonschema.Schema{}
|
||||
for kind, schemaStr := range newCheck.AdditionalSchemaStrings {
|
||||
val := jsonschema.RootSchema{}
|
||||
val := jsonschema.Schema{}
|
||||
err := UnmarshalYAMLOrJSON([]byte(schemaStr), &val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -268,38 +289,38 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err
|
||||
}
|
||||
|
||||
// CheckPodSpec checks a pod spec against the schema
|
||||
func (check SchemaCheck) CheckPodSpec(pod *corev1.PodSpec) (bool, []jsonschema.ValError, error) {
|
||||
return check.CheckObject(pod)
|
||||
func (check SchemaCheck) CheckPodSpec(ctx context.Context, pod *corev1.PodSpec) (bool, []jsonschema.KeyError, error) {
|
||||
return check.CheckObject(ctx, pod)
|
||||
}
|
||||
|
||||
// CheckPodTemplate checks a pod template against the schema
|
||||
func (check SchemaCheck) CheckPodTemplate(podTemplate interface{}) (bool, []jsonschema.ValError, error) {
|
||||
return check.CheckObject(podTemplate)
|
||||
func (check SchemaCheck) CheckPodTemplate(ctx context.Context, podTemplate any) (bool, []jsonschema.KeyError, error) {
|
||||
return check.CheckObject(ctx, podTemplate)
|
||||
}
|
||||
|
||||
// CheckController checks a controler's spec against the schema
|
||||
func (check SchemaCheck) CheckController(bytes []byte) (bool, []jsonschema.ValError, error) {
|
||||
errs, err := check.Validator.ValidateBytes(bytes)
|
||||
func (check SchemaCheck) CheckController(ctx context.Context, bytes []byte) (bool, []jsonschema.KeyError, error) {
|
||||
errs, err := check.Validator.ValidateBytes(ctx, bytes)
|
||||
return len(errs) == 0, errs, err
|
||||
}
|
||||
|
||||
// CheckContainer checks a container spec against the schema
|
||||
func (check SchemaCheck) CheckContainer(container *corev1.Container) (bool, []jsonschema.ValError, error) {
|
||||
return check.CheckObject(container)
|
||||
func (check SchemaCheck) CheckContainer(ctx context.Context, container *corev1.Container) (bool, []jsonschema.KeyError, error) {
|
||||
return check.CheckObject(ctx, container)
|
||||
}
|
||||
|
||||
// CheckObject checks arbitrary data against the schema
|
||||
func (check SchemaCheck) CheckObject(obj interface{}) (bool, []jsonschema.ValError, 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
|
||||
}
|
||||
errs, err := check.Validator.ValidateBytes(bytes)
|
||||
errs, err := check.Validator.ValidateBytes(ctx, bytes)
|
||||
return len(errs) == 0, errs, err
|
||||
}
|
||||
|
||||
// CheckAdditionalObjects looks for an object that passes the specified additional schema
|
||||
func (check SchemaCheck) CheckAdditionalObjects(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)
|
||||
@@ -309,7 +330,7 @@ func (check SchemaCheck) CheckAdditionalObjects(groupkind string, objects []inte
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
errs, err := val.ValidateBytes(bytes)
|
||||
errs, err := val.ValidateBytes(ctx, bytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -335,19 +356,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
|
||||
|
||||
@@ -16,6 +16,7 @@ package dashboard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
@@ -140,7 +141,7 @@ func stripUnselectedNamespaces(data *validator.AuditData, selectedNamespaces []s
|
||||
}
|
||||
|
||||
// GetRouter returns a mux router serving all routes necessary for the dashboard
|
||||
func GetRouter(c config.Configuration, auditPath string, port int, basePath string, auditData *validator.AuditData) (*mux.Router, error) {
|
||||
func GetRouter(ctx context.Context, c config.Configuration, auditPath string, port int, basePath string, auditData *validator.AuditData) (*mux.Router, error) {
|
||||
router := mux.NewRouter().PathPrefix(basePath).Subrouter()
|
||||
|
||||
assetsSubFS, err := fs.Sub(assetsFS, "assets")
|
||||
@@ -176,7 +177,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
|
||||
}
|
||||
|
||||
var auditDataObj validator.AuditData
|
||||
auditDataObj, err = validator.RunAudit(adjustedConf, k)
|
||||
auditDataObj, err = validator.RunAudit(ctx, adjustedConf, k)
|
||||
if err != nil {
|
||||
http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -212,7 +213,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
|
||||
|
||||
logrus.Infof("Running audit")
|
||||
var auditData validator.AuditData
|
||||
auditData, err = validator.RunAudit(adjustedConf, k)
|
||||
auditData, err = validator.RunAudit(ctx, adjustedConf, k)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error getting audit data: %v", err)
|
||||
http.Error(w, "Error running audit", 500)
|
||||
|
||||
@@ -16,6 +16,7 @@ package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
@@ -157,10 +158,5 @@ func getCategoryInfo(category string) string {
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(list, a)
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package fix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -19,7 +20,7 @@ const templateCloseMarker = "POLARIS_CLOSE_TMPL"
|
||||
|
||||
var ErrFilesPathRequired = errors.New("files-path flag is required")
|
||||
|
||||
func Execute(config config.Configuration, filesPath string, isTemplate bool, checksToFix ...string) error {
|
||||
func Execute(ctx context.Context, config config.Configuration, filesPath string, isTemplate bool, checksToFix ...string) error {
|
||||
if filesPath == "" {
|
||||
return ErrFilesPathRequired
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func Execute(config config.Configuration, filesPath string, isTemplate bool, che
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating resource provider from yaml: %v", err)
|
||||
}
|
||||
results, err := validator.ApplyAllSchemaChecksToResourceProvider(&config, kubeResources)
|
||||
results, err := validator.ApplyAllSchemaChecksToResourceProvider(ctx, &config, kubeResources)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying schema check to the resources %s: %v", fullFilePath, err)
|
||||
}
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
package insights
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type cluster struct {
|
||||
Name string `json:"Name"`
|
||||
AuthToken string `json:"AuthToken"`
|
||||
Organization string `json:"Organization"`
|
||||
Status string `json:"Status"`
|
||||
}
|
||||
|
||||
type reportJob struct {
|
||||
ID int `json:"id"`
|
||||
Status string `json:"status"`
|
||||
TimeTakenInMs int `json:"timeTaken"`
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
UpsertCluster(clusterName string) (*cluster, error)
|
||||
SendReport(cluster cluster, reportType, reportVersion string, payload []byte) (*reportJob, error)
|
||||
GetReportJob(clusterName string, reportJobID int) (*reportJob, error)
|
||||
IsTokenValid() (bool, error)
|
||||
}
|
||||
|
||||
type HTTPClient struct {
|
||||
insightsHost string
|
||||
organization string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewHTTPClient(host, organization, token string) Client {
|
||||
return HTTPClient{host, organization, token}
|
||||
}
|
||||
|
||||
func (ic HTTPClient) UpsertCluster(clusterName string) (*cluster, error) {
|
||||
clusterURL := fmt.Sprintf("%s/v0/organizations/%s/clusters/%s?showToken=true", ic.insightsHost, ic.organization, clusterName)
|
||||
req, err := http.NewRequest("GET", clusterURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building request for fetching cluster: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+ic.token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making request fetching cluster: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if isSuccessful2XX(resp.StatusCode) {
|
||||
// cluster already created
|
||||
logrus.Infof("cluster %q found...", clusterName)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response body: %w", err)
|
||||
}
|
||||
var c cluster
|
||||
err = json.Unmarshal(body, &c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
return &c, nil
|
||||
} else {
|
||||
logrus.Warnf("not able to fetch cluster, expected 2xx - received %d, will try to create", resp.StatusCode)
|
||||
}
|
||||
|
||||
logrus.Infof("cluster %q not found... creating..", clusterName)
|
||||
|
||||
req, err = http.NewRequest("POST", clusterURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building request for creating cluster: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+ic.token)
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making request for creating cluster: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response body: %w", err)
|
||||
}
|
||||
|
||||
if !isSuccessful2XX(resp.StatusCode) {
|
||||
return nil, fmt.Errorf("creating cluster, expected 200 OK received %s: %v", resp.Status, string(body))
|
||||
}
|
||||
|
||||
var c cluster
|
||||
err = json.Unmarshal(body, &c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
|
||||
logrus.Infof("cluster %q created...", clusterName)
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (ic HTTPClient) SendReport(cluster cluster, reportType, reportVersion string, payload []byte) (*reportJob, error) {
|
||||
uploadReportURL := fmt.Sprintf("%s/v0/organizations/%s/clusters/%s/data/%s", ic.insightsHost, ic.organization, cluster.Name, reportType)
|
||||
req, err := http.NewRequest("POST", uploadReportURL, bytes.NewBuffer(payload))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building request for output: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+cluster.AuthToken)
|
||||
req.Header.Set("X-Fairwinds-Report-Version", reportVersion)
|
||||
req.Header.Set("X-Fairwinds-Report-Priority", "4") // should have higher priority than the default 5
|
||||
req.Header.Set("X-Fairwinds-Agent-Version", "")
|
||||
req.Header.Set("X-Fairwinds-Agent-Chart-Version", "")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making request for output: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response body: %w", err)
|
||||
}
|
||||
if !isSuccessful2XX(resp.StatusCode) {
|
||||
return nil, fmt.Errorf("sending %s report, expected 200 OK received %s: %v", reportType, resp.Status, string(body))
|
||||
}
|
||||
|
||||
var rj reportJob
|
||||
err = json.Unmarshal(body, &rj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
logrus.Debugf("%s report sent to fairwinds insights", reportType)
|
||||
|
||||
return &rj, nil
|
||||
}
|
||||
|
||||
func isSuccessful2XX(statusCode int) bool {
|
||||
return statusCode >= 200 && statusCode < 300
|
||||
}
|
||||
|
||||
func (ic HTTPClient) GetReportJob(clusterName string, reportJobID int) (*reportJob, error) {
|
||||
reportJobsURL := fmt.Sprintf("%s/v0/organizations/%s/clusters/%s/report-jobs/%d", ic.insightsHost, ic.organization, clusterName, reportJobID)
|
||||
req, err := http.NewRequest("GET", reportJobsURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building request for fetching report-job: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+ic.token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("making request fetching report-job: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if !isSuccessful2XX(resp.StatusCode) {
|
||||
return nil, fmt.Errorf("fetching report-job, expected 200 OK received %s", resp.Status)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response body: %w", err)
|
||||
}
|
||||
var rj reportJob
|
||||
err = json.Unmarshal(body, &rj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling response body: %w", err)
|
||||
}
|
||||
return &rj, nil
|
||||
}
|
||||
|
||||
// IsTokenValid checks if the token is valid by fetching the organization
|
||||
func (ic HTTPClient) IsTokenValid() (bool, error) {
|
||||
organizationURL := fmt.Sprintf("%s/v0/organizations/%s", ic.insightsHost, ic.organization)
|
||||
req, err := http.NewRequest("GET", organizationURL, nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("building request for fetching organization: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+ic.token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("making request fetching organization: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if isSuccessful2XX(resp.StatusCode) {
|
||||
return true, nil // token is valid
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
|
||||
return false, nil // token is invalid
|
||||
}
|
||||
|
||||
// unexpected error
|
||||
return false, fmt.Errorf("fetching organization, expected 200 OK - received %s", resp.Status)
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package insights
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
workloads "github.com/fairwindsops/insights-plugins/plugins/workloads/pkg"
|
||||
"github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type insightsReporter struct {
|
||||
client Client
|
||||
}
|
||||
|
||||
func NewInsightsReporter(client Client) *insightsReporter {
|
||||
return &insightsReporter{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
type WorkloadsReport struct {
|
||||
Version string
|
||||
Payload workloads.ClusterWorkloadReport
|
||||
}
|
||||
|
||||
type PolarisReport struct {
|
||||
Version string
|
||||
Payload validator.AuditData
|
||||
}
|
||||
|
||||
// ReportAuditToFairwindsInsights report audit to insights
|
||||
// 1 - check if cluster exists, otherwise create it
|
||||
// 2 - send workload report
|
||||
// 3 - send polaris report
|
||||
// 4 - checks if report job is completed for 3 minutes
|
||||
// 5 - display link to Fairwinds Insights
|
||||
func (ir insightsReporter) ReportAuditToFairwindsInsights(clusterName string, wr WorkloadsReport, pr PolarisReport) error {
|
||||
cluster, err := ir.client.UpsertCluster(clusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
workloadsPayload, err := json.MarshalIndent(wr.Payload, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling data: %w", err)
|
||||
}
|
||||
_, err = ir.client.SendReport(*cluster, "workloads", wr.Version, workloadsPayload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
polarisPayload, err := json.MarshalIndent(pr.Payload, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling data: %w", err)
|
||||
}
|
||||
reportJob, err := ir.client.SendReport(*cluster, "polaris", pr.Version, polarisPayload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success, err := verifyReportJobCompletion(&ir, cluster.Name, reportJob.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !success {
|
||||
return fmt.Errorf("timed out waiting for report job to complete")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyReportJobCompletion checks Insights for reportJob completion (timeout after 3 minutes)
|
||||
func verifyReportJobCompletion(ir *insightsReporter, clusterName string, reportJobID int) (bool, error) {
|
||||
defer func() { fmt.Println() }()
|
||||
logrus.Println("Processing (this usually takes 1-3 minutes)...")
|
||||
for i := 0; i < 60; i++ {
|
||||
reportJob, err := ir.client.GetReportJob(clusterName, reportJobID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if reportJob.Status == "completed" {
|
||||
return true, nil
|
||||
}
|
||||
fmt.Print(".")
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
+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
|
||||
@@ -157,7 +157,7 @@ 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()
|
||||
lastKey := ""
|
||||
@@ -219,7 +219,7 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
|
||||
|
||||
func cacheSingleObject(ctx context.Context, apiVersion, kind, namespace, name string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper, objectCache map[string]unstructured.Unstructured) error {
|
||||
logrus.Debugf("Caching a single %s", kind)
|
||||
object, err := getObject(ctx, namespace, kind, apiVersion, name, dynamicClient, restMapper)
|
||||
object, err := GetObject(ctx, namespace, kind, apiVersion, name, dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
logrus.Warnf("error retrieving object %s/%s/%s/%s because of error: %v", kind, apiVersion, namespace, name, err)
|
||||
return err
|
||||
@@ -252,7 +252,7 @@ func cacheAllObjectsOfKind(ctx context.Context, apiVersion, kind string, dynamic
|
||||
return nil
|
||||
}
|
||||
|
||||
func getObject(ctx context.Context, namespace, kind, version, name string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) (*unstructured.Unstructured, error) {
|
||||
func GetObject(ctx context.Context, namespace, kind, version, name string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) (*unstructured.Unstructured, error) {
|
||||
fqKind := schema.FromAPIVersionAndKind(version, kind)
|
||||
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
if err != nil {
|
||||
@@ -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
|
||||
|
||||
+23
-11
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -169,7 +171,7 @@ func CreateResourceProviderFromResource(ctx context.Context, workload string) (*
|
||||
version := parts[2]
|
||||
name := parts[3]
|
||||
|
||||
obj, err := getObject(ctx, namespace, kind, version, name, dynamicClient, restMapper)
|
||||
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)
|
||||
}
|
||||
@@ -304,6 +306,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 {
|
||||
@@ -383,6 +393,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 +453,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 +466,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 +480,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 +494,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -161,7 +161,6 @@ func createPathAndFindNodes(node *yaml.Node, selectors []string, create bool) ([
|
||||
node.Value = ""
|
||||
case yaml.SequenceNode:
|
||||
return nil, errors.Errorf("parent node is array, use /*/ or /0/../%v/ instead of .%v to access its item(s) first", len(node.Content)-1, currentSelector)
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("parent node is of unknown kind %v", node.Kind)
|
||||
}
|
||||
@@ -225,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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
@@ -36,7 +37,7 @@ func TestValidatePDB(t *testing.T) {
|
||||
res, err := kube.NewGenericResourceFromUnstructured(pdb, nil)
|
||||
res.Kind = "PodDisruptionBudget"
|
||||
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, nil, res)
|
||||
actualResult, err := applyNonControllerSchemaChecks(context.Background(), &c, nil, res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -76,7 +77,7 @@ func TestValidateIngress(t *testing.T) {
|
||||
}
|
||||
res.Kind = "Ingress"
|
||||
|
||||
actualResult, err := applyNonControllerSchemaChecks(&c, nil, res)
|
||||
actualResult, err := applyNonControllerSchemaChecks(context.Background(), &c, nil, res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -69,7 +70,7 @@ func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourc
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
|
||||
var results ResultSet
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, nil, workload, container, false)
|
||||
results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, workload, container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func TestValidateResourcesEmptyConfig(t *testing.T) {
|
||||
Name: "Empty",
|
||||
}
|
||||
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{}, nil, getEmptyWorkload(t, ""), container, false)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{}, nil, getEmptyWorkload(t, ""), container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -190,7 +191,7 @@ func TestValidateHealthChecks(t *testing.T) {
|
||||
for idx, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, nil, controller, tt.container, tt.isInit)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.probes}, nil, controller, tt.container, tt.isInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -304,7 +305,7 @@ func TestValidateImage(t *testing.T) {
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, nil, controller, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.image}, nil, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -421,7 +422,7 @@ func TestValidateNetworking(t *testing.T) {
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyWorkload(t, "")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, nil, controller, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.networkConf}, nil, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -926,7 +927,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
|
||||
assert.NoError(t, err)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, nil, workload, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.securityConf}, nil, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1071,7 +1072,7 @@ func TestValidateRunAsRoot(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil)
|
||||
assert.NoError(t, err)
|
||||
results, err := applyContainerSchemaChecks(&config, nil, workload, tt.container, false)
|
||||
results, err := applyContainerSchemaChecks(context.Background(), &config, nil, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestValidateController(t *testing.T) {
|
||||
}
|
||||
|
||||
var actualResult Result
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, deployment)
|
||||
actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func TestControllerLevelChecks(t *testing.T) {
|
||||
Category: "Reliability",
|
||||
}
|
||||
for _, controller := range res.Resources["Deployment"] {
|
||||
actualResult, err := applyControllerSchemaChecks(&c, nil, controller)
|
||||
actualResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, controller)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -99,7 +99,7 @@ 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)
|
||||
@@ -137,7 +137,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
"livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Reliability"},
|
||||
}
|
||||
var actualResult Result
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, deployment)
|
||||
actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
Dangers: uint(0),
|
||||
}
|
||||
expectedResults = ResultSet{}
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, job)
|
||||
actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, job)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
Dangers: uint(0),
|
||||
}
|
||||
expectedResults = ResultSet{}
|
||||
actualResult, err = applyControllerSchemaChecks(&c, nil, cronjob)
|
||||
actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, cronjob)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
resources := []kube.GenericResource{workload}
|
||||
|
||||
var actualResults []Result
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
c.Exemptions = []conf.Exemption{{
|
||||
Namespace: "foo",
|
||||
}}
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -233,7 +233,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
resources[0].ObjectMeta.SetAnnotations(map[string]string{
|
||||
exemptionAnnotationKey: "true",
|
||||
})
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func TestControllerExemptions(t *testing.T) {
|
||||
assert.EqualValues(t, expectedExemptSum, actualResults[0].GetSummary())
|
||||
|
||||
c.DisallowExemptions = true
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources)
|
||||
actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/qri-io/jsonschema"
|
||||
)
|
||||
|
||||
type validatorFunction func(test schemaTestCase) (bool, []jsonschema.ValError, error)
|
||||
type validatorFunction func(test schemaTestCase) (bool, []jsonschema.KeyError, error)
|
||||
|
||||
var validatorMapper = map[string]validatorFunction{}
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
@@ -16,6 +16,7 @@ package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -29,13 +30,13 @@ import (
|
||||
)
|
||||
|
||||
// RunAudit runs a full Polaris audit and returns an AuditData object
|
||||
func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) {
|
||||
func RunAudit(ctx context.Context, config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) {
|
||||
displayName := config.DisplayName
|
||||
if displayName == "" {
|
||||
displayName = kubeResources.SourceName
|
||||
}
|
||||
|
||||
results, err := ApplyAllSchemaChecksToResourceProvider(&config, kubeResources)
|
||||
results, err := ApplyAllSchemaChecksToResourceProvider(ctx, &config, kubeResources)
|
||||
if err != nil {
|
||||
return AuditData{}, err
|
||||
}
|
||||
@@ -49,6 +50,7 @@ func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (
|
||||
ClusterInfo: ClusterInfo{
|
||||
Version: kubeResources.ServerVersion,
|
||||
Nodes: len(kubeResources.Nodes),
|
||||
Pods: len(kubeResources.Pods),
|
||||
Namespaces: len(kubeResources.Namespaces),
|
||||
Controllers: kubeResources.Resources.GetNumberOfControllers(),
|
||||
},
|
||||
|
||||
@@ -45,12 +45,15 @@ func TestGetTemplateData(t *testing.T) {
|
||||
score := uint(0)
|
||||
|
||||
var actualAudit AuditData
|
||||
actualAudit, err = RunAudit(c, resources)
|
||||
actualAudit, err = RunAudit(context.Background(), c, resources)
|
||||
assert.Equal(t, err, nil, "error should be nil")
|
||||
assert.Equal(t, score, actualAudit.Score, "")
|
||||
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user