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

feat: add vpol status (#11956)

* feat: add vpol status

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: update status API

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update code-gen manifests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* feat: reconcile vpol.status.conditions

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: add missing files

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: add default webhook filters

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update codegen

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: update codegen

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: enable .status subresource

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* chore: add missing files

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix: linter

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2025-02-05 22:16:53 +08:00 committed by GitHub
parent de71b19b6e
commit 1f3d82893b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 781 additions and 27 deletions

View file

@ -2,12 +2,16 @@ package v2alpha1
import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:object:generate=false
type GenericPolicy interface {
metav1.Object
GetMatchConstraints() admissionregistrationv1.MatchResources
GetMatchConditions() []admissionregistrationv1.MatchCondition
GetFailurePolicy() admissionregistrationv1.FailurePolicyType
GetWebhookConfiguration() *WebhookConfiguration
GetVariables() []admissionregistrationv1.Variable
GetStatus() *PolicyStatus
}

View file

@ -0,0 +1,39 @@
package v2alpha1
import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type PolicyConditionType string
const (
PolicyConditionTypeWebhookConfigured PolicyConditionType = "WebhookConfigured"
PolicyConditionTypePolicyCached PolicyConditionType = "PolicyCached"
PolicyConditionTypeRBACPermissionsGranted PolicyConditionType = "RBACPermissionsGranted"
)
type PolicyStatus struct {
// The ready of a policy is a high-level summary of where the policy is in its lifecycle.
// The conditions array, the reason and message fields contain more detail about the policy's status.
// +optional
Ready bool `json:"ready,omitempty"`
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
func (status *PolicyStatus) SetReadyByCondition(c PolicyConditionType, s metav1.ConditionStatus, message string) {
reason := "Succeeded"
if s != metav1.ConditionTrue {
reason = "Failed"
}
newCondition := metav1.Condition{
Type: string(c),
Reason: reason,
Status: s,
Message: message,
}
meta.SetStatusCondition(&status.Conditions, newCondition)
}

View file

@ -8,13 +8,18 @@ import (
// +genclient
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=validatingpolicies,scope="Cluster",shortName=vpol,categories=kyverno
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ValidatingPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ValidatingPolicySpec `json:"spec"`
// Status contains policy runtime data.
// +optional
Status PolicyStatus `json:"status,omitempty"`
}
func (s *ValidatingPolicy) GetMatchConstraints() admissionregistrationv1.MatchResources {
@ -32,10 +37,18 @@ func (s *ValidatingPolicy) GetFailurePolicy() admissionregistrationv1.FailurePol
return *s.Spec.FailurePolicy
}
func (s *ValidatingPolicy) GetWebhookConfiguration() *WebhookConfiguration {
return s.Spec.WebhookConfiguration
}
func (s *ValidatingPolicy) GetVariables() []admissionregistrationv1.Variable {
return s.Spec.Variables
}
func (s *ValidatingPolicy) GetStatus() *PolicyStatus {
return &s.Status
}
// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View file

@ -288,12 +288,36 @@ func (in *PolicyRef) DeepCopy() *PolicyRef {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.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 PolicyStatus.
func (in *PolicyStatus) DeepCopy() *PolicyStatus {
if in == nil {
return nil
}
out := new(PolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingPolicy) DeepCopyInto(out *ValidatingPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}

View file

@ -24,7 +24,11 @@ spec:
singular: validatingpolicy
scope: Cluster
versions:
- name: v2alpha1
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v2alpha1
schema:
openAPIV3Schema:
properties:
@ -682,9 +686,76 @@ spec:
type: integer
type: object
type: object
status:
description: Status contains policy runtime data.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
ready:
description: |-
The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy's status.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
{{- end }}

View file

@ -18,7 +18,11 @@ spec:
singular: validatingpolicy
scope: Cluster
versions:
- name: v2alpha1
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v2alpha1
schema:
openAPIV3Schema:
properties:
@ -676,8 +680,75 @@ spec:
type: integer
type: object
type: object
status:
description: Status contains policy runtime data.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
ready:
description: |-
The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy's status.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -18,7 +18,11 @@ spec:
singular: validatingpolicy
scope: Cluster
versions:
- name: v2alpha1
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v2alpha1
schema:
openAPIV3Schema:
properties:
@ -676,8 +680,75 @@ spec:
type: integer
type: object
type: object
status:
description: Status contains policy runtime data.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
ready:
description: |-
The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy's status.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -48885,7 +48885,11 @@ spec:
singular: validatingpolicy
scope: Cluster
versions:
- name: v2alpha1
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v2alpha1
schema:
openAPIV3Schema:
properties:
@ -49543,11 +49547,78 @@ spec:
type: integer
type: object
type: object
status:
description: Status contains policy runtime data.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
ready:
description: |-
The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy's status.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition

View file

@ -7580,6 +7580,20 @@ WebhookConfiguration
</table>
</td>
</tr>
<tr>
<td>
<code>status</code><br/>
<em>
<a href="#kyverno.io/v2alpha1.PolicyStatus">
PolicyStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Status contains policy runtime data.</p>
</td>
</tr>
</tbody>
</table>
<hr />
@ -7884,6 +7898,10 @@ If left empty for namespaced resources, all resources from all namespaces will b
</tbody>
</table>
<hr />
<h3 id="kyverno.io/v2alpha1.PolicyConditionType">PolicyConditionType
(<code>string</code> alias)</p></h3>
<p>
</p>
<h3 id="kyverno.io/v2alpha1.PolicyRef">PolicyRef
</h3>
<p>
@ -7925,6 +7943,51 @@ string
</tbody>
</table>
<hr />
<h3 id="kyverno.io/v2alpha1.PolicyStatus">PolicyStatus
</h3>
<p>
(<em>Appears on:</em>
<a href="#kyverno.io/v2alpha1.ValidatingPolicy">ValidatingPolicy</a>)
</p>
<p>
</p>
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>ready</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy&rsquo;s status.</p>
</td>
</tr>
<tr>
<td>
<code>conditions</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#condition-v1-meta">
[]Kubernetes meta/v1.Condition
</a>
</em>
</td>
<td>
<em>(Optional)</em>
</td>
</tr>
</tbody>
</table>
<hr />
<h3 id="kyverno.io/v2alpha1.ValidatingPolicySpec">ValidatingPolicySpec
</h3>
<p>

View file

@ -649,6 +649,35 @@ Required.</p>
</tr>
<tr>
<td><code>status</code>
</br>
<a href="#kyverno-io-v2alpha1-PolicyStatus">
<span style="font-family: monospace">PolicyStatus</span>
</a>
</td>
<td>
<p>Status contains policy runtime data.</p>
</td>
</tr>
</tbody>
@ -1361,6 +1390,95 @@ If left empty for namespaced resources, all resources from all namespaces will b
</td>
</tr>
</tbody>
</table>
<H3 id="kyverno-io-v2alpha1-PolicyStatus">PolicyStatus
</H3>
<p>
(<em>Appears in:</em>
<a href="#kyverno-io-v2alpha1-ValidatingPolicy">ValidatingPolicy</a>)
</p>
<p></p>
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ready</code>
</br>
<span style="font-family: monospace">bool</span>
</td>
<td>
<p>The ready of a policy is a high-level summary of where the policy is in its lifecycle.
The conditions array, the reason and message fields contain more detail about the policy's status.</p>
</td>
</tr>
<tr>
<td><code>conditions</code>
</br>
<span style="font-family: monospace">[]meta/v1.Condition</span>
</td>
<td>
</td>
</tr>

View file

@ -0,0 +1,54 @@
/*
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 applyconfiguration-gen. DO NOT EDIT.
package v2alpha1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PolicyStatusApplyConfiguration represents an declarative configuration of the PolicyStatus type for use
// with apply.
type PolicyStatusApplyConfiguration struct {
Ready *bool `json:"ready,omitempty"`
Conditions []v1.Condition `json:"conditions,omitempty"`
}
// PolicyStatusApplyConfiguration constructs an declarative configuration of the PolicyStatus type for use with
// apply.
func PolicyStatus() *PolicyStatusApplyConfiguration {
return &PolicyStatusApplyConfiguration{}
}
// WithReady sets the Ready field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Ready field is set to the value of the last call.
func (b *PolicyStatusApplyConfiguration) WithReady(value bool) *PolicyStatusApplyConfiguration {
b.Ready = &value
return b
}
// WithConditions adds the given value to the Conditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Conditions field.
func (b *PolicyStatusApplyConfiguration) WithConditions(values ...v1.Condition) *PolicyStatusApplyConfiguration {
for i := range values {
b.Conditions = append(b.Conditions, values[i])
}
return b
}

View file

@ -30,6 +30,7 @@ type ValidatingPolicyApplyConfiguration struct {
v1.TypeMetaApplyConfiguration `json:",inline"`
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
Spec *ValidatingPolicySpecApplyConfiguration `json:"spec,omitempty"`
Status *PolicyStatusApplyConfiguration `json:"status,omitempty"`
}
// ValidatingPolicy constructs an declarative configuration of the ValidatingPolicy type for use with
@ -207,3 +208,11 @@ func (b *ValidatingPolicyApplyConfiguration) WithSpec(value *ValidatingPolicySpe
b.Spec = value
return b
}
// WithStatus sets the Status field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Status field is set to the value of the last call.
func (b *ValidatingPolicyApplyConfiguration) WithStatus(value *PolicyStatusApplyConfiguration) *ValidatingPolicyApplyConfiguration {
b.Status = value
return b
}

View file

@ -223,6 +223,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &kyvernov2alpha1.KubernetesResourceApplyConfiguration{}
case v2alpha1.SchemeGroupVersion.WithKind("PolicyRef"):
return &kyvernov2alpha1.PolicyRefApplyConfiguration{}
case v2alpha1.SchemeGroupVersion.WithKind("PolicyStatus"):
return &kyvernov2alpha1.PolicyStatusApplyConfiguration{}
case v2alpha1.SchemeGroupVersion.WithKind("ValidatingPolicy"):
return &kyvernov2alpha1.ValidatingPolicyApplyConfiguration{}
case v2alpha1.SchemeGroupVersion.WithKind("ValidatingPolicySpec"):

View file

@ -95,6 +95,17 @@ func (c *FakeValidatingPolicies) Update(ctx context.Context, validatingPolicy *v
return obj.(*v2alpha1.ValidatingPolicy), 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 *FakeValidatingPolicies) UpdateStatus(ctx context.Context, validatingPolicy *v2alpha1.ValidatingPolicy, opts v1.UpdateOptions) (*v2alpha1.ValidatingPolicy, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(validatingpoliciesResource, "status", validatingPolicy), &v2alpha1.ValidatingPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v2alpha1.ValidatingPolicy), err
}
// Delete takes name of the validatingPolicy and deletes it. Returns an error if one occurs.
func (c *FakeValidatingPolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.

View file

@ -40,6 +40,7 @@ type ValidatingPoliciesGetter interface {
type ValidatingPolicyInterface interface {
Create(ctx context.Context, validatingPolicy *v2alpha1.ValidatingPolicy, opts v1.CreateOptions) (*v2alpha1.ValidatingPolicy, error)
Update(ctx context.Context, validatingPolicy *v2alpha1.ValidatingPolicy, opts v1.UpdateOptions) (*v2alpha1.ValidatingPolicy, error)
UpdateStatus(ctx context.Context, validatingPolicy *v2alpha1.ValidatingPolicy, opts v1.UpdateOptions) (*v2alpha1.ValidatingPolicy, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.ValidatingPolicy, error)
@ -128,6 +129,21 @@ func (c *validatingPolicies) Update(ctx context.Context, validatingPolicy *v2alp
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 *validatingPolicies) UpdateStatus(ctx context.Context, validatingPolicy *v2alpha1.ValidatingPolicy, opts v1.UpdateOptions) (result *v2alpha1.ValidatingPolicy, err error) {
result = &v2alpha1.ValidatingPolicy{}
err = c.client.Put().
Resource("validatingpolicies").
Name(validatingPolicy.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(validatingPolicy).
Do(ctx).
Into(result)
return
}
// Delete takes name of the validatingPolicy and deletes it. Returns an error if one occurs.
func (c *validatingPolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().

View file

@ -111,6 +111,17 @@ func (c *withLogging) Update(arg0 context.Context, arg1 *github_com_kyverno_kyve
}
return ret0, ret1
}
func (c *withLogging) UpdateStatus(arg0 context.Context, arg1 *github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, arg2 k8s_io_apimachinery_pkg_apis_meta_v1.UpdateOptions) (*github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, error) {
start := time.Now()
logger := c.logger.WithValues("operation", "UpdateStatus")
ret0, ret1 := c.inner.UpdateStatus(arg0, arg1, arg2)
if err := multierr.Combine(ret1); err != nil {
logger.Error(err, "UpdateStatus failed", "duration", time.Since(start))
} else {
logger.Info("UpdateStatus done", "duration", time.Since(start))
}
return ret0, ret1
}
func (c *withLogging) Watch(arg0 context.Context, arg1 k8s_io_apimachinery_pkg_apis_meta_v1.ListOptions) (k8s_io_apimachinery_pkg_watch.Interface, error) {
start := time.Now()
logger := c.logger.WithValues("operation", "Watch")
@ -156,6 +167,10 @@ func (c *withMetrics) Update(arg0 context.Context, arg1 *github_com_kyverno_kyve
defer c.recorder.RecordWithContext(arg0, "update")
return c.inner.Update(arg0, arg1, arg2)
}
func (c *withMetrics) UpdateStatus(arg0 context.Context, arg1 *github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, arg2 k8s_io_apimachinery_pkg_apis_meta_v1.UpdateOptions) (*github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, error) {
defer c.recorder.RecordWithContext(arg0, "update_status")
return c.inner.UpdateStatus(arg0, arg1, arg2)
}
func (c *withMetrics) Watch(arg0 context.Context, arg1 k8s_io_apimachinery_pkg_apis_meta_v1.ListOptions) (k8s_io_apimachinery_pkg_watch.Interface, error) {
defer c.recorder.RecordWithContext(arg0, "watch")
return c.inner.Watch(arg0, arg1)
@ -314,6 +329,27 @@ func (c *withTracing) Update(arg0 context.Context, arg1 *github_com_kyverno_kyve
}
return ret0, ret1
}
func (c *withTracing) UpdateStatus(arg0 context.Context, arg1 *github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, arg2 k8s_io_apimachinery_pkg_apis_meta_v1.UpdateOptions) (*github_com_kyverno_kyverno_api_kyverno_v2alpha1.ValidatingPolicy, error) {
var span trace.Span
if tracing.IsInSpan(arg0) {
arg0, span = tracing.StartChildSpan(
arg0,
"",
fmt.Sprintf("KUBE %s/%s/%s", c.client, c.kind, "UpdateStatus"),
trace.WithAttributes(
tracing.KubeClientGroupKey.String(c.client),
tracing.KubeClientKindKey.String(c.kind),
tracing.KubeClientOperationKey.String("UpdateStatus"),
),
)
defer span.End()
}
ret0, ret1 := c.inner.UpdateStatus(arg0, arg1, arg2)
if span != nil {
tracing.SetSpanStatus(span, ret1)
}
return ret0, ret1
}
func (c *withTracing) Watch(arg0 context.Context, arg1 k8s_io_apimachinery_pkg_apis_meta_v1.ListOptions) (k8s_io_apimachinery_pkg_watch.Interface, error) {
var span trace.Span
if tracing.IsInSpan(arg0) {

View file

@ -126,6 +126,10 @@ type controller struct {
// state
lock sync.Mutex
policyState map[string]sets.Set[string]
// vpolState records validatingpolicies that are configured
// successfully in webhook object, non-thread safe
vpolState map[string]bool
}
func NewController(
@ -193,6 +197,7 @@ func NewController(
config.MutatingWebhookConfigurationName: sets.New[string](),
config.ValidatingWebhookConfigurationName: sets.New[string](),
},
vpolState: make(map[string]bool),
}
if _, _, err := controllerutils.AddDefaultEventHandlers(logger, mwcInformer.Informer(), queue); err != nil {
logger.Error(err, "failed to register event handlers")
@ -376,6 +381,13 @@ func (c *controller) recordPolicyState(webhookConfigurationName string, policies
}
}
func (c *controller) recordValidatingPolicyState(validatingpolicies ...kyvernov2alpha1.GenericPolicy) {
c.vpolState = make(map[string]bool)
for _, policy := range validatingpolicies {
c.vpolState[policy.GetName()] = true
}
}
func (c *controller) reconcileResourceValidatingWebhookConfiguration(ctx context.Context) error {
if c.autoUpdateWebhooks {
return c.reconcileValidatingWebhookConfiguration(ctx, c.autoUpdateWebhooks, c.buildResourceValidatingWebhookConfiguration)
@ -642,6 +654,41 @@ func (c *controller) updatePolicyStatuses(ctx context.Context, webhookType strin
return nil
}
func (c *controller) updateValidatingPolicyStatuses(ctx context.Context) error {
vpols, err := c.getValidatingPolicies()
if err != nil {
return err
}
updateStatusFunc := func(vpol kyvernov2alpha1.GenericPolicy) error {
status := vpol.GetStatus()
status.SetReadyByCondition(kyvernov2alpha1.PolicyConditionTypeWebhookConfigured, metav1.ConditionTrue, "Webhook configured")
return nil
}
var errs []error
for _, vpol := range vpols {
if !c.vpolState[vpol.GetName()] {
continue
}
err := controllerutils.UpdateStatus(
ctx,
vpol.(*kyvernov2alpha1.ValidatingPolicy),
c.kyvernoClient.KyvernoV2alpha1().ValidatingPolicies(),
func(vpol *kyvernov2alpha1.ValidatingPolicy) error {
return updateStatusFunc(vpol)
},
func(a *kyvernov2alpha1.ValidatingPolicy, b *kyvernov2alpha1.ValidatingPolicy) bool {
return datautils.DeepEqual(a.Status, b.Status)
},
)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", vpol.GetName(), err))
}
}
return multierr.Combine(errs...)
}
func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, namespace, name string) error {
if c.autoDeleteWebhooks && c.runtime.IsGoingDown() {
return c.reconcileWebhookDeletion(ctx)
@ -666,9 +713,16 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
if err := c.reconcileResourceValidatingWebhookConfiguration(ctx); err != nil {
return err
}
var errs []error
if err := c.updatePolicyStatuses(ctx, config.ValidatingWebhookConfigurationName); err != nil {
return err
errs = append(errs, fmt.Errorf("failed to update policy statuses: %w", err))
}
if err := c.updateValidatingPolicyStatuses(ctx); err != nil {
errs = append(errs, fmt.Errorf("failed to update validating policy statuses: %w", err))
}
return multierr.Combine(errs...)
}
case config.PolicyValidatingWebhookConfigurationName:
return c.reconcilePolicyValidatingWebhookConfiguration(ctx)
@ -953,25 +1007,26 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con
errs = append(errs, fmt.Errorf("failed to build webhook rules for policies: %v", err))
}
if err := c.buildForValidatingPolicies(ctx, cfg, caBundle, webhookConfig); err != nil {
if err := c.buildForValidatingPolicies(cfg, caBundle, webhookConfig); err != nil {
errs = append(errs, fmt.Errorf("failed to build webhook rules for validatingpolicies: %v", err))
}
return webhookConfig, multierr.Combine(errs...)
}
func (c *controller) buildForValidatingPolicies(ctx context.Context, cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
func (c *controller) buildForValidatingPolicies(cfg config.Configuration, caBundle []byte, result *admissionregistrationv1.ValidatingWebhookConfiguration) error {
if !c.watchdogCheck() {
return nil
}
vpols, err := c.vpolLister.List(labels.Everything())
vpols, err := c.getValidatingPolicies()
if err != nil {
return err
}
webhooks := buildWebhookRules(c.server, c.servicePort, caBundle, vpols)
webhooks := buildWebhookRules(cfg, c.server, c.servicePort, caBundle, vpols)
result.Webhooks = append(result.Webhooks, webhooks...)
c.recordValidatingPolicyState(vpols...)
return nil
}
@ -1020,7 +1075,7 @@ func (c *controller) buildForPolicies(ctx context.Context, cfg config.Configurat
webhooks = append(webhooks, fineGrainedFailList...)
result.Webhooks = c.buildResourceValidatingWebhookRules(caBundle, webhookCfg, sideEffects, webhooks)
} else {
c.recordPolicyState(config.MutatingWebhookConfigurationName)
c.recordPolicyState(config.ValidatingWebhookConfigurationName)
}
return nil
}
@ -1077,6 +1132,19 @@ func (c *controller) getAllPolicies() ([]kyvernov1.PolicyInterface, error) {
return policies, nil
}
func (c *controller) getValidatingPolicies() ([]kyvernov2alpha1.GenericPolicy, error) {
validatingpolicies, err := c.vpolLister.List(labels.Everything())
if err != nil {
return nil, err
}
vpols := make([]kyvernov2alpha1.GenericPolicy, 0)
for _, vpol := range validatingpolicies {
vpols = append(vpols, vpol)
}
return vpols, nil
}
func (c *controller) getLease() (*coordinationv1.Lease, error) {
return c.leaseLister.Leases(config.KyvernoNamespace()).Get("kyverno-health")
}

View file

@ -7,7 +7,7 @@ import (
"k8s.io/utils/ptr"
)
func buildWebhookRules(server string, servicePort int32, caBundle []byte, vpols []*kyvernov2alpha1.ValidatingPolicy) (webhooks []admissionregistrationv1.ValidatingWebhook) {
func buildWebhookRules(cfg config.Configuration, server string, servicePort int32, caBundle []byte, vpols []kyvernov2alpha1.GenericPolicy) (webhooks []admissionregistrationv1.ValidatingWebhook) {
var (
webhookIgnoreList []admissionregistrationv1.ValidatingWebhook
webhookFailList []admissionregistrationv1.ValidatingWebhook
@ -26,30 +26,39 @@ func buildWebhookRules(server string, servicePort int32, caBundle []byte, vpols
AdmissionReviewVersions: []string{"v1"},
}
)
if cfg.GetWebhook().NamespaceSelector != nil {
webhookIgnore.NamespaceSelector = cfg.GetWebhook().NamespaceSelector
webhookFail.NamespaceSelector = cfg.GetWebhook().NamespaceSelector
}
if cfg.GetWebhook().ObjectSelector != nil {
webhookIgnore.ObjectSelector = cfg.GetWebhook().ObjectSelector
webhookFail.ObjectSelector = cfg.GetWebhook().ObjectSelector
}
for _, vpol := range vpols {
webhook := admissionregistrationv1.ValidatingWebhook{}
failurePolicyIgnore := vpol.Spec.FailurePolicy != nil && *vpol.Spec.FailurePolicy == admissionregistrationv1.Ignore
failurePolicyIgnore := vpol.GetFailurePolicy() == admissionregistrationv1.Ignore
if failurePolicyIgnore {
webhook.FailurePolicy = ptr.To(admissionregistrationv1.Ignore)
} else {
webhook.FailurePolicy = ptr.To(admissionregistrationv1.Fail)
}
// TODO(shuting): exclude?
for _, match := range vpol.Spec.MatchConstraints.ResourceRules {
for _, match := range vpol.GetMatchConstraints().ResourceRules {
webhook.Rules = append(webhook.Rules, match.RuleWithOperations)
}
fineGrainedWebhook := false
if vpol.Spec.MatchConditions != nil {
webhook.MatchConditions = vpol.Spec.MatchConditions
if vpol.GetMatchConditions() != nil {
webhook.MatchConditions = vpol.GetMatchConditions()
fineGrainedWebhook = true
}
if vpol.Spec.MatchConstraints.MatchPolicy != nil && *vpol.Spec.MatchConstraints.MatchPolicy == admissionregistrationv1.Exact {
webhook.MatchPolicy = vpol.Spec.MatchConstraints.MatchPolicy
if vpol.GetMatchConstraints().MatchPolicy != nil && *vpol.GetMatchConstraints().MatchPolicy == admissionregistrationv1.Exact {
webhook.MatchPolicy = vpol.GetMatchConstraints().MatchPolicy
fineGrainedWebhook = true
}
if vpol.Spec.WebhookConfiguration != nil && vpol.Spec.WebhookConfiguration.TimeoutSeconds != nil {
webhook.TimeoutSeconds = vpol.Spec.WebhookConfiguration.TimeoutSeconds
if vpol.GetWebhookConfiguration() != nil && vpol.GetWebhookConfiguration().TimeoutSeconds != nil {
webhook.TimeoutSeconds = vpol.GetWebhookConfiguration().TimeoutSeconds
fineGrainedWebhook = true
}
@ -57,12 +66,12 @@ func buildWebhookRules(server string, servicePort int32, caBundle []byte, vpols
webhook.SideEffects = &noneOnDryRun
webhook.AdmissionReviewVersions = []string{"v1"}
if failurePolicyIgnore {
webhook.Name = config.ValidatingPolicyWebhookName + "-ignore-finegrained-" + vpol.Name
webhook.ClientConfig = newClientConfig(server, servicePort, caBundle, config.ValidatingPolicyServicePath+"/ignore"+config.FineGrainedWebhookPath+"/"+vpol.Name)
webhook.Name = config.ValidatingPolicyWebhookName + "-ignore-finegrained-" + vpol.GetName()
webhook.ClientConfig = newClientConfig(server, servicePort, caBundle, "/validate/ignore"+config.FineGrainedWebhookPath+"/"+vpol.GetName())
webhookIgnoreList = append(webhookIgnoreList, webhook)
} else {
webhook.Name = config.ValidatingPolicyWebhookName + "-fail-finegrained-" + vpol.Name
webhook.ClientConfig = newClientConfig(server, servicePort, caBundle, config.ValidatingPolicyServicePath+"/fail"+config.FineGrainedWebhookPath+"/"+vpol.Name)
webhook.Name = config.ValidatingPolicyWebhookName + "-fail-finegrained-" + vpol.GetName()
webhook.ClientConfig = newClientConfig(server, servicePort, caBundle, "/validate/fail"+config.FineGrainedWebhookPath+"/"+vpol.GetName())
webhookFailList = append(webhookFailList, webhook)
}
} else {

View file

@ -141,7 +141,7 @@ func TestBuildWebhookRules(t *testing.T) {
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ValidatingPolicyWebhookName + "-ignore-finegrained-test-fine-grained-ignore",
ClientConfig: newClientConfig("", 0, nil, config.ValidatingPolicyServicePath+"/ignore"+config.FineGrainedWebhookPath+"/test-fine-grained-ignore"),
ClientConfig: newClientConfig("", 0, nil, "/validate/ignore"+config.FineGrainedWebhookPath+"/test-fine-grained-ignore"),
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -201,7 +201,7 @@ func TestBuildWebhookRules(t *testing.T) {
expectedWebhooks: []admissionregistrationv1.ValidatingWebhook{
{
Name: config.ValidatingPolicyWebhookName + "-fail-finegrained-test-fine-grained-fail",
ClientConfig: newClientConfig("", 0, nil, config.ValidatingPolicyServicePath+"/fail"+config.FineGrainedWebhookPath+"/test-fine-grained-fail"),
ClientConfig: newClientConfig("", 0, nil, "/validate/fail"+config.FineGrainedWebhookPath+"/test-fine-grained-fail"),
Rules: []admissionregistrationv1.RuleWithOperations{
{
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
@ -229,7 +229,11 @@ func TestBuildWebhookRules(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
webhooks := buildWebhookRules("", 0, nil, tt.vpols)
var vpols []kyvernov2alpha1.GenericPolicy
for _, vpol := range tt.vpols {
vpols = append(vpols, vpol)
}
webhooks := buildWebhookRules(config.NewDefaultConfiguration(false), "", 0, nil, vpols)
assert.Equal(t, len(tt.expectedWebhooks), len(webhooks))
for i, expect := range tt.expectedWebhooks {
assert.Equal(t, expect.Name, webhooks[i].Name)
@ -246,7 +250,7 @@ func TestBuildWebhookRules(t *testing.T) {
assert.Equal(t, expect.TimeoutSeconds, webhooks[i].TimeoutSeconds)
}
if expect.ClientConfig.Service != nil {
assert.Equal(t, expect.ClientConfig.Service.Path, webhooks[i].ClientConfig.Service.Path)
assert.Equal(t, *webhooks[i].ClientConfig.Service.Path, *expect.ClientConfig.Service.Path)
}
}
})