mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48fc87a663 | ||
|
|
1ee4df0c81 | ||
|
|
b73aabc42e | ||
|
|
e60793a022 | ||
|
|
c8394bf9d7 | ||
|
|
0b765df04d | ||
|
|
3e96c582f0 | ||
|
|
391b802d4d | ||
|
|
f295dd0fb5 | ||
|
|
97687761a7 | ||
|
|
c856874564 | ||
|
|
09525d01a1 | ||
|
|
10e82cf0ae | ||
|
|
a5144c45b2 | ||
|
|
9be34236cb | ||
|
|
e7eb079921 | ||
|
|
d55cd4bdf8 | ||
|
|
75997f0e93 | ||
|
|
b0d86cdcd2 | ||
|
|
5595de4ef9 | ||
|
|
4ca4c8f0f5 | ||
|
|
4b1d6635e0 | ||
|
|
0a26f3f578 | ||
|
|
85a439653d | ||
|
|
ba63e6691a | ||
|
|
07747d76f2 | ||
|
|
b876d301c8 | ||
|
|
8bfed75498 | ||
|
|
52a5aa8f74 | ||
|
|
8e3cf1ddd9 | ||
|
|
f1bbe9236b | ||
|
|
65c5ff59ca | ||
|
|
166b39b695 | ||
|
|
ef4ea66d49 | ||
|
|
b472f34266 | ||
|
|
1ddd2d985a | ||
|
|
727c6fa2c3 | ||
|
|
f531103fa0 | ||
|
|
d909dfd00b | ||
|
|
6abfbb3570 | ||
|
|
b75db7fc5b | ||
|
|
501744a65f |
+10
-12
@@ -1,7 +1,7 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
rok8s: fairwinds/rok8s-scripts@11
|
||||
rok8s: fairwinds/rok8s-scripts@12
|
||||
oss-docs: fairwinds/oss-docs@0
|
||||
|
||||
executors:
|
||||
@@ -44,20 +44,20 @@ references:
|
||||
sudo apt-get install -yqq jq git
|
||||
|
||||
echo "Installing KIND"
|
||||
curl -sLO https://github.com/kubernetes-sigs/kind/releases/download/v0.14.0/kind-linux-amd64
|
||||
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.21.12/bin/linux/amd64/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.21.12
|
||||
kind create cluster --wait=90s --image kindest/node:v1.25.9@sha256:c08d6c52820aa42e533b70bce0c2901183326d86dcdcbedecc9343681db45161
|
||||
docker ps -a
|
||||
|
||||
kubectl version
|
||||
@@ -68,11 +68,10 @@ references:
|
||||
./get_helm.sh
|
||||
|
||||
echo "Installing cert-manager"
|
||||
kubectl create namespace 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 0.16.1 --set "installCRDs=true" --wait
|
||||
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
|
||||
@@ -104,10 +103,10 @@ references:
|
||||
command: |
|
||||
apk --update add curl yq
|
||||
cd /tmp
|
||||
curl -LO https://releases.hashicorp.com/vault/1.9.3/vault_1.9.3_linux_amd64.zip
|
||||
unzip vault_1.9.3_linux_amd64.zip
|
||||
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
|
||||
@@ -115,6 +114,7 @@ jobs:
|
||||
executor: vm
|
||||
steps:
|
||||
- checkout
|
||||
- *set_environment_variables
|
||||
- *install_k8s
|
||||
- *test_k8s
|
||||
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
# The goreleaser image tag determins the version of Go.
|
||||
# Manually check goreleaser images for their version of Go.
|
||||
# Ref: https://hub.docker.com/r/goreleaser/goreleaser/tags
|
||||
- image: goreleaser/goreleaser:v1.11.4
|
||||
- image: goreleaser/goreleaser:v1.18.2
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
@@ -162,8 +162,6 @@ workflows:
|
||||
only: /.*/
|
||||
- build_and_push:
|
||||
context: org-global
|
||||
requires:
|
||||
- test
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/[0-9]+/
|
||||
|
||||
@@ -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.10
|
||||
FROM alpine:3.17
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
|
||||
@@ -17,4 +17,4 @@ mkdir polaris
|
||||
tar -xzf $TARGET_FILE -C polaris
|
||||
rm $TARGET_FILE
|
||||
echo "polaris" >> $GITHUB_PATH
|
||||
echo "::set-output name=version::$INPUT_VERSION"
|
||||
echo "version=$INPUT_VERSION" >> $GITHUB_OUTPUT
|
||||
@@ -7,7 +7,7 @@ jobs:
|
||||
build-int:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup polaris
|
||||
uses: ./.github/actions/setup-polaris
|
||||
with:
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
build-ext:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup polaris
|
||||
uses: fairwindsops/polaris/.github/actions/setup-polaris@master
|
||||
with:
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
* @rbren @mggude
|
||||
* @rbren @sudermanjr
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM alpine:20230208
|
||||
FROM alpine:3.19
|
||||
|
||||
LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.vendor="FairwindsOps, Inc." \
|
||||
|
||||
@@ -38,8 +38,9 @@ and network with fellow Kubernetes users.
|
||||
or
|
||||
[join the user group](https://www.fairwinds.com/open-source-software-user-group) to get involved!
|
||||
|
||||
<a href="https://www.fairwinds.com/t-shirt-offer?utm_source=polaris&utm_medium=polaris&utm_campaign=polaris-tshirt">
|
||||
<img src="https://www.fairwinds.com/hubfs/Doc_Banners/Fairwinds_OSS_User_Group_740x125_v6.png" alt="Love Fairwinds Open Source? Share your business email and job title and we'll send you a free Fairwinds t-shirt!" />
|
||||
<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
|
||||
|
||||
@@ -5,7 +5,6 @@ target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required: ["serviceAccountName"]
|
||||
properties:
|
||||
serviceAccountName:
|
||||
type: string
|
||||
@@ -15,12 +14,11 @@ schema:
|
||||
const: true
|
||||
additionalSchemaStrings:
|
||||
ServiceAccount: |
|
||||
{{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
|
||||
type: object
|
||||
required:
|
||||
- metadata
|
||||
{{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
|
||||
- automountServiceAccountToken
|
||||
{{ end }}
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
@@ -34,3 +32,4 @@ additionalSchemaStrings:
|
||||
type: boolean
|
||||
const: false
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -37,9 +37,10 @@ schemaString: |
|
||||
const: "cluster-admin"
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
type: object
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
{{ if (ne .roleRef.name "view") }}
|
||||
{{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
@@ -86,3 +87,4 @@ additionalSchemaStrings:
|
||||
- "patch"
|
||||
- "delete"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -37,6 +37,7 @@ additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
type: object
|
||||
# Do not alert on default ClusterRoleBindings.
|
||||
{{ if (ne .roleRef.name "view") }}
|
||||
{{ if and (ne .metadata.name "cluster-admin") (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
@@ -80,3 +81,4 @@ additionalSchemaStrings:
|
||||
- const: 'get'
|
||||
- const: 'create'
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
successMessage: Label app.kubernetes.io/name matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/name must match metadata.name
|
||||
successMessage: Label app.kubernetes.io/instance matches metadata.name
|
||||
failureMessage: Label app.kubernetes.io/instance must match metadata.name
|
||||
category: Reliability
|
||||
target: Controller
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
@@ -11,7 +12,7 @@ schema:
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
required: ["app.kubernetes.io/name"]
|
||||
required: ["app.kubernetes.io/instance"]
|
||||
properties:
|
||||
app.kubernetes.io/name:
|
||||
app.kubernetes.io/instance:
|
||||
const: "{{ .metadata.name }}"
|
||||
@@ -9,12 +9,18 @@ schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
labels:
|
||||
template:
|
||||
type: object
|
||||
minProperties: 1
|
||||
properites:
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
minProperties: 1
|
||||
additionalSchemaStrings:
|
||||
policy/PodDisruptionBudget: |
|
||||
type: object
|
||||
@@ -30,7 +36,7 @@ additionalSchemaStrings:
|
||||
matchLabels:
|
||||
type: object
|
||||
anyOf:
|
||||
{{ range $key, $value := .metadata.labels }}
|
||||
{{ range $key, $value := .spec.template.metadata.labels }}
|
||||
- properties:
|
||||
"{{ $key }}":
|
||||
type: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
successMessage: Priority class has been set
|
||||
failureMessage: Priority class should be set
|
||||
category: Security
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
|
||||
@@ -45,10 +45,10 @@ schemaString: |
|
||||
const: "cluster-admin"
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
type: object
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
{{ if eq .roleRef.kind "ClusterRole" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
|
||||
@@ -43,10 +43,10 @@ schemaString: |
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/ClusterRole: |
|
||||
type: object
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
{{ if eq .roleRef.kind "ClusterRole" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
|
||||
@@ -46,10 +46,10 @@ schemaString: |
|
||||
minLength: 1
|
||||
additionalSchemaStrings:
|
||||
rbac.authorization.k8s.io/Role: |
|
||||
type: object
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
{{ if eq .roleRef.kind "Role" }}
|
||||
{{ if and (not (hasPrefix .metadata.name "system:")) (ne .metadata.name "gce:podsecuritypolicy:calico-sa") }}
|
||||
# This schema is validated for all roleBindings, regardless of their roleRef.
|
||||
type: object
|
||||
required: ["metadata", "rules"]
|
||||
allOf:
|
||||
- properties:
|
||||
|
||||
@@ -10,7 +10,9 @@ schemaString: |
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
oneOf:
|
||||
anyOf:
|
||||
- not:
|
||||
required: ["value"]
|
||||
- required: ["name", "value"]
|
||||
properties:
|
||||
name:
|
||||
|
||||
@@ -2,6 +2,10 @@ successMessage: Pod has a valid topology spread constraint
|
||||
failureMessage: Pod should be configured with a valid topology spread constraint
|
||||
category: Reliability
|
||||
target: PodSpec
|
||||
controllers:
|
||||
exclude:
|
||||
- Job
|
||||
- CronJob
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
@@ -10,12 +14,4 @@ schema:
|
||||
properties:
|
||||
topologySpreadConstraints:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
topologyKey:
|
||||
anyOf:
|
||||
- type: string
|
||||
const: "kubernetes.io/hostname"
|
||||
- type: string
|
||||
const: "topology.kubernetes.io/zone"
|
||||
minItems: 1
|
||||
|
||||
+104
-13
@@ -17,14 +17,21 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
@@ -42,9 +49,15 @@ var (
|
||||
resourceToAudit string
|
||||
useColor bool
|
||||
helmChart string
|
||||
helmValues string
|
||||
helmValues []string
|
||||
helmSkipTests bool
|
||||
checks []string
|
||||
auditNamespace string
|
||||
severityLevel string
|
||||
skipSslValidation bool
|
||||
uploadInsights bool
|
||||
clusterName string
|
||||
quiet bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -60,9 +73,15 @@ func init() {
|
||||
auditCmd.PersistentFlags().StringVar(&displayName, "display-name", "", "An optional identifier for the audit.")
|
||||
auditCmd.PersistentFlags().StringVar(&resourceToAudit, "resource", "", "Audit a specific resource, in the format namespace/kind/version/name, e.g. nginx-ingress/Deployment.apps/v1/default-backend.")
|
||||
auditCmd.PersistentFlags().StringVar(&helmChart, "helm-chart", "", "Will fill out Helm template")
|
||||
auditCmd.PersistentFlags().StringVar(&helmValues, "helm-values", "", "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&helmValues, "helm-values", []string{}, "Optional flag to add helm values")
|
||||
auditCmd.PersistentFlags().BoolVar(&helmSkipTests, "helm-skip-tests", false, "Corresponds to --skip-tests of helm template")
|
||||
auditCmd.PersistentFlags().StringSliceVar(&checks, "checks", []string{}, "Optional flag to specify specific checks to check")
|
||||
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{
|
||||
@@ -95,14 +114,32 @@ var auditCmd = &cobra.Command{
|
||||
}
|
||||
if helmChart != "" {
|
||||
var err error
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues)
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues, helmSkipTests)
|
||||
if err != nil {
|
||||
logrus.Infof("Couldn't process helm chart: %v", err)
|
||||
logrus.Errorf("Couldn't process helm chart: %v", err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k, err := kube.CreateResourceProvider(context.TODO(), auditPath, resourceToAudit, config)
|
||||
ctx := context.TODO()
|
||||
k, err := kube.CreateResourceProvider(ctx, auditPath, resourceToAudit, config)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Kubernetes resources %v", err)
|
||||
os.Exit(1)
|
||||
@@ -114,7 +151,43 @@ var auditCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
outputAudit(auditData, auditOutputFile, auditOutputURL, auditOutputFormat, useColor, onlyShowFailedTests)
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
summary := auditData.GetSummary()
|
||||
score := summary.GetScore()
|
||||
@@ -129,7 +202,7 @@ var auditCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// ProcessHelmTemplates turns helm into yaml to be processed by Polaris or the other tools.
|
||||
func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
func ProcessHelmTemplates(helmChart string, helmValues []string, helmSkipTests bool) (string, error) {
|
||||
cmd := exec.Command("helm", "dependency", "update", helmChart)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -147,8 +220,12 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
"--output-dir",
|
||||
dir,
|
||||
}
|
||||
if helmValues != "" {
|
||||
params = append(params, "--values", helmValues)
|
||||
for _, v := range helmValues {
|
||||
params = append(params, "--values", v)
|
||||
}
|
||||
|
||||
if helmSkipTests {
|
||||
params = append(params, "--skip-tests")
|
||||
}
|
||||
|
||||
cmd = exec.Command("helm", params...)
|
||||
@@ -161,10 +238,20 @@ func ProcessHelmTemplates(helmChart, helmValues string) (string, error) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool) {
|
||||
func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFormat string, useColor bool, onlyShowFailedTests bool, severityLevel string) {
|
||||
if onlyShowFailedTests {
|
||||
auditData = auditData.RemoveSuccessfulResults()
|
||||
}
|
||||
|
||||
if severityLevel != "" {
|
||||
switch severityLevel {
|
||||
case "danger":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityDanger)
|
||||
case "warning":
|
||||
auditData = auditData.FilterResultsBySeverityLevel(cfg.SeverityWarning)
|
||||
}
|
||||
}
|
||||
|
||||
var outputBytes []byte
|
||||
var err error
|
||||
if outputFormat == "score" {
|
||||
@@ -202,9 +289,13 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
} else {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
client := &http.Client{}
|
||||
if skipSslValidation {
|
||||
transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||
client = &http.Client{Transport: transport}
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error making request for output: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -223,7 +314,7 @@ func outputAudit(auditData validator.AuditData, outputFile, outputURL, outputFor
|
||||
}
|
||||
|
||||
if outputFile != "" {
|
||||
err := os.WriteFile(outputFile, []byte(outputBytes), 0644)
|
||||
err := os.WriteFile(outputFile, outputBytes, 0644)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error writing output to file: %v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
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
|
||||
},
|
||||
}
|
||||
+17
-7
@@ -16,18 +16,25 @@ package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configPath string
|
||||
var disallowExemptions, disallowConfigExemptions, disallowAnnotationExemptions, fixChecks bool
|
||||
var logLevel string
|
||||
var auditPath string
|
||||
var displayName string
|
||||
var kubeContext string
|
||||
var (
|
||||
configPath string
|
||||
disallowExemptions bool
|
||||
disallowConfigExemptions bool
|
||||
disallowAnnotationExemptions bool
|
||||
fixChecks bool
|
||||
logLevel string
|
||||
auditPath string
|
||||
displayName string
|
||||
kubeContext string
|
||||
insightsHost string
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
@@ -41,6 +48,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowConfigExemptions, "disallow-config-exemptions", "", false, "Disallow exemptions set within the configuration file.")
|
||||
rootCmd.PersistentFlags().BoolVarP(&disallowAnnotationExemptions, "disallow-annotation-exemptions", "", false, "Disallow any exemption defined as a controller annotation.")
|
||||
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "", logrus.InfoLevel.String(), "Logrus log level to be output (trace, debug, info, warning, error, fatal, panic).")
|
||||
rootCmd.PersistentFlags().StringVar(&insightsHost, "insights-host", "https://insights.fairwinds.com", "Fairwinds Insights host URL")
|
||||
}
|
||||
|
||||
var config conf.Configuration
|
||||
@@ -77,7 +85,9 @@ var rootCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
if !strings.HasPrefix(cmd.Use, "audit") {
|
||||
os.Stderr.WriteString("\n\nWant more? Automate Polaris for free with Fairwinds Insights!\n🚀 https://fairwinds.com/insights-signup/polaris 🚀 \n")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
k8sConfig "sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
)
|
||||
|
||||
var webhookPort int
|
||||
@@ -51,6 +52,12 @@ var webhookCmd = &cobra.Command{
|
||||
mgr, err := manager.New(k8sConfig.GetConfigOrDie(), manager.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
WebhookServer: webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
Port: webhookPort,
|
||||
CertName: "tls.crt",
|
||||
KeyName: "tls.key",
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to set up overall controller manager: %v", err)
|
||||
@@ -61,9 +68,6 @@ var webhookCmd = &cobra.Command{
|
||||
if os.IsNotExist(err) {
|
||||
panic("Cert does not exist")
|
||||
}
|
||||
server := mgr.GetWebhookServer()
|
||||
server.CertName = "tls.crt"
|
||||
server.KeyName = "tls.key"
|
||||
|
||||
if !enableMutations && !enableValidations {
|
||||
logrus.Errorf("One of --mutate or --validate must be set to true")
|
||||
@@ -71,10 +75,10 @@ var webhookCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if enableValidations {
|
||||
fwebhook.NewValidateWebhook(mgr, fwebhook.Validator{Config: config, Client: mgr.GetClient()})
|
||||
fwebhook.NewValidateWebhook(mgr, config)
|
||||
}
|
||||
if enableMutations {
|
||||
fwebhook.NewMutateWebhook(mgr, fwebhook.Mutator{Config: config, Client: mgr.GetClient()})
|
||||
fwebhook.NewMutateWebhook(mgr, config)
|
||||
}
|
||||
logrus.Infof("Polaris webhook server listening on port %d", webhookPort)
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
|
||||
@@ -53,9 +53,30 @@ This means Polaris will remediate the issue it finds, rather than rejecting
|
||||
the deployment.
|
||||
|
||||
To enable the mutating webhook, add `--set webhook.mutate=true` to your
|
||||
Helm instlallation command.
|
||||
Helm installation command.
|
||||
|
||||
By default, the only mutation enabled is `pullPolicyNotAlways`. If you'd like to
|
||||
The following default checks currently have mutation support enabled:
|
||||
* `hostPIDSet`
|
||||
* `hostNetworkSet`
|
||||
* `hostIPCSet`
|
||||
* `priorityClassNotSet`
|
||||
* `hostPortSet`
|
||||
* `pullPolicyNotAlways`
|
||||
* `deploymentMissingReplicas`
|
||||
* `dangerousCapabilities`
|
||||
* `cpuLimitsMissing`
|
||||
* `memoryLimitsMissing`
|
||||
* `livenessProbeMissing`
|
||||
* `memoryRequestsMissing`
|
||||
* `cpuRequestsMissing`
|
||||
* `runAsPrivileged`
|
||||
* `readinessProbeMissing`
|
||||
* `privilegeEscalationAllowed`
|
||||
* `notReadOnlyRootFilesystem`
|
||||
* `insecureCapabilities`
|
||||
* `runAsRootAllowed`
|
||||
|
||||
If you'd like to
|
||||
enable other mutations, you can set the `webhook.mutations` flag.
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,44 @@ meta:
|
||||
content: "Fairwinds Polaris | Changelog"
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
* Change `metadataAndNameMismatched` to `metadataAndInstanceMismatched`
|
||||
|
||||
## 8.1.1
|
||||
* Add category for `metadataAndNameMismatched`.
|
||||
* Fix category for `priorityClassNotSet`.
|
||||
|
||||
## 8.1.0
|
||||
* Add `insights-host` global flag to configure Fairwinds Insights host (defaults to `https://insights.fairwinds.com`).
|
||||
* Add new `auth` sub-commands be able to authenticate on Polaris using Fairwinds Insights credentials
|
||||
- `login` - login using Fairwinds Insights credentials via the web interface or provide a token
|
||||
- `logout` - logout from Fairwinds Insights
|
||||
- `status` - show relevant information regarding login state
|
||||
- `token` - prints the token from local storage
|
||||
* Add new `audit` flags to be able to upload Workloads and Polaris results to Fairwinds Insights
|
||||
- `upload-insights` - indicates that the results should be uploaded to Fairwinds Insights. (defaults to `false`)
|
||||
- `cluster-name` - cluster name that the results belongs to. Creates the cluster if it does not exist. (required if `upload-insights` is used)
|
||||
|
||||
|
||||
## 8.0.0
|
||||
* Change default severity from `ignore` to `warning` for `priorityClassNotSet`, `metadataAndNameMismatched`, `missingPodDisruptionBudget`, `automountServiceAccountToken`, `missingNetworkPolicy` checks.
|
||||
* Change default severity from `warning` to `danger` for `sensitiveContainerEnvVar`, `sensitiveConfigmapContent`, `clusterrolePodExecAttach`, `rolePodExecAttach`, `clusterrolebindingPodExecAttach`, `rolebindingClusterRolePodExecAttach`, `rolebindingRolePodExecAttach`,`clusterrolebindingClusterAdmin`,`rolebindingClusterAdminClusterRole`,`rolebindingClusterAdminRole` checks.
|
||||
|
||||
## 7.4.0
|
||||
* Skip https certificate verification (#920)
|
||||
|
||||
## 7.3.0
|
||||
* Add a check for `topologySpreadConstraint` (#879)
|
||||
|
||||
## 7.2.0
|
||||
* Enable new RBAC / sensitive content / Pod exec checks, add `hasPrefix` and `hasSuffix` functions to the GO template, exempt `system:` name prefixes for RBAC checks, sensitive content checks ignore `valueFrom`, (#832)
|
||||
|
||||
## 7.1.0
|
||||
* Let Polaris modify YAML without losing comments/formatting (#821)
|
||||
* Add checks for RBAC allowing exec or attaching to a Pod (#820)
|
||||
* Add `clusterrolebindingClusterAdmin`, `rolebindingClusterAdminRole`, and `rolebindingClusterAdminClusterRole` checks + schema tests (#823)
|
||||
|
||||
## 7.0.2
|
||||
* Fixes for pretty CLI output
|
||||
* Some new checks (disabled by default)
|
||||
|
||||
@@ -14,9 +14,10 @@ key | default | description
|
||||
`livenessProbeMissing` | `warning` | Fails when a liveness probe is not configured for a pod.
|
||||
`tagNotSpecified` | `danger` | Fails when an image tag is either not specified or `latest`.
|
||||
`pullPolicyNotAlways` | `warning` | Fails when an image pull policy is not `always`.
|
||||
`priorityClassNotSet` | `ignore` | Fails when a priorityClassName is not set for a pod.
|
||||
`priorityClassNotSet` | `warning` | Fails when a priorityClassName is not set for a pod.
|
||||
`deploymentMissingReplicas` | `warning` | Fails when there is only one replica for a deployment.
|
||||
`missingPodDisruptionBudget` | `ignore`
|
||||
`missingPodDisruptionBudget` | `warning` | Fails when PDB is missing.
|
||||
`metadataAndInstanceMismatched` | `warning` | Fails when label `app.kubernetes.io/instance` and `metadata.name` mismatch
|
||||
`topologySpreadConstraint` | `warning` | Fails when there is no topology spread constraint on the pod
|
||||
|
||||
## Background
|
||||
@@ -69,4 +70,4 @@ spec:
|
||||
- [Kubernetes Docs: Configure Liveness and Readiness Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/)
|
||||
- [Utilizing Kubernetes Liveness and Readiness Probes to Automatically Recover From Failure](https://medium.com/spire-labs/utilizing-kubernetes-liveness-and-readiness-probes-to-automatically-recover-from-failure-2fe0314f2b2e)
|
||||
- [Kubernetes Liveness and Readiness Probes: How to Avoid Shooting Yourself in the Foot](https://blog.colinbreck.com/kubernetes-liveness-and-readiness-probes-how-to-avoid-shooting-yourself-in-the-foot/)
|
||||
- [Topology Spread Cosntraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
- [Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
|
||||
@@ -11,8 +11,10 @@ for privilege escalation.
|
||||
|
||||
key | default | description
|
||||
----|---------|------------
|
||||
`automountServiceAccountToken` | `warning` | Fails when `automountServiceAccountToken` is automounted.
|
||||
`hostIPCSet` | `danger` | Fails when `hostIPC` attribute is configured.
|
||||
`hostPIDSet` | `danger` | Fails when `hostPID` attribute is configured.
|
||||
`linuxHardening` | `danger` | Fails when neither `AppArmor`, `Seccomp`, `SELinux`, or dropping Linux Capabilities is in use.
|
||||
`notReadOnlyRootFilesystem` | `warning` | Fails when `securityContext.readOnlyRootFilesystem` is not true.
|
||||
`privilegeEscalationAllowed` | `danger` | Fails when `securityContext.allowPrivilegeEscalation` is true.
|
||||
`runAsRootAllowed` | `warning` | Fails when `securityContext.runAsNonRoot` is not true.
|
||||
@@ -22,6 +24,17 @@ key | default | description
|
||||
`hostNetworkSet` | `warning` | Fails when `hostNetwork` attribute is configured.
|
||||
`hostPortSet` | `warning` | Fails when `hostPort` attribute is configured.
|
||||
`tlsSettingsMissing` | `warning` | Fails when an Ingress lacks TLS settings.
|
||||
`sensitiveContainerEnvVar` | `danger` | Fails when the container sets potentially sensitive environment variables.
|
||||
`sensitiveConfigmapContent` | `danger` | Fails when potentially sensitive content is detected in the ConfigMap keys or values.
|
||||
`missingNetworkPolicy` | `warning`
|
||||
`clusterrolePodExecAttach` | `danger` | Fails when the ClusterRole allows Pods/exec or pods/attach.
|
||||
`rolePodExecAttach` | `danger` | Fails when the Role allows Pods/exec or pods/attach.
|
||||
`clusterrolebindingPodExecAttach` | `danger` | Fails when the ClusterRoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`rolebindingRolePodExecAttach` | `danger` | Fails when the RoleBinding references a Role that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`rolebindingClusterRolePodExecAttach` | `danger` | Fails when the RoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist.
|
||||
`clusterrolebindingClusterAdmin` | `danger` | Fails when the ClusterRoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions.
|
||||
`rolebindingClusterAdminClusterRole` | `danger` | Fails when the RoleBinding references the default cluster-admin ClusterRole or one with wildcard permissions.
|
||||
`rolebindingClusterAdminRole` | `danger` | Fails when the RoleBinding references a Role with wildcard permissions.
|
||||
|
||||
## Background
|
||||
|
||||
|
||||
+31
-2
@@ -9,14 +9,18 @@ meta:
|
||||
# top-level commands
|
||||
audit
|
||||
Runs a one-time audit.
|
||||
auth
|
||||
Authenticate polaris with Fairwinds Insights
|
||||
dashboard
|
||||
Runs the webserver for Polaris dashboard.
|
||||
fix
|
||||
Fix Infrastructure as code files.
|
||||
help
|
||||
Prints help, if you give it a command then it will print help for that command. Same as -h
|
||||
version
|
||||
Prints the version of Polaris
|
||||
webhook
|
||||
Runs the webhook webserver
|
||||
Runs the webhook webserver.
|
||||
|
||||
# global flags
|
||||
-c, --config string Location of Polaris configuration file.
|
||||
@@ -25,6 +29,7 @@ webhook
|
||||
--disallow-config-exemptions Disallow exemptions set within the configuration file.
|
||||
--disallow-annotation-exemptions Disallow any exemption defined as a controller annotation.
|
||||
--kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster.
|
||||
--insights-host string Fairwinds Insights host URL. (default "https://insights.fairwinds.com")
|
||||
--log-level string Logrus log level. (default "info")
|
||||
|
||||
# dashboard flags
|
||||
@@ -38,24 +43,48 @@ webhook
|
||||
|
||||
# audit flags
|
||||
--audit-path string If specified, audits one or more YAML files instead of a cluster.
|
||||
--checks stringArray Optional flag to specify specific checks to check
|
||||
--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")
|
||||
--helm-chart string Will fill out Helm template
|
||||
--helm-values string Optional flag to add helm values
|
||||
--helm-skip-tests bool Corresponds to --skip-tests of helm template
|
||||
-h, --help help for audit
|
||||
--namespace string Namespace to audit. Only applies to in-cluster audits
|
||||
--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
|
||||
--files-path string mutate and fix one or more YAML files in a specified folder
|
||||
-h, --help help for fix
|
||||
--template set to true when modifyng a YAML template, like a Helm chart (experimental)
|
||||
|
||||
|
||||
# webhook flags
|
||||
--disable-webhook-config-installer disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping.
|
||||
-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:
|
||||
|
||||
@@ -49,8 +49,7 @@ 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
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
kubectl create ns cert-manager
|
||||
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version 0.16.1 --set "installCRDs=true" --wait
|
||||
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
|
||||
```
|
||||
to avoid the final cleanup for debugging purposes, you can run
|
||||
|
||||
Generated
+1877
-1787
File diff suppressed because it is too large
Load Diff
@@ -6,11 +6,17 @@ checks:
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
topologySpreadConstraint: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
memoryRequestsMissing: warning
|
||||
memoryLimitsMissing: warning
|
||||
|
||||
# security
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
@@ -25,6 +31,7 @@ checks:
|
||||
insecureCapabilities: warning
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
@@ -39,7 +46,6 @@ checks:
|
||||
resourceLimits: warning
|
||||
imageRegistry: danger
|
||||
|
||||
|
||||
exemptions:
|
||||
- controllerNames:
|
||||
- my-network-controller
|
||||
|
||||
+16
-16
@@ -1,14 +1,14 @@
|
||||
checks:
|
||||
# reliability
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: ignore
|
||||
priorityClassNotSet: warning
|
||||
tagNotSpecified: danger
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
metadataAndNameMismatched: ignore
|
||||
metadataAndInstanceMismatched: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: ignore
|
||||
missingPodDisruptionBudget: warning
|
||||
topologySpreadConstraint: warning
|
||||
|
||||
# efficiency
|
||||
@@ -16,12 +16,13 @@ checks:
|
||||
cpuLimitsMissing: warning
|
||||
memoryRequestsMissing: warning
|
||||
memoryLimitsMissing: warning
|
||||
|
||||
# security
|
||||
automountServiceAccountToken: ignore
|
||||
automountServiceAccountToken: warning
|
||||
hostIPCSet: danger
|
||||
hostPIDSet: danger
|
||||
linuxHardening: warning
|
||||
missingNetworkPolicy: ignore
|
||||
missingNetworkPolicy: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: danger
|
||||
runAsRootAllowed: danger
|
||||
@@ -31,17 +32,16 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
# These are initially warning and will later be promoted to danger.
|
||||
sensitiveContainerEnvVar: warning
|
||||
sensitiveConfigmapContent: warning
|
||||
clusterrolePodExecAttach: warning
|
||||
rolePodExecAttach: warning
|
||||
clusterrolebindingPodExecAttach: warning
|
||||
rolebindingClusterRolePodExecAttach: warning
|
||||
rolebindingRolePodExecAttach: warning
|
||||
clusterrolebindingClusterAdmin: warning
|
||||
rolebindingClusterAdminClusterRole: warning
|
||||
rolebindingClusterAdminRole: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
rolePodExecAttach: danger
|
||||
clusterrolebindingPodExecAttach: danger
|
||||
rolebindingClusterRolePodExecAttach: danger
|
||||
rolebindingRolePodExecAttach: danger
|
||||
clusterrolebindingClusterAdmin: danger
|
||||
rolebindingClusterAdminClusterRole: danger
|
||||
rolebindingClusterAdminRole: danger
|
||||
|
||||
|
||||
mutations:
|
||||
|
||||
@@ -1,58 +1,56 @@
|
||||
module github.com/fairwindsops/polaris
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.14.1
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/fairwindsops/controller-utils v0.3.0
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gobuffalo/packr/v2 v2.8.3
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/qri-io/jsonschema v0.1.1
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.26.1
|
||||
k8s.io/apimachinery v0.26.1
|
||||
k8s.io/client-go v0.26.1
|
||||
sigs.k8s.io/controller-runtime v0.14.4
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0
|
||||
github.com/qri-io/jsonschema v0.1.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.0
|
||||
k8s.io/apimachinery v0.29.0
|
||||
k8s.io/client-go v0.29.0
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/gobuffalo/logger v1.0.7 // indirect
|
||||
github.com/gobuffalo/packd v1.0.2 // indirect
|
||||
github.com/gobuffalo/logger v1.0.6 // indirect
|
||||
github.com/gobuffalo/packd v1.0.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/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/karrick/godirwalk v1.17.0 // indirect
|
||||
github.com/karrick/godirwalk v1.16.1 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/markbates/errx v1.1.0 // indirect
|
||||
github.com/markbates/oncer v1.0.0 // indirect
|
||||
@@ -60,27 +58,31 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.39.0 // indirect
|
||||
github.com/prometheus/client_golang v1.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/qri-io/jsonpointer v0.1.1 // indirect
|
||||
golang.org/x/net v0.6.0 // indirect
|
||||
golang.org/x/oauth2 v0.5.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/component-base v0.26.1 // indirect
|
||||
k8s.io/klog/v2 v2.90.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230202010329-39b3636cbaa3 // indirect
|
||||
k8s.io/utils v0.0.0-20230202215443-34013725500c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/component-base v0.27.2 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
)
|
||||
|
||||
@@ -37,9 +37,12 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@@ -48,9 +51,7 @@ 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -60,35 +61,36 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
|
||||
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/fairwindsops/controller-utils v0.3.0 h1:otfA6HZ7oVzF690AdGwVU4+6Q5Uie6G6oOF7MBT+L4g=
|
||||
github.com/fairwindsops/controller-utils v0.3.0/go.mod h1:Plr++8B/A+TlhbVBG0sNhToaaQKo7LoFNIfdKi58vpM=
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c h1:js6wazy/7V7cZI43affIa2FymWZ61rOp/H26HEE3ChY=
|
||||
github.com/fairwindsops/insights-plugins/plugins/workloads v0.0.0-20230601204422-5c789e15990c/go.mod h1:bNIBr22uoqvgi3JzUg7Hg9QidCsvXWAhquNQRyDGJCo=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
@@ -96,22 +98,20 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU=
|
||||
github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
|
||||
github.com/gobuffalo/logger v1.0.7 h1:LTLwWelETXDYyqF/ASf0nxaIcdEOIJNxRokPcfI/xbU=
|
||||
github.com/gobuffalo/logger v1.0.7/go.mod h1:u40u6Bq3VVvaMcy5sRBclD8SXhBYPS0Qk95ubt+1xJM=
|
||||
github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0=
|
||||
github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY=
|
||||
github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw=
|
||||
github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8=
|
||||
github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY=
|
||||
github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -147,12 +147,13 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
|
||||
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -164,8 +165,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -183,6 +185,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
@@ -213,12 +216,13 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -230,16 +234,16 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
|
||||
github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -254,14 +258,18 @@ github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2
|
||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@@ -280,8 +288,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/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.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
|
||||
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -292,24 +300,24 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
|
||||
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
||||
github.com/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.1 h1:t//Doa/gvMqJ0bDhG7PGIKfaWGGxRVaffp+bcvBGGEk=
|
||||
github.com/qri-io/jsonschema v0.1.1/go.mod h1:QpzJ6gBQ0GYgGmh7mDQ1YsvvhSgE4rYj0k8t5MBOmUY=
|
||||
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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
@@ -318,21 +326,19 @@ 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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
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/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -344,20 +350,19 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
@@ -368,10 +373,9 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
@@ -383,6 +387,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -418,6 +423,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -455,8 +461,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -469,8 +476,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -482,11 +489,13 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -528,16 +537,17 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -546,8 +556,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -605,12 +617,14 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -682,7 +696,6 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -703,7 +716,6 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -716,12 +728,10 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
@@ -734,7 +744,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
@@ -746,29 +755,29 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=
|
||||
k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=
|
||||
k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI=
|
||||
k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
|
||||
k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
|
||||
k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
|
||||
k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
|
||||
k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4=
|
||||
k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU=
|
||||
k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M=
|
||||
k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20230202010329-39b3636cbaa3 h1:vV3ZKAUX0nMjTflyfVea98dTfROpIxDaEsQws0FT2Ts=
|
||||
k8s.io/kube-openapi v0.0.0-20230202010329-39b3636cbaa3/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0=
|
||||
k8s.io/utils v0.0.0-20230202215443-34013725500c h1:YVqDar2X7YiQa/DVAXFMDIfGF8uGrHQemlrwRU5NlVI=
|
||||
k8s.io/utils v0.0.0-20230202215443-34013725500c/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
|
||||
k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
|
||||
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
|
||||
k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8=
|
||||
k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38=
|
||||
k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo=
|
||||
k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/controller-runtime v0.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M=
|
||||
sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
|
||||
sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU=
|
||||
sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
var (
|
||||
// BuiltInChecks contains the checks that come pre-installed w/ Polaris
|
||||
BuiltInChecks = map[string]SchemaCheck{}
|
||||
schemaBox = (*packr.Box)(nil)
|
||||
// We explicitly set the order to avoid thrash in the
|
||||
// tests as we migrate toward JSON schema
|
||||
checkOrder = []string{
|
||||
@@ -56,7 +55,7 @@ var (
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndNameMismatched",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
"missingNetworkPolicy",
|
||||
"sensitiveConfigmapContent",
|
||||
@@ -72,7 +71,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
schemaBox = packr.New("Schemas", "../../checks")
|
||||
schemaBox := packr.New("Schemas", "../../checks")
|
||||
for _, checkID := range checkOrder {
|
||||
contents, err := schemaBox.Find(checkID + ".yaml")
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRequiredFieldsOnBuiltInChecks(t *testing.T) {
|
||||
for _, v := range BuiltInChecks {
|
||||
assert.NotEmpty(t, v.SuccessMessage)
|
||||
assert.NotEmpty(t, v.FailureMessage)
|
||||
assert.NotEmpty(t, v.Category)
|
||||
assert.NotEmpty(t, v.Target)
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,9 @@ func (max resourceMaximum) Validate(path string, data interface{}, errs *[]jsons
|
||||
}
|
||||
|
||||
func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.ValError) {
|
||||
if resNum, ok := i.(float64); ok {
|
||||
i = fmt.Sprintf("%f", resNum)
|
||||
}
|
||||
resStr, ok := i.(string)
|
||||
if !ok {
|
||||
return resource.Quantity{}, &[]jsonschema.ValError{
|
||||
@@ -236,11 +239,15 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templated := w.String()
|
||||
if strings.TrimSpace(templated) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if kind == "" {
|
||||
newCheck.SchemaString = w.String()
|
||||
newCheck.SchemaString = templated
|
||||
} else {
|
||||
newCheck.AdditionalSchemaStrings[kind] = w.String()
|
||||
newCheck.AdditionalSchemaStrings[kind] = templated
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
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
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func NewGenericResourceFromBytes(contentBytes []byte) (GenericResource, error) {
|
||||
}
|
||||
|
||||
// ResolveControllerFromPod builds a new workload for a given Pod
|
||||
func ResolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
|
||||
func ResolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient dynamic.Interface, restMapper meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
|
||||
workload, err := resolveControllerFromPod(ctx, podResource, dynamicClient, restMapper, objectCache)
|
||||
if err != nil {
|
||||
return workload, err
|
||||
@@ -150,7 +150,7 @@ func ResolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
|
||||
return workload, err
|
||||
}
|
||||
|
||||
func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
|
||||
func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod, dynamicClient dynamic.Interface, restMapper meta.RESTMapper, objectCache map[string]unstructured.Unstructured) (GenericResource, error) {
|
||||
podWorkload, err := NewGenericResourceFromPod(podResource, nil)
|
||||
if err != nil {
|
||||
return podWorkload, err
|
||||
@@ -217,7 +217,7 @@ func resolveControllerFromPod(ctx context.Context, podResource kubeAPICoreV1.Pod
|
||||
return workload, nil
|
||||
}
|
||||
|
||||
func cacheSingleObject(ctx context.Context, apiVersion, kind, namespace, name string, dynamicClient dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) error {
|
||||
func cacheSingleObject(ctx context.Context, apiVersion, kind, namespace, name string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper, objectCache map[string]unstructured.Unstructured) error {
|
||||
logrus.Debugf("Caching a single %s", kind)
|
||||
object, err := getObject(ctx, namespace, kind, apiVersion, name, dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
@@ -230,10 +230,10 @@ func cacheSingleObject(ctx context.Context, apiVersion, kind, namespace, name st
|
||||
return nil
|
||||
}
|
||||
|
||||
func cacheAllObjectsOfKind(ctx context.Context, apiVersion, kind string, dynamicClient dynamic.Interface, restMapper *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) error {
|
||||
func cacheAllObjectsOfKind(ctx context.Context, apiVersion, kind string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper, objectCache map[string]unstructured.Unstructured) error {
|
||||
logrus.Debugf("Caching all %s", kind)
|
||||
fqKind := schema.FromAPIVersionAndKind(apiVersion, kind)
|
||||
mapping, err := (*restMapper).RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving mapping of API %s and Kind %s because of error: %v", apiVersion, kind, err)
|
||||
return err
|
||||
@@ -252,9 +252,9 @@ func cacheAllObjectsOfKind(ctx context.Context, apiVersion, kind string, dynamic
|
||||
return nil
|
||||
}
|
||||
|
||||
func getObject(ctx context.Context, namespace, kind, version, name string, dynamicClient dynamic.Interface, restMapper *meta.RESTMapper) (*unstructured.Unstructured, error) {
|
||||
func getObject(ctx context.Context, namespace, kind, version, name string, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) (*unstructured.Unstructured, error) {
|
||||
fqKind := schema.FromAPIVersionAndKind(version, kind)
|
||||
mapping, err := (*restMapper).RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+55
-99
@@ -26,6 +26,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fairwindsops/controller-utils/pkg/controller"
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -33,12 +34,12 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
k8sYaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth" // Required for other auth providers like GKE.
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
@@ -145,20 +146,13 @@ func CreateResourceProvider(ctx context.Context, directory, workload string, c c
|
||||
|
||||
// CreateResourceProviderFromResource creates a new ResourceProvider that just contains one workload
|
||||
func CreateResourceProviderFromResource(ctx context.Context, workload string) (*ResourceProvider, error) {
|
||||
kubeConf, configError := config.GetConfig()
|
||||
if configError != nil {
|
||||
logrus.Errorf("Error fetching KubeConfig: %v", configError)
|
||||
return nil, configError
|
||||
}
|
||||
kube, err := kubernetes.NewForConfig(kubeConf)
|
||||
dynamicClient, restMapper, clientSet, _, err := GetKubeClient(ctx, "")
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating Kubernetes client: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
serverVersion, err := kube.Discovery().ServerVersion()
|
||||
serverVersion, err := clientSet.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logrus.Errorf("Error fetching Cluster API version: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error fetching Cluster API version: %w", err)
|
||||
}
|
||||
resources := newResourceProvider(serverVersion.Major+"."+serverVersion.Minor, "Resource", workload)
|
||||
|
||||
@@ -171,28 +165,14 @@ func CreateResourceProviderFromResource(ctx context.Context, workload string) (*
|
||||
version := parts[2]
|
||||
name := parts[3]
|
||||
|
||||
dynamicInterface, err := dynamic.NewForConfig(kubeConf)
|
||||
obj, err := getObject(ctx, namespace, kind, version, name, dynamicClient, restMapper)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error connecting to dynamic interface: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
groupResources, err := restmapper.GetAPIGroupResources(kube.Discovery())
|
||||
if err != nil {
|
||||
logrus.Errorf("Error getting API Group resources: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(groupResources)
|
||||
obj, err := getObject(ctx, namespace, kind, version, name, dynamicInterface, &restMapper)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not find workload %s: %v", workload, err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Could not find workload %s: %w", workload, err)
|
||||
}
|
||||
workloadObj, err := NewGenericResourceFromUnstructured(*obj, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not parse workload %s: %v", workload, err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Could not parse workload %s: %w", workload, err)
|
||||
}
|
||||
|
||||
resources.Resources.addResource(workloadObj)
|
||||
return &resources, nil
|
||||
}
|
||||
@@ -243,22 +223,37 @@ func CreateResourceProviderFromYaml(yamlContent string) *ResourceProvider {
|
||||
|
||||
// CreateResourceProviderFromCluster creates a new ResourceProvider using live data from a cluster
|
||||
func CreateResourceProviderFromCluster(ctx context.Context, c conf.Configuration) (*ResourceProvider, error) {
|
||||
kubeConf, configError := config.GetConfigWithContext(c.KubeContext)
|
||||
if configError != nil {
|
||||
logrus.Errorf("Error fetching KubeConfig: %v", configError)
|
||||
return nil, configError
|
||||
}
|
||||
api, err := kubernetes.NewForConfig(kubeConf)
|
||||
dynamicClient, _, clientSet, clusterHost, err := GetKubeClient(ctx, c.KubeContext)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating Kubernetes client: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
dynamicInterface, err := dynamic.NewForConfig(kubeConf)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error connecting to dynamic interface: %v", err)
|
||||
return nil, err
|
||||
return CreateResourceProviderFromAPI(ctx, clientSet, clusterHost, dynamicClient, c)
|
||||
}
|
||||
|
||||
func GetKubeClient(ctx context.Context, kubeContext string) (dynamic.Interface, meta.RESTMapper, kubernetes.Interface, string, error) {
|
||||
var kubeConf *rest.Config
|
||||
var err error
|
||||
if len(kubeContext) > 0 {
|
||||
kubeConf, err = config.GetConfigWithContext(kubeContext)
|
||||
} else {
|
||||
kubeConf, err = config.GetConfig()
|
||||
}
|
||||
return CreateResourceProviderFromAPI(ctx, api, kubeConf.Host, dynamicInterface, c)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error fetching KubeConfig: %v", err)
|
||||
}
|
||||
clientSet, err := kubernetes.NewForConfig(kubeConf)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error creating Kubernetes client: %v", err)
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(kubeConf)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error connecting to dynamic interface: %v", err)
|
||||
}
|
||||
resources, err := restmapper.GetAPIGroupResources(clientSet.Discovery())
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", fmt.Errorf("Error getting API Group resources: %v", err)
|
||||
}
|
||||
return dynamicClient, restmapper.NewDiscoveryRESTMapper(resources), clientSet, kubeConf.Host, nil
|
||||
}
|
||||
|
||||
// CreateResourceProviderFromAPI creates a new ResourceProvider from an existing k8s interface
|
||||
@@ -302,13 +297,6 @@ 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 {
|
||||
@@ -343,11 +331,15 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
var kubernetesResources []GenericResource
|
||||
for _, kind := range additionalKinds {
|
||||
groupKind := parseGroupKind(maybeTransformKindIntoGroupKind(string(kind)))
|
||||
mapping, err := (restMapper).RESTMapping(groupKind)
|
||||
mapping, err := restMapper.RESTMapping(groupKind)
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving mapping of Kind %s because of error: %v", kind, err)
|
||||
return nil, err
|
||||
}
|
||||
if c.Namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
logrus.Infof("Skipping %s because of auditing specific namespace", mapping.GroupVersionKind)
|
||||
continue
|
||||
}
|
||||
|
||||
logrus.Info("Loading " + kind)
|
||||
objects, err := dynamic.Resource(mapping.Resource).Namespace(c.Namespace).List(ctx, metav1.ListOptions{})
|
||||
@@ -363,17 +355,24 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
kubernetesResources = append(kubernetesResources, res)
|
||||
}
|
||||
}
|
||||
|
||||
objectCache := map[string]unstructured.Unstructured{}
|
||||
|
||||
logrus.Info("Loading controllers")
|
||||
controllers, err := LoadControllers(ctx, pods.Items, dynamic, &restMapper, objectCache)
|
||||
client := controller.Client{
|
||||
Context: ctx,
|
||||
Dynamic: dynamic,
|
||||
RESTMapper: restMapper,
|
||||
}
|
||||
topControllers, err := client.GetAllTopControllersSummary("")
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading controllers from pods: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error while getting all TopControllers: %v", err)
|
||||
}
|
||||
for _, workload := range topControllers {
|
||||
topController := workload.TopController
|
||||
workloadObj, err := NewGenericResourceFromUnstructured(topController, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse workload %v: %w", workload, err)
|
||||
}
|
||||
kubernetesResources = append(kubernetesResources, workloadObj)
|
||||
}
|
||||
// resources loaded from custom checks can also contain controllers and thus would be added twice to the provider
|
||||
kubernetesResources = deduplicateControllers(append(kubernetesResources, controllers...))
|
||||
|
||||
provider.Nodes = nodes.Items
|
||||
provider.Namespaces = namespaces.Items
|
||||
@@ -382,49 +381,6 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
return &provider, nil
|
||||
}
|
||||
|
||||
// LoadControllers loads a list of controllers from the kubeResources Pods
|
||||
func LoadControllers(ctx context.Context, pods []corev1.Pod, dynamicClient dynamic.Interface, restMapperPointer *meta.RESTMapper, objectCache map[string]unstructured.Unstructured) ([]GenericResource, error) {
|
||||
interfaces := []GenericResource{}
|
||||
deduped := map[string]*corev1.Pod{}
|
||||
for idx, pod := range pods {
|
||||
owners := pod.ObjectMeta.OwnerReferences
|
||||
if len(owners) == 0 {
|
||||
deduped[pod.ObjectMeta.Namespace+"/Pod/"+pod.ObjectMeta.Name] = &pods[idx]
|
||||
continue
|
||||
}
|
||||
deduped[pod.ObjectMeta.Namespace+"/"+owners[0].Kind+"/"+owners[0].Name] = &pods[idx]
|
||||
}
|
||||
for key, pod := range deduped {
|
||||
logrus.Debugf("Resolving controller from pod %s", key)
|
||||
workload, err := ResolveControllerFromPod(ctx, *pod, dynamicClient, restMapperPointer, objectCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
interfaces = append(interfaces, workload)
|
||||
}
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
// Because the controllers with an Owner take on the name of the Owner, this eliminates any duplicates.
|
||||
// In cases like CronJobs older children can hang around, so this takes the most recent.
|
||||
func deduplicateControllers(inputResources []GenericResource) []GenericResource {
|
||||
controllerMap := make(map[string]GenericResource)
|
||||
for _, controller := range inputResources {
|
||||
key := controller.ObjectMeta.GetNamespace() + "/" + controller.Kind + "/" + controller.ObjectMeta.GetName()
|
||||
oldController, ok := controllerMap[key]
|
||||
if !ok || controller.ObjectMeta.GetCreationTimestamp().Time.After(oldController.ObjectMeta.GetCreationTimestamp().Time) {
|
||||
controllerMap[key] = controller
|
||||
}
|
||||
}
|
||||
results := make([]GenericResource, len(controllerMap))
|
||||
idx := 0
|
||||
for _, controller := range controllerMap {
|
||||
results[idx] = controller
|
||||
idx++
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (resources *ResourceProvider) addResourcesFromReader(reader io.Reader) error {
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
|
||||
+23
-18
@@ -17,6 +17,7 @@ package kube
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -47,8 +48,8 @@ func TestGetResourcesFromPath(t *testing.T) {
|
||||
namespaceCount[controller.ObjectMeta.GetNamespace()]++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, 11, provider.Resources.GetLength())
|
||||
assert.Equal(t, 10, namespaceCount[""])
|
||||
assert.Equal(t, 10, provider.Resources.GetLength())
|
||||
assert.Equal(t, 9, namespaceCount[""])
|
||||
assert.Equal(t, 1, namespaceCount["two"])
|
||||
}
|
||||
|
||||
@@ -64,8 +65,8 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
|
||||
assert.Equal(t, 1, len(resources.Resources["extensions/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["extensions/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
assert.Equal(t, 1, len(resources.Resources["apps/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["apps/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
@@ -87,8 +88,8 @@ func TestAddResourcesFromReader(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
|
||||
assert.Equal(t, 1, len(resources.Resources["extensions/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["extensions/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
assert.Equal(t, 1, len(resources.Resources["apps/Deployment"]), "Should have one controller")
|
||||
assert.Equal(t, "dashboard", resources.Resources["apps/Deployment"][0].PodSpec.Containers[0].Name)
|
||||
|
||||
assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
|
||||
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
|
||||
@@ -151,20 +152,24 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want.SourceType, resources.SourceType)
|
||||
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.Resources), "Should have 5 controllers")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, tt.want.SourceType, resources.SourceType)
|
||||
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")
|
||||
for k, v := range resources.Resources {
|
||||
fmt.Println("cont", k, v)
|
||||
}
|
||||
assert.Equal(t, 5, len(resources.Resources), "Should have 5 controllers")
|
||||
|
||||
for _, controllers := range resources.Resources {
|
||||
for _, ctrl := range controllers {
|
||||
expectedNames[ctrl.ObjectMeta.GetName()] = true
|
||||
for _, controllers := range resources.Resources {
|
||||
for _, ctrl := range controllers {
|
||||
expectedNames[ctrl.ObjectMeta.GetName()] = true
|
||||
}
|
||||
}
|
||||
for name, val := range expectedNames {
|
||||
assert.Equal(t, true, val, name)
|
||||
}
|
||||
}
|
||||
for name, val := range expectedNames {
|
||||
assert.Equal(t, true, val, name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: batch/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test-deployment
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test-deployment-2
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: test
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
app: test
|
||||
template:
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
app: test
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: busybox
|
||||
command: ["tail"]
|
||||
args: ["-f", "/dev/null"]
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
name: polaris
|
||||
---
|
||||
# Source: polaris/templates/dashboard.deployment.yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--
|
||||
# Source: polaris/templates/dashboard.deployment.yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
::::
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestControllerLevelChecks(t *testing.T) {
|
||||
|
||||
res, err := kube.CreateResourceProviderFromPath("../kube/test_files/test_1")
|
||||
assert.Equal(t, nil, err, "Error should be nil")
|
||||
assert.Equal(t, 11, res.Resources.GetLength())
|
||||
assert.Equal(t, 10, res.Resources.GetLength())
|
||||
testResources(res)
|
||||
|
||||
replicaSpec := map[string]interface{}{"replicas": 2}
|
||||
|
||||
@@ -53,6 +53,26 @@ type AuditData struct {
|
||||
Score uint
|
||||
}
|
||||
|
||||
// FilterResultsBySeverityLevel includes results according to the provided severity level:
|
||||
// 'danger' is the least verbose, 'warning' is medium verbosity, default behavior will
|
||||
// include all results, which currently also includes 'ignore'
|
||||
func (res AuditData) FilterResultsBySeverityLevel(severityLevel config.Severity) AuditData {
|
||||
resCopy := res
|
||||
resCopy.Results = []Result{}
|
||||
|
||||
filteredResults := funk.Map(res.Results, func(auditDataResult Result) Result {
|
||||
return auditDataResult.filterResultsBySeverityLevel(severityLevel)
|
||||
}).([]Result)
|
||||
|
||||
for _, result := range filteredResults {
|
||||
if result.isNotEmpty() {
|
||||
resCopy.Results = append(resCopy.Results, result)
|
||||
}
|
||||
}
|
||||
|
||||
return resCopy
|
||||
}
|
||||
|
||||
// RemoveSuccessfulResults removes all tests that have passed
|
||||
func (res AuditData) RemoveSuccessfulResults() AuditData {
|
||||
resCopy := res
|
||||
@@ -108,6 +128,25 @@ func (res ResultSet) removeSuccessfulResults() ResultSet {
|
||||
return newResults
|
||||
}
|
||||
|
||||
func (res ResultSet) filterResultsBySeverityLevel(severityLevel config.Severity) ResultSet {
|
||||
newResults := ResultSet{}
|
||||
for k, resultMessage := range res {
|
||||
switch severityLevel {
|
||||
case config.SeverityDanger:
|
||||
if resultMessage.Severity == config.SeverityDanger {
|
||||
newResults[k] = resultMessage
|
||||
}
|
||||
case config.SeverityWarning:
|
||||
if resultMessage.Severity == config.SeverityDanger || resultMessage.Severity == config.SeverityWarning {
|
||||
newResults[k] = resultMessage
|
||||
}
|
||||
default:
|
||||
return res
|
||||
}
|
||||
}
|
||||
return newResults
|
||||
}
|
||||
|
||||
// Result provides results for a Kubernetes object
|
||||
type Result struct {
|
||||
Name string
|
||||
@@ -128,6 +167,16 @@ func (res Result) removeSuccessfulResults() Result {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res Result) filterResultsBySeverityLevel(severityLevel config.Severity) Result {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
if res.PodResult != nil {
|
||||
podCopy := res.PodResult.filterResultsBySeverityLevel(severityLevel)
|
||||
resCopy.PodResult = &podCopy
|
||||
}
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res Result) isNotEmpty() bool {
|
||||
if res.PodResult != nil {
|
||||
return res.PodResult.isNotEmpty()
|
||||
@@ -151,6 +200,15 @@ func (res PodResult) removeSuccessfulResults() PodResult {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res PodResult) filterResultsBySeverityLevel(severityLevel config.Severity) PodResult {
|
||||
resCopy := PodResult{}
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
resCopy.ContainerResults = funk.Map(res.ContainerResults, func(containerResult ContainerResult) ContainerResult {
|
||||
return containerResult.filterResultsBySeverityLevel(severityLevel)
|
||||
}).([]ContainerResult)
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res PodResult) isNotEmpty() bool {
|
||||
for _, cr := range res.ContainerResults {
|
||||
if cr.isNotEmpty() {
|
||||
@@ -172,6 +230,12 @@ func (res ContainerResult) removeSuccessfulResults() ContainerResult {
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res ContainerResult) filterResultsBySeverityLevel(severityLevel config.Severity) ContainerResult {
|
||||
resCopy := res
|
||||
resCopy.Results = res.Results.filterResultsBySeverityLevel(severityLevel)
|
||||
return resCopy
|
||||
}
|
||||
|
||||
func (res ContainerResult) isNotEmpty() bool {
|
||||
return res.Results.isNotEmpty()
|
||||
}
|
||||
|
||||
+31
-5
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/fairwindsops/polaris/pkg/mutation"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
@@ -35,41 +36,66 @@ type Mutator struct {
|
||||
decoder *admission.Decoder
|
||||
}
|
||||
|
||||
var _ admission.Handler = &Mutator{}
|
||||
|
||||
// NewMutateWebhook creates a mutating admission webhook for the apiType.
|
||||
func NewMutateWebhook(mgr manager.Manager, mutator Mutator) {
|
||||
func NewMutateWebhook(mgr manager.Manager, c config.Configuration) {
|
||||
path := "/mutate"
|
||||
|
||||
mutator := Mutator{
|
||||
Client: mgr.GetClient(),
|
||||
decoder: admission.NewDecoder(runtime.NewScheme()),
|
||||
Config: c,
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while validating resource: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if results == nil {
|
||||
logrus.Infof("Not mutating owned pod")
|
||||
return nil, nil
|
||||
}
|
||||
patches := mutation.GetMutationsFromResult(results)
|
||||
originalYaml, err := yaml.JSONToYAML(kubeResources.OriginalObjectJSON)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to convert JSON to YAML: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
mutatedYamlStr, err := mutation.ApplyAllMutations(string(originalYaml), patches)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to apply mutations: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return jsonpatch.CreatePatch(originalYaml, []byte(mutatedYamlStr))
|
||||
|
||||
mutatedJson, err := yaml.YAMLToJSON([]byte(mutatedYamlStr))
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to convert YAML to JSON: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ops, err := jsonpatch.CreatePatch(kubeResources.OriginalObjectJSON, mutatedJson)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create patch from mutation: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (m *Mutator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
logrus.Info("Starting request")
|
||||
logrus.Info("Starting mutation request")
|
||||
patches, err := m.mutate(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error while getting mutations: %v", err)
|
||||
return admission.Errored(403, err)
|
||||
}
|
||||
if patches == nil {
|
||||
logrus.Infof("No patches generated")
|
||||
return admission.Allowed("Allowed")
|
||||
}
|
||||
logrus.Infof("Generated %d patches", len(patches))
|
||||
return admission.Patched("", patches...)
|
||||
}
|
||||
|
||||
+22
-21
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
@@ -38,19 +39,14 @@ type Validator struct {
|
||||
Config config.Configuration
|
||||
}
|
||||
|
||||
// InjectDecoder injects the decoder.
|
||||
func (v *Validator) InjectDecoder(d *admission.Decoder) error {
|
||||
logrus.Info("Injecting decoder")
|
||||
v.decoder = d
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ admission.Handler = &Validator{}
|
||||
|
||||
// NewValidateWebhook creates a validating admission webhook for the apiType.
|
||||
func NewValidateWebhook(mgr manager.Manager, validator Validator) {
|
||||
func NewValidateWebhook(mgr manager.Manager, c config.Configuration) {
|
||||
path := "/validate"
|
||||
|
||||
validator := Validator{
|
||||
Client: mgr.GetClient(),
|
||||
decoder: admission.NewDecoder(runtime.NewScheme()),
|
||||
Config: c,
|
||||
}
|
||||
mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &validator})
|
||||
}
|
||||
|
||||
@@ -60,35 +56,40 @@ func (v *Validator) handleInternal(req admission.Request) (*validator.Result, ku
|
||||
|
||||
// GetValidatedResults returns the validated results.
|
||||
func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) {
|
||||
var controller kube.GenericResource
|
||||
var resource kube.GenericResource
|
||||
var err error
|
||||
if kind == "Pod" {
|
||||
if decoder == nil {
|
||||
panic("Decoder is nil!")
|
||||
}
|
||||
pod := corev1.Pod{}
|
||||
err := decoder.Decode(req, &pod)
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
logrus.Errorf("Failed to decode pod: %v", err)
|
||||
return nil, resource, err
|
||||
}
|
||||
if len(pod.ObjectMeta.OwnerReferences) > 0 {
|
||||
logrus.Infof("Allowing owned pod %s/%s to pass through webhook", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
return nil, controller, nil
|
||||
return nil, resource, nil
|
||||
}
|
||||
controller, err = kube.NewGenericResourceFromPod(pod, pod)
|
||||
resource, err = kube.NewGenericResourceFromPod(pod, pod)
|
||||
} else {
|
||||
controller, err = kube.NewGenericResourceFromBytes(req.Object.Raw)
|
||||
resource, err = kube.NewGenericResourceFromBytes(req.Object.Raw)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
logrus.Errorf("Failed to create resource: %v", err)
|
||||
return nil, resource, err
|
||||
}
|
||||
controllerResult, err := validator.ApplyAllSchemaChecks(&config, nil, controller)
|
||||
resourceResult, err := validator.ApplyAllSchemaChecks(&config, nil, resource)
|
||||
if err != nil {
|
||||
return nil, controller, err
|
||||
return nil, resource, err
|
||||
}
|
||||
return &controllerResult, controller, nil
|
||||
return &resourceResult, resource, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
logrus.Info("Starting request")
|
||||
logrus.Info("Starting admission request")
|
||||
result, _, err := v.handleInternal(req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error validating request: %v", err)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# This succeeds because automounting is disabled on the pod, and there is no specified service account
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-pod
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: nginx-foo
|
||||
image: nginx-foo
|
||||
ports:
|
||||
- containerPort: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- ALL
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
env: test
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
+1
-1
@@ -3,7 +3,7 @@ kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: not-nginx
|
||||
app.kubernetes.io/instance: not-nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
+1
-1
@@ -3,7 +3,7 @@ kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
app.kubernetes.io/instance: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
foo: bar
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
image: zookeeper
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: zookeeper-pdb
|
||||
spec:
|
||||
minAvailable: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
@@ -2,10 +2,11 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
|
||||
@@ -2,10 +2,12 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zookeeper
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: zookeeper
|
||||
foo: bar
|
||||
spec:
|
||||
containers:
|
||||
- name: zookeeper
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
containers:
|
||||
exclude:
|
||||
- initContainer
|
||||
successMessage: Resource limits are within the required range
|
||||
failureMessage: Resource limits should be within the required range
|
||||
category: Resources
|
||||
target: Container
|
||||
schema:
|
||||
'$schema': http://json-schema.org/draft-07/schema
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
required:
|
||||
- limits
|
||||
properties:
|
||||
limits:
|
||||
type: object
|
||||
required:
|
||||
- memory
|
||||
- cpu
|
||||
properties:
|
||||
memory:
|
||||
type: string
|
||||
resourceMinimum: 100Mi # 104857600 bytes
|
||||
resourceMaximum: 6G
|
||||
cpu:
|
||||
type: string
|
||||
resourceMinimum: 100m
|
||||
resourceMaximum: "2"
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 250Mi
|
||||
cpu: 0.05
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 104857599
|
||||
cpu: 1
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 250Mi
|
||||
cpu: 1.5
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app.kubernetes.io/name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
resources:
|
||||
limits:
|
||||
memory: 104857600
|
||||
cpu: 1
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
role: service
|
||||
tier: backend
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: test
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [services]
|
||||
verbs: [get, list]
|
||||
---
|
||||
# check when there are no cluster roles present
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: test
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: test
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: test
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
role: service
|
||||
tier: backend
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: test
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [services]
|
||||
verbs: [get, list]
|
||||
---
|
||||
# check when there are no cluster roles present
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: test
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: test
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: test
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
role: service
|
||||
tier: backend
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: test
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [services]
|
||||
verbs: [get, list]
|
||||
---
|
||||
# check when there are no cluster roles present
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: test
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: test
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: test
|
||||
+1
-4
@@ -19,10 +19,7 @@ spec:
|
||||
app.kubernetes.io/name: basic-demo
|
||||
app.kubernetes.io/instance: demo
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: farglebargle
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
topologySpreadConstraints: []
|
||||
containers:
|
||||
- name: basic-demo
|
||||
image: "quay.io/fairwinds/docker-demo:latest"
|
||||
@@ -21,8 +21,13 @@ spec:
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: "topology.kubernetes.io/zone"
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: DoNotSchedule
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: foo
|
||||
matchLabelKeys:
|
||||
- pod-template-hash
|
||||
containers:
|
||||
- name: basic-demo
|
||||
image: "quay.io/fairwinds/docker-demo:latest"
|
||||
|
||||
+14
-40
@@ -18,13 +18,10 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
extv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -80,9 +77,9 @@ func MockNakedPod() corev1.Pod {
|
||||
}
|
||||
|
||||
// MockIngress creates an ingress object
|
||||
func MockIngress() extv1beta1.Ingress {
|
||||
return extv1beta1.Ingress{
|
||||
Spec: extv1beta1.IngressSpec{},
|
||||
func MockIngress() networkingv1.Ingress {
|
||||
return networkingv1.Ingress{
|
||||
Spec: networkingv1.IngressSpec{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +158,11 @@ func MockJob(namespace, name string) (batchv1.Job, corev1.Pod) {
|
||||
}
|
||||
|
||||
// MockCronJob creates a CronJob object.
|
||||
func MockCronJob(namespace, name string) (batchv1beta1.CronJob, corev1.Pod) {
|
||||
cj := batchv1beta1.CronJob{}
|
||||
func MockCronJob(namespace, name string) (batchv1.CronJob, corev1.Pod) {
|
||||
cj := batchv1.CronJob{}
|
||||
p := MockPod()
|
||||
spec := map[string]interface{}{}
|
||||
pod := MockController("batch/v1beta1", "CronJob", namespace, name, spec, p.Spec, &cj)
|
||||
pod := MockController("batch/v1", "CronJob", namespace, name, spec, p.Spec, &cj)
|
||||
cj.Spec.JobTemplate.Spec.Template.Spec = pod.Spec
|
||||
|
||||
return cj, pod
|
||||
@@ -190,9 +187,6 @@ func MockNamespace(name string) corev1.Namespace {
|
||||
// SetupTestAPI creates a test kube API struct.
|
||||
func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Interface) {
|
||||
scheme := runtime.NewScheme()
|
||||
appsv1.AddToScheme(scheme)
|
||||
corev1.AddToScheme(scheme)
|
||||
policyv1beta1.AddToScheme(scheme)
|
||||
fake.AddToScheme(scheme)
|
||||
dynamicClient := dynamicFake.NewSimpleDynamicClient(scheme, objects...)
|
||||
k := fake.NewSimpleClientset(objects...)
|
||||
@@ -201,7 +195,6 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
|
||||
GroupVersion: corev1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
|
||||
{Name: "serviceaccounts", Namespaced: true, Kind: "ServiceAccount"},
|
||||
{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
|
||||
},
|
||||
@@ -209,35 +202,16 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
|
||||
{
|
||||
GroupVersion: appsv1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||
{Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"},
|
||||
{Name: "statefulsets", Namespaced: true, Kind: "StatefulSet"},
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment", Version: "v1"},
|
||||
{Name: "daemonsets", Namespaced: true, Kind: "DaemonSet", Version: "v1"},
|
||||
{Name: "statefulsets", Namespaced: true, Kind: "StatefulSet", Version: "v1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: batchv1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: batchv1beta1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: appsv1beta2.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||
{Name: "deployments/scale", Namespaced: true, Kind: "Scale", Group: "apps", Version: "v1beta2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: appsv1beta1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "statefulsets", Namespaced: true, Kind: "StatefulSet"},
|
||||
{Name: "statefulsets/scale", Namespaced: true, Kind: "Scale", Group: "apps", Version: "v1beta1"},
|
||||
{Name: "cronjobs", Namespaced: true, Kind: "CronJob", Version: "v1"},
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job", Version: "v1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -248,7 +222,7 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: policyv1beta1.SchemeGroupVersion.String(),
|
||||
GroupVersion: policyv1.SchemeGroupVersion.String(),
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "poddisruptionbudgets", Namespaced: true, Kind: "PodDisruptionBudget", Version: "v1"},
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
deploymentMissingReplicas: warning
|
||||
priorityClassNotSet: ignore
|
||||
priorityClassNotSet: warning
|
||||
runAsRootAllowed: danger
|
||||
cpuRequestsMissing: warning
|
||||
cpuLimitsMissing: warning
|
||||
@@ -27,6 +27,7 @@ checks:
|
||||
memoryLimitsMissing: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
insecureCapabilities: warning
|
||||
`
|
||||
|
||||
func TestMutations(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: batch/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: test
|
||||
@@ -21,4 +21,4 @@ spec:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
restartPolicy: OnFailure
|
||||
restartPolicy: OnFailure
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
spec:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect:
|
||||
effect:
|
||||
containers:
|
||||
- name: fluentd-elasticsearch
|
||||
image: gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
readOnly: true
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
@@ -44,4 +44,4 @@ spec:
|
||||
- name: varlibdockercontainers
|
||||
hostPath:
|
||||
path: /var/lib/docker/containers
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
spec:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect:
|
||||
effect:
|
||||
containers:
|
||||
- name: fluentd-elasticsearch
|
||||
image: gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
readOnly: true
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
@@ -44,4 +44,4 @@ spec:
|
||||
- name: varlibdockercontainers
|
||||
hostPath:
|
||||
path: /var/lib/docker/containers
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
- containerPort: 80
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
|
||||
@@ -7,15 +7,15 @@ spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pi
|
||||
image: perl:5.3
|
||||
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
- name: pi
|
||||
image: perl:5.3
|
||||
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
restartPolicy: Never
|
||||
restartPolicy: Never
|
||||
|
||||
@@ -19,9 +19,9 @@ spec:
|
||||
- containerPort: 80
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
- ALL
|
||||
|
||||
@@ -53,4 +53,4 @@ spec:
|
||||
storageClassName: "my-storage-class"
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
storage: 1Gi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: batch/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: test
|
||||
@@ -15,10 +15,10 @@ spec:
|
||||
- whoami
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
restartPolicy: OnFailure
|
||||
restartPolicy: OnFailure
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
spec:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect:
|
||||
effect:
|
||||
containers:
|
||||
- name: fluentd-elasticsearch
|
||||
image: gcr.io/fluentd-elasticsearch/fluentd:v2.5.1
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
readOnly: true
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
@@ -44,4 +44,4 @@ spec:
|
||||
- name: varlibdockercontainers
|
||||
hostPath:
|
||||
path: /var/lib/docker/containers
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
securityContext:
|
||||
@@ -26,4 +27,4 @@ spec:
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
- ALL
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: nginx-2
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
@@ -60,7 +60,7 @@ function clean_up() {
|
||||
echo "Uninstalling webhook and webhook config"
|
||||
kubectl delete validatingwebhookconfigurations polaris-webhook --wait=false || true
|
||||
kubectl delete validatingwebhookconfigurations polaris-validate-webhook --wait=false || true
|
||||
kubectl delete validatingwebhookconfigurations polaris-mutate-webhook --wait=false || true
|
||||
kubectl delete mutatingwebhookconfigurations polaris-mutate-webhook --wait=false || true
|
||||
kubectl -n polaris delete deploy -l app=polaris --wait=false || true
|
||||
echo -e "\n\nDone cleaning up\n\n"
|
||||
}
|
||||
@@ -82,11 +82,12 @@ kubectl create ns tests
|
||||
echo "Installing a bad deployment"
|
||||
kubectl apply -n scale-test -f ./test/webhook_cases/failing_test.deployment.yaml
|
||||
|
||||
echo "Installing the webhook"
|
||||
echo "Installing the webhook at version $CI_SHA1"
|
||||
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
|
||||
helm install polaris fairwinds-stable/polaris --namespace polaris --create-namespace \
|
||||
--set dashboard.enable=false \
|
||||
--set webhook.enable=true \
|
||||
--set webhook.mutate=true \
|
||||
--set image.tag=$CI_SHA1
|
||||
|
||||
echo "Waiting for the webhook to come online"
|
||||
@@ -105,6 +106,7 @@ for filename in test/webhook_cases/passing_test.*.yaml; do
|
||||
if ! kubectl apply -n tests -f $filename; then
|
||||
ALL_TESTS_PASSED=0
|
||||
echo -e "${RED}****Test Failed: Polaris prevented a resource with no configuration issues****${NC}"
|
||||
kubectl logs -n polaris deploy/polaris-webhook
|
||||
else
|
||||
echo -e "${GREEN}****Test Passed: Polaris correctly allowed this resource****${NC}"
|
||||
fi
|
||||
@@ -118,7 +120,7 @@ for filename in test/webhook_cases/failing_test.*.yaml; do
|
||||
if kubectl apply -n tests -f $filename; then
|
||||
ALL_TESTS_PASSED=0
|
||||
echo -e "${RED}****Test Failed: Polaris should have prevented this resource due to configuration issues.****${NC}"
|
||||
kubectl logs -n polaris $(kubectl get po -oname -n polaris | grep webhook)
|
||||
kubectl logs -n polaris deploy/polaris-webhook
|
||||
else
|
||||
echo -e "${GREEN}****Test Passed: Polaris correctly prevented this resource****${NC}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user