Compare commits

...
53 Commits
Author SHA1 Message Date
renovate[bot]andlnx01 c0e23a233b Update cimg/go Docker tag to v1.26.6 (#1229)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 10:45:05 -03:00
renovate[bot]andlnx01 3bfea9bf10 Update module github.com/sirupsen/logrus to v1.10.0 (#1227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 14:05:50 -03:00
jdesouza 236ca14fca INS-2798: Refactor polaris to make it more compatible with other oss projects (#1226)
* INS-2798: Refactor polaris to make it more compatible with other oss projects

* INS-2798: Refactor polaris to make it more compatible with other oss projects

* Fix
2026-08-19 12:19:00 -03:00
renovate[bot]andlnx01 9fe4a72c77 Update module github.com/stretchr/testify to v1.12.0 (#1224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-17 08:48:48 -03:00
Terraform User dafa0b74fc Managed by Terraform 2026-08-06 16:10:01 +00:00
f0a370c757 Update dependency rok8s to v16 (#1221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: jdesouza <james@fairwinds.com>
2026-08-06 09:26:30 -03:00
cb741e3eb1 Update all non-major dependencies (#1219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: jdesouza <james@fairwinds.com>
2026-08-06 09:14:18 -03:00
renovate[bot]andlnx01 b65142e8be Pin dependency rok8s to 14.4.0 (#1218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 08:58:02 -03:00
Terraform User 31bc0fdb84 Managed by Terraform 2026-08-05 14:12:49 +00:00
292a1c257c Bump k8s.io/client-go from 0.35.4 to 0.36.3 (#1217)
* Bump k8s.io/client-go from 0.35.4 to 0.36.3

Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.35.4 to 0.36.3.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.35.4...v0.36.3)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.36.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump sigs.k8s.io/controller-runtime to v0.24.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vitor Vezani <vitor.vezani@fairwinds.com>
2026-08-03 11:03:11 -03:00
dependabot[bot]andlnx01 06ef2d3f70 Bump k8s.io/apimachinery from 0.36.2 to 0.36.3 (#1216)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.36.2 to 0.36.3.
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.36.2...v0.36.3)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.36.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-03 10:44:28 -03:00
Vitor Rodrigo Vezani 4bdf5315b6 Add pod support to ResourceProvider and update related tests (#1214) 2026-07-21 13:19:25 -03:00
dependabot[bot]andlnx01 278cb9008c Bump golang.org/x/net from 0.51.0 to 0.55.0 (#1208)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.51.0 to 0.55.0.
- [Commits](https://github.com/golang/net/compare/v0.51.0...v0.55.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.55.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 18:09:58 -03:00
d6858e0fe4 Bump alpine from 3.23.4 to 3.24.1 (#1209)
Bumps alpine from 3.23.4 to 3.24.1.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 3.24.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vitor Rodrigo Vezani <vitor.vezani@fairwinds.com>
2026-07-06 17:36:37 -03:00
dependabot[bot]andlnx01 78fe4cb11c Bump k8s.io/apimachinery from 0.36.1 to 0.36.2 (#1210)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.36.1 to 0.36.2.
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.36.1...v0.36.2)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 17:33:43 -03:00
dependabot[bot]andlnx01 387e787a40 Bump k8s.io/apimachinery from 0.35.4 to 0.36.1 (#1197)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.35.4 to 0.36.1.
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.35.4...v0.36.1)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.36.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-01 08:46:46 -03:00
Terraform User 1fdfec73a1 Managed by Terraform 2026-04-27 18:39:00 +00:00
Vitor Rodrigo Vezani aee298e2e0 add notice to include registry change and immutable images notice (#1191)
* add notice to include registry change and immutable images notice

* try to fix tests

* Refactor goreleaser script to conditionally skip Docker tags based on release status

* remove if
2026-04-27 12:42:05 -03:00
Vitor Rodrigo Vezani 0444cf92bc unify release process along with others OSS projects (#1192)
* unify release process along with others OSS projects

* remove docker loging

* override polaris image repository for tests

* fix polaris

* adding snapshot back

* store snapshot version into circleci workspace and load with kind on k8s cluster

* add test back

* add test back

* fix polaris config
2026-04-27 12:24:11 -03:00
Terraform User 50e4c63079 Managed by Terraform 2026-04-24 20:23:41 +00:00
Terraform User 7d053a6cbd Managed by Terraform 2026-04-23 15:34:04 +00:00
Terraform User d50336387b Managed by Terraform 2026-04-23 14:42:23 +00:00
Vitor Rodrigo Vezani 1554c90ece Add GCR support (#1189) 2026-04-22 10:45:37 -03:00
jdesouza 9dfb4262e6 INS-2239: Fix polaris vulnerabilities (#1187)
* Reapply "Fix vulnerabilities"

This reverts commit 27878a4670.

* Fix

* Fix

* Fix

* Fix
2026-04-21 15:32:09 -03:00
jdesouza 27878a4670 Revert "Fix vulnerabilities"
This reverts commit a46eeb28b6.
2026-04-21 14:50:44 -03:00
jdesouza a46eeb28b6 Fix vulnerabilities 2026-04-21 14:49:25 -03:00
Vitor Rodrigo Vezani fd862afadc remove adhoc envsubst process on goreleaser (#1186)
* remove adhoc envsubst process

* use envOrDefault instead of sprig's default
2026-04-21 13:05:27 -03:00
Terraform User ee9f568ebd Managed by Terraform 2026-04-15 20:14:16 +00:00
jdesouza 47c7deddfd INS-2152: polaris libs bump (#1182)
* Upgrade libs

* INS-2152: polaris libs bump
2026-04-01 15:17:52 -03:00
jdesouza 2007a2c317 INS-2026: Bump libs and fix vulnerabilities for polaris (#1176)
* Bump

* Bump

* Bump

* Bump

* Bump

* Fix

* FIx

* FIx

* Fix
2026-03-09 14:16:16 -03:00
jdesouza a5af7bb34e INS-1950: Go 1.26 and bump libs for polaris (#1172)
* Go 1.26

* Go 1.26

* Go 1.26

* Go 1.26
2026-02-24 12:22:14 -03:00
jdesouza 893e5de589 Revert "Go 1.26"
This reverts commit f6eed6589d.
2026-02-23 08:23:23 -03:00
jdesouza f6eed6589d Go 1.26 2026-02-23 08:22:16 -03:00
jdesouza 517c8899d7 INS-1829: Bump libs and fix vulnerabilities (#1171)
* Fix vulnerability

* Bump
2026-01-30 16:21:23 -03:00
jdesouza e2df222ded INS-1683: polaris: Bump go to 1.25.5 for fixing vulnerability (#1162)
* INS-1683: polaris: Bump go to 1.25.5 for fixing vulnerability

* INS-1683: polaris: Bump go to 1.25.5 for fixing vulnerability
2025-12-08 10:50:39 -03:00
jdesouza d45d67cdd2 Revert "INS-1683: polaris: Bump go to 1.25.5 for fixing vulnerability"
This reverts commit e8a113388e.
2025-12-08 09:48:07 -03:00
jdesouza e8a113388e INS-1683: polaris: Bump go to 1.25.5 for fixing vulnerability 2025-12-08 09:44:58 -03:00
jdesouza 21ef1f495c INS-1562: Fix polaris vulnerabilities (#1156) 2025-10-31 11:01:10 -03:00
jdesouza ec1ba2f2db INS-1303: fix CVE-2025-47907 (#1141)
* Revert "Revert "INS-1303: fix CVE-2025-47907""

This reverts commit 39fb27c6dd.

* INS-1303: fix CVE-2025-47907
2025-08-18 13:01:19 -03:00
jdesouza 39fb27c6dd Revert "INS-1303: fix CVE-2025-47907"
This reverts commit e51b90ef79.
2025-08-18 10:48:27 -03:00
jdesouza e51b90ef79 INS-1303: fix CVE-2025-47907 2025-08-18 10:46:11 -03:00
Andy Suderman 4de082b443 docs: update documentation from template (#1140) 2025-08-01 13:24:25 -06:00
dependabot[bot]andlnx01 264dc51f4c Bump sigs.k8s.io/yaml from 1.4.0 to 1.5.0 (#1127)
---
updated-dependencies:
- dependency-name: sigs.k8s.io/yaml
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-25 11:23:39 -03:00
jdesouza 45b1f6a1a5 Bumped libs (#1139) 2025-07-25 10:18:03 -03:00
Vitor Rodrigo Vezaniandjdesouza 38e540e0cf Remove flags from audit command and remove auth command (#1138)
* update libs

* Remove unused authentication code and dependencies from the Polaris project, streamlining the audit process by eliminating the upload insights feature and related flags.

* remove insights reporter

* remove lingering libs

* update docs

* INS-1251: Polaris: upgrade github.com/qri-io/jsonschema to v0.2.1 (#1135)

* Bump lins

* Code refactoring

* Fixign issues

* Fixing issues

* Fixing issues

* Fixing issues

* [WIP]

* [WIP]

* [WIP]

* Trying to fix tests

* Trying to fix tests

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Revert go mod

* Revert go mod

* Revert go mod

* Revert go mod

* Fixing issues

* Fixing issue

* Code refactoring

* Updating json schema version

* Updating json schema version

* fix go mod

* fix go sum

---------

Co-authored-by: jdesouza <james@fairwinds.com>
2025-07-24 16:48:23 -03:00
jdesouza 2b17c31957 INS-1251: Polaris: upgrade github.com/qri-io/jsonschema to v0.2.1 (#1135)
* Bump lins

* Code refactoring

* Fixign issues

* Fixing issues

* Fixing issues

* Fixing issues

* [WIP]

* [WIP]

* [WIP]

* Trying to fix tests

* Trying to fix tests

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Fixing issues

* Revert go mod

* Revert go mod

* Revert go mod

* Revert go mod

* Fixing issues

* Fixing issue

* Code refactoring

* Updating json schema version

* Updating json schema version
2025-07-24 13:46:37 -03:00
jdesouza 813d9c0a2a INS-1097: Fix CVE-2025-22874 for polaris (#1122)
* Bumping polaris libs

* Fixing lint

* Fixed vuln

* Fixed vuln

* Fixed vuln

* Fixed vuln
2025-06-17 08:58:10 -03:00
jdesouza d7d30ac611 INS-933 - pullPolicyNotAlways is crashing webhook mutations (#1115)
* Debugging

* Debugging

* Trying to fix

* Revert

* Debugging

* Debugging

* Debugging

* Fixing issue

* Fixing issue

* Fixing issue

* Fixing issue

* Fixing issue

* Fixing issue
2025-04-21 17:07:12 -03:00
dependabot[bot]andlnx01 8fe7a94260 Bump golang.org/x/net from 0.33.0 to 0.36.0 (#1108)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.36.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 13:26:47 -03:00
dependabot[bot]andlnx01 f29571e8bb Bump k8s.io/client-go from 0.32.0 to 0.32.2 (#1103)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.32.0 to 0.32.2.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.32.0...v0.32.2)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 13:21:50 -03:00
dependabot[bot]andlnx01 a74714b5e5 Bump k8s.io/apimachinery from 0.32.0 to 0.32.2 (#1102)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.32.0 to 0.32.2.
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.32.0...v0.32.2)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 13:16:16 -03:00
dependabot[bot]andlnx01 ba04b7e330 Bump sigs.k8s.io/controller-runtime from 0.19.3 to 0.20.2 (#1101)
Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.19.3 to 0.20.2.
- [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases)
- [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.19.3...v0.20.2)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 12:42:36 -03:00
youM d70591b1b8 fixed pdbMinAvailableGreaterThanHPAMinReplicas and added validation for pdbMinAvailableEqualToHPAMinReplicas. (#1073)
* rename pdbMinAvailableGreaterThanHPAMinReplicas to pdbMinAvailableGreaterThanOrEqualToHPAMinReplicas

* modify doc

* Revert "modify doc"

This reverts commit faffffc298cde23a99c5e19528123f249d92d314.

* Revert "rename pdbMinAvailableGreaterThanHPAMinReplicas to pdbMinAvailableGreaterThanOrEqualToHPAMinReplicas"

This reverts commit b93f283acc279b90c7e7a9d8dbbe53b2f8ab9a31.

* fix test

* fix impl
2025-04-14 11:36:32 -03:00
110 changed files with 1064 additions and 1857 deletions
+132 -140
View File
@@ -1,180 +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.22
- 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:v2.3.2
steps:
- checkout
- setup_remote_docker
- *install_vault_alpine
- *install_vault_machine
- rok8s/get_vault_env:
vault_path: repo/global/env
- rok8s/get_vault_env:
vault_path: repo/polaris/env
- *set_environment_variables
- run: docker login quay.io -u="${FAIRWINDS_QUAY_USER}" -p="${FAIRWINDS_QUAY_TOKEN}"
- install_goreleaser_dependencies
- run: scripts/goreleaser.sh
- run:
name: docker login Google Artifact Registry
command: |
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 -1
View File
@@ -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 \
-20
View File
@@ -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"
+2 -2
View File
@@ -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:
-2
View File
@@ -1,5 +1,3 @@
# goreleaser is run via a wrapper that creates .goreleaser.yml from .goreleaser.yml.envsubst
.goreleaser.yml
# dist
# # Binaries for programs and plugins
.go-version
+116
View File
@@ -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"
-114
View File
@@ -1,114 +0,0 @@
version: 2
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
env:
- GOBIN={{ .Env.TMPDIR }}/go-bin
before:
hooks:
- go mod download
builds:
- id: polaris
ldflags:
- -X main.Version={{.Version}} -X main.Commit={{.Commit}} -s -w
env:
- CGO_ENABLED=0
- GO111MODULE=on
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm
- arm64
goarm:
- 6
- 7
archives:
- id: polaris
builds: ["polaris"]
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
signs:
- cmd: cosign
args: ["sign-blob", "--key=hashivault://cosign", "-output-signature=${signature}", "${artifact}"]
artifacts: checksum
release:
# This is replaced using `envsubst`, depending on the git branch.
disable: ${skip_release}
prerelease: auto
footer: |
You can verify the signature of the checksums.txt file using [cosign](https://github.com/sigstore/cosign).
```
cosign verify-blob checksums.txt --signature=checksums.txt.sig --key https://artifacts.fairwinds.com/cosign.pub
```
brews:
- name: polaris
# This is replaced using `envsubst`, depending on the git branch.
skip_upload: ${skip_release}
repository:
owner: FairwindsOps
name: homebrew-tap
directory: Formula
description: Open Source Best Practices for Kubernetes
url_template: "https://github.com/FairwindsOps/polaris/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
test: |
system "#{bin}/polaris version"
dockers:
# There are multiple images to match the `--platform` docker build flag with
# combinations of `GOOS`, `GOARCH`, and `GOARM`
- image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
- image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
use: buildx
goarch: arm64
goos: linux
build_flag_templates:
- "--platform=linux/arm64"
docker_manifests:
# Create DOcker manifests that make multiple architectures available within a tag,
# and provide partial-version tags like 2, and 2.2.
- name_template: quay.io/fairwinds/polaris:{{ .FullCommit }}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
- name_template: quay.io/fairwinds/polaris:{{ .Env.feature_docker_tag }}
# This is replaced using `envsubst`, depending on the git branch.
skip_push: ${skip_feature_docker_tags}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
- name_template: quay.io/fairwinds/polaris:latest
# This is replaced using `envsubst`, depending on the git branch.
skip_push: ${skip_release}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
- name_template: quay.io/fairwinds/polaris:{{ .Tag }}
# This is replaced using `envsubst`, depending on the git branch.
skip_push: ${skip_release}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
- name_template: quay.io/fairwinds/polaris:{{ .Major }}
# This is replaced using `envsubst`, depending on the git branch.
skip_push: ${skip_release}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
- name_template: quay.io/fairwinds/polaris:{{ .Major }}.{{ .Minor }}
# This is replaced using `envsubst`, depending on the git branch.
skip_push: ${skip_release}
image_templates:
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-amd64"
- "quay.io/fairwinds/polaris:{{ .FullCommit }}-arm64"
+2 -2
View File
@@ -1,4 +1,4 @@
FROM alpine:3.21
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
+39 -12
View File
@@ -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-2na8gtwb4-DGQ4qgmQbczQyB2NlFlYQQ)
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>
## 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
View File
@@ -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)
-81
View File
@@ -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
},
}
+2 -1
View File
@@ -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
View File
@@ -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")
+2 -1
View File
@@ -15,6 +15,7 @@
package cmd
import (
"context"
"os"
"github.com/sirupsen/logrus"
@@ -76,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 {
-16
View File
@@ -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:
+3 -3
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -42,7 +42,7 @@ polaris dashboard --port 8080 --audit-path=./deploy/
### Local Docker container
```
docker run -d -p8080:8080 -v ~/.kube/config:/opt/app/config:ro quay.io/fairwinds/polaris:1.2 polaris dashboard --kubeconfig /opt/app/config
docker run -d -p8080:8080 -v ~/.kube/config:/opt/app/config:ro us-docker.pkg.dev/fairwinds-ops/oss/polaris:1.2 polaris dashboard --kubeconfig /opt/app/config
```
## Using the Dashboard
Executable
+35
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -4,4 +4,4 @@ options:
images:
docker:
- quay.io/fairwinds/polaris:$CI_SHA1
- us-docker.pkg.dev/fairwinds-ops/oss/polaris:$CI_SHA1
+60 -57
View File
@@ -1,83 +1,86 @@
module github.com/fairwindsops/polaris
go 1.23.0
toolchain go1.23.4
go 1.26.2
require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/fairwindsops/controller-utils v0.3.4
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20241211185107-d14f77fb8b16
github.com/fatih/color v1.18.0
github.com/fatih/color v1.19.0
github.com/gorilla/mux v1.8.1
github.com/pkg/errors v0.9.1
github.com/qri-io/jsonschema v0.1.2
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
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.4.0
gomodules.xyz/jsonpatch/v2 v2.5.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.32.0
k8s.io/apimachinery v0.32.0
k8s.io/client-go v0.32.0
sigs.k8s.io/controller-runtime v0.19.3
sigs.k8s.io/yaml v1.4.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.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // 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.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // 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/klauspost/compress v1.17.11 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // 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.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.61.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/qri-io/jsonpointer v0.1.1 // indirect
github.com/samber/lo v1.47.0 // indirect
github.com/spf13/pflag v1.0.5 // 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
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // 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
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // 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
)
+145 -182
View File
@@ -1,256 +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.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.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
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.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
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/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20241211185107-d14f77fb8b16 h1:y3+g/C4Tfb3g7I4iusxibAeN/OASP+W8R32NrpC9Mu0=
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20241211185107-d14f77fb8b16/go.mod h1:KN0OLQN6jYlxba9O7qHnfF44Ry6viGKE2DZZyRa/2Zk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
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.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.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/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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/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-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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
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.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/onsi/ginkgo/v2 v2.27.4 h1:fcEcQW/A++6aZAZQNUmNjvA9PSOzefMJBerHJ4t8v8Y=
github.com/onsi/ginkgo/v2 v2.27.4/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
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.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
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.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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-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.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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=
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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-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.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw=
sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
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=
-53
View File
@@ -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
}
-45
View File
@@ -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")
}
}
-291
View File
@@ -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
}
-35
View File
@@ -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
}
-46
View File
@@ -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)
}
-20
View File
@@ -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
}
@@ -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.
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -3,7 +3,7 @@ failureMessage: HostPath volumes must be forbidden
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:
volumes:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -3,7 +3,7 @@ failureMessage: Privileged access to the host is disallowed
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:
containers:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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}}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -3,7 +3,7 @@ failureMessage: Proc mount must not be changed from the default
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:
containers:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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
+5 -5
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+12 -12
View File
@@ -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
@@ -142,35 +142,35 @@ func TestConfigNoServerError(t *testing.T) {
}
func TestConfigWithCustomChecks(t *testing.T) {
valid := map[string]interface{}{
"securityContext": map[string]interface{}{
valid := map[string]any{
"securityContext": map[string]any{
"foo": "bar",
},
}
invalid := map[string]interface{}{
"notSecurityContext": map[string]interface{}{},
invalid := map[string]any{
"notSecurityContext": map[string]any{},
}
parsedConf, err := Parse([]byte(confCustomChecks))
assert.NoError(t, err, "Expected no error when parsing YAML config")
assert.Equal(t, 1, len(parsedConf.CustomChecks))
check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]interface{}{})
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)
}
+2 -2
View File
@@ -75,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
@@ -105,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:
+83 -67
View File
@@ -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
+4 -3
View File
@@ -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)
+2 -6
View File
@@ -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
View File
@@ -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)
}
-200
View File
@@ -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)
}
-89
View File
@@ -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
}
+11 -11
View File
@@ -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 := ""
@@ -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
+22 -10
View File
@@ -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),
}
}
@@ -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
+1
View File
@@ -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)
}
+1 -2
View File
@@ -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
+3 -3
View File
@@ -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",
+3 -2
View File
@@ -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)
}
+8 -7
View File
@@ -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)
}
+10 -10
View File
@@ -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)
}
+1 -1
View File
@@ -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{}
+4 -2
View File
@@ -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(),
},
+4 -1
View File
@@ -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
+15 -13
View File
@@ -250,15 +250,16 @@ func fillString(id string, l int) string {
// GetPrettyOutput returns a human-readable string
func (res AuditData) GetPrettyOutput(useColor bool) string {
color.NoColor = !useColor
str := titleColor.Sprint(fmt.Sprintf("Polaris audited %s %s at %s\n", res.SourceType, res.SourceName, res.AuditTime))
str += color.CyanString(fmt.Sprintf(" Nodes: %d | Namespaces: %d | Controllers: %d\n", res.ClusterInfo.Nodes, res.ClusterInfo.Namespaces, res.ClusterInfo.Controllers))
str += color.GreenString(fmt.Sprintf(" Final score: %d\n", res.Score))
str += "\n"
var str strings.Builder
str.WriteString(titleColor.Sprint(fmt.Sprintf("Polaris audited %s %s at %s\n", res.SourceType, res.SourceName, res.AuditTime)))
str.WriteString(color.CyanString(fmt.Sprintf(" Nodes: %d | Namespaces: %d | Controllers: %d\n", res.ClusterInfo.Nodes, res.ClusterInfo.Namespaces, res.ClusterInfo.Controllers)))
str.WriteString(color.GreenString(fmt.Sprintf(" Final score: %d\n", res.Score)))
str.WriteString("\n")
for _, result := range res.Results {
str += result.GetPrettyOutput() + "\n"
str.WriteString(result.GetPrettyOutput() + "\n")
}
color.NoColor = false
return str
return str.String()
}
// GetPrettyOutput returns a human-readable string
@@ -277,11 +278,12 @@ func (res Result) GetPrettyOutput() string {
// GetPrettyOutput returns a human-readable string
func (res PodResult) GetPrettyOutput() string {
str := res.Results.GetPrettyOutput()
var str strings.Builder
str.WriteString(res.Results.GetPrettyOutput())
for _, cont := range res.ContainerResults {
str += cont.GetPrettyOutput()
str.WriteString(cont.GetPrettyOutput())
}
return str
return str.String()
}
// GetPrettyOutput returns a human-readable string
@@ -296,7 +298,7 @@ const minIDLength = 40
// GetPrettyOutput returns a human-readable string
func (res ResultSet) GetPrettyOutput() string {
indent := " "
str := ""
var str strings.Builder
for _, msg := range res {
status := color.GreenString(successMessage)
if !msg.Success {
@@ -309,8 +311,8 @@ func (res ResultSet) GetPrettyOutput() string {
if color.NoColor {
status = strings.Fields(status)[1] // remove emoji
}
str += fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status)
str += fmt.Sprintf("%s %s - %s\n", indent, msg.Category, msg.Message)
str.WriteString(fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status))
str.WriteString(fmt.Sprintf("%s %s - %s\n", indent, msg.Category, msg.Message))
}
return str
return str.String()
}
+4 -4
View File
@@ -19,7 +19,7 @@ func init() {
registerCustomChecks("pdbMinAvailableGreaterThanHPAMinReplicas", pdbMinAvailableGreaterThanHPAMinReplicas)
}
func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []jsonschema.ValError, error) {
func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
if test.ResourceProvider == nil {
return true, nil, nil
}
@@ -69,12 +69,12 @@ func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []json
}
}
if attachedHPA.Spec.MinReplicas != nil && pdbMinAvailable >= int(*attachedHPA.Spec.MinReplicas) {
return false, []jsonschema.ValError{
if attachedHPA.Spec.MinReplicas != nil && pdbMinAvailable > int(*attachedHPA.Spec.MinReplicas) {
return false, []jsonschema.KeyError{
{
PropertyPath: "spec.minAvailable",
InvalidValue: pdbMinAvailable,
Message: fmt.Sprintf("The minAvailable value in the PodDisruptionBudget(%s) is %d, which is greater or equal than the minReplicas value in the HorizontalPodAutoscaler(%s) (%d)", attachedPDB.Name, pdbMinAvailable, attachedHPA.Name, *attachedHPA.Spec.MinReplicas),
Message: fmt.Sprintf("The minAvailable value in the PodDisruptionBudget(%s) is %d, which is greater than the minReplicas value in the HorizontalPodAutoscaler(%s) (%d)", attachedPDB.Name, pdbMinAvailable, attachedHPA.Name, *attachedHPA.Spec.MinReplicas),
},
}, nil
}
+6 -5
View File
@@ -15,6 +15,7 @@
package validator
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@@ -57,7 +58,7 @@ func TestValidatePod(t *testing.T) {
"hostProcess": {ID: "hostProcess", Message: "Privileged access to the host check is valid", Success: true, Severity: "warning", Category: "Security"},
}
actualPodResult, err := applyControllerSchemaChecks(&c, nil, deployment)
actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, deployment)
if err != nil {
panic(err)
}
@@ -115,7 +116,7 @@ func TestInvalidIPCPod(t *testing.T) {
"hostProcess": {ID: "hostProcess", Message: "Privileged access to the host is disallowed", Success: false, Severity: "warning", Category: "Security"},
}
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload)
if err != nil {
panic(err)
}
@@ -151,7 +152,7 @@ func TestInvalidNetworkPod(t *testing.T) {
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
}
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload)
if err != nil {
panic(err)
}
@@ -187,7 +188,7 @@ func TestInvalidPIDPod(t *testing.T) {
"hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Security"},
}
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload)
if err != nil {
panic(err)
}
@@ -230,7 +231,7 @@ func TestExemption(t *testing.T) {
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"},
}
actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload)
actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload)
if err != nil {
panic(err)
}
+78 -46
View File
@@ -15,6 +15,8 @@
package validator
import (
"context"
"encoding/json"
"errors"
"fmt"
"sort"
@@ -97,7 +99,7 @@ func resolveCheck(conf *config.Configuration, checkID string, test schemaTestCas
// getTemplateInput augments a schemaTestCase.Resource.Resource.Object with
// Polaris built-in variables. The result can be used as input for
// CheckSchema.TemplateForResource().
func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
func getTemplateInput(test schemaTestCase) (map[string]any, error) {
templateInput := test.Resource.Resource.Object
if templateInput == nil {
return nil, nil
@@ -111,7 +113,7 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
if err != nil {
return nil, err
}
podTemplateMap, ok := test.Resource.PodTemplate.(map[string]interface{})
podTemplateMap, ok := test.Resource.PodTemplate.(map[string]any)
if ok {
err := unstructured.SetNestedMap(templateInput, podTemplateMap, "Polaris", "PodTemplate")
if err != nil {
@@ -133,7 +135,7 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
return templateInput, nil
}
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.ValError) ResultMessage {
func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.KeyError) ResultMessage {
details := []string{}
for _, issue := range issues {
details = append(details, issue.Message)
@@ -172,13 +174,13 @@ func hasExemptionAnnotation(objMeta metaV1.Object, checkID string) bool {
}
// ApplyAllSchemaChecksToResourceProvider applies all available checks to a ResourceProvider
func ApplyAllSchemaChecksToResourceProvider(conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) {
func ApplyAllSchemaChecksToResourceProvider(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) {
results := []Result{}
if resourceProvider == nil {
return nil, errors.New("No resource provider set, cannot apply schema checks")
}
for _, resources := range resourceProvider.Resources {
kindResults, err := ApplyAllSchemaChecksToAllResources(conf, resourceProvider, resources)
kindResults, err := ApplyAllSchemaChecksToAllResources(ctx, conf, resourceProvider, resources)
if err != nil {
return results, err
}
@@ -188,10 +190,10 @@ func ApplyAllSchemaChecksToResourceProvider(conf *config.Configuration, resource
}
// ApplyAllSchemaChecksToAllResources applies available checks to a list of resources
func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resources []kube.GenericResource) ([]Result, error) {
func ApplyAllSchemaChecksToAllResources(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resources []kube.GenericResource) ([]Result, error) {
results := []Result{}
for _, resource := range resources {
result, err := ApplyAllSchemaChecks(conf, resourceProvider, resource)
result, err := ApplyAllSchemaChecks(ctx, conf, resourceProvider, resource)
if err != nil {
return results, err
}
@@ -203,37 +205,37 @@ func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProv
}
// ApplyAllSchemaChecks applies available checks to a single resource
func ApplyAllSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
func ApplyAllSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
if resource.PodSpec == nil {
return applyNonControllerSchemaChecks(conf, resourceProvider, resource)
return applyNonControllerSchemaChecks(ctx, conf, resourceProvider, resource)
}
return applyControllerSchemaChecks(conf, resourceProvider, resource)
return applyControllerSchemaChecks(ctx, conf, resourceProvider, resource)
}
func applyNonControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
func applyNonControllerSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
finalResult := Result{
Kind: resource.Kind,
Name: resource.ObjectMeta.GetName(),
Namespace: resource.ObjectMeta.GetNamespace(),
}
resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, false)
resultSet, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, false)
finalResult.Results = resultSet
return finalResult, err
}
func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) {
finalResult := Result{
Kind: resource.Kind,
Name: resource.ObjectMeta.GetName(),
Namespace: resource.ObjectMeta.GetNamespace(),
}
resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, true)
resultSet, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, true)
if err != nil {
return finalResult, err
}
finalResult.Results = resultSet
nonControllerResults, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, false)
nonControllerResults, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, false)
if err != nil {
return finalResult, err
}
@@ -244,7 +246,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
finalResult.Results[key] = val
}
podRS, err := applyPodSchemaChecks(conf, resourceProvider, resource)
podRS, err := applyPodSchemaChecks(ctx, conf, resourceProvider, resource)
if err != nil {
return finalResult, err
}
@@ -255,7 +257,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
finalResult.PodResult = &podRes
for _, container := range resource.PodSpec.InitContainers {
results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, true)
results, err := applyContainerSchemaChecks(ctx, conf, resourceProvider, resource, &container, true)
if err != nil {
return finalResult, err
}
@@ -266,7 +268,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
podRes.ContainerResults = append(podRes.ContainerResults, cRes)
}
for _, container := range resource.PodSpec.Containers {
results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, false)
results, err := applyContainerSchemaChecks(ctx, conf, resourceProvider, resource, &container, false)
if err != nil {
return finalResult, err
}
@@ -280,7 +282,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k
return finalResult, nil
}
func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, res kube.GenericResource, isController bool) (ResultSet, error) {
func applyTopLevelSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, res kube.GenericResource, isController bool) (ResultSet, error) {
test := schemaTestCase{
ResourceProvider: resources,
Resource: res,
@@ -288,19 +290,19 @@ func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.Resou
if isController {
test.Target = config.TargetController
}
return applySchemaChecks(conf, test)
return applySchemaChecks(ctx, conf, test)
}
func applyPodSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource) (ResultSet, error) {
func applyPodSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource) (ResultSet, error) {
test := schemaTestCase{
Target: config.TargetPodSpec,
ResourceProvider: resources,
Resource: controller,
}
return applySchemaChecks(conf, test)
return applySchemaChecks(ctx, conf, test)
}
func applyContainerSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) {
func applyContainerSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) {
test := schemaTestCase{
Target: config.TargetContainer,
ResourceProvider: resources,
@@ -308,14 +310,14 @@ func applyContainerSchemaChecks(conf *config.Configuration, resources *kube.Reso
Container: container,
IsInitContainer: isInit,
}
return applySchemaChecks(conf, test)
return applySchemaChecks(ctx, conf, test)
}
func applySchemaChecks(conf *config.Configuration, test schemaTestCase) (ResultSet, error) {
func applySchemaChecks(ctx context.Context, conf *config.Configuration, test schemaTestCase) (ResultSet, error) {
results := ResultSet{}
checkIDs := getSortedKeys(conf.Checks)
for _, checkID := range checkIDs {
result, err := applySchemaCheck(conf, checkID, test)
result, err := applySchemaCheck(ctx, conf, checkID, test)
if err != nil {
return results, err
}
@@ -326,7 +328,7 @@ func applySchemaChecks(conf *config.Configuration, test schemaTestCase) (ResultS
return results, nil
}
func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTestCase) (*ResultMessage, error) {
func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID string, test schemaTestCase) (*ResultMessage, error) {
check, err := resolveCheck(conf, checkID, test)
if err != nil {
return nil, err
@@ -334,45 +336,75 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
return nil, nil
}
var passes bool
var issues []jsonschema.ValError
var issues []jsonschema.KeyError
var prefix string
emptyValidator := true
validatorBytes, err := json.Marshal(check.Validator)
if err != nil {
return nil, err
}
if string(validatorBytes) != "" && string(validatorBytes) != "null" && string(validatorBytes) != "{}" {
emptyValidator = false
}
if check.SchemaTarget != "" {
if check.SchemaTarget == config.TargetPodSpec && check.Target == config.TargetContainer {
podCopy := *test.Resource.PodSpec
podCopy.InitContainers = []corev1.Container{}
podCopy.Containers = []corev1.Container{*test.Container}
containerIndex := funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
containerIndex := -1
if !test.IsInitContainer {
containerIndex = funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
} else {
containerIndex = funk.IndexOf(test.Resource.PodSpec.InitContainers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
}
prefix = getJSONSchemaPrefix(test.Resource.Kind)
if prefix != "" {
prefix += "/containers/" + strconv.Itoa(containerIndex)
if test.IsInitContainer {
prefix += "/initContainers/" + strconv.Itoa(containerIndex)
} else {
prefix += "/containers/" + strconv.Itoa(containerIndex)
}
}
passes, issues, err = check.CheckPodSpec(&podCopy)
passes, issues, err = check.CheckPodSpec(ctx, &podCopy)
} else {
return nil, fmt.Errorf("Unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget)
}
} else if check.Target == config.TargetPodSpec {
passes, issues, err = check.CheckPodSpec(test.Resource.PodSpec)
passes, issues, err = check.CheckPodSpec(ctx, test.Resource.PodSpec)
prefix = getJSONSchemaPrefix(test.Resource.Kind)
} else if check.Target == config.TargetPodTemplate {
passes, issues, err = check.CheckPodTemplate(test.Resource.PodTemplate)
passes, issues, err = check.CheckPodTemplate(ctx, test.Resource.PodTemplate)
prefix = getJSONSchemaPrefix(test.Resource.Kind)
} else if check.Target == config.TargetContainer {
containerIndex := funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
containerIndex := -1
if !test.IsInitContainer {
containerIndex = funk.IndexOf(test.Resource.PodSpec.Containers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
} else {
containerIndex = funk.IndexOf(test.Resource.PodSpec.InitContainers, func(value corev1.Container) bool {
return value.Name == test.Container.Name
})
}
prefix = getJSONSchemaPrefix(test.Resource.Kind)
if prefix != "" {
prefix += "/containers/" + strconv.Itoa(containerIndex)
if test.IsInitContainer {
prefix += "/initContainers/" + strconv.Itoa(containerIndex)
} else {
prefix += "/containers/" + strconv.Itoa(containerIndex)
}
}
passes, issues, err = check.CheckContainer(test.Container)
} else if check.Validator.SchemaURI != "" {
passes, issues, err = check.CheckObject(test.Resource.Resource.Object)
passes, issues, err = check.CheckContainer(ctx, test.Container)
} else if !emptyValidator {
passes, issues, err = check.CheckObject(ctx, test.Resource.Resource.Object)
} else if validatorMapper[checkID] != nil {
passes, issues, err = validatorMapper[checkID](test)
} else {
passes, issues, err = true, []jsonschema.ValError{}, nil
passes, issues, err = true, []jsonschema.KeyError{}, nil
}
if err != nil {
return nil, err
@@ -393,10 +425,10 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes
resources = funk.Filter(resources, func(res kube.GenericResource) bool {
return res.ObjectMeta.GetNamespace() == "" || res.ObjectMeta.GetNamespace() == namespace
}).([]kube.GenericResource)
objects := funk.Map(resources, func(res kube.GenericResource) interface{} {
objects := funk.Map(resources, func(res kube.GenericResource) any {
return res.Resource.Object
}).([]interface{})
passes, err = check.CheckAdditionalObjects(groupkind, objects)
}).([]any)
passes, err = check.CheckAdditionalObjects(ctx, groupkind, objects)
if err != nil {
return nil, err
}
+5 -4
View File
@@ -15,6 +15,7 @@
package validator
import (
"context"
"testing"
conf "github.com/fairwindsops/polaris/pkg/config"
@@ -58,7 +59,7 @@ customChecks:
category: Efficiency
target: Container
schema:
'$schema': http://json-schema.org/draft-07/schema
'$schema': https://json-schema.org/draft/2019-09/schema
type: object
required:
- resources
@@ -86,7 +87,7 @@ customChecks:
exclude:
- initContainer
schema:
'$schema': http://json-schema.org/draft-07/schema
'$schema': https://json-schema.org/draft/2019-09/schema
type: object
required:
- resources
@@ -158,14 +159,14 @@ func TestValidateResourcesInit(t *testing.T) {
assert.NoError(t, err, "Expected no error when parsing config")
var results ResultSet
results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, false)
results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, controller, emptyContainer, false)
if err != nil {
panic(err)
}
assert.Equal(t, uint(1), results.GetSummary().Dangers)
assert.Equal(t, uint(1), results.GetSummary().Warnings)
results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, true)
results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, controller, emptyContainer, true)
if err != nil {
panic(err)
}
+4 -4
View File
@@ -37,7 +37,7 @@ type Mutator struct {
}
// NewMutateWebhook creates a mutating admission webhook for the apiType.
func NewMutateWebhook(mgr manager.Manager, c config.Configuration) {
func NewMutateWebhook(ctx context.Context, mgr manager.Manager, c config.Configuration) {
path := "/mutate"
decoder := admission.NewDecoder(runtime.NewScheme())
mutator := Mutator{
@@ -48,8 +48,8 @@ func NewMutateWebhook(mgr manager.Manager, c config.Configuration) {
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &mutator})
}
func (m *Mutator) mutate(req admission.Request) ([]jsonpatch.Operation, error) {
results, kubeResources, err := GetValidatedResults(req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config)
func (m *Mutator) mutate(ctx context.Context, req admission.Request) ([]jsonpatch.Operation, error) {
results, kubeResources, err := GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config)
if err != nil {
logrus.Errorf("Error while validating resource: %v", err)
return nil, err
@@ -87,7 +87,7 @@ func (m *Mutator) mutate(req admission.Request) ([]jsonpatch.Operation, error) {
// Handle for Validator to run validation checks.
func (m *Mutator) Handle(ctx context.Context, req admission.Request) admission.Response {
logrus.Info("Starting mutation request")
patches, err := m.mutate(req)
patches, err := m.mutate(ctx, req)
if err != nil {
logrus.Errorf("Error while getting mutations: %v", err)
return admission.Errored(403, err)
+12 -10
View File
@@ -19,6 +19,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/fairwindsops/controller-utils/pkg/controller"
"github.com/fairwindsops/polaris/pkg/config"
@@ -52,12 +53,12 @@ func NewValidateWebhook(mgr manager.Manager, c config.Configuration) {
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &validator})
}
func (v *Validator) handleInternal(req admission.Request) (*validator.Result, kube.GenericResource, error) {
return GetValidatedResults(req.AdmissionRequest.Kind.Kind, v.decoder, req, v.Config)
func (v *Validator) handleInternal(ctx context.Context, req admission.Request) (*validator.Result, kube.GenericResource, error) {
return GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, v.decoder, req, v.Config)
}
// GetValidatedResults returns the validated results.
func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) {
func GetValidatedResults(ctx context.Context, kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) {
var resource kube.GenericResource
var err error
rawBytes := req.Object.Raw
@@ -106,7 +107,7 @@ func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.
logrus.Errorf("Failed to create resource: %v", err)
return nil, resource, err
}
resourceResult, err := validator.ApplyAllSchemaChecks(&config, nil, resource)
resourceResult, err := validator.ApplyAllSchemaChecks(ctx, &config, nil, resource)
if err != nil {
return nil, resource, err
}
@@ -116,7 +117,7 @@ func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.
// Handle for Validator to run validation checks.
func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response {
logrus.Info("Starting admission request")
result, _, err := v.handleInternal(req)
result, _, err := v.handleInternal(ctx, req)
if err != nil {
logrus.Errorf("Error validating request: %v", err)
return admission.Errored(http.StatusBadRequest, err)
@@ -135,11 +136,12 @@ func (v *Validator) Handle(ctx context.Context, req admission.Request) admission
}
func getFailureReason(result validator.Result) string {
reason := "\nPolaris prevented this deployment due to configuration problems:\n"
var reason strings.Builder
reason.WriteString("\nPolaris prevented this deployment due to configuration problems:\n")
for _, message := range result.Results {
if !message.Success && message.Severity == config.SeverityDanger {
reason += fmt.Sprintf("- %s: %s\n", result.Kind, message.Message)
reason.WriteString(fmt.Sprintf("- %s: %s\n", result.Kind, message.Message))
}
}
@@ -147,18 +149,18 @@ func getFailureReason(result validator.Result) string {
if podResult != nil {
for _, message := range podResult.Results {
if !message.Success && message.Severity == config.SeverityDanger {
reason += fmt.Sprintf("- Pod: %s\n", message.Message)
reason.WriteString(fmt.Sprintf("- Pod: %s\n", message.Message))
}
}
for _, containerResult := range podResult.ContainerResults {
for _, message := range containerResult.Results {
if !message.Success && message.Severity == config.SeverityDanger {
reason += fmt.Sprintf("- Container %s: %s\n", containerResult.Name, message.Message)
reason.WriteString(fmt.Sprintf("- Container %s: %s\n", containerResult.Name, message.Message))
}
}
}
}
return reason
return reason.String()
}
+97
View File
@@ -0,0 +1,97 @@
// DO NOT EDIT - Managed by Terraform
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard"
],
"labels": ["dependencies"],
"prConcurrentLimit": 10,
"branchConcurrentLimit": 10,
"minimumReleaseAge": "7 days",
"enabledManagers": [
"gomod",
"dockerfile",
"circleci",
"custom.regex"
],
"postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths"],
"customManagers": [
{
"description": "HashiCorp Vault download URL in CircleCI",
"customType": "regex",
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
"matchStrings": [
"https://releases\\.hashicorp\\.com/vault/(?<currentValue>[\\d.]+)/vault_[\\d.]+_linux_amd64\\.zip"
],
"depNameTemplate": "hashicorp/vault",
"datasourceTemplate": "hashicorp-releases",
"autoReplaceStringTemplate": "https://releases.hashicorp.com/vault/{{{newValue}}}/vault_{{{newValue}}}_linux_amd64.zip"
},
{
"description": "HashiCorp Vault archive name in CircleCI (sha256 line must be updated manually)",
"customType": "regex",
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
"matchStrings": [
"vault_(?<currentValue>[\\d.]+)_linux_amd64\\.zip"
],
"depNameTemplate": "hashicorp/vault",
"datasourceTemplate": "hashicorp-releases",
"autoReplaceStringTemplate": "vault_{{{newValue}}}_linux_amd64.zip"
},
{
"description": "golangci-lint install.sh pin in CircleCI",
"customType": "regex",
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
"matchStrings": [
"golangci/golangci-lint/HEAD/install.sh \\| sh -s -- -b /usr/local/bin v(?<currentValue>[0-9.]+)"
],
"depNameTemplate": "golangci/golangci-lint",
"datasourceTemplate": "github-releases",
"extractVersionTemplate": "^v?(?<version>.*)$",
"autoReplaceStringTemplate": "golangci/golangci-lint/HEAD/install.sh | sh -s -- -b /usr/local/bin v{{{newValue}}}"
},
{
"description": "Kind node image for rok8s-scripts kubernetes_e2e_tests",
"customType": "regex",
"managerFilePatterns": ["/(^|/)\\.circleci/config\\.yml$/"],
"matchStrings": [
"kind_node_image:\\s*\"kindest/node:(?<currentValue>v[^\"\\s]+)\""
],
"depNameTemplate": "kindest/node",
"datasourceTemplate": "docker",
"autoReplaceStringTemplate": "kind_node_image: \"kindest/node:{{{newValue}}}\""
}
],
"packageRules": [
{
"description": "Group all non-major (minor + patch) updates into a single PR",
"matchUpdateTypes": ["minor", "patch", "digest", "lockFileMaintenance"],
"groupName": "all non-major dependencies",
"groupSlug": "all-non-major",
"minimumReleaseAge": "7 days"
},
{
"description": "Keep major updates separate and also enforce minimum age",
"matchUpdateTypes": ["major"],
"minimumReleaseAge": "7 days"
},
{
"description": "Disable replace updates",
"matchManagers": ["gomod"],
"matchDepTypes": ["replace"],
"enabled": false
},
{
"description": "Every upgrade: upstream release must be at least 7 days old",
"matchManagers": ["gomod", "dockerfile", "circleci", "custom.regex"],
"minimumReleaseAge": "7 days"
},
{
"description": "Group HashiCorp Vault version bumps (sha256 checksum still needs manual update)",
"groupName": "HashiCorp Vault",
"matchManagers": ["custom.regex"],
"matchDepNames": ["hashicorp/vault"]
}
]
}
-63
View File
@@ -1,63 +0,0 @@
#!/usr/bin/env sh
# Wrap goreleaser by using envsubst on .goreleaser.yml,
# and creating a temporary git tag.
function cleanup {
if [ "${CIRCLE_TAG}" == "" ] ; then
echo "${this_script} deleting git tag ${temporary_git_tag} for goreleaser"
unset GORELEASER_CURRENT_TAG
git tag -d ${temporary_git_tag}
fi
}
set -eE # errexit and errtrace
trap 'cleanup' ERR
this_script="$(basename $0)"
hash envsubst
hash goreleaser
if [ "${TMPDIR}" == "" ] ; then
export TMPDIR="/tmp"
echo "${this_script} temporarily set the TMPDIR environment variable to ${TMPDIR}, used for a temporary GOBIN environment variable"
fi
export skip_feature_docker_tags=false
export skip_release=true
if [ "${CIRCLE_TAG}" == "" ] ; then
# Create a temporary tag for goreleaser, incrementing the last tag.
last_git_tag="$(git describe --tags --abbrev=0 2>/dev/null)"
if [ "${last_git_tag}" == "" ] ; then
echo "${this_script} is unable to determine the last git tag so a temporary tag can be created, using: git describe --tags --abbrev=0"
exit 1
fi
if [ "$(git config user.email)" == "" ] ; then
# git will use this env var as its user.email.
# git tag -m is used in case tags are manually pushed by accident,
# however git tag -m requires an email.
export EMAIL='goreleaser_ci@fairwinds.com'
echo "${this_script} using ${EMAIL} temporarily as the git user.email"
fi
temporary_git_tag=$(echo "${last_git_tag}" | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')-rc
echo "${this_script} creating temporary git tag ${temporary_git_tag} for goreleaser, the last real tag is ${last_git_tag}"
# The -f is included to overwrite existing tags, perhaps from previous CI jobs.
git tag -f -m "temporary local tag for goreleaser" ${temporary_git_tag}
export GORELEASER_CURRENT_TAG=${temporary_git_tag}
# Use an adjusted git feature branch name as a docker tag.
export feature_docker_tag=$(echo "${CIRCLE_BRANCH:0:26}" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/-\+$//')
echo "${this_script} also using docker tag ${feature_docker_tag} since ${CIRCLE_BRANCH} is a feature branch"
else
export GORELEASER_CURRENT_TAG=${CIRCLE_TAG}
echo "${this_script} setting skip_release to false, and skip_feature_docker_tags to true, because CIRCLE_TAG is set"
export skip_feature_docker_tags=true
export skip_release=false
fi
echo "${this_script} using git tag ${GORELEASER_CURRENT_TAG}"
# Only substitute specific variables, as goreleaser uses shell variable syntax
# for its `signs` section `signature` and `artifact` variables.
cat .goreleaser.yml.envsubst |envsubst '${skip_release} ${skip_feature_docker_tags} ${feature_docker_tag}' >.goreleaser.yml
goreleaser --skip=sign $@
if [ $? -eq 0 ] ; then
echo "${this_script} removing the temporary .goreleaser.yml since goreleaser was successful"
rm .goreleaser.yml # Keep git clean for additional goreleaser runs
fi
cleanup

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