mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-23 22:26:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e60793a022 | ||
|
|
c8394bf9d7 | ||
|
|
0b765df04d | ||
|
|
3e96c582f0 | ||
|
|
391b802d4d | ||
|
|
f295dd0fb5 | ||
|
|
97687761a7 | ||
|
|
c856874564 | ||
|
|
09525d01a1 | ||
|
|
10e82cf0ae | ||
|
|
a5144c45b2 | ||
|
|
9be34236cb | ||
|
|
e7eb079921 | ||
|
|
d55cd4bdf8 | ||
|
|
75997f0e93 | ||
|
|
b0d86cdcd2 | ||
|
|
5595de4ef9 |
+1
-1
@@ -1,2 +1,2 @@
|
||||
## DO NOT EDIT - Managed by Terraform
|
||||
* @rbren @mggude
|
||||
* @rbren @sudermanjr
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.18
|
||||
FROM alpine:3.19
|
||||
|
||||
LABEL org.opencontainers.image.authors="FairwindsOps, Inc." \
|
||||
org.opencontainers.image.vendor="FairwindsOps, Inc." \
|
||||
|
||||
@@ -37,10 +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:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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:
|
||||
@@ -12,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+35
-10
@@ -24,6 +24,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
workloads "github.com/fairwindsops/insights-plugins/plugins/workloads"
|
||||
workloadsPkg "github.com/fairwindsops/insights-plugins/plugins/workloads/pkg"
|
||||
@@ -48,12 +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() {
|
||||
@@ -69,12 +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{
|
||||
@@ -107,7 +114,7 @@ var auditCmd = &cobra.Command{
|
||||
}
|
||||
if helmChart != "" {
|
||||
var err error
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues)
|
||||
auditPath, err = ProcessHelmTemplates(helmChart, helmValues, helmSkipTests)
|
||||
if err != nil {
|
||||
logrus.Errorf("Couldn't process helm chart: %v", err)
|
||||
os.Exit(1)
|
||||
@@ -172,10 +179,14 @@ var auditCmd = &cobra.Command{
|
||||
logrus.Errorf("reporting audit file to insights: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logrus.Println("Success! You can see your results at:")
|
||||
logrus.Printf("%s/orgs/%s/clusters/%s/action-items\n", insightsHost, auth.Organization, clusterName)
|
||||
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)
|
||||
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()
|
||||
@@ -191,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 {
|
||||
@@ -209,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...)
|
||||
@@ -223,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" {
|
||||
|
||||
+4
-1
@@ -16,6 +16,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -84,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")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ meta:
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
* Change `metadataAndNameMismatched` to `metadataAndInstanceMismatched`
|
||||
|
||||
## 8.1.1
|
||||
* Add category for `metadataAndNameMismatched`.
|
||||
* Fix category for `priorityClassNotSet`.
|
||||
|
||||
@@ -17,7 +17,7 @@ key | default | description
|
||||
`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` | `warning` | Fails when PDB is missing.
|
||||
`metadataAndNameMismatched` | `warning` | Fails when label `app.kubernetes.io/name` and `metadata.name` mismatch
|
||||
`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
|
||||
@@ -70,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 Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
- [Topology Spread Constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/)
|
||||
|
||||
+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:
|
||||
|
||||
@@ -9,7 +9,7 @@ checks:
|
||||
topologySpreadConstraint: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
metadataAndNameMismatched: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
|
||||
# efficiency
|
||||
cpuRequestsMissing: warning
|
||||
|
||||
@@ -6,7 +6,7 @@ checks:
|
||||
pullPolicyNotAlways: warning
|
||||
readinessProbeMissing: warning
|
||||
livenessProbeMissing: warning
|
||||
metadataAndNameMismatched: warning
|
||||
metadataAndInstanceMismatched: warning
|
||||
pdbDisruptionsIsZero: warning
|
||||
missingPodDisruptionBudget: warning
|
||||
topologySpreadConstraint: warning
|
||||
|
||||
@@ -3,7 +3,8 @@ module github.com/fairwindsops/polaris
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
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
|
||||
@@ -16,9 +17,9 @@ require (
|
||||
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.27.2
|
||||
k8s.io/apimachinery v0.27.2
|
||||
k8s.io/client-go v0.27.2
|
||||
k8s.io/api v0.27.3
|
||||
k8s.io/apimachinery v0.27.4
|
||||
k8s.io/client-go v0.27.3
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
@@ -30,7 +31,6 @@ require (
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fairwindsops/controller-utils v0.3.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
|
||||
@@ -37,8 +37,8 @@ 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.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||
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/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
@@ -375,6 +375,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
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=
|
||||
@@ -400,6 +401,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=
|
||||
@@ -435,6 +437,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=
|
||||
@@ -472,6 +475,7 @@ 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.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -499,6 +503,7 @@ 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=
|
||||
@@ -546,14 +551,14 @@ 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-20220422013727-9388b58f7150/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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.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-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
@@ -565,6 +570,8 @@ 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.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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -624,6 +631,7 @@ 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.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
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=
|
||||
@@ -766,13 +774,13 @@ 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.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo=
|
||||
k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4=
|
||||
k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y=
|
||||
k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg=
|
||||
k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE=
|
||||
k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ=
|
||||
k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
|
||||
k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8=
|
||||
k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48=
|
||||
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.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
|
||||
@@ -55,7 +55,7 @@ var (
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndNameMismatched",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
"missingNetworkPolicy",
|
||||
"sensitiveConfigmapContent",
|
||||
|
||||
@@ -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{
|
||||
|
||||
+16
-59
@@ -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,7 +34,6 @@ 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"
|
||||
@@ -297,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 {
|
||||
@@ -358,17 +351,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
|
||||
@@ -377,49 +377,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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
+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"
|
||||
|
||||
Reference in New Issue
Block a user