1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Merge branch 'master' into v1.1.0

This commit is contained in:
shivkumar dudhani 2019-12-12 16:54:42 -08:00
commit 10fc1b47ba
28 changed files with 181 additions and 54 deletions

View file

@ -28,7 +28,7 @@ This policy requires that all pods have CPU and memory resource requests and lim
````yaml
apiVersion: kyverno.io/v1
kind: Policy
kind: ClusterPolicy
metadata:
name: check-cpu-memory
spec:
@ -62,7 +62,7 @@ This policy sets the imagePullPolicy to Always if the image tag is latest:
````yaml
apiVersion: kyverno.io/v1
kind: Policy
kind: ClusterPolicy
metadata:
name: set-image-pull-policy
spec:
@ -90,7 +90,7 @@ This policy sets the Zookeeper and Kafka connection strings for all namespaces w
````yaml
apiVersion: kyverno.io/v1
kind: Policy
kind: ClusterPolicy
metadata:
name: "zk-kafka-address"
spec:

View file

@ -447,7 +447,7 @@ spec:
image: nirmata/kyvernopre:latest
containers:
- name: kyverno
image: nirmata/kyverno:latest
image: nirmata/kyverno:v1.0.0
args:
- "--filterK8Resources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"
# customize webhook timout

View file

@ -26,7 +26,7 @@ func Mutate(policyContext PolicyContext) (response EngineResponse) {
defer func() {
response.PolicyResponse.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
glog.V(4).Infof("Mutation Rules appplied count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
}()
incrementAppliedRuleCount := func() {
// rules applied succesfully count

View file

@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/golang/glog"
@ -31,12 +32,17 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp
// resource does not satisfy the overlay pattern, we don't apply this rule
if !reflect.DeepEqual(overlayerr, overlayError{}) {
switch overlayerr.statusCode {
// condition key is not present in the resource, don't apply this rule
// consider as success
case conditionNotPresent:
glog.V(3).Infof("Skip applying rule '%s' on resource '%s/%s/%s': %s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg())
response.Success = true
return response, resource
// conditions are not met, don't apply this rule
// consider as failure
case conditionFailure:
glog.Errorf("Resource %s/%s/%s does not meet the conditions in the rule %s with overlay pattern %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Name, rule.Mutation.Overlay)
glog.V(3).Infof("Skip applying rule '%s' on resource '%s/%s/%s': %s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg())
//TODO: send zero response and not consider this as applied?
response.Success = false
response.Success = true
response.Message = overlayerr.ErrorMsg()
return response, resource
// rule application failed
@ -71,7 +77,7 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp
patchResource, err = ApplyPatches(resourceRaw, patches)
if err != nil {
msg := fmt.Sprintf("failed to apply JSON patches: %v", err)
glog.V(2).Info(msg)
glog.V(2).Infof("%s, patches=%s", msg, string(JoinPatches(patches)))
response.Success = false
response.Message = msg
return response, resource
@ -95,10 +101,16 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp
func processOverlayPatches(resource, overlay interface{}) ([][]byte, overlayError) {
if path, overlayerr := meetConditions(resource, overlay); !reflect.DeepEqual(overlayerr, overlayError{}) {
if overlayerr.statusCode == conditionFailure {
switch overlayerr.statusCode {
// anchor key does not exist in the resource, skip applying policy
case conditionNotPresent:
glog.V(4).Infof("Mutate rule: skip applying policy: %v at %s", overlayerr, path)
return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Policy not applied, condition tag not present: %v at %s", overlayerr.ErrorMsg(), path))
// anchor key is not satisfied in the resource, skip applying policy
case conditionFailure:
// anchor key is not satisfied in the resource, skip applying policy
glog.V(4).Infof("Mutate rule: failed to validate condition at %s, err: %v", path, overlayerr)
return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Conditions are not met at %s, %v", path, overlayerr))
return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Policy not applied, conditions are not met at %s, %v", path, overlayerr))
}
}
@ -333,10 +345,7 @@ func processSubtree(overlay interface{}, path string, op string) ([]byte, error)
path = path[:len(path)-1]
}
if path == "" {
path = "/"
}
path = preparePath(path)
value := prepareJSONValue(overlay)
patchStr := fmt.Sprintf(`{ "op": "%s", "path": "%s", "value": %s }`, op, path, value)
@ -350,6 +359,20 @@ func processSubtree(overlay interface{}, path string, op string) ([]byte, error)
return []byte(patchStr), nil
}
func preparePath(path string) string {
if path == "" {
path = "/"
}
annPath := "/metadata/annotations/"
// escape slash in annotation patch
if strings.Contains(path, annPath) {
p := path[len(annPath):]
path = annPath + strings.ReplaceAll(p, "/", "~1")
}
return path
}
// converts overlay to JSON string to be inserted into the JSON Patch
func prepareJSONValue(overlay interface{}) string {
var err error

View file

@ -98,7 +98,7 @@ func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, pat
}
} else {
// noAnchorKey doesn't exist in resource
continue
return curPath, newOverlayError(conditionNotPresent, fmt.Sprintf("resource field is not present %s", noAnchorKey))
}
}
return "", overlayError{}
@ -110,8 +110,8 @@ func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, pat
// resource - A: B2
func compareOverlay(resource, overlay interface{}, path string) (string, overlayError) {
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
glog.V(4).Infof("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource)
return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource))
glog.V(4).Infof("Found anchor on different types of element: overlay %T, resource %T", overlay, resource)
return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T", overlay, resource))
}
switch typedOverlay := overlay.(type) {
@ -122,7 +122,7 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay
curPath := path + noAnchorKey + "/"
resourceVal, ok := typedResource[noAnchorKey]
if !ok {
return curPath, newOverlayError(conditionFailure, fmt.Sprintf("field %s is not present", noAnchorKey))
return curPath, newOverlayError(conditionFailure, fmt.Sprintf("Field %s is not present", noAnchorKey))
}
if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
@ -140,10 +140,10 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay
case string, float64, int, int64, bool, nil:
if !ValidateValueWithPattern(resource, overlay) {
glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay)
return path, newOverlayError(conditionFailure, fmt.Sprintf("failed validating value %v with overlay %v", resource, overlay))
return path, newOverlayError(conditionFailure, fmt.Sprintf("Failed validating value %v with overlay %v", resource, overlay))
}
default:
return path, newOverlayError(conditionFailure, fmt.Sprintf("overlay has unknown type %T, value %v", overlay, overlay))
return path, newOverlayError(conditionFailure, fmt.Sprintf("Overlay has unknown type %T, value %v", overlay, overlay))
}
return "", overlayError{}
@ -156,12 +156,14 @@ func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]in
curPath := path + key + "/"
resourceValue, ok := resourceMap[key]
if !ok {
if !hasNestedAnchors(overlayValue) {
// policy: "(image)": "*:latest",
// "imagePullPolicy": "IfNotPresent",
// resource: "(image)": "*:latest",
// the above case should be allowed
continue
}
}
if newPath, err := checkConditions(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}

View file

@ -194,7 +194,7 @@ func TestMeetConditions_anchosInSameObject(t *testing.T) {
json.Unmarshal(overlayRaw, &overlay)
_, err := meetConditions(resource, overlay)
assert.Error(t, err, "[overlayError:0] failed validating value 443 with overlay 444")
assert.Error(t, err, "[overlayError:0] Failed validating value 443 with overlay 444")
}
func TestMeetConditions_anchorOnPeer(t *testing.T) {
@ -444,7 +444,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
json.Unmarshal(overlayRaw, &overlay)
_, err := meetConditions(resource, overlay)
assert.Error(t, err, "[overlayError:0] failed validating value true with overlay false")
assert.Error(t, err, "[overlayError:0] Failed validating value true with overlay false")
overlayRaw = []byte(`{
"spec": {
@ -594,7 +594,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
json.Unmarshal(overlayRaw, &overlay)
_, err = meetConditions(resource, overlay)
assert.Error(t, err, "[overlayError:0] failed validating value ENV_VALUE with overlay ENV_VALUE1")
assert.Error(t, err, "[overlayError:0] Failed validating value ENV_VALUE with overlay ENV_VALUE1")
}
func TestMeetConditions_AtleastOneExist(t *testing.T) {

View file

@ -6,6 +6,7 @@ type codeKey int
const (
conditionFailure codeKey = iota
conditionNotPresent
overlayFailure
)

View file

@ -494,7 +494,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.Error(t, err, "[overlayError:0] Conditions are not met at /spec/template/metadata/labels/app/, [overlayError:0] failed validating value nginx with overlay nginx1")
assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /spec/template/metadata/labels/app/, [overlayError:0] Failed validating value nginx with overlay nginx1")
assert.Assert(t, len(patches) == 0)
}
@ -807,7 +807,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.Error(t, err, "[overlayError:0] Conditions are not met at /subsets/0/ports/0/port/, [overlayError:0] failed validating value 443 with overlay 444")
assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /subsets/0/ports/0/port/, [overlayError:0] Failed validating value 443 with overlay 444")
assert.Assert(t, len(patches) == 0)
}

View file

@ -21,7 +21,7 @@ const (
func (k MsgKey) String() string {
return [...]string{
"Policy violation on resource '%s'. The rule(s) '%s' failed to apply",
"Policy violation on resource '%s'. The rule(s) '%s' not satisfied",
"Failed to process rule '%s' of policy '%s'.",
"Policy applied successfully on the resource '%s'",
"Rule(s) '%s' of Policy '%s' applied successfully",

View file

@ -122,7 +122,7 @@ func applyPolicyOnRaw(policy *kyverno.ClusterPolicy, rawResource []byte, gvk *me
for _, r := range engineResponse.PolicyResponse.Rules {
glog.Warning(r.Message)
}
return patchedResource, fmt.Errorf("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
return patchedResource, fmt.Errorf("policy %s on resource %s/%s not satisfied", policy.Name, rname, rns)
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
}

View file

@ -89,7 +89,7 @@ func generateEventsPerEr(er engine.EngineResponse) []event.Info {
e.Namespace = "" // event generate on namespace resource
e.Name = er.PolicyResponse.Resource.Name
e.Reason = "Failure"
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' failed to apply. %v", er.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' not satisfied. %v", er.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
eventInfos = append(eventInfos, e)
}
if er.IsSuccesful() {
@ -102,6 +102,6 @@ func generateEventsPerEr(er engine.EngineResponse) []event.Info {
e.Namespace = ""
e.Name = er.PolicyResponse.Policy
e.Reason = "Failure"
e.Message = fmt.Sprintf("failed to apply policy '%s' rules '%v' on resource '%s/%s/%s'", er.PolicyResponse.Policy, er.GetFailedRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
e.Message = fmt.Sprintf("policy '%s' rules '%v' on resource '%s/%s/%s' not stasified", er.PolicyResponse.Policy, er.GetFailedRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
return eventInfos
}

View file

@ -108,8 +108,8 @@ func generateEventsPerEr(er engine.EngineResponse) []event.Info {
e.Kind = er.PolicyResponse.Resource.Kind
e.Namespace = er.PolicyResponse.Resource.Namespace
e.Name = er.PolicyResponse.Resource.Name
e.Reason = "Failure"
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' failed to apply. %v", er.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
e.Reason = event.PolicyViolation.String()
e.Message = fmt.Sprintf("policy '%s' (%s) rule '%s' not satisfied. %v", er.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
eventInfos = append(eventInfos, e)
}
if er.IsSuccesful() {
@ -122,8 +122,8 @@ func generateEventsPerEr(er engine.EngineResponse) []event.Info {
e.Kind = "ClusterPolicy"
e.Namespace = ""
e.Name = er.PolicyResponse.Policy
e.Reason = "Failure"
e.Message = fmt.Sprintf("failed to apply policy '%s' rules '%v' on resource '%s/%s/%s'", er.PolicyResponse.Policy, er.GetFailedRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
e.Reason = event.PolicyViolation.String()
e.Message = fmt.Sprintf("policy '%s' rules '%v' not satisfied on resource '%s/%s/%s'", er.PolicyResponse.Policy, er.GetFailedRules(), er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name)
eventInfos = append(eventInfos, e)
return eventInfos
}

View file

@ -251,7 +251,8 @@ func validateResponse(t *testing.T, er engine.PolicyResponse, expected engine.Po
// rules
if len(er.Rules) != len(expected.Rules) {
t.Error("rule count: error")
t.Errorf("rule count error, er.Rules=%d, expected.Rules=%d", len(er.Rules), len(expected.Rules))
return
}
if len(er.Rules) == len(expected.Rules) {
// if there are rules being applied then we compare the rule response

View file

@ -112,6 +112,10 @@ func Test_add_safe_to_evict_annotation2(t *testing.T) {
testScenario(t, "test/scenarios/samples/best_practices/add_safe_to_evict2.yaml")
}
func Test_add_safe_to_evict_annotation3(t *testing.T) {
testScenario(t, "test/scenarios/samples/best_practices/add_safe_to_evict3.yaml")
}
func Test_validate_restrict_automount_sa_token_pass(t *testing.T) {
testScenario(t, "test/scenarios/samples/more/restrict_automount_sa_token.yaml")
}

View file

@ -21,6 +21,11 @@ func generateEvents(engineResponses []engine.EngineResponse, onUpdate bool) []ev
// dont create events on success
continue
}
// default behavior is audit
reason := event.PolicyViolation
if er.PolicyResponse.ValidationFailureAction == Enforce {
reason = event.RequestBlocked
}
failedRules := er.GetFailedRules()
filedRulesStr := strings.Join(failedRules, ";")
if onUpdate {
@ -32,7 +37,7 @@ func generateEvents(engineResponses []engine.EngineResponse, onUpdate bool) []ev
er.PolicyResponse.Resource.APIVersion,
er.PolicyResponse.Resource.Namespace,
er.PolicyResponse.Resource.Name,
event.RequestBlocked.String(),
reason.String(),
event.FPolicyApplyBlockUpdate,
filedRulesStr,
er.PolicyResponse.Policy,
@ -46,7 +51,7 @@ func generateEvents(engineResponses []engine.EngineResponse, onUpdate bool) []ev
kyverno.SchemeGroupVersion.String(),
"",
er.PolicyResponse.Policy,
event.RequestBlocked.String(),
reason.String(),
event.FPolicyBlockResourceUpdate,
er.PolicyResponse.Resource.Namespace+"/"+er.PolicyResponse.Resource.Name,
filedRulesStr,

View file

@ -88,9 +88,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
glog.V(4).Infof("eval: %v %s/%s/%s ", time.Since(evalTime), request.Kind, request.Namespace, request.Name)
// report time
reportTime := time.Now()
// ADD EVENTS
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
ws.eventGen.Add(events...)
// If Validation fails then reject the request
// violations are created with resource owner(if exist) on "enforce"
@ -102,6 +99,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
glog.V(4).Infof("resource %s/%s/%s is blocked\n", newR.GetKind(), newR.GetNamespace(), newR.GetName())
pvInfos := generatePV(engineResponses, true)
ws.pvGenerator.Add(pvInfos...)
// ADD EVENTS
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
ws.eventGen.Add(events...)
sendStat(true)
return false, getErrorMsg(engineResponses)
}
@ -110,6 +110,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
pvInfos := generatePV(engineResponses, blocked)
ws.pvGenerator.Add(pvInfos...)
// ADD EVENTS
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
ws.eventGen.Add(events...)
sendStat(false)
// report time end
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)

View file

@ -1,6 +1,6 @@
# Configure namespace limits and quotas
To limit the number of resources like CPU and memory, as well as objects that may be consumed by workloads in a namespace, it is important to configure resource limits and quotas for each namespace.
To limit the number of resources like CPU and memory, as well as objects that may be consumed by workloads in a namespace, it is important to configure resource limits and quotas for each namespace. The generated default limitrange sets the default quotas for a container.
## Additional Information
@ -32,4 +32,22 @@ spec:
requests.memory: '16Gi'
limits.cpu: '4'
limits.memory: '16Gi'
- name: generate-limitrange
match:
resources:
kinds:
- Namespace
generate:
kind: LimitRange
name: "default-limitrange"
data:
spec:
limits:
- default:
cpu: 500m
memory: 1Gi
defaultRequest:
cpu: 200m
memory: 256Mi
type: Container
````

View file

@ -14,7 +14,7 @@ This policy matches and mutates pods with `emptyDir` and `hostPath` volumes, to
````yaml
apiVersion: "kyverno.io/v1"
kind: "ClusterPolicy"
kind: ClusterPolicy
metadata:
name: "add-safe-to-evict"
spec:

View file

@ -8,7 +8,7 @@ The volume of type `hostPath` allows pods to use host bind mounts (i.e. director
````yaml
apiVersion: "kyverno.io/v1"
kind: "ClusterPolicy"
kind: ClusterPolicy
metadata:
name: "disallow-bind-mounts"
spec:

View file

@ -23,7 +23,7 @@ These policies are highly recommended.
13. [Require pod resource requests and limits](RequirePodRequestsLimits.md)
14. [Require pod `livenessProbe` and `readinessProbe`](RequirePodProbes.md)
15. [Add default network policy](AddDefaultNetworkPolicy.md)
16. [Add namespace resource quotas](AddNamespaceResourceQuota.md)
16. [Add namespace quotas](AddNamespaceQuotas.md)
17. [Add `safe-to-evict` for pods with `emptyDir` and `hostPath` volumes](AddSafeToEvict.md)
## Additional Policies

View file

@ -24,3 +24,21 @@ spec:
requests.memory: '16Gi'
limits.cpu: '4'
limits.memory: '16Gi'
- name: generate-limitrange
match:
resources:
kinds:
- Namespace
generate:
kind: LimitRange
name: "default-limitrange"
data:
spec:
limits:
- default:
cpu: 500m
memory: 1Gi
defaultRequest:
cpu: 200m
memory: 256Mi
type: Container

View file

@ -19,5 +19,5 @@ spec:
pattern:
spec:
=(volumes):
=(hostPath):
- =(hostPath):
path: "!/var/run/docker.sock"

View file

@ -0,0 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
name: pod-with-default-volume
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-wkknl
readOnly: true
volumes:
- name: default-token-wkknl
secret:
defaultMode: 420
secretName: default-token-wkknl

View file

@ -0,0 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
name: pod-with-default-volume
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-wkknl
readOnly: true
volumes:
- name: default-token-wkknl
secret:
defaultMode: 420
secretName: default-token-wkknl

View file

@ -19,3 +19,6 @@ expected:
- name: generate-resourcequota
type: Generation
success: true
- name: generate-limitrange
type: Generation
success: true

View file

@ -13,7 +13,7 @@ expected:
namespace: ''
name: pod-with-hostpath
rules:
- name: annotate-empty-dir
- name: annotate-host-path
type: Mutation
success: true
message: "successfully processed overlay"

View file

@ -0,0 +1,15 @@
# file path is relative to project root
input:
policy: samples/best_practices/add_safe_to_evict.yaml
resource: test/resources/pod-with-default-volume.yaml
expected:
mutation:
patchedresource: test/resources/pod-with-default-volume.yaml
policyresponse:
policy: add-safe-to-evict
resource:
kind: Pod
apiVersion: v1
namespace: ''
name: pod-with-default-volume
rules:

View file

@ -14,5 +14,5 @@ expected:
rules:
- name: validate-docker-sock-mount
type: Validation
message: "Validation error: Use of the Docker Unix socket is not allowed; Validation rule 'validate-docker-sock-mount' failed at path '/spec/volumes/'"
message: "Validation error: Use of the Docker Unix socket is not allowed; Validation rule 'validate-docker-sock-mount' failed at path '/spec/volumes/0/hostPath/path/'"
success: false