initial implementation of updated configuration syntax

This commit is contained in:
Rob Scott
2019-03-15 17:31:47 -04:00
parent 2c64e6cb52
commit 3de9493b68
3 changed files with 260 additions and 117 deletions

View File

@@ -1,35 +1,107 @@
resources:
requests:
cpu:
min: 50m
max: 1
memory:
min: 100M
max: 3G
limits:
cpu:
min: 150m
max: 2
memory:
min: 150M
max: 4G
healthChecks:
readiness:
require: true
liveness:
require: true
cpuRequests:
onAbsence: warning
warning:
below: 50m
above: 1000m
error:
below: 500m
above: 2000m
cpuLimits:
onAbsence: warning
warning:
below: 50m
above: 1000m
error:
below: 500m
above: 2000m
memoryRequests:
onAbsence: warning
warning:
below: 50M
above: 2G
error:
below: 100M
above: 4G
memoryLimits:
onAbsence: warning
warning:
below: 50M
above: 2G
error:
below: 100M
above: 4G
resources:
cpuRequests:
error:
below: 100m
above: 1
warning:
below: 200m
above: 800m
memoryRequests:
error:
below: 100M
above: 3G
warning:
below: 200M
above: 2G
cpuLimits:
error:
below: 100m
above: 2
warning:
below: 300m
above: 1800m
memoryLimits:
error:
below: 200M
above: 6G
warning:
below: 300M
above: 4G
images:
tagRequired: true
whitelistRepos:
- gcr.io
hostNetwork:
hostAlias:
require: true
hostIPC:
require: true
hostNetwork:
require: true
hostPID:
require: true
hostPort:
require: true
tagNotSpecified: 'error'
pullPolicyNotAlways: 'warning'
error:
whitelist:
- gcr.io/*
warning:
blacklist:
- docker.io/*
healthChecks:
readinessProbeMissing: warning
livenessProbeMissing: warning
networking:
hostAliasSet: error
hostIPCSet: error
hostNetworkSet: error
hostPIDSet: error
hostPortSet: error
security:
runAsPriviliged: warning
notReadOnlyRootFileSystem: warning
runAsNonRoot: warning
capabilities:
warning:
blacklist:
- CHOWN
- SYS_CHROOT
- AUDIT_WRITE
error:
whitelist:
- CHOWN
- DAC_OVERRIDE
- FSETID
- FOWNER
- MKNOD
- NET_RAW
- SETGID
- SETUID
- SETFCAP
- SETPCAP
- NET_BIND_SERVICE
- SYS_CHROOT
- KILL
- AUDIT_WRITE

View File

@@ -6,58 +6,89 @@ import (
"io"
"io/ioutil"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/yaml"
)
// Configuration contains all of the config for the validation checks.
type Configuration struct {
Resources RequestsAndLimits `json:"resources"`
HealthChecks Probes `json:"healthChecks"`
Images Images `json:"images"`
HostNetworking HostNetworking `json:"hostNetworking"`
Resources Resources `json:"resources"`
HealthChecks HealthChecks `json:"healthChecks"`
Images Images `json:"images"`
Networking Networking `json:"networking"`
Security Security `json:"security"`
}
// RequestsAndLimits contains config for resource requests and limits.
type RequestsAndLimits struct {
Requests ResourceList `json:"requests"`
Limits ResourceList `json:"limits"`
// Severity represents the severity of action to take (Ignore, Warning, Error).
type Severity string
// Resources contains config for resource requests and limits.
type Resources struct {
CPURequests Resource `json:"cpuRequests"`
CPULimits Resource `json:"cpuLimits"`
MemoryRequests Resource `json:"memoryRequests"`
MemoryLimits Resource `json:"memoryLimits"`
}
// ResourceList maps the resource name to a range on min and max values.
type ResourceList map[corev1.ResourceName]ResourceMinMax
// ResourceMinMax sets a range for a min and max setting for a resource.
type ResourceMinMax struct {
Min *resource.Quantity `json:"min"`
Max *resource.Quantity `json:"max"`
// Resource contains config for requests or limits for a specific resource.
type Resource struct {
Absent Severity `json:"absent"`
Warning ResourceRange `json:"warning"`
Error ResourceRange `json:"error"`
}
// Probes contains config for the readiness and liveness probes.
type Probes struct {
Readiness ResourceRequire `json:"readiness"`
Liveness ResourceRequire `json:"liveness"`
// ResourceRange can contain below and above conditions for validation.
type ResourceRange struct {
Below *resource.Quantity `json:"below"`
Above *resource.Quantity `json:"above"`
}
// ResourceRequire indicates if this resource should be validated.
type ResourceRequire struct {
Require bool `json:"require"`
// HealthChecks contains config for readiness and liveness probes.
type HealthChecks struct {
ReadinessProbeMissing Severity `json:"readinessProbeMissing"`
LivenessProbeMissing Severity `json:"livenessProbeMissing"`
}
// Images contains the config for images.
type Images struct {
TagRequired bool `json:"tagRequired"`
WhitelistRepos []string `json:"whitelistRepos"`
TagNotSpecified Severity `json:"tagNotSpecified"`
PullPolicyNotAlways Severity `json:"pullPolicyNotAlways"`
Repositories Repositories `json:"repositories"`
}
// HostNetworking contains the config for host networking validations.
type HostNetworking struct {
HostAlias ResourceRequire `json:"hostAlias"`
HostIPC ResourceRequire `json:"hostIPC"`
HostNetwork ResourceRequire `json:"hostNetwork"`
HostPID ResourceRequire `json:"hostPID"`
HostPort ResourceRequire `json:"hostPort"`
// Repositories provides lists of patterns to match or avoid in image tags.
type Repositories struct {
Error WhitelistBlacklist `json:"error"`
Warning WhitelistBlacklist `json:"warning"`
}
// WhitelistBlacklist can contain a whitelist or blacklist.
type WhitelistBlacklist struct {
Whitelist []string `json:"whitelist"`
Blacklist []string `json:"blacklist"`
}
// Networking contains the config for networking validations.
type Networking struct {
HostAliasSet Severity `json:"hostAliasSet"`
HostIPCSet Severity `json:"hostIPCSet"`
HostNetworkSet Severity `json:"hostNetworkSet"`
HostPIDSet Severity `json:"hostPIDSet"`
HostPortSet Severity `json:"hostPortSet"`
}
// Security contains the config for security validations.
type Security struct {
runAsNonRoot Severity `json:"runAsNonRoot"`
runAsPriviliged Severity `json:"runAsPriviliged"`
notReadOnlyRootFileSystem Severity `json:"notReadOnlyRootFileSystem"`
capabilities SecurityCapabilities `json:"capabilities"`
}
// SecurityCapabilities contains the config for security capabilities validations.
type SecurityCapabilities struct {
Error WhitelistBlacklist `json:"error"`
Warning WhitelistBlacklist `json:"warning"`
}
// ParseFile parses config from a file.

View File

@@ -25,42 +25,76 @@ var resourceConfInvalid1 = `test`
var resourceConfYAML1 = `---
resources:
requests:
cpu:
min: 100m
max: 1
memory:
min: 100M
max: 3G
limits:
cpu:
min: 150m
max: 2
memory:
min: 150M
max: 4G
cpuRequests:
error:
below: 100m
above: 1
warning:
below: 200m
above: 800m
memoryRequests:
error:
below: 100M
above: 3G
warning:
below: 200M
above: 2G
cpuLimits:
error:
below: 100m
above: 2
warning:
below: 300m
above: 1800m
memoryLimits:
error:
below: 200M
above: 6G
warning:
below: 300M
above: 4G
`
var resourceConfJSON1 = `{
"resources": {
"requests": {
"cpu": {
"min": "100m",
"max": "1"
"cpuRequests": {
"error": {
"below": "100m",
"above": 1
},
"memory": {
"min": "100M",
"max": "3G"
"warning": {
"below": "200m",
"above": "800m"
}
},
"limits": {
"cpu": {
"min": "150m",
"max": "2"
"memoryRequests": {
"error": {
"below": "100M",
"above": "3G"
},
"memory": {
"min": "150M",
"max": "4G"
"warning": {
"below": "200M",
"above": "2G"
}
},
"cpuLimits": {
"error": {
"below": "100m",
"above": 2
},
"warning": {
"below": "300m",
"above": "1800m"
}
},
"memoryLimits": {
"error": {
"below": "200M",
"above": "6G"
},
"warning": {
"below": "300M",
"above": "4G"
}
}
}
@@ -74,34 +108,40 @@ func TestParseError(t *testing.T) {
func TestParseYaml(t *testing.T) {
parsedConf, err := Parse([]byte(resourceConfYAML1))
assert.NoError(t, err, "Expected no error when parsing config")
assert.NoError(t, err, "Expected no error when parsing YAML config")
requests := parsedConf.Resources.Requests
assert.Equal(t, int64(100), requests["cpu"].Min.ScaledValue(resource.Milli))
assert.Equal(t, int64(1000), requests["cpu"].Max.ScaledValue(resource.Milli))
assert.Equal(t, int64(100), requests["memory"].Min.ScaledValue(resource.Mega))
assert.Equal(t, int64(3000), requests["memory"].Max.ScaledValue(resource.Mega))
limits := parsedConf.Resources.Limits
assert.Equal(t, int64(150), limits["cpu"].Min.ScaledValue(resource.Milli))
assert.Equal(t, int64(2000), limits["cpu"].Max.ScaledValue(resource.Milli))
assert.Equal(t, int64(150), limits["memory"].Min.ScaledValue(resource.Mega))
assert.Equal(t, int64(4000), limits["memory"].Max.ScaledValue(resource.Mega))
testParsedConfig(t, &parsedConf)
}
func TestParseJson(t *testing.T) {
parsedConf, err := Parse([]byte(resourceConfJSON1))
assert.NoError(t, err, "Expected no error when parsing config")
assert.NoError(t, err, "Expected no error when parsing JSON config")
requests := parsedConf.Resources.Requests
assert.Equal(t, int64(100), requests["cpu"].Min.ScaledValue(resource.Milli))
assert.Equal(t, int64(1000), requests["cpu"].Max.ScaledValue(resource.Milli))
assert.Equal(t, int64(100), requests["memory"].Min.ScaledValue(resource.Mega))
assert.Equal(t, int64(3000), requests["memory"].Max.ScaledValue(resource.Mega))
limits := parsedConf.Resources.Limits
assert.Equal(t, int64(150), limits["cpu"].Min.ScaledValue(resource.Milli))
assert.Equal(t, int64(2000), limits["cpu"].Max.ScaledValue(resource.Milli))
assert.Equal(t, int64(150), limits["memory"].Min.ScaledValue(resource.Mega))
assert.Equal(t, int64(4000), limits["memory"].Max.ScaledValue(resource.Mega))
testParsedConfig(t, &parsedConf)
}
func testParsedConfig(t *testing.T, config *Configuration) {
cpuRequests := config.Resources.CPURequests
assert.Equal(t, int64(100), cpuRequests.Error.Below.ScaledValue(resource.Milli))
assert.Equal(t, int64(1000), cpuRequests.Error.Above.ScaledValue(resource.Milli))
assert.Equal(t, int64(200), cpuRequests.Warning.Below.ScaledValue(resource.Milli))
assert.Equal(t, int64(800), cpuRequests.Warning.Above.ScaledValue(resource.Milli))
memRequests := config.Resources.MemoryRequests
assert.Equal(t, int64(100), memRequests.Error.Below.ScaledValue(resource.Mega))
assert.Equal(t, int64(3000), memRequests.Error.Above.ScaledValue(resource.Mega))
assert.Equal(t, int64(200), memRequests.Warning.Below.ScaledValue(resource.Mega))
assert.Equal(t, int64(2000), memRequests.Warning.Above.ScaledValue(resource.Mega))
cpuLimits := config.Resources.CPULimits
assert.Equal(t, int64(100), cpuLimits.Error.Below.ScaledValue(resource.Milli))
assert.Equal(t, int64(2000), cpuLimits.Error.Above.ScaledValue(resource.Milli))
assert.Equal(t, int64(300), cpuLimits.Warning.Below.ScaledValue(resource.Milli))
assert.Equal(t, int64(1800), cpuLimits.Warning.Above.ScaledValue(resource.Milli))
memLimits := config.Resources.MemoryLimits
assert.Equal(t, int64(200), memLimits.Error.Below.ScaledValue(resource.Mega))
assert.Equal(t, int64(6000), memLimits.Error.Above.ScaledValue(resource.Mega))
assert.Equal(t, int64(300), memLimits.Warning.Below.ScaledValue(resource.Mega))
assert.Equal(t, int64(4000), memLimits.Warning.Above.ScaledValue(resource.Mega))
}