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:
Robert Brennan
2020-04-27 10:43:02 -04:00
committed by GitHub
co-authored by Robert Brennan
parent f559f3328b
commit 6792fba91f
36 changed files with 301 additions and 791 deletions
+7 -4
View File
@@ -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
View File
@@ -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 {
+3 -3
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -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.
+2 -9
View File
@@ -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
View File
@@ -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:
+5 -4
View File
@@ -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
+3
View File
@@ -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=
-1
View File
@@ -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"`
+1 -5
View File
@@ -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)
}
-165
View File
@@ -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
}
-118
View File
@@ -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
View File
@@ -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)
}
+2 -2
View File
@@ -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())
}
+64
View File
@@ -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
}
+4 -4
View File
@@ -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 {
+30 -54
View File
@@ -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)
}
+6 -7
View File
@@ -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"
}
+16 -23
View File
@@ -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)
-21
View File
@@ -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
}
-17
View File
@@ -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
}
-17
View File
@@ -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
}
-91
View File
@@ -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
}
-17
View File
@@ -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
}
-18
View File
@@ -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
}
-21
View File
@@ -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
}
-8
View File
@@ -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{
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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 {