mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-18 19:59:20 +00:00
Delete controllers package (#270)
* rename root fs check * speed up docker build * refactor webhook to be more generic * delete controllers pkg * revert deploy * fix example config * remove controllersToScan config * fix lint error * fix webhook name * FileSystem -> Filesystem * update deps * skip node owners * clean up meta tracking Co-authored-by: Robert Brennan <bobby.brennan@gmail.com>
This commit is contained in:
co-authored by
Robert Brennan
parent
f559f3328b
commit
6792fba91f
+7
-4
@@ -3,13 +3,16 @@ WORKDIR /go/src/github.com/fairwindsops/polaris/
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://proxy.golang.org
|
||||
|
||||
COPY . .
|
||||
RUN go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN go mod download
|
||||
RUN go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
|
||||
COPY . .
|
||||
RUN packr2 build -a -o polaris *.go
|
||||
|
||||
FROM alpine:3.10
|
||||
|
||||
+39
-8
@@ -15,7 +15,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -23,6 +22,14 @@ import (
|
||||
fwebhook "github.com/fairwindsops/polaris/pkg/webhook"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
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"
|
||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
k8sConfig "sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
@@ -30,6 +37,28 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
)
|
||||
|
||||
var supportedVersions = map[string]runtime.Object{
|
||||
"appsv1/Deployment": &appsv1.Deployment{},
|
||||
"appsv1beta1/Deployment": &appsv1beta1.Deployment{},
|
||||
"appsv1beta2/Deployment": &appsv1beta2.Deployment{},
|
||||
|
||||
"appsv1/StatefulSet": &appsv1.StatefulSet{},
|
||||
"appsv1beta1/StatefulSet": &appsv1beta1.StatefulSet{},
|
||||
"appsv1beta2/StatefulSet": &appsv1beta2.StatefulSet{},
|
||||
|
||||
"appsv1/DaemonSet": &appsv1.DaemonSet{},
|
||||
"appsv1beta2/DaemonSet": &appsv1beta2.DaemonSet{},
|
||||
|
||||
"batchv1/Job": &batchv1.Job{},
|
||||
|
||||
"batchv1beta1/CronJob": &batchv1beta1.CronJob{},
|
||||
"batchv2alpha1/CronJob": &batchv2alpha1.CronJob{},
|
||||
|
||||
"corev1/ReplicationController": &corev1.ReplicationController{},
|
||||
|
||||
"corev1/Pod": &corev1.Pod{},
|
||||
}
|
||||
|
||||
var webhookPort int
|
||||
var disableWebhookConfigInstaller bool
|
||||
|
||||
@@ -102,14 +131,16 @@ var webhookCmd = &cobra.Command{
|
||||
// Should only register controllers that are configured to be scanned
|
||||
logrus.Debug("Registering webhooks to the webhook server")
|
||||
var webhooks []webhook.Webhook
|
||||
for index, controllerToScan := range config.ControllersToScan {
|
||||
for innerIndex, supportedAPIType := range controllerToScan.ListSupportedAPIVersions() {
|
||||
webhookName := strings.ToLower(fmt.Sprintf("%s-%d-%d", controllerToScan, index, innerIndex))
|
||||
hook := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: config}, supportedAPIType)
|
||||
if hook != nil {
|
||||
webhooks = append(webhooks, hook)
|
||||
}
|
||||
for name, supportedAPIType := range supportedVersions {
|
||||
webhookName := strings.ToLower(name)
|
||||
webhookName = strings.ReplaceAll(webhookName, "/", "-")
|
||||
hook, err := fwebhook.NewWebhook(webhookName, mgr, fwebhook.Validator{Config: config}, supportedAPIType)
|
||||
if err != nil {
|
||||
logrus.Warningf("Couldn't build webhook %s: %v", webhookName, err)
|
||||
continue
|
||||
}
|
||||
webhooks = append(webhooks, hook)
|
||||
logrus.Infof("%s webhook started", webhookName)
|
||||
}
|
||||
|
||||
if err = as.Register(webhooks...); err != nil {
|
||||
|
||||
@@ -33,7 +33,7 @@ data:
|
||||
# security
|
||||
hostIPCSet: error
|
||||
hostPIDSet: error
|
||||
notReadOnlyRootFileSystem: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: error
|
||||
runAsRootAllowed: warning
|
||||
runAsPrivileged: error
|
||||
@@ -100,7 +100,7 @@ data:
|
||||
- tiller
|
||||
- kube2iam
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- cert-manager
|
||||
- dns-controller
|
||||
@@ -128,7 +128,7 @@ data:
|
||||
- goldilocks
|
||||
- insights-agent-goldilocks-vpa-install
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- insights-agent-goldilocks-controller
|
||||
rules:
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ data:
|
||||
# security
|
||||
hostIPCSet: error
|
||||
hostPIDSet: error
|
||||
notReadOnlyRootFileSystem: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: error
|
||||
runAsRootAllowed: warning
|
||||
runAsPrivileged: error
|
||||
@@ -113,7 +113,7 @@ data:
|
||||
- tiller
|
||||
- kube2iam
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- cert-manager
|
||||
- dns-controller
|
||||
@@ -141,7 +141,7 @@ data:
|
||||
- goldilocks
|
||||
- insights-agent-goldilocks-vpa-install
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- insights-agent-goldilocks-controller
|
||||
rules:
|
||||
|
||||
@@ -6,7 +6,7 @@ key | default | description
|
||||
----|---------|------------
|
||||
`security.hostIPCSet` | `error` | Fails when `hostIPC` attribute is configured.
|
||||
`security.hostPIDSet` | `error` | Fails when `hostPID` attribute is configured.
|
||||
`security.notReadOnlyRootFileSystem` | `warning` | Fails when `securityContext.readOnlyRootFilesystem` is not true.
|
||||
`security.notReadOnlyRootFilesystem` | `warning` | Fails when `securityContext.readOnlyRootFilesystem` is not true.
|
||||
`security.privilegeEscalationAllowed` | `error` | Fails when `securityContext.allowPrivilegeEscalation` is true.
|
||||
`security.runAsRootAllowed` | `error` | Fails when `securityContext.runAsNonRoot` is not true.
|
||||
`security.runAsPrivileged` | `error` | Fails when `securityContext.privileged` is true.
|
||||
|
||||
@@ -17,7 +17,7 @@ checks:
|
||||
# security
|
||||
hostIPCSet: error
|
||||
hostPIDSet: error
|
||||
notReadOnlyRootFileSystem: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: error
|
||||
runAsRootAllowed: warning
|
||||
runAsPrivileged: error
|
||||
@@ -72,13 +72,6 @@ customChecks:
|
||||
not:
|
||||
pattern: ^quay.io
|
||||
|
||||
controllersToScan:
|
||||
- Deployments
|
||||
- StatefulSets
|
||||
- DaemonSets
|
||||
- CronJobs
|
||||
- Jobs
|
||||
- ReplicationControllers
|
||||
exemptions:
|
||||
- controllerNames:
|
||||
- dns-controller
|
||||
@@ -133,7 +126,7 @@ exemptions:
|
||||
- tiller
|
||||
- kube2iam
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- cert-manager
|
||||
- dns-controller
|
||||
|
||||
+3
-10
@@ -16,19 +16,12 @@ checks:
|
||||
# security
|
||||
hostIPCSet: error
|
||||
hostPIDSet: error
|
||||
notReadOnlyRootFileSystem: warning
|
||||
notReadOnlyRootFilesystem: warning
|
||||
privilegeEscalationAllowed: error
|
||||
runAsRootAllowed: warning
|
||||
runAsPrivileged: error
|
||||
dangerousCapabilities: error
|
||||
insecureCapabilities: warning
|
||||
controllersToScan:
|
||||
- Deployments
|
||||
- StatefulSets
|
||||
- DaemonSets
|
||||
- CronJobs
|
||||
- Jobs
|
||||
- ReplicationControllers
|
||||
exemptions:
|
||||
- controllerNames:
|
||||
- dns-controller
|
||||
@@ -83,7 +76,7 @@ exemptions:
|
||||
- tiller
|
||||
- kube2iam
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- cert-manager
|
||||
- dns-controller
|
||||
@@ -111,7 +104,7 @@ exemptions:
|
||||
- goldilocks
|
||||
- insights-agent-goldilocks-vpa-install
|
||||
rules:
|
||||
- notReadOnlyRootFileSystem
|
||||
- notReadOnlyRootFilesystem
|
||||
- controllerNames:
|
||||
- insights-agent-goldilocks-controller
|
||||
rules:
|
||||
|
||||
@@ -47,16 +47,17 @@ require (
|
||||
github.com/markbates/safe v1.0.1
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
|
||||
github.com/modern-go/reflect2 v1.0.1
|
||||
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709
|
||||
github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/prometheus/client_golang v0.9.3
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/common v0.9.1
|
||||
github.com/prometheus/procfs v0.0.11
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/qri-io/jsonschema v0.1.1
|
||||
github.com/rogpeppe/go-internal v1.5.2
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
@@ -76,7 +77,7 @@ require (
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
|
||||
golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40
|
||||
|
||||
@@ -250,6 +250,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -284,11 +285,13 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
type Configuration struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
Checks map[string]Severity `json:"checks"`
|
||||
ControllersToScan []SupportedController `json:"controllersToScan"`
|
||||
CustomChecks map[string]SchemaCheck `json:"customChecks"`
|
||||
Exemptions []Exemption `json:"exemptions"`
|
||||
DisallowExemptions bool `json:"disallowExemptions"`
|
||||
|
||||
@@ -31,16 +31,13 @@ var confInvalid = `test`
|
||||
var confValidYAML = `
|
||||
checks:
|
||||
cpuRequestsMissing: warning
|
||||
controllersToScan:
|
||||
- Deployments
|
||||
`
|
||||
|
||||
var confValidJSON = `
|
||||
{
|
||||
"checks": {
|
||||
"cpuRequestsMissing": "warning"
|
||||
},
|
||||
"controllersToScan": ["Deployments"]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -160,5 +157,4 @@ func TestConfigWithCustomChecks(t *testing.T) {
|
||||
func testParsedConfig(t *testing.T, config *Configuration) {
|
||||
assert.Equal(t, SeverityWarning, config.Checks["cpuRequestsMissing"])
|
||||
assert.Equal(t, Severity(""), config.Checks["cpuLimitsMissing"])
|
||||
assert.ElementsMatch(t, []SupportedController{Deployments}, config.ControllersToScan)
|
||||
}
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
// Unsupported is the default enum for non-defined controller types
|
||||
Unsupported SupportedController = iota
|
||||
// Deployments are a supported controller for scanning pod specs
|
||||
Deployments
|
||||
// StatefulSets are a supported controller for scanning pod specs
|
||||
StatefulSets
|
||||
// DaemonSets are a supported controller for scanning pod specs
|
||||
DaemonSets
|
||||
// Jobs are a supported controller for scanning pod specs
|
||||
Jobs
|
||||
// CronJobs are a supported controller for scanning pod specs
|
||||
CronJobs
|
||||
// ReplicationControllers are supported controllers for scanning pod specs
|
||||
ReplicationControllers
|
||||
// NakedPods are a pseudo-controller for scanning pod specs
|
||||
NakedPods
|
||||
)
|
||||
|
||||
// ControllerStrings are strongly ordered to match the SupportedController enum
|
||||
var ControllerStrings = []string{
|
||||
"Unsupported",
|
||||
"Deployment",
|
||||
"StatefulSet",
|
||||
"DaemonSet",
|
||||
"Job",
|
||||
"CronJob",
|
||||
"ReplicationController",
|
||||
"NakedPod",
|
||||
}
|
||||
|
||||
// stringLookupForSupportedControllers is the list of lowercase singular and plural strings for string to enum lookup
|
||||
var stringLookupForSupportedControllers = map[string]SupportedController{
|
||||
"deployment": Deployments,
|
||||
"deployments": Deployments,
|
||||
"statefulset": StatefulSets,
|
||||
"statefulsets": StatefulSets,
|
||||
"daemonset": DaemonSets,
|
||||
"daemonsets": DaemonSets,
|
||||
"job": Jobs,
|
||||
"jobs": Jobs,
|
||||
"cronjob": CronJobs,
|
||||
"cronjobs": CronJobs,
|
||||
"replicationcontroller": ReplicationControllers,
|
||||
"replicationcontrollers": ReplicationControllers,
|
||||
"nakedpod": NakedPods,
|
||||
"nakedpods": NakedPods,
|
||||
}
|
||||
|
||||
// SupportedController is a constant item of a controller that is supported for scanning pod specs
|
||||
type SupportedController int
|
||||
|
||||
// String returns the string name for a given SupportedController enum
|
||||
func (s SupportedController) String() string {
|
||||
return ControllerStrings[s]
|
||||
}
|
||||
|
||||
// MarshalJSON manages writing the enum into json data or error on unsupported value
|
||||
func (s SupportedController) MarshalJSON() ([]byte, error) {
|
||||
if s == Unsupported {
|
||||
return []byte{}, fmt.Errorf("Unsupported is not a valid Supported Controller")
|
||||
}
|
||||
buffer := bytes.NewBufferString(`"`)
|
||||
buffer.WriteString(s.String())
|
||||
buffer.WriteString(`"`)
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON handles reading json data into enum
|
||||
func (s *SupportedController) UnmarshalJSON(b []byte) error {
|
||||
var j string
|
||||
err := json.Unmarshal(b, &j)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*s = GetSupportedControllerFromString(j)
|
||||
if *s == Unsupported {
|
||||
return fmt.Errorf("Unsupported controller kind: %s", j)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSupportedAPIVersions for SupportedController returns all the apimachinery object type supported
|
||||
func (s SupportedController) ListSupportedAPIVersions() []runtime.Object {
|
||||
var supportedVersions []runtime.Object
|
||||
switch s {
|
||||
case Deployments:
|
||||
supportedVersions = []runtime.Object{
|
||||
&appsv1.Deployment{},
|
||||
&appsv1beta1.Deployment{},
|
||||
&appsv1beta2.Deployment{},
|
||||
}
|
||||
case StatefulSets:
|
||||
supportedVersions = []runtime.Object{
|
||||
&appsv1.StatefulSet{},
|
||||
&appsv1beta1.StatefulSet{},
|
||||
&appsv1beta2.StatefulSet{},
|
||||
}
|
||||
case DaemonSets:
|
||||
supportedVersions = []runtime.Object{
|
||||
&appsv1.DaemonSet{},
|
||||
&appsv1beta2.DaemonSet{},
|
||||
}
|
||||
case Jobs:
|
||||
supportedVersions = []runtime.Object{
|
||||
&batchv1.Job{},
|
||||
}
|
||||
case CronJobs:
|
||||
supportedVersions = []runtime.Object{
|
||||
&batchv1beta1.CronJob{},
|
||||
&batchv2alpha1.CronJob{},
|
||||
}
|
||||
case ReplicationControllers:
|
||||
supportedVersions = []runtime.Object{
|
||||
&corev1.ReplicationController{},
|
||||
}
|
||||
case NakedPods:
|
||||
supportedVersions = []runtime.Object{
|
||||
&corev1.Pod{},
|
||||
}
|
||||
}
|
||||
return supportedVersions
|
||||
}
|
||||
|
||||
// GetSupportedControllerFromString fuzzy matches a string with a SupportedController Enum
|
||||
func GetSupportedControllerFromString(str string) SupportedController {
|
||||
lowerStr := strings.ToLower(str)
|
||||
controller, keyFound := stringLookupForSupportedControllers[lowerStr]
|
||||
if !keyFound {
|
||||
controller = Unsupported
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
// CheckIfKindIsConfiguredForValidation takes a kind (in string format) and checks if Polaris is configured to scan this type of controller
|
||||
func (c Configuration) CheckIfKindIsConfiguredForValidation(kind string) bool {
|
||||
controller := GetSupportedControllerFromString(kind)
|
||||
if controller != Unsupported {
|
||||
for _, controllerToScan := range c.ControllersToScan {
|
||||
if controller == controllerToScan {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// Copyright 2019 FairwindsOps Inc
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type checkMarshal struct {
|
||||
Controllers []SupportedController `json:"controllers"`
|
||||
}
|
||||
|
||||
func TestUnmarshalSupportedControllers(t *testing.T) {
|
||||
for idx, controllerString := range ControllerStrings {
|
||||
// Check taking all strings and convert them into enums
|
||||
object := checkMarshal{}
|
||||
jsonBytes := []byte(fmt.Sprintf(`{"controllers":["%v"]}`, controllerString))
|
||||
err := json.Unmarshal(jsonBytes, &object)
|
||||
if idx == 0 {
|
||||
if err == nil {
|
||||
// Assure the first element always should NOT unmarshal
|
||||
t.Errorf("Expected the first element (%s) to fail json unmarshal. First element in this array should always be 'Unsupported'", controllerString)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("Could not unmarshal json (%s) to a Supported controller; Received (%v)", jsonBytes, err)
|
||||
}
|
||||
}
|
||||
|
||||
badJSON := []byte(`{"controllers":[{"not":"valid_structure"}]}`)
|
||||
err := json.Unmarshal(badJSON, &checkMarshal{})
|
||||
if err == nil {
|
||||
t.Error("expected invalid schema json to fail unmarshal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalSupportedControllers(t *testing.T) {
|
||||
for idx, controllerString := range ControllerStrings {
|
||||
controllerType := GetSupportedControllerFromString(controllerString)
|
||||
if idx == 0 {
|
||||
assert.Equal(t, SupportedController(0), controllerType)
|
||||
} else {
|
||||
assert.NotEqual(t, SupportedController(0), controllerType)
|
||||
}
|
||||
|
||||
object := checkMarshal{
|
||||
Controllers: []SupportedController{controllerType},
|
||||
}
|
||||
_, err := json.Marshal(object)
|
||||
if idx == 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Expected (%s) to throw an error. Reserving the first element in the enum to be an invalid config", controllerString)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("Could not write json output for element (%s); Received Error: (%s)", controllerString, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckIfControllerKindIsConfiguredForValidation(t *testing.T) {
|
||||
config := Configuration{}
|
||||
for _, controllerString := range ControllerStrings[1:] {
|
||||
controllerEnum := GetSupportedControllerFromString(controllerString)
|
||||
assert.NotEqual(t, SupportedController(0), controllerEnum)
|
||||
config.ControllersToScan = append(config.ControllersToScan, controllerEnum)
|
||||
}
|
||||
|
||||
validControllerKinds := []string{
|
||||
"deployment",
|
||||
"statefulset",
|
||||
}
|
||||
|
||||
invalidControllerKinds := []string{
|
||||
"nonExistent",
|
||||
}
|
||||
|
||||
for _, kind := range validControllerKinds {
|
||||
if ok := config.CheckIfKindIsConfiguredForValidation(kind); !ok {
|
||||
t.Errorf("Kind (%s) expected to be valid for configuration.", kind)
|
||||
}
|
||||
}
|
||||
|
||||
for _, kind := range invalidControllerKinds {
|
||||
if ok := config.CheckIfKindIsConfiguredForValidation(kind); ok {
|
||||
t.Errorf("Kind (%s) should not be a valid controller to check", kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSupportedControllerFromString(t *testing.T) {
|
||||
fixture := map[string]SupportedController{
|
||||
"": Unsupported,
|
||||
"asdfasdf": Unsupported,
|
||||
"\000": Unsupported,
|
||||
"deployMENTS": Deployments,
|
||||
"JOB": Jobs,
|
||||
}
|
||||
|
||||
for inputString, expectedType := range fixture {
|
||||
resolvedType := GetSupportedControllerFromString(inputString)
|
||||
assert.Equal(t, expectedType, resolvedType, fmt.Sprintf("Expected (%s) to return (%s) controller type.", inputString, expectedType))
|
||||
}
|
||||
}
|
||||
+28
-18
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -31,13 +30,15 @@ type ResourceProvider struct {
|
||||
SourceType string
|
||||
Nodes []corev1.Node
|
||||
Namespaces []corev1.Namespace
|
||||
Controllers []controllers.GenericController
|
||||
Controllers []GenericWorkload
|
||||
}
|
||||
|
||||
type k8sResource struct {
|
||||
Kind string `yaml:"kind"`
|
||||
}
|
||||
|
||||
var podSpecFields = []string{"jobTemplate", "spec", "template"}
|
||||
|
||||
// CreateResourceProvider returns a new ResourceProvider object to interact with k8s resources
|
||||
func CreateResourceProvider(directory string) (*ResourceProvider, error) {
|
||||
if directory != "" {
|
||||
@@ -54,7 +55,7 @@ func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error)
|
||||
SourceName: directory,
|
||||
Nodes: []corev1.Node{},
|
||||
Namespaces: []corev1.Namespace{},
|
||||
Controllers: []controllers.GenericController{},
|
||||
Controllers: []GenericWorkload{},
|
||||
}
|
||||
|
||||
addYaml := func(contents string) error {
|
||||
@@ -155,37 +156,46 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
|
||||
}
|
||||
|
||||
// LoadControllers loads a list of controllers from the kubeResources Pods
|
||||
func LoadControllers(pods []corev1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) []controllers.GenericController {
|
||||
interfaces := []controllers.GenericController{}
|
||||
func LoadControllers(pods []corev1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) []GenericWorkload {
|
||||
interfaces := []GenericWorkload{}
|
||||
deduped := map[string]corev1.Pod{}
|
||||
for _, pod := range pods {
|
||||
interfaces = append(interfaces, controllers.NewGenericPodController(pod, dynamicClientPointer, restMapperPointer))
|
||||
owners := pod.ObjectMeta.OwnerReferences
|
||||
if len(owners) == 0 {
|
||||
deduped[pod.ObjectMeta.Namespace+"/Pod/"+pod.ObjectMeta.Name] = pod
|
||||
continue
|
||||
}
|
||||
deduped[pod.ObjectMeta.Namespace+"/"+owners[0].Kind+"/"+owners[0].Name] = pod
|
||||
}
|
||||
for _, pod := range deduped {
|
||||
interfaces = append(interfaces, NewGenericWorkload(pod, dynamicClientPointer, restMapperPointer))
|
||||
}
|
||||
return deduplicateControllers(interfaces)
|
||||
}
|
||||
|
||||
// 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(inputControllers []controllers.GenericController) []controllers.GenericController {
|
||||
controllerMap := make(map[string]controllers.GenericController)
|
||||
func deduplicateControllers(inputControllers []GenericWorkload) []GenericWorkload {
|
||||
controllerMap := make(map[string]GenericWorkload)
|
||||
for _, controller := range inputControllers {
|
||||
key := controller.GetNamespace() + "/" + controller.GetKind() + "/" + controller.Name
|
||||
key := controller.ObjectMeta.GetNamespace() + "/" + controller.Kind + "/" + controller.ObjectMeta.GetName()
|
||||
oldController, ok := controllerMap[key]
|
||||
if !ok || controller.CreatedTime.After(oldController.CreatedTime) {
|
||||
if !ok || controller.ObjectMeta.GetCreationTimestamp().Time.After(oldController.ObjectMeta.GetCreationTimestamp().Time) {
|
||||
controllerMap[key] = controller
|
||||
}
|
||||
}
|
||||
results := make([]controllers.GenericController, 0)
|
||||
results := make([]GenericWorkload, 0)
|
||||
for _, controller := range controllerMap {
|
||||
results = append(results, controller)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getPodSpec(yaml map[string]interface{}) interface{} {
|
||||
allowedChildren := []string{"jobTemplate", "spec", "template"}
|
||||
for _, child := range allowedChildren {
|
||||
// GetPodSpec looks inside arbitrary YAML for a PodSpec
|
||||
func GetPodSpec(yaml map[string]interface{}) interface{} {
|
||||
for _, child := range podSpecFields {
|
||||
if childYaml, ok := yaml[child]; ok {
|
||||
return getPodSpec(childYaml.(map[string]interface{}))
|
||||
return GetPodSpec(childYaml.(map[string]interface{}))
|
||||
}
|
||||
}
|
||||
return yaml
|
||||
@@ -209,7 +219,7 @@ func addResourceFromString(contents string, resources *ResourceProvider) error {
|
||||
} else if resource.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
err = decoder.Decode(&pod)
|
||||
resources.Controllers = append(resources.Controllers, controllers.NewGenericPodController(pod, nil, nil))
|
||||
resources.Controllers = append(resources.Controllers, NewGenericWorkload(pod, nil, nil))
|
||||
} else {
|
||||
yamlNode := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(contentBytes, &yamlNode)
|
||||
@@ -221,7 +231,7 @@ func addResourceFromString(contents string, resources *ResourceProvider) error {
|
||||
finalDoc["metadata"] = yamlNode["metadata"]
|
||||
finalDoc["apiVersion"] = "v1"
|
||||
finalDoc["kind"] = "Pod"
|
||||
finalDoc["spec"] = getPodSpec(yamlNode)
|
||||
finalDoc["spec"] = GetPodSpec(yamlNode)
|
||||
marshaledYaml, err := yaml.Marshal(finalDoc)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not marshal yaml: %v", err)
|
||||
@@ -230,7 +240,7 @@ func addResourceFromString(contents string, resources *ResourceProvider) error {
|
||||
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(marshaledYaml), 1000)
|
||||
pod := corev1.Pod{}
|
||||
err = decoder.Decode(&pod)
|
||||
newController := controllers.NewGenericPodController(pod, nil, nil)
|
||||
newController := NewGenericWorkload(pod, nil, nil)
|
||||
newController.Kind = resource.Kind
|
||||
resources.Controllers = append(resources.Controllers, newController)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestGetResourcesFromPath(t *testing.T) {
|
||||
assert.Equal(t, 8, len(resources.Controllers), "Should have eight controllers")
|
||||
namespaceCount := map[string]int{}
|
||||
for _, controller := range resources.Controllers {
|
||||
namespaceCount[controller.GetNamespace()]++
|
||||
namespaceCount[controller.ObjectMeta.GetNamespace()]++
|
||||
}
|
||||
assert.Equal(t, 7, namespaceCount[""], "Should have seven controller in default namespace")
|
||||
assert.Equal(t, 1, namespaceCount["two"], "Should have one controller in namespace 'two'")
|
||||
@@ -72,5 +72,5 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
|
||||
assert.Equal(t, 1, len(resources.Controllers), "Should have 1 controller")
|
||||
|
||||
assert.Equal(t, "", resources.Controllers[0].ObjectMeta.Name)
|
||||
assert.Equal(t, "", resources.Controllers[0].ObjectMeta.GetName())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
kubeAPICoreV1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// GenericWorkload is a base implementation with some free methods for inherited structs
|
||||
type GenericWorkload struct {
|
||||
Kind string
|
||||
PodSpec kubeAPICoreV1.PodSpec
|
||||
ObjectMeta kubeAPIMetaV1.Object
|
||||
}
|
||||
|
||||
// NewGenericWorkload builds a new workload for a given Pod
|
||||
func NewGenericWorkload(originalResource kubeAPICoreV1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) GenericWorkload {
|
||||
workload := GenericWorkload{}
|
||||
workload.PodSpec = originalResource.Spec
|
||||
workload.ObjectMeta = originalResource.ObjectMeta.GetObjectMeta()
|
||||
workload.Kind = "Pod"
|
||||
fmt.Println("get workload", workload.ObjectMeta.GetNamespace(), workload.ObjectMeta.GetName())
|
||||
|
||||
if dynamicClientPointer == nil || restMapperPointer == nil {
|
||||
return workload
|
||||
}
|
||||
// If an owner exists then set the name to the workload.
|
||||
// This allows us to handle CRDs creating Workloads or DeploymentConfigs in OpenShift.
|
||||
owners := workload.ObjectMeta.GetOwnerReferences()
|
||||
for len(owners) > 0 {
|
||||
if len(owners) > 1 {
|
||||
logrus.Warn("More than 1 owner found")
|
||||
}
|
||||
firstOwner := owners[0]
|
||||
if firstOwner.Kind == "Node" {
|
||||
break
|
||||
}
|
||||
workload.Kind = firstOwner.Kind
|
||||
|
||||
dynamicClient := *dynamicClientPointer
|
||||
restMapper := *restMapperPointer
|
||||
fqKind := schema.FromAPIVersionAndKind(firstOwner.APIVersion, firstOwner.Kind)
|
||||
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving mapping %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
|
||||
return workload
|
||||
}
|
||||
parent, err := dynamicClient.Resource(mapping.Resource).Namespace(workload.ObjectMeta.GetNamespace()).Get(firstOwner.Name, kubeAPIMetaV1.GetOptions{})
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving parent object %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
|
||||
return workload
|
||||
}
|
||||
objMeta, err := meta.Accessor(parent)
|
||||
workload.ObjectMeta = objMeta
|
||||
owners = parent.GetOwnerReferences()
|
||||
}
|
||||
|
||||
return workload
|
||||
}
|
||||
@@ -16,13 +16,13 @@ package validator
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// ValidateContainer validates a single container from a given controller
|
||||
func ValidateContainer(conf *config.Configuration, controller controllers.GenericController, container *corev1.Container, isInit bool) (ContainerResult, error) {
|
||||
func ValidateContainer(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ContainerResult, error) {
|
||||
results, err := applyContainerSchemaChecks(conf, controller, container, isInit)
|
||||
if err != nil {
|
||||
return ContainerResult{}, err
|
||||
@@ -37,9 +37,9 @@ func ValidateContainer(conf *config.Configuration, controller controllers.Generi
|
||||
}
|
||||
|
||||
// ValidateAllContainers validates both init and regular containers
|
||||
func ValidateAllContainers(conf *config.Configuration, controller controllers.GenericController) ([]ContainerResult, error) {
|
||||
func ValidateAllContainers(conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) {
|
||||
results := []ContainerResult{}
|
||||
pod := controller.GetPodSpec()
|
||||
pod := controller.PodSpec
|
||||
for _, container := range pod.InitContainers {
|
||||
result, err := ValidateContainer(conf, controller, &container, true)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,10 +19,9 @@ import (
|
||||
"testing"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -51,26 +50,24 @@ exemptions:
|
||||
- foo
|
||||
`
|
||||
|
||||
func getEmptyController(name string) controllers.GenericController {
|
||||
return controllers.NewDeploymentController(appsv1.Deployment{
|
||||
func getEmptyWorkload(name string) kube.GenericWorkload {
|
||||
workload := kube.NewGenericWorkload(corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{},
|
||||
},
|
||||
})
|
||||
}, nil, nil)
|
||||
return workload
|
||||
}
|
||||
|
||||
func testValidate(t *testing.T, container *corev1.Container, resourceConf *string, controllerName string, expectedErrors []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
|
||||
testValidateWithController(t, container, resourceConf, getEmptyController(controllerName), expectedErrors, expectedWarnings, expectedSuccesses)
|
||||
testValidateWithWorkload(t, container, resourceConf, getEmptyWorkload(controllerName), expectedErrors, expectedWarnings, expectedSuccesses)
|
||||
}
|
||||
|
||||
func testValidateWithController(t *testing.T, container *corev1.Container, resourceConf *string, controller controllers.GenericController, expectedErrors []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
|
||||
func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourceConf *string, workload kube.GenericWorkload, expectedErrors []ResultMessage, expectedWarnings []ResultMessage, expectedSuccesses []ResultMessage) {
|
||||
parsedConf, err := conf.Parse([]byte(*resourceConf))
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
|
||||
results, err := applyContainerSchemaChecks(&parsedConf, controller, container, false)
|
||||
results, err := applyContainerSchemaChecks(&parsedConf, workload, container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -91,7 +88,7 @@ func TestValidateResourcesEmptyConfig(t *testing.T) {
|
||||
Name: "Empty",
|
||||
}
|
||||
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{}, getEmptyController(""), container, false)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{}, getEmptyWorkload(""), container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -187,7 +184,7 @@ func TestValidateHealthChecks(t *testing.T) {
|
||||
|
||||
for idx, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyController("")
|
||||
controller := getEmptyWorkload("")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, controller, tt.container, tt.isInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -301,7 +298,7 @@ func TestValidateImage(t *testing.T) {
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyController("")
|
||||
controller := getEmptyWorkload("")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -418,7 +415,7 @@ func TestValidateNetworking(t *testing.T) {
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := getEmptyController("")
|
||||
controller := getEmptyWorkload("")
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, controller, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -442,7 +439,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
standardConf := map[string]conf.Severity{
|
||||
"runAsRootAllowed": conf.SeverityWarning,
|
||||
"runAsPrivileged": conf.SeverityError,
|
||||
"notReadOnlyRootFileSystem": conf.SeverityWarning,
|
||||
"notReadOnlyRootFilesystem": conf.SeverityWarning,
|
||||
"privilegeEscalationAllowed": conf.SeverityError,
|
||||
"dangerousCapabilities": conf.SeverityError,
|
||||
"insecureCapabilities": conf.SeverityWarning,
|
||||
@@ -450,7 +447,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
strongConf := map[string]conf.Severity{
|
||||
"runAsRootAllowed": conf.SeverityError,
|
||||
"runAsPrivileged": conf.SeverityError,
|
||||
"notReadOnlyRootFileSystem": conf.SeverityError,
|
||||
"notReadOnlyRootFilesystem": conf.SeverityError,
|
||||
"privilegeEscalationAllowed": conf.SeverityError,
|
||||
"dangerousCapabilities": conf.SeverityError,
|
||||
"insecureCapabilities": conf.SeverityError,
|
||||
@@ -543,7 +540,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "warning",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem should be read only",
|
||||
Success: false,
|
||||
Severity: "warning",
|
||||
@@ -610,7 +607,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "warning",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem should be read only",
|
||||
Success: false,
|
||||
Severity: "warning",
|
||||
@@ -653,7 +650,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "warning",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem should be read only",
|
||||
Success: false,
|
||||
Severity: "warning",
|
||||
@@ -696,7 +693,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "warning",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem should be read only",
|
||||
Success: false,
|
||||
Severity: "warning",
|
||||
@@ -715,7 +712,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "warning",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem is read only",
|
||||
Success: true,
|
||||
Severity: "warning",
|
||||
@@ -770,7 +767,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "error",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem is read only",
|
||||
Success: true,
|
||||
Severity: "error",
|
||||
@@ -801,7 +798,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "error",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem is read only",
|
||||
Success: true,
|
||||
Severity: "error",
|
||||
@@ -844,7 +841,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "error",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem is read only",
|
||||
Success: true,
|
||||
Severity: "error",
|
||||
@@ -887,7 +884,7 @@ func TestValidateSecurity(t *testing.T) {
|
||||
Severity: "error",
|
||||
Category: "Security",
|
||||
}, {
|
||||
ID: "notReadOnlyRootFileSystem",
|
||||
ID: "notReadOnlyRootFilesystem",
|
||||
Message: "Filesystem is read only",
|
||||
Success: true,
|
||||
Severity: "error",
|
||||
@@ -922,17 +919,8 @@ func TestValidateSecurity(t *testing.T) {
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := controllers.NewDeploymentController(appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: *tt.pod,
|
||||
},
|
||||
},
|
||||
})
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, controller, tt.container, false)
|
||||
workload := kube.NewGenericWorkload(corev1.Pod{Spec: *tt.pod}, nil, nil)
|
||||
results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1075,17 +1063,8 @@ func TestValidateRunAsRoot(t *testing.T) {
|
||||
}
|
||||
for idx, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := controllers.NewDeploymentController(appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: *tt.pod,
|
||||
},
|
||||
},
|
||||
})
|
||||
results, err := applyContainerSchemaChecks(&config, controller, tt.container, false)
|
||||
workload := kube.NewGenericWorkload(corev1.Pod{Spec: *tt.pod}, nil, nil)
|
||||
results, err := applyContainerSchemaChecks(&config, workload, tt.container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1185,7 +1164,7 @@ func TestValidateResourcesEmptyContainerCPURequestsExempt(t *testing.T) {
|
||||
|
||||
expectedSuccesses := []ResultMessage{}
|
||||
|
||||
controller := controllers.NewDeploymentController(appsv1.Deployment{
|
||||
workload := kube.NewGenericWorkload(corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Annotations: map[string]string{
|
||||
@@ -1193,9 +1172,6 @@ func TestValidateResourcesEmptyContainerCPURequestsExempt(t *testing.T) {
|
||||
"polaris.fairwinds.com/memoryRequestsMissing-exempt": "truthy", // Don't actually exempt this controller from memoryRequestsMissing
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{},
|
||||
},
|
||||
})
|
||||
testValidateWithController(t, &container, &resourceConfMinimal, controller, expectedErrors, expectedWarnings, expectedSuccesses)
|
||||
}, nil, nil)
|
||||
testValidateWithWorkload(t, &container, &resourceConfMinimal, workload, expectedErrors, expectedWarnings, expectedSuccesses)
|
||||
}
|
||||
|
||||
@@ -21,21 +21,20 @@ import (
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
controller "github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
)
|
||||
|
||||
const exemptionAnnotationKey = "polaris.fairwinds.com/exempt"
|
||||
|
||||
// ValidateController validates a single controller, returns a ControllerResult.
|
||||
func ValidateController(conf *conf.Configuration, controller controller.GenericController) (ControllerResult, error) {
|
||||
func ValidateController(conf *conf.Configuration, controller kube.GenericWorkload) (ControllerResult, error) {
|
||||
podResult, err := ValidatePod(conf, controller)
|
||||
if err != nil {
|
||||
return ControllerResult{}, err
|
||||
}
|
||||
result := ControllerResult{
|
||||
Kind: controller.GetKind(),
|
||||
Name: controller.GetName(),
|
||||
Namespace: controller.GetObjectMeta().Namespace,
|
||||
Kind: controller.Kind,
|
||||
Name: controller.ObjectMeta.GetName(),
|
||||
Namespace: controller.ObjectMeta.GetNamespace(),
|
||||
Results: ResultSet{},
|
||||
PodResult: podResult,
|
||||
}
|
||||
@@ -64,8 +63,8 @@ func ValidateControllers(config *conf.Configuration, kubeResources *kube.Resourc
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func hasExemptionAnnotation(ctrl controller.GenericController) bool {
|
||||
annot := ctrl.GetObjectMeta().Annotations
|
||||
func hasExemptionAnnotation(ctrl kube.GenericWorkload) bool {
|
||||
annot := ctrl.ObjectMeta.GetAnnotations()
|
||||
val := annot[exemptionAnnotationKey]
|
||||
return strings.ToLower(val) == "true"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
controller "github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/test"
|
||||
)
|
||||
|
||||
@@ -33,7 +32,8 @@ func TestValidateController(t *testing.T) {
|
||||
"hostPIDSet": conf.SeverityError,
|
||||
},
|
||||
}
|
||||
deployment := controller.NewDeploymentController(test.MockDeploy())
|
||||
deployment := kube.NewGenericWorkload(test.MockPod(), nil, nil)
|
||||
deployment.Kind = "Deployment"
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(2),
|
||||
Warnings: uint(0),
|
||||
@@ -62,18 +62,11 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
"readinessProbeMissing": conf.SeverityError,
|
||||
"livenessProbeMissing": conf.SeverityWarning,
|
||||
},
|
||||
ControllersToScan: []conf.SupportedController{
|
||||
conf.Deployments,
|
||||
conf.StatefulSets,
|
||||
conf.DaemonSets,
|
||||
conf.Jobs,
|
||||
conf.CronJobs,
|
||||
conf.ReplicationControllers,
|
||||
},
|
||||
}
|
||||
deploymentBase := test.MockDeploy()
|
||||
deploymentBase.Spec.Template.Spec.InitContainers = []corev1.Container{test.MockContainer("test")}
|
||||
deployment := controller.NewDeploymentController(deploymentBase)
|
||||
pod := test.MockPod()
|
||||
pod.Spec.InitContainers = []corev1.Container{test.MockContainer("test")}
|
||||
deployment := kube.NewGenericWorkload(pod, nil, nil)
|
||||
deployment.Kind = "Deployment"
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(0),
|
||||
Warnings: uint(1),
|
||||
@@ -93,7 +86,8 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
assert.EqualValues(t, ResultSet{}, actualResult.PodResult.ContainerResults[0].Results)
|
||||
assert.EqualValues(t, expectedResults, actualResult.PodResult.ContainerResults[1].Results)
|
||||
|
||||
job := controller.NewJobController(test.MockJob())
|
||||
job := kube.NewGenericWorkload(test.MockPod(), nil, nil)
|
||||
job.Kind = "Job"
|
||||
expectedSum = CountSummary{
|
||||
Successes: uint(0),
|
||||
Warnings: uint(0),
|
||||
@@ -109,7 +103,8 @@ func TestSkipHealthChecks(t *testing.T) {
|
||||
assert.EqualValues(t, expectedSum, actualResult.GetSummary())
|
||||
assert.EqualValues(t, expectedResults, actualResult.PodResult.ContainerResults[0].Results)
|
||||
|
||||
cronjob := controller.NewCronJobController(test.MockCronJob())
|
||||
cronjob := kube.NewGenericWorkload(test.MockPod(), nil, nil)
|
||||
cronjob.Kind = "CronJob"
|
||||
expectedSum = CountSummary{
|
||||
Successes: uint(0),
|
||||
Warnings: uint(0),
|
||||
@@ -132,14 +127,12 @@ func TestControllerExemptions(t *testing.T) {
|
||||
"readinessProbeMissing": conf.SeverityError,
|
||||
"livenessProbeMissing": conf.SeverityWarning,
|
||||
},
|
||||
ControllersToScan: []conf.SupportedController{
|
||||
conf.Deployments,
|
||||
},
|
||||
}
|
||||
newController := test.MockGenericController()
|
||||
newController.Kind = "Deployment"
|
||||
pod := test.MockPod()
|
||||
workload := kube.NewGenericWorkload(pod, nil, nil)
|
||||
workload.Kind = "Deployment"
|
||||
resources := &kube.ResourceProvider{
|
||||
Controllers: []controller.GenericController{newController},
|
||||
Controllers: []kube.GenericWorkload{workload},
|
||||
}
|
||||
|
||||
expectedSum := CountSummary{
|
||||
@@ -155,9 +148,9 @@ func TestControllerExemptions(t *testing.T) {
|
||||
assert.Equal(t, "Deployment", actualResults[0].Kind)
|
||||
assert.EqualValues(t, expectedSum, actualResults[0].GetSummary())
|
||||
|
||||
resources.Controllers[0].ObjectMeta.Annotations = map[string]string{
|
||||
resources.Controllers[0].ObjectMeta.SetAnnotations(map[string]string{
|
||||
exemptionAnnotationKey: "true",
|
||||
}
|
||||
})
|
||||
actualResults, err = ValidateControllers(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
kubeAPIBatchV1beta1 "k8s.io/api/batch/v1beta1"
|
||||
)
|
||||
|
||||
// NewCronJobController builds a new controller interface for Deployments
|
||||
func NewCronJobController(originalResource kubeAPIBatchV1beta1.CronJob) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.JobTemplate.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.CronJobs.String()
|
||||
if controller.Name == "" {
|
||||
logrus.Warn("Name is missing from controller", originalResource.Namespace)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
kubeAPIAppsV1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
// NewDaemonSetController builds a new controller interface for Deployments
|
||||
func NewDaemonSetController(originalResource kubeAPIAppsV1.DaemonSet) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.DaemonSets.String()
|
||||
return controller
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
kubeAPIAppsV1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
// NewDeploymentController builds a new controller interface for Deployments
|
||||
func NewDeploymentController(originalResource kubeAPIAppsV1.Deployment) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.Deployments.String()
|
||||
return controller
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
kubeAPICoreV1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
kubeAPIMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// GenericController is a base implementation with some free methods for inherited structs
|
||||
type GenericController struct {
|
||||
Name string
|
||||
Namespace string
|
||||
PodSpec kubeAPICoreV1.PodSpec
|
||||
ObjectMeta kubeAPIMetaV1.ObjectMeta
|
||||
Kind string
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
// GetPodSpec returns the original kubernetes template pod spec
|
||||
func (g GenericController) GetPodSpec() *kubeAPICoreV1.PodSpec {
|
||||
return &g.PodSpec
|
||||
}
|
||||
|
||||
// GetObjectMeta returns the metadata
|
||||
func (g GenericController) GetObjectMeta() kubeAPIMetaV1.ObjectMeta {
|
||||
return g.ObjectMeta
|
||||
}
|
||||
|
||||
// GetKind returns the supportedcontroller enum type
|
||||
func (g GenericController) GetKind() string {
|
||||
return g.Kind
|
||||
}
|
||||
|
||||
// GetName is inherited by all controllers using generic controller to get the name of the controller
|
||||
func (g GenericController) GetName() string {
|
||||
return g.Name
|
||||
}
|
||||
|
||||
// GetNamespace is inherited by all controllers using generic controller to get the namespace of the controller
|
||||
func (g GenericController) GetNamespace() string {
|
||||
return g.Namespace
|
||||
}
|
||||
|
||||
// NewGenericPodController builds a new controller interface for anytype of Pod
|
||||
func NewGenericPodController(originalResource kubeAPICoreV1.Pod, dynamicClientPointer *dynamic.Interface, restMapperPointer *meta.RESTMapper) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = "Pod"
|
||||
controller.CreatedTime = controller.GetObjectMeta().CreationTimestamp.Time
|
||||
|
||||
owners := controller.GetObjectMeta().OwnerReferences
|
||||
if dynamicClientPointer == nil || restMapperPointer == nil {
|
||||
return controller
|
||||
}
|
||||
// If an owner exists then set the name to the controller.
|
||||
// This allows us to handle CRDs creating Controllers or DeploymentConfigs in OpenShift.
|
||||
for len(owners) > 0 {
|
||||
if len(owners) > 1 {
|
||||
logrus.Warn("More than 1 owner found")
|
||||
}
|
||||
firstOwner := owners[0]
|
||||
controller.Kind = firstOwner.Kind
|
||||
controller.Name = firstOwner.Name
|
||||
|
||||
dynamicClient := *dynamicClientPointer
|
||||
restMapper := *restMapperPointer
|
||||
fqKind := schema.FromAPIVersionAndKind(firstOwner.APIVersion, firstOwner.Kind)
|
||||
mapping, err := restMapper.RESTMapping(fqKind.GroupKind(), fqKind.Version)
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving mapping %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
|
||||
return controller
|
||||
}
|
||||
getParents, err := dynamicClient.Resource(mapping.Resource).Namespace(controller.GetObjectMeta().Namespace).Get(firstOwner.Name, kubeAPIMetaV1.GetOptions{})
|
||||
if err != nil {
|
||||
logrus.Warnf("Error retrieving parent object %s of API %s and Kind %s because of error: %v ", firstOwner.Name, firstOwner.APIVersion, firstOwner.Kind, err)
|
||||
return controller
|
||||
}
|
||||
owners = getParents.GetOwnerReferences()
|
||||
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
kubeAPIBatchV1 "k8s.io/api/batch/v1"
|
||||
)
|
||||
|
||||
// NewJobController builds a new controller interface for Deployments
|
||||
func NewJobController(originalResource kubeAPIBatchV1.Job) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.Jobs.String()
|
||||
return controller
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
kubeAPICoreV1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// NewNakedPodController builds a new controller interface for NakedPods
|
||||
func NewNakedPodController(originalResource kubeAPICoreV1.Pod) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.NakedPods.String()
|
||||
|
||||
return controller
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
kubeAPICoreV1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// NewReplicationControllerController builds a new controller interface for Deployments
|
||||
func NewReplicationControllerController(originalResource kubeAPICoreV1.ReplicationController) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.ReplicationControllers.String()
|
||||
return controller
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
kubeAPIAppsV1 "k8s.io/api/apps/v1"
|
||||
)
|
||||
|
||||
// NewStatefulSetController builds a statefulset controller
|
||||
func NewStatefulSetController(originalResource kubeAPIAppsV1.StatefulSet) GenericController {
|
||||
controller := GenericController{}
|
||||
controller.Name = originalResource.Name
|
||||
controller.Namespace = originalResource.Namespace
|
||||
controller.PodSpec = originalResource.Spec.Template.Spec
|
||||
controller.ObjectMeta = originalResource.ObjectMeta
|
||||
controller.Kind = config.StatefulSets.String()
|
||||
if controller.Name == "" {
|
||||
logrus.Warn("Name is missing from controller", originalResource.Namespace)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
@@ -23,14 +23,6 @@ func TestGetTemplateData(t *testing.T) {
|
||||
"readinessProbeMissing": conf.SeverityError,
|
||||
"livenessProbeMissing": conf.SeverityWarning,
|
||||
},
|
||||
ControllersToScan: []conf.SupportedController{
|
||||
conf.Deployments,
|
||||
conf.StatefulSets,
|
||||
conf.DaemonSets,
|
||||
conf.Jobs,
|
||||
conf.CronJobs,
|
||||
conf.ReplicationControllers,
|
||||
},
|
||||
}
|
||||
|
||||
sum := CountSummary{
|
||||
|
||||
@@ -16,11 +16,11 @@ package validator
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
)
|
||||
|
||||
// ValidatePod validates that each pod conforms to the Polaris config, returns a ResourceResult.
|
||||
func ValidatePod(conf *config.Configuration, controller controllers.GenericController) (PodResult, error) {
|
||||
func ValidatePod(conf *config.Configuration, controller kube.GenericWorkload) (PodResult, error) {
|
||||
podResults, err := applyPodSchemaChecks(conf, controller)
|
||||
if err != nil {
|
||||
return PodResult{}, err
|
||||
|
||||
+14
-18
@@ -17,13 +17,12 @@ package validator
|
||||
import (
|
||||
"testing"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/fairwindsops/polaris/test"
|
||||
)
|
||||
|
||||
func TestValidatePod(t *testing.T) {
|
||||
@@ -39,7 +38,7 @@ func TestValidatePod(t *testing.T) {
|
||||
k8s, _ := test.SetupTestAPI()
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
p := test.MockPod()
|
||||
deployment := controllers.NewDeploymentController(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: p}})
|
||||
deployment := kube.NewGenericWorkload(p, nil, nil)
|
||||
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(4),
|
||||
@@ -77,7 +76,7 @@ func TestInvalidIPCPod(t *testing.T) {
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
p := test.MockPod()
|
||||
p.Spec.HostIPC = true
|
||||
deployment := controllers.NewDeploymentController(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: p}})
|
||||
workload := kube.NewGenericWorkload(p, nil, nil)
|
||||
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(3),
|
||||
@@ -90,7 +89,7 @@ func TestInvalidIPCPod(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "error", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := ValidatePod(&c, deployment)
|
||||
actualPodResult, err := ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -114,7 +113,7 @@ func TestInvalidNeworkPod(t *testing.T) {
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
p := test.MockPod()
|
||||
p.Spec.HostNetwork = true
|
||||
deployment := controllers.NewDeploymentController(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: p}})
|
||||
workload := kube.NewGenericWorkload(p, nil, nil)
|
||||
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(3),
|
||||
@@ -128,7 +127,7 @@ func TestInvalidNeworkPod(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "error", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := ValidatePod(&c, deployment)
|
||||
actualPodResult, err := ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -152,7 +151,7 @@ func TestInvalidPIDPod(t *testing.T) {
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
p := test.MockPod()
|
||||
p.Spec.HostPID = true
|
||||
deployment := controllers.NewDeploymentController(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: p}})
|
||||
workload := kube.NewGenericWorkload(p, nil, nil)
|
||||
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(3),
|
||||
@@ -166,7 +165,7 @@ func TestInvalidPIDPod(t *testing.T) {
|
||||
"hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Networking"},
|
||||
}
|
||||
|
||||
actualPodResult, err := ValidatePod(&c, deployment)
|
||||
actualPodResult, err := ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -196,13 +195,10 @@ func TestExemption(t *testing.T) {
|
||||
k8s = test.SetupAddControllers(k8s, "test")
|
||||
p := test.MockPod()
|
||||
p.Spec.HostIPC = true
|
||||
meta := metav1.ObjectMeta{
|
||||
p.ObjectMeta = metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
}
|
||||
deploySpec := appsv1.DeploymentSpec{
|
||||
Template: p,
|
||||
}
|
||||
deployment := controllers.NewDeploymentController(appsv1.Deployment{ObjectMeta: meta, Spec: deploySpec})
|
||||
workload := kube.NewGenericWorkload(p, nil, nil)
|
||||
|
||||
expectedSum := CountSummary{
|
||||
Successes: uint(3),
|
||||
@@ -214,7 +210,7 @@ func TestExemption(t *testing.T) {
|
||||
"hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "error", Category: "Security"},
|
||||
}
|
||||
|
||||
actualPodResult, err := ValidatePod(&c, deployment)
|
||||
actualPodResult, err := ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+11
-11
@@ -12,7 +12,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -37,7 +37,7 @@ var (
|
||||
"hostPortSet",
|
||||
"runAsRootAllowed",
|
||||
"runAsPrivileged",
|
||||
"notReadOnlyRootFileSystem",
|
||||
"notReadOnlyRootFilesystem",
|
||||
"privilegeEscalationAllowed",
|
||||
"dangerousCapabilities",
|
||||
"insecureCapabilities",
|
||||
@@ -74,7 +74,7 @@ func parseCheck(rawBytes []byte) (config.SchemaCheck, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func resolveCheck(conf *config.Configuration, checkID string, controller controllers.GenericController, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) {
|
||||
func resolveCheck(conf *config.Configuration, checkID string, controller kube.GenericWorkload, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) {
|
||||
check, ok := conf.CustomChecks[checkID]
|
||||
if !ok {
|
||||
check, ok = builtInChecks[checkID]
|
||||
@@ -82,10 +82,10 @@ func resolveCheck(conf *config.Configuration, checkID string, controller control
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Check %s not found", checkID)
|
||||
}
|
||||
if !conf.IsActionable(check.ID, controller.GetName()) {
|
||||
if !conf.IsActionable(check.ID, controller.ObjectMeta.GetName()) {
|
||||
return nil, nil
|
||||
}
|
||||
if !check.IsActionable(target, controller.GetKind(), isInitContainer) {
|
||||
if !check.IsActionable(target, controller.Kind, isInitContainer) {
|
||||
return nil, nil
|
||||
}
|
||||
return &check, nil
|
||||
@@ -110,10 +110,10 @@ func getExemptKey(checkID string) string {
|
||||
return fmt.Sprintf("polaris.fairwinds.com/%s-exempt", checkID)
|
||||
}
|
||||
|
||||
func applyPodSchemaChecks(conf *config.Configuration, controller controllers.GenericController) (ResultSet, error) {
|
||||
func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) {
|
||||
results := ResultSet{}
|
||||
checkIDs := getSortedKeys(conf.Checks)
|
||||
objectAnnotations := controller.GetObjectMeta().Annotations
|
||||
objectAnnotations := controller.ObjectMeta.GetAnnotations()
|
||||
for _, checkID := range checkIDs {
|
||||
exemptValue := objectAnnotations[getExemptKey(checkID)]
|
||||
if strings.ToLower(exemptValue) == "true" {
|
||||
@@ -126,7 +126,7 @@ func applyPodSchemaChecks(conf *config.Configuration, controller controllers.Gen
|
||||
} else if check == nil {
|
||||
continue
|
||||
}
|
||||
passes, err := check.CheckPod(controller.GetPodSpec())
|
||||
passes, err := check.CheckPod(&controller.PodSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,10 +135,10 @@ func applyPodSchemaChecks(conf *config.Configuration, controller controllers.Gen
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func applyContainerSchemaChecks(conf *config.Configuration, controller controllers.GenericController, container *corev1.Container, isInit bool) (ResultSet, error) {
|
||||
func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ResultSet, error) {
|
||||
results := ResultSet{}
|
||||
checkIDs := getSortedKeys(conf.Checks)
|
||||
objectAnnotations := controller.GetObjectMeta().Annotations
|
||||
objectAnnotations := controller.ObjectMeta.GetAnnotations()
|
||||
for _, checkID := range checkIDs {
|
||||
exemptValue := objectAnnotations[getExemptKey(checkID)]
|
||||
if strings.ToLower(exemptValue) == "true" {
|
||||
@@ -152,7 +152,7 @@ func applyContainerSchemaChecks(conf *config.Configuration, controller controlle
|
||||
}
|
||||
var passes bool
|
||||
if check.SchemaTarget == config.TargetPod {
|
||||
podCopy := *controller.GetPodSpec()
|
||||
podCopy := controller.PodSpec
|
||||
podCopy.InitContainers = []corev1.Container{}
|
||||
podCopy.Containers = []corev1.Container{*container}
|
||||
passes, err = check.CheckPod(&podCopy)
|
||||
|
||||
@@ -138,7 +138,7 @@ func TestValidateResourcesPartiallyValid(t *testing.T) {
|
||||
|
||||
func TestValidateResourcesInit(t *testing.T) {
|
||||
emptyContainer := &corev1.Container{}
|
||||
controller := getEmptyController("")
|
||||
controller := getEmptyWorkload("")
|
||||
|
||||
parsedConf, err := conf.Parse([]byte(resourceConfRanges))
|
||||
assert.NoError(t, err, "Expected no error when parsing config")
|
||||
@@ -259,4 +259,4 @@ func TestValidateCustomCheckExemptions(t *testing.T) {
|
||||
},
|
||||
}
|
||||
testValidate(t, &container, &customCheckExemptions, "notexempt", expectedErrors, expectedWarnings, expectedSuccesses)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,16 @@ package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/config"
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
validator "github.com/fairwindsops/polaris/pkg/validator"
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -64,7 +62,7 @@ func (v *Validator) InjectDecoder(d types.Decoder) error {
|
||||
var _ admission.Handler = &Validator{}
|
||||
|
||||
// NewWebhook creates a validating admission webhook for the apiType.
|
||||
func NewWebhook(name string, mgr manager.Manager, validator Validator, apiType runtime.Object) *admission.Webhook {
|
||||
func NewWebhook(name string, mgr manager.Manager, validator Validator, apiType runtime.Object) (*admission.Webhook, error) {
|
||||
name = fmt.Sprintf("%s.k8s.io", name)
|
||||
path := fmt.Sprintf("/validating-%s", name)
|
||||
|
||||
@@ -78,86 +76,56 @@ func NewWebhook(name string, mgr manager.Manager, validator Validator, apiType r
|
||||
Handlers(&validator).
|
||||
Build()
|
||||
if err != nil {
|
||||
logrus.Errorf("Error building webhook: %v", err)
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
logrus.Info(name + " webhook started")
|
||||
return webhook
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func (v *Validator) handleInternal(ctx context.Context, req types.Request) (*validator.PodResult, error) {
|
||||
pod := corev1.Pod{}
|
||||
if req.AdmissionRequest.Kind.Kind == "Pod" {
|
||||
err := v.decoder.Decode(req, &pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
decoded := map[string]interface{}{}
|
||||
err := json.Unmarshal(req.AdmissionRequest.Object.Raw, &decoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podMap := kube.GetPodSpec(decoded)
|
||||
encoded, err := json.Marshal(podMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(encoded, &pod.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
controller := kube.NewGenericWorkload(pod, nil, nil)
|
||||
controller.Kind = req.AdmissionRequest.Kind.Kind
|
||||
controllerResult, err := validator.ValidateController(&v.Config, controller)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &controllerResult.PodResult, nil
|
||||
}
|
||||
|
||||
// Handle for Validator to run validation checks.
|
||||
func (v *Validator) Handle(ctx context.Context, req types.Request) types.Response {
|
||||
var err error
|
||||
var podResult validator.PodResult
|
||||
|
||||
if req.AdmissionRequest.Kind.Kind == "Pod" {
|
||||
pod := corev1.Pod{}
|
||||
err = v.decoder.Decode(req, &pod) // err is handled below
|
||||
nakedPod := controllers.NewNakedPodController(pod)
|
||||
if err == nil {
|
||||
podResult, err = validator.ValidatePod(&v.Config, nakedPod)
|
||||
}
|
||||
} else {
|
||||
var controller controllers.GenericController
|
||||
if yes := v.Config.CheckIfKindIsConfiguredForValidation(req.AdmissionRequest.Kind.Kind); !yes {
|
||||
logrus.Warnf("Skipping, kind (%s) isn't something we are configured to scan", req.AdmissionRequest.Kind.Kind)
|
||||
return admission.ValidationResponse(true, fmt.Sprintf("Skipping: (%s) isn't something we're configured to scan.", req.AdmissionRequest.Kind.Kind))
|
||||
}
|
||||
|
||||
// We should never hit this case unless something is misconfiured in CheckIfKindIsConfiguredForValidation
|
||||
controllerType := config.GetSupportedControllerFromString(req.AdmissionRequest.Kind.Kind)
|
||||
if controllerType == config.Unsupported {
|
||||
msg := fmt.Errorf("Expected Kind (%s) to be a supported type", req.AdmissionRequest.Kind.Kind)
|
||||
logrus.Error(msg)
|
||||
return admission.ErrorResponse(http.StatusInternalServerError, msg)
|
||||
}
|
||||
|
||||
// For each type, perform the scan
|
||||
// TODO: This isn't really that elegant due to the decoder and NewXXXController setup :( could use love
|
||||
switch controllerType {
|
||||
case config.Deployments:
|
||||
deploy := appsv1.Deployment{}
|
||||
err = v.decoder.Decode(req, &deploy)
|
||||
controller = controllers.NewDeploymentController(deploy)
|
||||
case config.StatefulSets:
|
||||
statefulSet := appsv1.StatefulSet{}
|
||||
err = v.decoder.Decode(req, &statefulSet)
|
||||
controller = controllers.NewStatefulSetController(statefulSet)
|
||||
case config.DaemonSets:
|
||||
daemonSet := appsv1.DaemonSet{}
|
||||
err = v.decoder.Decode(req, &daemonSet)
|
||||
controller = controllers.NewDaemonSetController(daemonSet)
|
||||
case config.Jobs:
|
||||
job := batchv1.Job{}
|
||||
err = v.decoder.Decode(req, &job)
|
||||
controller = controllers.NewJobController(job)
|
||||
case config.CronJobs:
|
||||
cronJob := batchv1beta1.CronJob{}
|
||||
err = v.decoder.Decode(req, &cronJob)
|
||||
controller = controllers.NewCronJobController(cronJob)
|
||||
case config.ReplicationControllers:
|
||||
replicationController := corev1.ReplicationController{}
|
||||
err = v.decoder.Decode(req, &replicationController)
|
||||
controller = controllers.NewReplicationControllerController(replicationController)
|
||||
}
|
||||
if err == nil {
|
||||
var controllerResult validator.ControllerResult
|
||||
controllerResult, err = validator.ValidateController(&v.Config, controller)
|
||||
podResult = controllerResult.PodResult
|
||||
}
|
||||
}
|
||||
|
||||
podResult, err := v.handleInternal(ctx, req)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error validating request: %v", err)
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
allowed := true
|
||||
reason := ""
|
||||
numErrors := podResult.GetSummary().Errors
|
||||
if numErrors > 0 {
|
||||
allowed = false
|
||||
reason = getFailureReason(podResult)
|
||||
reason = getFailureReason(*podResult)
|
||||
}
|
||||
logrus.Infof("%d validation errors found when validating %s", numErrors, podResult.Name)
|
||||
return admission.ValidationResponse(allowed, reason)
|
||||
+16
-21
@@ -1,7 +1,6 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/fairwindsops/polaris/pkg/validator/controllers"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
@@ -24,9 +23,9 @@ func MockContainer(name string) corev1.Container {
|
||||
}
|
||||
|
||||
// MockPod creates a pod object.
|
||||
func MockPod() corev1.PodTemplateSpec {
|
||||
func MockPod() corev1.Pod {
|
||||
c1 := MockContainer("test")
|
||||
p := corev1.PodTemplateSpec{
|
||||
p := corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
c1,
|
||||
@@ -36,13 +35,6 @@ func MockPod() corev1.PodTemplateSpec {
|
||||
return p
|
||||
}
|
||||
|
||||
// MockGenericController creates a generic controller object for testing.
|
||||
func MockGenericController() controllers.GenericController {
|
||||
return controllers.GenericController{
|
||||
PodSpec: MockPod().Spec,
|
||||
}
|
||||
}
|
||||
|
||||
// MockNakedPod creates a pod object.
|
||||
func MockNakedPod() corev1.Pod {
|
||||
return corev1.Pod{
|
||||
@@ -55,7 +47,7 @@ func MockDeploy() appsv1.Deployment {
|
||||
p := MockPod()
|
||||
d := appsv1.Deployment{
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
return d
|
||||
@@ -66,7 +58,7 @@ func MockStatefulSet() appsv1.StatefulSet {
|
||||
p := MockPod()
|
||||
s := appsv1.StatefulSet{
|
||||
Spec: appsv1.StatefulSetSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
return s
|
||||
@@ -74,29 +66,32 @@ func MockStatefulSet() appsv1.StatefulSet {
|
||||
|
||||
// MockDaemonSet creates a DaemonSet object.
|
||||
func MockDaemonSet() appsv1.DaemonSet {
|
||||
p := MockPod()
|
||||
return appsv1.DaemonSet{
|
||||
Spec: appsv1.DaemonSetSpec{
|
||||
Template: MockPod(),
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MockJob creates a Job object.
|
||||
func MockJob() batchv1.Job {
|
||||
p := MockPod()
|
||||
return batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: MockPod(),
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MockCronJob creates a CronJob object.
|
||||
func MockCronJob() batchv1beta1.CronJob {
|
||||
p := MockPod()
|
||||
return batchv1beta1.CronJob{
|
||||
Spec: batchv1beta1.CronJobSpec{
|
||||
JobTemplate: batchv1beta1.JobTemplateSpec{
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: MockPod(),
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -108,7 +103,7 @@ func MockReplicationController() corev1.ReplicationController {
|
||||
p := MockPod()
|
||||
return corev1.ReplicationController{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Template: &p,
|
||||
Template: &corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -166,7 +161,7 @@ func SetupAddExtraControllerVersions(k kubernetes.Interface, namespace string) k
|
||||
|
||||
dv1b1 := appsv1beta1.Deployment{
|
||||
Spec: appsv1beta1.DeploymentSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
if _, err := k.AppsV1beta1().Deployments(namespace).Create(&dv1b1); err != nil {
|
||||
@@ -175,7 +170,7 @@ func SetupAddExtraControllerVersions(k kubernetes.Interface, namespace string) k
|
||||
|
||||
dv1b2 := appsv1beta2.Deployment{
|
||||
Spec: appsv1beta2.DeploymentSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
if _, err := k.AppsV1beta2().Deployments(namespace).Create(&dv1b2); err != nil {
|
||||
@@ -184,7 +179,7 @@ func SetupAddExtraControllerVersions(k kubernetes.Interface, namespace string) k
|
||||
|
||||
ssv1b1 := appsv1beta1.StatefulSet{
|
||||
Spec: appsv1beta1.StatefulSetSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
if _, err := k.AppsV1beta1().StatefulSets(namespace).Create(&ssv1b1); err != nil {
|
||||
@@ -193,7 +188,7 @@ func SetupAddExtraControllerVersions(k kubernetes.Interface, namespace string) k
|
||||
|
||||
ssv1b2 := appsv1beta2.StatefulSet{
|
||||
Spec: appsv1beta2.StatefulSetSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
if _, err := k.AppsV1beta2().StatefulSets(namespace).Create(&ssv1b2); err != nil {
|
||||
@@ -202,7 +197,7 @@ func SetupAddExtraControllerVersions(k kubernetes.Interface, namespace string) k
|
||||
|
||||
dsv1b2 := appsv1beta2.DaemonSet{
|
||||
Spec: appsv1beta2.DaemonSetSpec{
|
||||
Template: p,
|
||||
Template: corev1.PodTemplateSpec{Spec: p.Spec},
|
||||
},
|
||||
}
|
||||
if _, err := k.AppsV1beta2().DaemonSets(namespace).Create(&dsv1b2); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user