1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-15 20:20:22 +00:00

Feature/namespaced policy 280 (#1058)

* namespaced policy crd and cache

* modified main.go

* removed kyverno

* implemented policy violation generator for namespaced policy on audit

* modified cache

* added validation for cluster resource types

* install.yaml

* install.yaml

* removed namespaces from crd and refactored code

* modified NamespacePolicy to Policy

* added ClusterRole aggregate for policies

* modified clusterrole
This commit is contained in:
Mohan B E 2020-08-19 21:37:23 +05:30 committed by GitHub
parent 64d1ee7a9d
commit f60deecdce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 2398 additions and 112 deletions

View file

@ -411,6 +411,269 @@ spec:
--- ---
apiVersion: apiextensions.k8s.io/v1beta1 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata:
name: policies.kyverno.io
spec:
group: kyverno.io
names:
kind: Policy
plural: policies
shortNames:
- pol
singular: policy
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
spec:
properties:
background:
type: boolean
rules:
items:
properties:
exclude:
properties:
clusterRoles:
items:
type: string
type: array
resources:
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
type: object
generate:
properties:
apiVersion:
type: string
clone:
properties:
name:
type: string
namespace:
type: string
required:
- namespace
- name
type: object
data:
AnyValue: {}
kind:
type: string
name:
type: string
namespace:
type: string
synchronize:
type: boolean
required:
- kind
- name
type: object
match:
properties:
clusterRoles:
items:
type: string
type: array
resources:
minProperties: 1
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
required:
- resources
type: object
mutate:
properties:
overlay:
AnyValue: {}
patchStrategicMerge:
AnyValue: {}
patches:
items:
properties:
op:
enum:
- add
- replace
- remove
type: string
path:
type: string
value:
AnyValue: {}
required:
- path
- op
type: object
type: array
patchesJson6902:
type: string
type: object
name:
type: string
preconditions:
items:
required:
- key
- operator
- value
type: object
type: array
validate:
properties:
anyPattern:
AnyValue: {}
deny:
properties:
conditions:
items:
properties:
key:
type: string
operator:
enum:
- Equal
- Equals
- NotEqual
- NotEquals
- In
- NotIn
type: string
value:
anyOf:
- type: string
- items: {}
type: array
required:
- key
- operator
- value
type: object
type: array
message:
type: string
pattern:
AnyValue: {}
type: object
required:
- name
- match
type: object
type: array
validationFailureAction:
enum:
- enforce
- audit
type: string
required:
- rules
status: {}
versions:
- name: v1
served: true
storage: true
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata: metadata:
name: policyviolations.kyverno.io name: policyviolations.kyverno.io
spec: spec:

View file

@ -171,7 +171,8 @@ func main() {
// Policy Status Handler - deals with all logic related to policy status // Policy Status Handler - deals with all logic related to policy status
statusSync := policystatus.NewSync( statusSync := policystatus.NewSync(
pclient, pclient,
pInformer.Kyverno().V1().ClusterPolicies().Lister()) pInformer.Kyverno().V1().ClusterPolicies().Lister(),
pInformer.Kyverno().V1().Policies().Lister())
// POLICY VIOLATION GENERATOR // POLICY VIOLATION GENERATOR
// -- generate policy violation // -- generate policy violation
@ -190,6 +191,7 @@ func main() {
policyCtrl, err := policy.NewPolicyController(pclient, policyCtrl, err := policy.NewPolicyController(pclient,
client, client,
pInformer.Kyverno().V1().ClusterPolicies(), pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
pInformer.Kyverno().V1().ClusterPolicyViolations(), pInformer.Kyverno().V1().ClusterPolicyViolations(),
pInformer.Kyverno().V1().PolicyViolations(), pInformer.Kyverno().V1().PolicyViolations(),
configData, configData,
@ -235,6 +237,7 @@ func main() {
pCacheController := policycache.NewPolicyCacheController( pCacheController := policycache.NewPolicyCacheController(
pInformer.Kyverno().V1().ClusterPolicies(), pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
log.Log.WithName("PolicyCacheController"), log.Log.WithName("PolicyCacheController"),
) )

View file

@ -274,6 +274,270 @@ spec:
--- ---
apiVersion: apiextensions.k8s.io/v1beta1 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata:
name: policies.kyverno.io
spec:
group: kyverno.io
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
kind: Policy
plural: policies
singular: policy
shortNames:
- pol
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
status: {}
spec:
required:
- rules
properties:
# default values to be handled by user
validationFailureAction:
type: string
enum:
- enforce # blocks the resorce api-reques if a rule fails.
- audit # allows resource creation and reports the failed validation rules as violations. Default
background:
type: boolean
rules:
type: array
items:
type: object
required:
- name
- match
properties:
name:
type: string
match:
type: object
required:
- resources
properties:
roles:
type: array
items:
type: string
clusterRoles:
type: array
items:
type: string
subjects:
type: array
items:
type: object
required:
- kind
- name
properties:
kind:
type: string
apiGroup:
type: string
name:
type: string
namespace:
type: string
resources:
type: object
minProperties: 1
properties:
kinds:
type: array
items:
type: string
name:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
exclude:
type: object
properties:
roles:
type: array
items:
type: string
clusterRoles:
type: array
items:
type: string
subjects:
type: array
items:
type: object
required:
- kind
- name
properties:
kind:
type: string
apiGroup:
type: string
name:
type: string
namespace:
type: string
resources:
type: object
properties:
kinds:
type: array
items:
type: string
name:
type: string
selector:
properties:
matchLabels:
type: object
additionalProperties:
type: string
matchExpressions:
type: array
items:
type: object
required:
- key
- operator
properties:
key:
type: string
operator:
type: string
values:
type: array
items:
type: string
preconditions:
type: array
items:
type: object
required:
- key # can be of any type
- operator # typed
- value # can be of any type
mutate:
type: object
properties:
overlay:
AnyValue: {}
patchStrategicMerge:
AnyValue: {}
patchesJson6902:
type: string
patches:
type: array
items:
type: object
required:
- path
- op
properties:
path:
type: string
op:
type: string
enum:
- add
- replace
- remove
value:
AnyValue: {}
validate:
type: object
properties:
message:
type: string
pattern:
AnyValue: {}
anyPattern:
AnyValue: {}
deny:
properties:
conditions:
type: array
items:
type: object
required:
- key # can be of any type
- operator # typed
- value # can be of any type
properties:
operator:
type: string
enum:
- Equal
- Equals
- NotEqual
- NotEquals
- In
- NotIn
key:
type: string
value:
anyOf:
- type: string
- type: array
items: {}
generate:
type: object
required:
- kind
- name
properties:
apiVersion:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
synchronize:
type: boolean
clone:
type: object
required:
- namespace
- name
properties:
namespace:
type: string
name:
type: string
data:
AnyValue: {}
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata: metadata:
name: clusterpolicyviolations.kyverno.io name: clusterpolicyviolations.kyverno.io
spec: spec:

View file

@ -416,6 +416,269 @@ spec:
--- ---
apiVersion: apiextensions.k8s.io/v1beta1 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata:
name: policies.kyverno.io
spec:
group: kyverno.io
names:
kind: Policy
plural: policies
shortNames:
- pol
singular: policy
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
spec:
properties:
background:
type: boolean
rules:
items:
properties:
exclude:
properties:
clusterRoles:
items:
type: string
type: array
resources:
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
type: object
generate:
properties:
apiVersion:
type: string
clone:
properties:
name:
type: string
namespace:
type: string
required:
- namespace
- name
type: object
data:
AnyValue: {}
kind:
type: string
name:
type: string
namespace:
type: string
synchronize:
type: boolean
required:
- kind
- name
type: object
match:
properties:
clusterRoles:
items:
type: string
type: array
resources:
minProperties: 1
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
required:
- resources
type: object
mutate:
properties:
overlay:
AnyValue: {}
patchStrategicMerge:
AnyValue: {}
patches:
items:
properties:
op:
enum:
- add
- replace
- remove
type: string
path:
type: string
value:
AnyValue: {}
required:
- path
- op
type: object
type: array
patchesJson6902:
type: string
type: object
name:
type: string
preconditions:
items:
required:
- key
- operator
- value
type: object
type: array
validate:
properties:
anyPattern:
AnyValue: {}
deny:
properties:
conditions:
items:
properties:
key:
type: string
operator:
enum:
- Equal
- Equals
- NotEqual
- NotEquals
- In
- NotIn
type: string
value:
anyOf:
- type: string
- items: {}
type: array
required:
- key
- operator
- value
type: object
type: array
message:
type: string
pattern:
AnyValue: {}
type: object
required:
- name
- match
type: object
type: array
validationFailureAction:
enum:
- enforce
- audit
type: string
required:
- rules
status: {}
versions:
- name: v1
served: true
storage: true
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata: metadata:
name: policyviolations.kyverno.io name: policyviolations.kyverno.io
spec: spec:
@ -650,17 +913,51 @@ rules:
- list - list
- watch - watch
--- ---
apiVersion: rbac.authorization.k8s.io/v1beta1 apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
labels: labels:
rbac.authorization.k8s.io/aggregate-to-view: "true" rbac.authorization.k8s.io/aggregate-to-admin: "true"
name: kyverno:view-policyviolations name: kyverno:admin-policies
rules:
- apiGroups:
- kyverno.io
resources:
- policies
verbs:
- "*"
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
labels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
name: kyverno:edit-policies-policyviolations
rules: rules:
- apiGroups: - apiGroups:
- kyverno.io - kyverno.io
resources: resources:
- policyviolations - policyviolations
- policies
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: kyverno:view-policies-policyviolations
rules:
- apiGroups:
- kyverno.io
resources:
- policyviolations
- policies
verbs: verbs:
- get - get
- list - list

View file

@ -416,6 +416,269 @@ spec:
--- ---
apiVersion: apiextensions.k8s.io/v1beta1 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata:
name: policies.kyverno.io
spec:
group: kyverno.io
names:
kind: Policy
plural: policies
shortNames:
- pol
singular: policy
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
spec:
properties:
background:
type: boolean
rules:
items:
properties:
exclude:
properties:
clusterRoles:
items:
type: string
type: array
resources:
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
type: object
generate:
properties:
apiVersion:
type: string
clone:
properties:
name:
type: string
namespace:
type: string
required:
- namespace
- name
type: object
data:
AnyValue: {}
kind:
type: string
name:
type: string
namespace:
type: string
synchronize:
type: boolean
required:
- kind
- name
type: object
match:
properties:
clusterRoles:
items:
type: string
type: array
resources:
minProperties: 1
properties:
kinds:
items:
type: string
type: array
name:
type: string
selector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
roles:
items:
type: string
type: array
subjects:
items:
properties:
apiGroup:
type: string
kind:
type: string
name:
type: string
namespace:
type: string
required:
- kind
- name
type: object
type: array
required:
- resources
type: object
mutate:
properties:
overlay:
AnyValue: {}
patchStrategicMerge:
AnyValue: {}
patches:
items:
properties:
op:
enum:
- add
- replace
- remove
type: string
path:
type: string
value:
AnyValue: {}
required:
- path
- op
type: object
type: array
patchesJson6902:
type: string
type: object
name:
type: string
preconditions:
items:
required:
- key
- operator
- value
type: object
type: array
validate:
properties:
anyPattern:
AnyValue: {}
deny:
properties:
conditions:
items:
properties:
key:
type: string
operator:
enum:
- Equal
- Equals
- NotEqual
- NotEquals
- In
- NotIn
type: string
value:
anyOf:
- type: string
- items: {}
type: array
required:
- key
- operator
- value
type: object
type: array
message:
type: string
pattern:
AnyValue: {}
type: object
required:
- name
- match
type: object
type: array
validationFailureAction:
enum:
- enforce
- audit
type: string
required:
- rules
status: {}
versions:
- name: v1
served: true
storage: true
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata: metadata:
name: policyviolations.kyverno.io name: policyviolations.kyverno.io
spec: spec:

18
go.sum
View file

@ -497,6 +497,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@ -747,6 +748,8 @@ github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t
github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e h1:EZ4Yi82Z8uK7OgebBoAQvQaEYoQy0OV4KdYFXoXdDgU= github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e h1:EZ4Yi82Z8uK7OgebBoAQvQaEYoQy0OV4KdYFXoXdDgU=
github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= github.com/nirmata/client-go v0.17.5-0.20200625181911-7e81180b291e/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
github.com/nirmata/client-go v1.5.1 h1:yBfS5sTI18MbRYagdc0Fl8qIl/F9dlYt73WNbEUDfA4=
github.com/nirmata/client-go v11.0.0+incompatible h1:BMHYEFCsTSicG2qImOMox+ophXWdNNcq250eCO2ZDio=
github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@ -756,6 +759,7 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -763,6 +767,7 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
@ -796,6 +801,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
@ -803,19 +809,23 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
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-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 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_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-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-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.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
@ -905,6 +915,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 h1:hNna6Fi0eP1f2sMBe/rJicDmaHmoXGe1Ta84FPYHLuE= github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 h1:hNna6Fi0eP1f2sMBe/rJicDmaHmoXGe1Ta84FPYHLuE=
@ -936,6 +947,7 @@ github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
@ -1174,7 +1186,9 @@ golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200226224502-204d844ad48d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200226224502-204d844ad48d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@ -1217,6 +1231,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
@ -1239,6 +1254,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@ -1250,6 +1266,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -1268,6 +1285,7 @@ k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZ
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag=
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
k8s.io/apiserver v0.17.4 h1:bYc9LvDPEF9xAL3fhbDzqNOQOAnNF2ZYCrMW8v52/mE= k8s.io/apiserver v0.17.4 h1:bYc9LvDPEF9xAL3fhbDzqNOQOAnNF2ZYCrMW8v52/mE=
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=

View file

@ -40,6 +40,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&PolicyViolationList{}, &PolicyViolationList{},
&GenerateRequest{}, &GenerateRequest{},
&GenerateRequestList{}, &GenerateRequestList{},
&Policy{},
&PolicyList{},
) )
metav1.AddToGroupVersion(scheme, SchemeGroupVersion) metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil return nil

View file

@ -93,6 +93,14 @@ type ClusterPolicyList struct {
Items []ClusterPolicy `json:"items" yaml:"items"` Items []ClusterPolicy `json:"items" yaml:"items"`
} }
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// PolicyList ...
type PolicyList struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ListMeta `json:"metadata" yaml:"metadata"`
Items []Policy `json:"items" yaml:"items"`
}
// +genclient // +genclient
// +genclient:nonNamespaced // +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@ -124,6 +132,8 @@ type PolicyViolationList struct {
Items []PolicyViolation `json:"items" yaml:"items"` Items []PolicyViolation `json:"items" yaml:"items"`
} }
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Policy contains rules to be applied to created resources // Policy contains rules to be applied to created resources
type Policy struct { type Policy struct {
metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"` metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"`

View file

@ -174,6 +174,29 @@ func (in *Condition) DeepCopy() *Condition {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Deny) DeepCopyInto(out *Deny) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deny.
func (in *Deny) DeepCopy() *Deny {
if in == nil {
return nil
}
out := new(Deny)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExcludeResources) DeepCopyInto(out *ExcludeResources) { func (in *ExcludeResources) DeepCopyInto(out *ExcludeResources) {
*out = *in *out = *in
@ -367,6 +390,47 @@ func (in *Policy) DeepCopy() *Policy {
return out return out
} }
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Policy) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PolicyList) DeepCopyInto(out *PolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Policy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList.
func (in *PolicyList) DeepCopy() *PolicyList {
if in == nil {
return nil
}
out := new(PolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PolicyList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) { func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) {
*out = *in *out = *in

View file

@ -29,8 +29,7 @@ import (
var scheme = runtime.NewScheme() var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme) var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
// var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{ var localSchemeBuilder = runtime.SchemeBuilder{
kyvernov1.AddToScheme, kyvernov1.AddToScheme,
} }

View file

@ -40,6 +40,10 @@ func (c *FakeKyvernoV1) GenerateRequests(namespace string) v1.GenerateRequestInt
return &FakeGenerateRequests{c, namespace} return &FakeGenerateRequests{c, namespace}
} }
func (c *FakeKyvernoV1) Policies(namespace string) v1.PolicyInterface {
return &FakePolicies{c, namespace}
}
func (c *FakeKyvernoV1) PolicyViolations(namespace string) v1.PolicyViolationInterface { func (c *FakeKyvernoV1) PolicyViolations(namespace string) v1.PolicyViolationInterface {
return &FakePolicyViolations{c, namespace} return &FakePolicyViolations{c, namespace}
} }

View file

@ -0,0 +1,140 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
kyvernov1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakePolicies implements PolicyInterface
type FakePolicies struct {
Fake *FakeKyvernoV1
ns string
}
var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1", Resource: "policies"}
var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1", Kind: "Policy"}
// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any.
func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *kyvernov1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(policiesResource, c.ns, name), &kyvernov1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*kyvernov1.Policy), err
}
// List takes label and field selectors, and returns the list of Policies that match those selectors.
func (c *FakePolicies) List(opts v1.ListOptions) (result *kyvernov1.PolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(policiesResource, policiesKind, c.ns, opts), &kyvernov1.PolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &kyvernov1.PolicyList{ListMeta: obj.(*kyvernov1.PolicyList).ListMeta}
for _, item := range obj.(*kyvernov1.PolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested policies.
func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(policiesResource, c.ns, opts))
}
// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *FakePolicies) Create(policy *kyvernov1.Policy) (result *kyvernov1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(policiesResource, c.ns, policy), &kyvernov1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*kyvernov1.Policy), err
}
// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *FakePolicies) Update(policy *kyvernov1.Policy) (result *kyvernov1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(policiesResource, c.ns, policy), &kyvernov1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*kyvernov1.Policy), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakePolicies) UpdateStatus(policy *kyvernov1.Policy) (*kyvernov1.Policy, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(policiesResource, "status", c.ns, policy), &kyvernov1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*kyvernov1.Policy), err
}
// Delete takes name of the policy and deletes it. Returns an error if one occurs.
func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(policiesResource, c.ns, name), &kyvernov1.Policy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(policiesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &kyvernov1.PolicyList{})
return err
}
// Patch applies the patch and returns the patched policy.
func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *kyvernov1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(policiesResource, c.ns, name, pt, data, subresources...), &kyvernov1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*kyvernov1.Policy), err
}

View file

@ -24,4 +24,6 @@ type ClusterPolicyViolationExpansion interface{}
type GenerateRequestExpansion interface{} type GenerateRequestExpansion interface{}
type PolicyExpansion interface{}
type PolicyViolationExpansion interface{} type PolicyViolationExpansion interface{}

View file

@ -29,6 +29,7 @@ type KyvernoV1Interface interface {
ClusterPoliciesGetter ClusterPoliciesGetter
ClusterPolicyViolationsGetter ClusterPolicyViolationsGetter
GenerateRequestsGetter GenerateRequestsGetter
PoliciesGetter
PolicyViolationsGetter PolicyViolationsGetter
} }
@ -49,6 +50,10 @@ func (c *KyvernoV1Client) GenerateRequests(namespace string) GenerateRequestInte
return newGenerateRequests(c, namespace) return newGenerateRequests(c, namespace)
} }
func (c *KyvernoV1Client) Policies(namespace string) PolicyInterface {
return newPolicies(c, namespace)
}
func (c *KyvernoV1Client) PolicyViolations(namespace string) PolicyViolationInterface { func (c *KyvernoV1Client) PolicyViolations(namespace string) PolicyViolationInterface {
return newPolicyViolations(c, namespace) return newPolicyViolations(c, namespace)
} }

View file

@ -0,0 +1,191 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
"time"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// PoliciesGetter has a method to return a PolicyInterface.
// A group's client should implement this interface.
type PoliciesGetter interface {
Policies(namespace string) PolicyInterface
}
// PolicyInterface has methods to work with Policy resources.
type PolicyInterface interface {
Create(*v1.Policy) (*v1.Policy, error)
Update(*v1.Policy) (*v1.Policy, error)
UpdateStatus(*v1.Policy) (*v1.Policy, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.Policy, error)
List(opts metav1.ListOptions) (*v1.PolicyList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Policy, err error)
PolicyExpansion
}
// policies implements PolicyInterface
type policies struct {
client rest.Interface
ns string
}
// newPolicies returns a Policies
func newPolicies(c *KyvernoV1Client, namespace string) *policies {
return &policies{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any.
func (c *policies) Get(name string, options metav1.GetOptions) (result *v1.Policy, err error) {
result = &v1.Policy{}
err = c.client.Get().
Namespace(c.ns).
Resource("policies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Policies that match those selectors.
func (c *policies) List(opts metav1.ListOptions) (result *v1.PolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1.PolicyList{}
err = c.client.Get().
Namespace(c.ns).
Resource("policies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested policies.
func (c *policies) Watch(opts metav1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("policies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *policies) Create(policy *v1.Policy) (result *v1.Policy, err error) {
result = &v1.Policy{}
err = c.client.Post().
Namespace(c.ns).
Resource("policies").
Body(policy).
Do().
Into(result)
return
}
// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *policies) Update(policy *v1.Policy) (result *v1.Policy, err error) {
result = &v1.Policy{}
err = c.client.Put().
Namespace(c.ns).
Resource("policies").
Name(policy.Name).
Body(policy).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *policies) UpdateStatus(policy *v1.Policy) (result *v1.Policy, err error) {
result = &v1.Policy{}
err = c.client.Put().
Namespace(c.ns).
Resource("policies").
Name(policy.Name).
SubResource("status").
Body(policy).
Do().
Into(result)
return
}
// Delete takes name of the policy and deletes it. Returns an error if one occurs.
func (c *policies) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("policies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *policies) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("policies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched policy.
func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Policy, err error) {
result = &v1.Policy{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("policies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View file

@ -59,6 +59,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().ClusterPolicyViolations().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().ClusterPolicyViolations().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("generaterequests"): case v1.SchemeGroupVersion.WithResource("generaterequests"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().GenerateRequests().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().GenerateRequests().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("policies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().Policies().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("policyviolations"): case v1.SchemeGroupVersion.WithResource("policyviolations"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().PolicyViolations().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1().PolicyViolations().Informer()}, nil

View file

@ -30,6 +30,8 @@ type Interface interface {
ClusterPolicyViolations() ClusterPolicyViolationInformer ClusterPolicyViolations() ClusterPolicyViolationInformer
// GenerateRequests returns a GenerateRequestInformer. // GenerateRequests returns a GenerateRequestInformer.
GenerateRequests() GenerateRequestInformer GenerateRequests() GenerateRequestInformer
// Policies returns a PolicyInformer.
Policies() PolicyInformer
// PolicyViolations returns a PolicyViolationInformer. // PolicyViolations returns a PolicyViolationInformer.
PolicyViolations() PolicyViolationInformer PolicyViolations() PolicyViolationInformer
} }
@ -60,6 +62,11 @@ func (v *version) GenerateRequests() GenerateRequestInformer {
return &generateRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} return &generateRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
} }
// Policies returns a PolicyInformer.
func (v *version) Policies() PolicyInformer {
return &policyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// PolicyViolations returns a PolicyViolationInformer. // PolicyViolations returns a PolicyViolationInformer.
func (v *version) PolicyViolations() PolicyViolationInformer { func (v *version) PolicyViolations() PolicyViolationInformer {
return &policyViolationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} return &policyViolationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View file

@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
time "time"
kyvernov1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces"
v1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// PolicyInformer provides access to a shared informer and lister for
// Policies.
type PolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.PolicyLister
}
type policyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewPolicyInformer constructs a new informer for Policy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredPolicyInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredPolicyInformer constructs a new informer for Policy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.KyvernoV1().Policies(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.KyvernoV1().Policies(namespace).Watch(options)
},
},
&kyvernov1.Policy{},
resyncPeriod,
indexers,
)
}
func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *policyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&kyvernov1.Policy{}, f.defaultInformer)
}
func (f *policyInformer) Lister() v1.PolicyLister {
return v1.NewPolicyLister(f.Informer().GetIndexer())
}

View file

@ -190,3 +190,43 @@ func (s generateRequestNamespaceLister) GetGenerateRequestsForClusterPolicy(poli
} }
return list, err return list, err
} }
type PolicyListerExpansion interface {
GetPolicyForPolicyViolation(pv *kyvernov1.PolicyViolation) ([]*kyvernov1.Policy, error)
}
func (p *policyLister) GetPolicyForPolicyViolation(pv *kyvernov1.PolicyViolation) ([]*kyvernov1.Policy, error) {
if len(pv.Labels) == 0 {
return nil, fmt.Errorf("no Policy found for PolicyViolation %v because it has no labels", pv.Name)
}
pList, err := p.List(labels.Everything())
if err != nil {
return nil, err
}
var policies []*kyvernov1.Policy
for _, p := range pList {
policyLabelmap := map[string]string{"policy": p.Name}
ls := &metav1.LabelSelector{}
err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil)
if err != nil {
return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err)
}
selector, err := metav1.LabelSelectorAsSelector(ls)
if err != nil {
return nil, fmt.Errorf("invalid label selector: %v", err)
}
// If a policy with a nil or empty selector creeps in, it should match nothing, not everything.
if selector.Empty() || !selector.Matches(labels.Set(pv.Labels)) {
continue
}
if p.Namespace != pv.Namespace {
continue
}
policies = append(policies, p)
}
return policies, err
}

View file

@ -0,0 +1,93 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// PolicyLister helps list Policies.
type PolicyLister interface {
// List lists all Policies in the indexer.
List(selector labels.Selector) (ret []*v1.Policy, err error)
// Policies returns an object that can list and get Policies.
Policies(namespace string) PolicyNamespaceLister
PolicyListerExpansion
}
// policyLister implements the PolicyLister interface.
type policyLister struct {
indexer cache.Indexer
}
// NewPolicyLister returns a new PolicyLister.
func NewPolicyLister(indexer cache.Indexer) PolicyLister {
return &policyLister{indexer: indexer}
}
// List lists all Policies in the indexer.
func (s *policyLister) List(selector labels.Selector) (ret []*v1.Policy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Policy))
})
return ret, err
}
// Policies returns an object that can list and get Policies.
func (s *policyLister) Policies(namespace string) PolicyNamespaceLister {
return policyNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// PolicyNamespaceLister helps list and get Policies.
type PolicyNamespaceLister interface {
// List lists all Policies in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.Policy, err error)
// Get retrieves the Policy from the indexer for a given namespace and name.
Get(name string) (*v1.Policy, error)
}
// policyNamespaceLister implements the PolicyNamespaceLister
// interface.
type policyNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Policies in the indexer for a given namespace.
func (s policyNamespaceLister) List(selector labels.Selector) (ret []*v1.Policy, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Policy))
})
return ret, err
}
// Get retrieves the Policy from the indexer for a given namespace and name.
func (s policyNamespaceLister) Get(name string) (*v1.Policy, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("policy"), name)
}
return obj.(*v1.Policy), nil
}

View file

@ -237,6 +237,11 @@ func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
c.DiscoveryClient = discoveryClient c.DiscoveryClient = discoveryClient
} }
// GetDiscoveryCache gets the discovery client cache
func (c *Client) GetDiscoveryCache() discovery.CachedDiscoveryInterface {
return memory.NewMemCacheClient(c.kclient.Discovery())
}
//ServerPreferredResources stores the cachedClient instance for discovery client //ServerPreferredResources stores the cachedClient instance for discovery client
type ServerPreferredResources struct { type ServerPreferredResources struct {
cachedClient discovery.CachedDiscoveryInterface cachedClient discovery.CachedDiscoveryInterface

View file

@ -2,7 +2,9 @@ package policy
import ( import (
"fmt" "fmt"
"strings"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
@ -32,3 +34,30 @@ func transformResource(resource unstructured.Unstructured) []byte {
} }
return data return data
} }
// convertPoliciesToClusterPolicies - convert array of Policy to array of ClusterPolicy
func convertPoliciesToClusterPolicies(nsPolicies []*kyverno.Policy) []*kyverno.ClusterPolicy {
var cpols []*kyverno.ClusterPolicy
for _, pol := range nsPolicies {
cpol := kyverno.ClusterPolicy(*pol)
cpols = append(cpols, &cpol)
}
return cpols
}
// convertPolicyToClusterPolicy - convert Policy to ClusterPolicy
func convertPolicyToClusterPolicy(nsPolicies *kyverno.Policy) *kyverno.ClusterPolicy {
cpol := kyverno.ClusterPolicy(*nsPolicies)
return &cpol
}
func getIsNamespacedPolicy(key string) (string, string, bool) {
namespace := ""
index := strings.Index(key, "/")
if index != -1 {
namespace = key[:index]
key = key[index+1:]
return namespace, key, true
}
return namespace, key, false
}

View file

@ -55,6 +55,9 @@ type PolicyController struct {
// pLister can list/get policy from the shared informer's store // pLister can list/get policy from the shared informer's store
pLister kyvernolister.ClusterPolicyLister pLister kyvernolister.ClusterPolicyLister
// npLister can list/get namespace policy from the shared informer's store
npLister kyvernolister.PolicyLister
// pvLister can list/get policy violation from the shared informer's store // pvLister can list/get policy violation from the shared informer's store
cpvLister kyvernolister.ClusterPolicyViolationLister cpvLister kyvernolister.ClusterPolicyViolationLister
@ -67,6 +70,9 @@ type PolicyController struct {
// pListerSynced returns true if the Policy store has been synced at least once // pListerSynced returns true if the Policy store has been synced at least once
pListerSynced cache.InformerSynced pListerSynced cache.InformerSynced
// npListerSynced returns true if the Policy store has been synced at least once
npListerSynced cache.InformerSynced
// pvListerSynced returns true if the Policy store has been synced at least once // pvListerSynced returns true if the Policy store has been synced at least once
cpvListerSynced cache.InformerSynced cpvListerSynced cache.InformerSynced
@ -95,6 +101,7 @@ type PolicyController struct {
func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
client *client.Client, client *client.Client,
pInformer kyvernoinformer.ClusterPolicyInformer, pInformer kyvernoinformer.ClusterPolicyInformer,
npInformer kyvernoinformer.PolicyInformer,
cpvInformer kyvernoinformer.ClusterPolicyViolationInformer, cpvInformer kyvernoinformer.ClusterPolicyViolationInformer,
nspvInformer kyvernoinformer.PolicyViolationInformer, nspvInformer kyvernoinformer.PolicyViolationInformer,
configHandler config.Interface, eventGen event.Interface, configHandler config.Interface, eventGen event.Interface,
@ -131,6 +138,12 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
UpdateFunc: pc.updatePolicy, UpdateFunc: pc.updatePolicy,
DeleteFunc: pc.deletePolicy, DeleteFunc: pc.deletePolicy,
}) })
// Policy informer event handler
npInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: pc.addNsPolicy,
UpdateFunc: pc.updateNsPolicy,
DeleteFunc: pc.deleteNsPolicy,
})
cpvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ cpvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: pc.addClusterPolicyViolation, AddFunc: pc.addClusterPolicyViolation,
@ -145,11 +158,13 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
}) })
pc.pLister = pInformer.Lister() pc.pLister = pInformer.Lister()
pc.npLister = npInformer.Lister()
pc.cpvLister = cpvInformer.Lister() pc.cpvLister = cpvInformer.Lister()
pc.nspvLister = nspvInformer.Lister() pc.nspvLister = nspvInformer.Lister()
pc.nsLister = namespaces.Lister() pc.nsLister = namespaces.Lister()
pc.pListerSynced = pInformer.Informer().HasSynced pc.pListerSynced = pInformer.Informer().HasSynced
pc.npListerSynced = npInformer.Informer().HasSynced
pc.cpvListerSynced = cpvInformer.Informer().HasSynced pc.cpvListerSynced = cpvInformer.Informer().HasSynced
pc.nspvListerSynced = nspvInformer.Informer().HasSynced pc.nspvListerSynced = nspvInformer.Informer().HasSynced
pc.nsListerSynced = namespaces.Informer().HasSynced pc.nsListerSynced = namespaces.Informer().HasSynced
@ -225,6 +240,54 @@ func (pc *PolicyController) deletePolicy(obj interface{}) {
pc.enqueuePolicy(p) pc.enqueuePolicy(p)
} }
func (pc *PolicyController) addNsPolicy(obj interface{}) {
logger := pc.log
p := obj.(*kyverno.Policy)
pol := convertPolicyToClusterPolicy(p)
if !pc.canBackgroundProcess(pol) {
return
}
logger.V(4).Info("queuing policy for background processing", "namespace", pol.Namespace, "name", pol.Name)
pc.enqueuePolicy(pol)
}
func (pc *PolicyController) updateNsPolicy(old, cur interface{}) {
logger := pc.log
oldP := old.(*kyverno.Policy)
curP := cur.(*kyverno.Policy)
ncurP := convertPolicyToClusterPolicy(curP)
if !pc.canBackgroundProcess(ncurP) {
return
}
logger.V(4).Info("updating namespace policy", "namespace", oldP.Namespace, "name", oldP.Name)
pc.enqueuePolicy(ncurP)
}
func (pc *PolicyController) deleteNsPolicy(obj interface{}) {
logger := pc.log
p, ok := obj.(*kyverno.Policy)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
logger.Info("couldnt get object from tomstone", "obj", obj)
return
}
p, ok = tombstone.Obj.(*kyverno.Policy)
if !ok {
logger.Info("tombstone container object that is not a policy", "obj", obj)
return
}
}
pol := convertPolicyToClusterPolicy(p)
logger.V(4).Info("deleting namespace policy", "namespace", pol.Namespace, "name", pol.Name)
// we process policies that are not set of background processing as we need to perform policy violation
// cleanup when a policy is deleted.
pc.enqueuePolicy(pol)
}
func (pc *PolicyController) enqueuePolicy(policy *kyverno.ClusterPolicy) { func (pc *PolicyController) enqueuePolicy(policy *kyverno.ClusterPolicy) {
logger := pc.log logger := pc.log
key, err := cache.MetaNamespaceKeyFunc(policy) key, err := cache.MetaNamespaceKeyFunc(policy)
@ -307,7 +370,16 @@ func (pc *PolicyController) syncPolicy(key string) error {
logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String()) logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime).String())
}() }()
policy, err := pc.pLister.Get(key) namespace, key, isNamespacedPolicy := getIsNamespacedPolicy(key)
var policy *kyverno.ClusterPolicy
var err error
if !isNamespacedPolicy {
policy, err = pc.pLister.Get(key)
} else {
var nspolicy *kyverno.Policy
nspolicy, err = pc.npLister.Policies(namespace).Get(key)
policy = convertPolicyToClusterPolicy(nspolicy)
}
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
go pc.deletePolicyViolations(key) go pc.deletePolicyViolations(key)
@ -327,7 +399,6 @@ func (pc *PolicyController) syncPolicy(key string) error {
engineResponses := pc.processExistingResources(policy) engineResponses := pc.processExistingResources(policy)
pc.cleanupAndReport(engineResponses) pc.cleanupAndReport(engineResponses)
return nil return nil
} }

View file

@ -18,6 +18,7 @@ func (pc *PolicyController) addNamespacedPolicyViolation(obj interface{}) {
} }
ps := pc.getPolicyForNamespacedPolicyViolation(pv) ps := pc.getPolicyForNamespacedPolicyViolation(pv)
if len(ps) == 0 { if len(ps) == 0 {
// there is no cluster policy for this violation, so we can delete this cluster policy violation // there is no cluster policy for this violation, so we can delete this cluster policy violation
logger.V(4).Info("namespaced policy violation does not belong to an active policy, will be cleaned up") logger.V(4).Info("namespaced policy violation does not belong to an active policy, will be cleaned up")
@ -46,6 +47,7 @@ func (pc *PolicyController) updateNamespacedPolicyViolation(old, cur interface{}
logger := pc.log.WithValues("kind", curPV.Kind, "namespace", curPV.Namespace, "name", curPV.Name) logger := pc.log.WithValues("kind", curPV.Kind, "namespace", curPV.Namespace, "name", curPV.Name)
ps := pc.getPolicyForNamespacedPolicyViolation(curPV) ps := pc.getPolicyForNamespacedPolicyViolation(curPV)
if len(ps) == 0 { if len(ps) == 0 {
// there is no namespaced policy for this violation, so we can delete this cluster policy violation // there is no namespaced policy for this violation, so we can delete this cluster policy violation
logger.V(4).Info("nameapced policy violation does not belong to an active policy, will be cleanedup") logger.V(4).Info("nameapced policy violation does not belong to an active policy, will be cleanedup")
@ -107,7 +109,17 @@ func (pc *PolicyController) deleteNamespacedPolicyViolation(obj interface{}) {
func (pc *PolicyController) getPolicyForNamespacedPolicyViolation(pv *kyverno.PolicyViolation) []*kyverno.ClusterPolicy { func (pc *PolicyController) getPolicyForNamespacedPolicyViolation(pv *kyverno.PolicyViolation) []*kyverno.ClusterPolicy {
logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name)
// Check for NamespacePolicies
nspol, err := pc.npLister.GetPolicyForPolicyViolation(pv)
if err != nil {
logger.V(4).Info("missing namespace policy for namespaced policy violation", "reason", err.Error())
return nil
}
if len(nspol) > 0 {
return convertPoliciesToClusterPolicies(nspol)
}
policies, err := pc.pLister.GetPolicyForNamespacedPolicyViolation(pv) policies, err := pc.pLister.GetPolicyForNamespacedPolicyViolation(pv)
if err != nil || len(policies) == 0 { if err != nil || len(policies) == 0 {
logger.V(4).Info("missing policy for namespaced policy violation", "reason", err.Error()) logger.V(4).Info("missing policy for namespaced policy violation", "reason", err.Error())
return nil return nil

View file

@ -47,6 +47,32 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
// as there are more than 1 operation in rule, not need to evaluate it further // as there are more than 1 operation in rule, not need to evaluate it further
return fmt.Errorf("path: spec.rules[%d]: %v", i, err) return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
} }
// validate Cluster Resources in namespaced cluster policy
// For namespaced cluster policy, ClusterResource type field and values are not allowed in match and exclude
if p.ObjectMeta.Namespace != "" {
// check unique kind
isUnique := func(kind string, resources []string) bool {
for _, k := range resources {
if kind == k {
return false
}
}
return true
}
clusterResources := make([]string, 0)
// Get all the cluster type kind supported by cluster
res, _ := client.GetDiscoveryCache().ServerPreferredResources()
for _, resList := range res {
for _, r := range resList.APIResources {
if r.Namespaced == false {
if isUnique(r.Kind, clusterResources) {
clusterResources = append(clusterResources, r.Kind)
}
}
}
}
return checkClusterResourceInMatchAndExclude(rule, clusterResources)
}
if doesMatchAndExcludeConflict(rule) { if doesMatchAndExcludeConflict(rule) {
return fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name) return fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name)
@ -401,3 +427,34 @@ func validateResourceDescription(rd kyverno.ResourceDescription) error {
} }
return nil return nil
} }
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
// Match and Exclude block
func checkClusterResourceInMatchAndExclude(rule kyverno.Rule, clusterResources []string) error {
// Contains Namespaces in Match->ResourceDescription
if len(rule.MatchResources.ResourceDescription.Namespaces) > 0 {
return fmt.Errorf("namespaced cluster policy : field namespaces not allowed in match.resources")
}
// Contains Namespaces in Exclude->ResourceDescription
if len(rule.ExcludeResources.ResourceDescription.Namespaces) > 0 {
return fmt.Errorf("namespaced cluster policy : field namespaces not allowed in exclude.resources")
}
// Contains "Cluster Wide Resources" in Match->ResourceDescription->Kinds
for _, kind := range rule.MatchResources.ResourceDescription.Kinds {
for _, k := range clusterResources {
if kind == k {
return fmt.Errorf("namespaced policy : cluster type value '%s' not allowed in match.resources.kinds", kind)
}
}
}
// Contains "Cluster Wide Resources" in Exclude->ResourceDescription->Kinds
for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds {
for _, k := range clusterResources {
if kind == k {
return fmt.Errorf("namespaced policy : cluster type value '%s' not allowed in exclude.resources.kinds", kind)
}
}
}
return nil
}

View file

@ -1,7 +1,5 @@
package policycache package policycache
// package main
import ( import (
"sync" "sync"
@ -11,9 +9,17 @@ import (
type pMap struct { type pMap struct {
sync.RWMutex sync.RWMutex
// dataMap field stores ClusterPolicies
dataMap map[PolicyType][]*kyverno.ClusterPolicy dataMap map[PolicyType][]*kyverno.ClusterPolicy
// nsDataMap field stores Namespaced Policies for each namespaces.
// The Policy is converted internally to ClusterPolicy and stored as a ClusterPolicy
// Since both the policy use same type (i.e. Policy), Both policies can be differentiated based on
// "Kind" or "namespace". When the Policy is converted it will retain the value of kind as "Policy".
// Cluster policy will be having namespace as Blank (""), but Policy will always be having namespace field and "default" value by default
nsDataMap map[string]map[PolicyType][]*kyverno.ClusterPolicy
// nameCacheMap stores the names of all existing policies in dataMap // nameCacheMap stores the names of all existing policies in dataMap
// Policy names are stored as <namespace>/<name>
nameCacheMap map[PolicyType]map[string]bool nameCacheMap map[PolicyType]map[string]bool
} }
@ -27,7 +33,7 @@ type policyCache struct {
type Interface interface { type Interface interface {
Add(policy *kyverno.ClusterPolicy) Add(policy *kyverno.ClusterPolicy)
Remove(policy *kyverno.ClusterPolicy) Remove(policy *kyverno.ClusterPolicy)
Get(pkey PolicyType) []*kyverno.ClusterPolicy Get(pkey PolicyType, nspace *string) []*kyverno.ClusterPolicy
} }
// newPolicyCache ... // newPolicyCache ...
@ -42,6 +48,7 @@ func newPolicyCache(log logr.Logger) Interface {
return &policyCache{ return &policyCache{
pMap{ pMap{
dataMap: make(map[PolicyType][]*kyverno.ClusterPolicy), dataMap: make(map[PolicyType][]*kyverno.ClusterPolicy),
nsDataMap: make(map[string]map[PolicyType][]*kyverno.ClusterPolicy),
nameCacheMap: namesCache, nameCacheMap: namesCache,
}, },
log, log,
@ -51,12 +58,13 @@ func newPolicyCache(log logr.Logger) Interface {
// Add a policy to cache // Add a policy to cache
func (pc *policyCache) Add(policy *kyverno.ClusterPolicy) { func (pc *policyCache) Add(policy *kyverno.ClusterPolicy) {
pc.pMap.add(policy) pc.pMap.add(policy)
pc.Logger.V(4).Info("policy is added to cache", "name", policy.GetName()) pc.Logger.V(4).Info("policy is added to cache", "name", policy.GetName())
} }
// Get the list of matched policies // Get the list of matched policies
func (pc *policyCache) Get(pkey PolicyType) []*kyverno.ClusterPolicy { func (pc *policyCache) Get(pkey PolicyType, nspace *string) []*kyverno.ClusterPolicy {
return pc.pMap.get(pkey) return pc.pMap.get(pkey, nspace)
} }
// Remove a policy from cache // Remove a policy from cache
@ -74,13 +82,28 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
validateEnforceMap := m.nameCacheMap[ValidateEnforce] validateEnforceMap := m.nameCacheMap[ValidateEnforce]
validateAuditMap := m.nameCacheMap[ValidateAudit] validateAuditMap := m.nameCacheMap[ValidateAudit]
generateMap := m.nameCacheMap[Generate] generateMap := m.nameCacheMap[Generate]
var pName = policy.GetName()
pSpace := policy.GetNamespace()
isNamespacedPolicy := false
if pSpace != "" {
pName = pSpace + "/" + pName
isNamespacedPolicy = true
// Initialize Namespace Cache Map
_, ok := m.nsDataMap[policy.GetNamespace()]
if !ok {
m.nsDataMap[policy.GetNamespace()] = make(map[PolicyType][]*kyverno.ClusterPolicy)
}
}
pName := policy.GetName()
for _, rule := range policy.Spec.Rules { for _, rule := range policy.Spec.Rules {
if rule.HasMutate() { if rule.HasMutate() {
if !mutateMap[pName] { if !mutateMap[pName] {
mutateMap[pName] = true mutateMap[pName] = true
if isNamespacedPolicy {
mutatePolicy := m.nsDataMap[policy.GetNamespace()][Mutate]
m.nsDataMap[policy.GetNamespace()][Mutate] = append(mutatePolicy, policy)
continue
}
mutatePolicy := m.dataMap[Mutate] mutatePolicy := m.dataMap[Mutate]
m.dataMap[Mutate] = append(mutatePolicy, policy) m.dataMap[Mutate] = append(mutatePolicy, policy)
} }
@ -91,7 +114,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
if enforcePolicy { if enforcePolicy {
if !validateEnforceMap[pName] { if !validateEnforceMap[pName] {
validateEnforceMap[pName] = true validateEnforceMap[pName] = true
if isNamespacedPolicy {
validatePolicy := m.nsDataMap[policy.GetNamespace()][ValidateEnforce]
m.nsDataMap[policy.GetNamespace()][ValidateEnforce] = append(validatePolicy, policy)
continue
}
validatePolicy := m.dataMap[ValidateEnforce] validatePolicy := m.dataMap[ValidateEnforce]
m.dataMap[ValidateEnforce] = append(validatePolicy, policy) m.dataMap[ValidateEnforce] = append(validatePolicy, policy)
} }
@ -101,7 +128,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
// ValidateAudit // ValidateAudit
if !validateAuditMap[pName] { if !validateAuditMap[pName] {
validateAuditMap[pName] = true validateAuditMap[pName] = true
if isNamespacedPolicy {
validatePolicy := m.nsDataMap[policy.GetNamespace()][ValidateAudit]
m.nsDataMap[policy.GetNamespace()][ValidateAudit] = append(validatePolicy, policy)
continue
}
validatePolicy := m.dataMap[ValidateAudit] validatePolicy := m.dataMap[ValidateAudit]
m.dataMap[ValidateAudit] = append(validatePolicy, policy) m.dataMap[ValidateAudit] = append(validatePolicy, policy)
} }
@ -111,7 +142,11 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
if rule.HasGenerate() { if rule.HasGenerate() {
if !generateMap[pName] { if !generateMap[pName] {
generateMap[pName] = true generateMap[pName] = true
if isNamespacedPolicy {
generatePolicy := m.nsDataMap[policy.GetNamespace()][Generate]
m.nsDataMap[policy.GetNamespace()][Generate] = append(generatePolicy, policy)
continue
}
generatePolicy := m.dataMap[Generate] generatePolicy := m.dataMap[Generate]
m.dataMap[Generate] = append(generatePolicy, policy) m.dataMap[Generate] = append(generatePolicy, policy)
} }
@ -125,30 +160,55 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
m.nameCacheMap[Generate] = generateMap m.nameCacheMap[Generate] = generateMap
} }
func (m *pMap) get(key PolicyType) []*kyverno.ClusterPolicy { func (m *pMap) get(key PolicyType, nspace *string) []*kyverno.ClusterPolicy {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
if nspace == nil || *nspace == "" {
return m.dataMap[key]
}
return m.nsDataMap[*nspace][key]
return m.dataMap[key]
} }
func (m *pMap) remove(policy *kyverno.ClusterPolicy) { func (m *pMap) remove(policy *kyverno.ClusterPolicy) {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
pName := policy.GetName() var pName = policy.GetName()
dataMap := m.dataMap pSpace := policy.GetNamespace()
for k, policies := range dataMap { isNamespacedPolicy := false
if pSpace != "" {
pName = pSpace + "/" + pName
isNamespacedPolicy = true
}
if !isNamespacedPolicy {
dataMap := m.dataMap
for k, policies := range dataMap {
var newPolicies []*kyverno.ClusterPolicy var newPolicies []*kyverno.ClusterPolicy
for _, p := range policies { for _, p := range policies {
if p.GetName() == pName { if p.GetName() == pName {
continue continue
}
newPolicies = append(newPolicies, p)
} }
newPolicies = append(newPolicies, p)
}
m.dataMap[k] = newPolicies m.dataMap[k] = newPolicies
}
} else {
dataMap := m.nsDataMap[pSpace]
for k, policies := range dataMap {
var newPolicies []*kyverno.ClusterPolicy
for _, p := range policies {
if (p.GetNamespace() + "/" + p.GetName()) == pName {
continue
}
newPolicies = append(newPolicies, p)
}
m.nsDataMap[pSpace][k] = newPolicies
}
} }
for _, nameCache := range m.nameCacheMap { for _, nameCache := range m.nameCacheMap {

View file

@ -17,21 +17,21 @@ func Test_All(t *testing.T) {
pCache.Add(policy) pCache.Add(policy)
// get // get
if len(pCache.Get(Mutate)) != 1 { if len(pCache.Get(Mutate, nil)) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate))) t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, nil)))
} }
if len(pCache.Get(ValidateEnforce)) != 1 { if len(pCache.Get(ValidateEnforce, nil)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
if len(pCache.Get(Generate)) != 1 { if len(pCache.Get(Generate, nil)) != 1 {
t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate))) t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, nil)))
} }
// remove // remove
pCache.Remove(policy) pCache.Remove(policy)
assert.Assert(t, len(pCache.Get(ValidateEnforce)) == 0) assert.Assert(t, len(pCache.Get(ValidateEnforce, nil)) == 0)
} }
func Test_Add_Duplicate_Policy(t *testing.T) { func Test_Add_Duplicate_Policy(t *testing.T) {
@ -42,16 +42,16 @@ func Test_Add_Duplicate_Policy(t *testing.T) {
pCache.Add(policy) pCache.Add(policy)
pCache.Add(policy) pCache.Add(policy)
if len(pCache.Get(Mutate)) != 1 { if len(pCache.Get(Mutate, nil)) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate))) t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, nil)))
} }
if len(pCache.Get(ValidateEnforce)) != 1 { if len(pCache.Get(ValidateEnforce, nil)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
if len(pCache.Get(Generate)) != 1 { if len(pCache.Get(Generate, nil)) != 1 {
t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate))) t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, nil)))
} }
} }
@ -66,12 +66,12 @@ func Test_Add_Validate_Audit(t *testing.T) {
pCache.Add(policy) pCache.Add(policy)
pCache.Add(policy) pCache.Add(policy)
if len(pCache.Get(ValidateEnforce)) != 1 { if len(pCache.Get(ValidateEnforce, nil)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
if len(pCache.Get(ValidateAudit)) != 1 { if len(pCache.Get(ValidateAudit, nil)) != 1 {
t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit))) t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit, nil)))
} }
} }
@ -80,18 +80,18 @@ func Test_Add_Remove(t *testing.T) {
policy := newPolicy(t) policy := newPolicy(t)
pCache.Add(policy) pCache.Add(policy)
if len(pCache.Get(ValidateEnforce)) != 1 { if len(pCache.Get(ValidateEnforce, nil)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
pCache.Remove(policy) pCache.Remove(policy)
if len(pCache.Get(ValidateEnforce)) != 0 { if len(pCache.Get(ValidateEnforce, nil)) != 0 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
pCache.Add(policy) pCache.Add(policy)
if len(pCache.Get(ValidateEnforce)) != 1 { if len(pCache.Get(ValidateEnforce, nil)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce))) t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, nil)))
} }
} }
@ -205,3 +205,196 @@ func newPolicy(t *testing.T) *kyverno.ClusterPolicy {
return policy return policy
} }
func newNsPolicy(t *testing.T) *kyverno.ClusterPolicy {
rawPolicy := []byte(`{
"metadata": {
"name": "test-policy",
"namespace": "test"
},
"spec": {
"validationFailureAction": "enforce",
"rules": [
{
"name": "deny-privileged-disallowpriviligedescalation",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"deny": {
"conditions": [
{
"key": "a",
"operator": "Equals",
"value": "a"
}
]
}
}
},
{
"name": "deny-privileged-disallowpriviligedescalation",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {
"pattern": {
"spec": {
"containers": [
{
"image": "!*:latest"
}
]
}
}
}
},
{
"name": "annotate-host-path",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"overlay": {
"metadata": {
"annotations": {
"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
}
}
}
}
},
{
"name": "default-deny-ingress",
"match": {
"resources": {
"kinds": [
"Namespace"
]
}
},
"generate": {
"kind": "NetworkPolicy",
"name": "default-deny-ingress",
"namespace": "{{request.object.metadata.name}}",
"data": {
"spec": {
"podSelector": {
},
"policyTypes": [
"Ingress"
]
}
}
}
}
]
}
}`)
var policy *kyverno.Policy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
return convertPolicyToClusterPolicy(policy)
}
func Test_Ns_All(t *testing.T) {
pCache := newPolicyCache(log.Log)
policy := newNsPolicy(t)
// add
pCache.Add(policy)
nspace := policy.GetNamespace()
// get
if len(pCache.Get(Mutate, &nspace)) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, &nspace)))
}
if len(pCache.Get(ValidateEnforce, &nspace)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
if len(pCache.Get(Generate, &nspace)) != 1 {
t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, &nspace)))
}
// remove
pCache.Remove(policy)
assert.Assert(t, len(pCache.Get(ValidateEnforce, &nspace)) == 0)
}
func Test_Ns_Add_Duplicate_Policy(t *testing.T) {
pCache := newPolicyCache(log.Log)
policy := newNsPolicy(t)
pCache.Add(policy)
pCache.Add(policy)
pCache.Add(policy)
nspace := policy.GetNamespace()
if len(pCache.Get(Mutate, &nspace)) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(pCache.Get(Mutate, &nspace)))
}
if len(pCache.Get(ValidateEnforce, &nspace)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
if len(pCache.Get(Generate, &nspace)) != 1 {
t.Errorf("expected 1 generate policy, found %v", len(pCache.Get(Generate, &nspace)))
}
}
func Test_Ns_Add_Validate_Audit(t *testing.T) {
pCache := newPolicyCache(log.Log)
policy := newNsPolicy(t)
nspace := policy.GetNamespace()
pCache.Add(policy)
pCache.Add(policy)
policy.Spec.ValidationFailureAction = "audit"
pCache.Add(policy)
pCache.Add(policy)
if len(pCache.Get(ValidateEnforce, &nspace)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
if len(pCache.Get(ValidateAudit, &nspace)) != 1 {
t.Errorf("expected 1 validate audit policy, found %v", len(pCache.Get(ValidateAudit, &nspace)))
}
}
func Test_Ns_Add_Remove(t *testing.T) {
pCache := newPolicyCache(log.Log)
policy := newNsPolicy(t)
pCache.Add(policy)
nspace := policy.GetNamespace()
if len(pCache.Get(ValidateEnforce, &nspace)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
pCache.Remove(policy)
if len(pCache.Get(ValidateEnforce, &nspace)) != 0 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
pCache.Add(policy)
if len(pCache.Get(ValidateEnforce, &nspace)) != 1 {
t.Errorf("expected 1 validate enforce policy, found %v", len(pCache.Get(ValidateEnforce, &nspace)))
}
}

View file

@ -15,14 +15,16 @@ import (
// This cache is only used in the admission webhook to fast retrieve // This cache is only used in the admission webhook to fast retrieve
// policies based on types (Mutate/ValidateEnforce/Generate). // policies based on types (Mutate/ValidateEnforce/Generate).
type Controller struct { type Controller struct {
pSynched cache.InformerSynced pSynched cache.InformerSynced
Cache Interface nspSynched cache.InformerSynced
log logr.Logger Cache Interface
log logr.Logger
} }
// NewPolicyCacheController create a new PolicyController // NewPolicyCacheController create a new PolicyController
func NewPolicyCacheController( func NewPolicyCacheController(
pInformer kyvernoinformer.ClusterPolicyInformer, pInformer kyvernoinformer.ClusterPolicyInformer,
nspInformer kyvernoinformer.PolicyInformer,
log logr.Logger) *Controller { log logr.Logger) *Controller {
pc := Controller{ pc := Controller{
@ -30,17 +32,33 @@ func NewPolicyCacheController(
log: log, log: log,
} }
// ClusterPolicy Informer
pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: pc.addPolicy, AddFunc: pc.addPolicy,
UpdateFunc: pc.updatePolicy, UpdateFunc: pc.updatePolicy,
DeleteFunc: pc.deletePolicy, DeleteFunc: pc.deletePolicy,
}) })
// Policy Informer
nspInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: pc.addNsPolicy,
UpdateFunc: pc.updateNsPolicy,
DeleteFunc: pc.deleteNsPolicy,
})
pc.pSynched = pInformer.Informer().HasSynced pc.pSynched = pInformer.Informer().HasSynced
pc.nspSynched = nspInformer.Informer().HasSynced
return &pc return &pc
} }
// convertPolicyToClusterPolicy - convert Policy to ClusterPolicy
// This will retain the kind of Policy and convert type to ClusterPolicy
func convertPolicyToClusterPolicy(nsPolicies *kyverno.Policy) *kyverno.ClusterPolicy {
cpol := kyverno.ClusterPolicy(*nsPolicies)
return &cpol
}
func (c *Controller) addPolicy(obj interface{}) { func (c *Controller) addPolicy(obj interface{}) {
p := obj.(*kyverno.ClusterPolicy) p := obj.(*kyverno.ClusterPolicy)
c.Cache.Add(p) c.Cache.Add(p)
@ -53,7 +71,6 @@ func (c *Controller) updatePolicy(old, cur interface{}) {
if reflect.DeepEqual(pOld.Spec, pNew.Spec) { if reflect.DeepEqual(pOld.Spec, pNew.Spec) {
return return
} }
c.Cache.Remove(pOld) c.Cache.Remove(pOld)
c.Cache.Add(pNew) c.Cache.Add(pNew)
} }
@ -63,6 +80,29 @@ func (c *Controller) deletePolicy(obj interface{}) {
c.Cache.Remove(p) c.Cache.Remove(p)
} }
// addNsPolicy - Add Policy to cache
func (c *Controller) addNsPolicy(obj interface{}) {
p := obj.(*kyverno.Policy)
c.Cache.Add(convertPolicyToClusterPolicy(p))
}
// updateNsPolicy - Update Policy of cache
func (c *Controller) updateNsPolicy(old, cur interface{}) {
npOld := old.(*kyverno.Policy)
npNew := cur.(*kyverno.Policy)
if reflect.DeepEqual(npOld.Spec, npNew.Spec) {
return
}
c.Cache.Remove(convertPolicyToClusterPolicy(npOld))
c.Cache.Add(convertPolicyToClusterPolicy(npNew))
}
// deleteNsPolicy - Delete Policy from cache
func (c *Controller) deleteNsPolicy(obj interface{}) {
p := obj.(*kyverno.Policy)
c.Cache.Remove(convertPolicyToClusterPolicy(p))
}
// Run waits until policy informer to be synced // Run waits until policy informer to be synced
func (c *Controller) Run(workers int, stopCh <-chan struct{}) { func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
logger := c.log logger := c.log

View file

@ -3,11 +3,11 @@ package policystatus
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
"strings"
"sync" "sync"
"time" "time"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned" "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
@ -52,6 +52,7 @@ type Sync struct {
Listener Listener Listener Listener
client *versioned.Clientset client *versioned.Clientset
lister kyvernolister.ClusterPolicyLister lister kyvernolister.ClusterPolicyLister
nsLister kyvernolister.PolicyLister
} }
type cache struct { type cache struct {
@ -60,7 +61,7 @@ type cache struct {
keyToMutex *keyToMutex keyToMutex *keyToMutex
} }
func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister) *Sync { func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister, nsLister kyvernolister.PolicyLister) *Sync {
return &Sync{ return &Sync{
cache: &cache{ cache: &cache{
dataMu: sync.RWMutex{}, dataMu: sync.RWMutex{},
@ -69,6 +70,7 @@ func NewSync(c *versioned.Clientset, lister kyvernolister.ClusterPolicyLister) *
}, },
client: c, client: c,
lister: lister, lister: lister,
nsLister: nsLister,
Listener: make(chan statusUpdater, 20), Listener: make(chan statusUpdater, 20),
} }
} }
@ -99,7 +101,6 @@ func (s *Sync) updateStatusCache(stopCh <-chan struct{}) {
status = policy.Status status = policy.Status
} }
} }
updatedStatus := statusUpdater.UpdateStatus(status) updatedStatus := statusUpdater.UpdateStatus(status)
s.cache.dataMu.Lock() s.cache.dataMu.Lock()
@ -127,18 +128,49 @@ func (s *Sync) updatePolicyStatus() {
s.cache.dataMu.Unlock() s.cache.dataMu.Unlock()
for policyName, status := range nameToStatus { for policyName, status := range nameToStatus {
policy, err := s.lister.Get(policyName) // Identify Policy and ClusterPolicy based on namespace in key
if err != nil { // key = <namespace>/<name> for namespacepolicy and key = <name> for clusterpolicy
continue // and update the respective policies
namespace := ""
isNamespacedPolicy := false
key := policyName
index := strings.Index(policyName, "/")
if index != -1 {
namespace = policyName[:index]
isNamespacedPolicy = true
policyName = policyName[index+1:]
}
if !isNamespacedPolicy {
policy, err := s.lister.Get(policyName)
if err != nil {
continue
}
policy.Status = status
_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy)
if err != nil {
s.cache.dataMu.Lock()
delete(s.cache.data, policyName)
s.cache.dataMu.Unlock()
log.Log.Error(err, "failed to update policy status")
}
} else {
policy, err := s.nsLister.Policies(namespace).Get(policyName)
if err != nil {
s.cache.dataMu.Lock()
delete(s.cache.data, key)
s.cache.dataMu.Unlock()
continue
}
policy.Status = status
_, err = s.client.KyvernoV1().Policies(namespace).UpdateStatus(policy)
if err != nil {
s.cache.dataMu.Lock()
delete(s.cache.data, key)
s.cache.dataMu.Unlock()
log.Log.Error(err, "failed to update namespace policy status")
}
} }
policy.Status = status
_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy)
if err != nil {
s.cache.dataMu.Lock()
delete(s.cache.data, policyName)
s.cache.dataMu.Unlock()
log.Log.Error(err, "failed to update policy status")
}
} }
} }

View file

@ -8,6 +8,7 @@ import (
"time" "time"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
lv1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
) )
type dummyStore struct { type dummyStore struct {
@ -52,11 +53,32 @@ func (dl dummyLister) ListResources(selector labels.Selector) (ret []*v1.Cluster
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }
// type dymmyNsNamespace struct {}
type dummyNsLister struct {
}
func (dl dummyNsLister) Policies(name string) lv1.PolicyNamespaceLister {
return dummyNsLister{}
}
func (dl dummyNsLister) List(selector labels.Selector) (ret []*v1.Policy, err error) {
return nil, fmt.Errorf("not implemented")
}
func (dl dummyNsLister) Get(name string) (*v1.Policy, error) {
return nil, fmt.Errorf("not implemented")
}
func (dl dummyNsLister) GetPolicyForPolicyViolation(pv *v1.PolicyViolation) ([]*v1.Policy, error) {
return nil, fmt.Errorf("not implemented")
}
func TestKeyToMutex(t *testing.T) { func TestKeyToMutex(t *testing.T) {
expectedCache := `{"policy1":{"rulesAppliedCount":100}}` expectedCache := `{"policy1":{"rulesAppliedCount":100}}`
stopCh := make(chan struct{}) stopCh := make(chan struct{})
s := NewSync(nil, dummyLister{}) s := NewSync(nil, dummyLister{}, dummyNsLister{})
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
go s.updateStatusCache(stopCh) go s.updateStatusCache(stopCh)
} }

View file

@ -25,7 +25,7 @@ func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caDat
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"deployments/*", []string{"deployments/*"},
"apps", "apps",
"v1", "v1",
[]admregapi.OperationType{admregapi.Update}, []admregapi.OperationType{admregapi.Update},
@ -49,7 +49,7 @@ func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"deployments/*", []string{"deployments/*"},
"apps", "apps",
"v1", "v1",
[]admregapi.OperationType{admregapi.Update}, []admregapi.OperationType{admregapi.Update},

View file

@ -63,7 +63,7 @@ func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
} }
// debug mutating webhook // debug mutating webhook
func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore failurePolicy := admregapi.Ignore
reinvocationPolicy := admregapi.NeverReinvocationPolicy reinvocationPolicy := admregapi.NeverReinvocationPolicy
@ -86,9 +86,7 @@ func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool
APIVersions: []string{ APIVersions: []string{
apiVersions, apiVersions,
}, },
Resources: []string{ Resources: resources,
resource,
},
}, },
}, },
}, },
@ -98,7 +96,7 @@ func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool
} }
} }
func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore failurePolicy := admregapi.Ignore
return admregapi.ValidatingWebhook{ return admregapi.ValidatingWebhook{
@ -118,9 +116,7 @@ func generateDebugValidatingWebhook(name, url string, caData []byte, validate bo
APIVersions: []string{ APIVersions: []string{
apiVersions, apiVersions,
}, },
Resources: []string{ Resources: resources,
resource,
},
}, },
}, },
}, },
@ -167,7 +163,7 @@ func generateDebugValidatingWebhook(name, url string, caData []byte, validate bo
// } // }
// mutating webhook // mutating webhook
func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook { func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore failurePolicy := admregapi.Ignore
reinvocationPolicy := admregapi.NeverReinvocationPolicy reinvocationPolicy := admregapi.NeverReinvocationPolicy
@ -194,9 +190,7 @@ func generateMutatingWebhook(name, servicePath string, caData []byte, validation
APIVersions: []string{ APIVersions: []string{
apiVersions, apiVersions,
}, },
Resources: []string{ Resources: resources,
resource,
},
}, },
}, },
}, },
@ -207,7 +201,7 @@ func generateMutatingWebhook(name, servicePath string, caData []byte, validation
} }
// validating webhook // validating webhook
func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook { func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resources []string, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore failurePolicy := admregapi.Ignore
return admregapi.ValidatingWebhook{ return admregapi.ValidatingWebhook{
@ -231,9 +225,7 @@ func generateValidatingWebhook(name, servicePath string, caData []byte, validati
APIVersions: []string{ APIVersions: []string{
apiVersions, apiVersions,
}, },
Resources: []string{ Resources: resources,
resource,
},
}, },
}, },
}, },

View file

@ -24,7 +24,7 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"clusterpolicies/*", []string{"clusterpolicies/*", "policies/*"},
"kyverno.io", "kyverno.io",
"v1", "v1",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},
@ -49,7 +49,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"clusterpolicies/*", []string{"clusterpolicies/*", "policies/*"},
"kyverno.io", "kyverno.io",
"v1", "v1",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},
@ -73,7 +73,7 @@ func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"clusterpolicies/*", []string{"clusterpolicies/*", "policies/*"},
"kyverno.io", "kyverno.io",
"v1", "v1",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},
@ -97,7 +97,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"clusterpolicies/*", []string{"clusterpolicies/*", "policies/*"},
"kyverno.io", "kyverno.io",
"v1", "v1",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},

View file

@ -24,7 +24,7 @@ func (wrc *WebhookRegistrationClient) constructDebugMutatingWebhookConfig(caData
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"*/*", []string{"*/*"},
"*", "*",
"*", "*",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},
@ -48,7 +48,7 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []by
caData, caData,
false, false,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"*/*", []string{"*/*"},
"*", "*",
"*", "*",
[]admregapi.OperationType{admregapi.Create, admregapi.Update}, []admregapi.OperationType{admregapi.Create, admregapi.Update},
@ -98,7 +98,7 @@ func (wrc *WebhookRegistrationClient) constructDebugValidatingWebhookConfig(caDa
caData, caData,
true, true,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"*/*", []string{"*/*"},
"*", "*",
"*", "*",
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete}, []admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete},
@ -122,7 +122,7 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(caData []
caData, caData,
false, false,
wrc.timeoutSeconds, wrc.timeoutSeconds,
"*/*", []string{"*/*"},
"*", "*",
"*", "*",
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete}, []admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete},

View file

@ -167,7 +167,7 @@ func convertResource(raw []byte, group, version, kind, namespace string) (unstru
func excludeKyvernoResources(kind string) bool { func excludeKyvernoResources(kind string) bool {
switch kind { switch kind {
case "ClusterPolicy", "ClusterPolicyViolation", "PolicyViolation", "GenerateRequest": case "ClusterPolicy", "ClusterPolicyViolation", "PolicyViolation", "GenerateRequest", "Policy":
return true return true
default: default:
return false return false

View file

@ -56,7 +56,7 @@ func (ws *WebhookServer) HandleMutation(
policyContext.Policy = *policy policyContext.Policy = *policy
engineResponse := engine.Mutate(policyContext) engineResponse := engine.Mutate(policyContext)
ws.statusListener.Send(mutateStats{resp: engineResponse}) ws.statusListener.Send(mutateStats{resp: engineResponse, namespace: policy.Namespace})
if !engineResponse.IsSuccessful() { if !engineResponse.IsSuccessful() {
logger.Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules()) logger.Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules())
continue continue
@ -118,11 +118,15 @@ func (ws *WebhookServer) HandleMutation(
} }
type mutateStats struct { type mutateStats struct {
resp response.EngineResponse resp response.EngineResponse
namespace string
} }
func (ms mutateStats) PolicyName() string { func (ms mutateStats) PolicyName() string {
return ms.resp.PolicyResponse.Policy if ms.namespace == "" {
return ms.resp.PolicyResponse.Policy
}
return ms.namespace + "/" + ms.resp.PolicyResponse.Policy
} }
func (ms mutateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus { func (ms mutateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {

View file

@ -272,12 +272,14 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1
}, },
} }
} }
logger.V(6).Info("received an admission request in mutating webhook") logger.V(6).Info("received an admission request in mutating webhook")
mutatePolicies := ws.pCache.Get(policycache.Mutate, nil)
validatePolicies := ws.pCache.Get(policycache.ValidateEnforce, nil)
generatePolicies := ws.pCache.Get(policycache.Generate, nil)
mutatePolicies := ws.pCache.Get(policycache.Mutate) // Get namespace policies from the cache for the requested resource namespace
validatePolicies := ws.pCache.Get(policycache.ValidateEnforce) nsMutatePolicies := ws.pCache.Get(policycache.Mutate, &request.Namespace)
generatePolicies := ws.pCache.Get(policycache.Generate) mutatePolicies = append(mutatePolicies, nsMutatePolicies...)
// getRoleRef only if policy has roles/clusterroles defined // getRoleRef only if policy has roles/clusterroles defined
var roles, clusterRoles []string var roles, clusterRoles []string
@ -420,7 +422,10 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
// push admission request to audit handler, this won't block the admission request // push admission request to audit handler, this won't block the admission request
ws.auditHandler.Add(request.DeepCopy()) ws.auditHandler.Add(request.DeepCopy())
policies := ws.pCache.Get(policycache.ValidateEnforce) policies := ws.pCache.Get(policycache.ValidateEnforce, nil)
// Get namespace policies from the cache for the requested resource namespace
nsPolicies := ws.pCache.Get(policycache.ValidateEnforce, &request.Namespace)
policies = append(policies, nsPolicies...)
if len(policies) == 0 { if len(policies) == 0 {
logger.V(4).Info("No enforce Validation policy found, returning") logger.V(4).Info("No enforce Validation policy found, returning")
return &v1beta1.AdmissionResponse{Allowed: true} return &v1beta1.AdmissionResponse{Allowed: true}

View file

@ -134,8 +134,10 @@ func (h *auditHandler) process(request *v1beta1.AdmissionRequest) error {
var err error var err error
logger := h.log.WithName("process") logger := h.log.WithName("process")
policies := h.pCache.Get(policycache.ValidateAudit) policies := h.pCache.Get(policycache.ValidateAudit, nil)
// Get namespace policies from the cache for the requested resource namespace
nsPolicies := h.pCache.Get(policycache.ValidateAudit, &request.Namespace)
policies = append(policies, nsPolicies...)
// getRoleRef only if policy has roles/clusterroles defined // getRoleRef only if policy has roles/clusterroles defined
if containRBACinfo(policies) { if containRBACinfo(policies) {
roles, clusterRoles, err = userinfo.GetRoleRef(h.rbLister, h.crbLister, request, h.configHandler) roles, clusterRoles, err = userinfo.GetRoleRef(h.rbLister, h.crbLister, request, h.configHandler)

View file

@ -87,7 +87,8 @@ func HandleValidation(
} }
engineResponses = append(engineResponses, engineResponse) engineResponses = append(engineResponses, engineResponse)
statusListener.Send(validateStats{ statusListener.Send(validateStats{
resp: engineResponse, resp: engineResponse,
namespace: policy.Namespace,
}) })
if !engineResponse.IsSuccessful() { if !engineResponse.IsSuccessful() {
logger.V(4).Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules()) logger.V(4).Info("failed to apply policy", "policy", policy.Name, "failed rules", engineResponse.GetFailedRules())
@ -126,11 +127,16 @@ func HandleValidation(
} }
type validateStats struct { type validateStats struct {
resp response.EngineResponse resp response.EngineResponse
namespace string
} }
func (vs validateStats) PolicyName() string { func (vs validateStats) PolicyName() string {
return vs.resp.PolicyResponse.Policy if vs.namespace == "" {
return vs.resp.PolicyResponse.Policy
}
return vs.namespace + "/" + vs.resp.PolicyResponse.Policy
} }
func (vs validateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus { func (vs validateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {