mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 16:06:56 +00:00
feat: add namespace support in CLI values (#11958)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
83289c7a00
commit
a30fc14d4d
9 changed files with 306 additions and 21 deletions
|
@ -1,5 +1,9 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// ValuesSpec declares values to be loaded by the Kyverno CLI
|
||||
type ValuesSpec struct {
|
||||
// GlobalValues are the global values
|
||||
|
@ -14,6 +18,9 @@ type ValuesSpec struct {
|
|||
// NamespaceSelectors are the namespace labels
|
||||
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector,omitempty"`
|
||||
|
||||
// Namespaces are the namespaces
|
||||
Namespaces []corev1.Namespace `json:"namespaces,omitempty"`
|
||||
|
||||
// Subresources are the subresource/parent resource mappings
|
||||
Subresources []Subresource `json:"subresources,omitempty"`
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||
"github.com/spf13/cobra"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -261,7 +262,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
|||
if err != nil {
|
||||
return rc, resources1, skippedInvalidPolicies, responses1, err
|
||||
}
|
||||
responses3, err := c.applyValidatingPolicies(vps, resources1, variables.NamespaceSelectors(), rc, dClient)
|
||||
responses3, err := c.applyValidatingPolicies(vps, resources1, variables.Namespace, rc, dClient)
|
||||
if err != nil {
|
||||
return rc, resources1, skippedInvalidPolicies, responses1, err
|
||||
}
|
||||
|
@ -315,7 +316,7 @@ func (c *ApplyCommandConfig) applyValidatingAdmissionPolicies(
|
|||
func (c *ApplyCommandConfig) applyValidatingPolicies(
|
||||
vps []kyvernov2alpha1.ValidatingPolicy,
|
||||
resources []*unstructured.Unstructured,
|
||||
namespaceSelectorMap map[string]map[string]string,
|
||||
namespaceProvider func(string) *corev1.Namespace,
|
||||
_ *processor.ResultCounts,
|
||||
_ dclient.Interface,
|
||||
) ([]engineapi.EngineResponse, error) {
|
||||
|
@ -325,12 +326,11 @@ func (c *ApplyCommandConfig) applyValidatingPolicies(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eng := engine.NewEngine(provider)
|
||||
eng := engine.NewEngine(provider, namespaceProvider)
|
||||
responses := make([]engineapi.EngineResponse, 0)
|
||||
for _, resource := range resources {
|
||||
request := engine.EngineRequest{
|
||||
Resource: resource,
|
||||
NamespaceLabels: namespaceSelectorMap,
|
||||
Resource: resource,
|
||||
}
|
||||
response, err := eng.Handle(ctx, request)
|
||||
if err != nil {
|
||||
|
|
|
@ -58,6 +58,97 @@ spec:
|
|||
- name
|
||||
type: object
|
||||
type: array
|
||||
namespaces:
|
||||
description: Namespaces are the namespaces
|
||||
items:
|
||||
description: |-
|
||||
Namespace provides a scope for Names.
|
||||
Use of multiple namespaces is optional.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
description: |-
|
||||
Standard object's metadata.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
type: object
|
||||
spec:
|
||||
description: |-
|
||||
Spec defines the behavior of the Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
properties:
|
||||
finalizers:
|
||||
description: |-
|
||||
Finalizers is an opaque list of values that must be empty to permanently remove object from storage.
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
items:
|
||||
description: FinalizerName is the name identifying a finalizer
|
||||
during namespace lifecycle.
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
status:
|
||||
description: |-
|
||||
Status describes the current status of a Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
properties:
|
||||
conditions:
|
||||
description: Represents the latest available observations of
|
||||
a namespace's current state.
|
||||
items:
|
||||
description: NamespaceCondition contains details about state
|
||||
of namespace.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: Last time the condition transitioned from
|
||||
one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: Human-readable message indicating details
|
||||
about last transition.
|
||||
type: string
|
||||
reason:
|
||||
description: Unique, one-word, CamelCase reason for the
|
||||
condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: Status of the condition, one of True, False,
|
||||
Unknown.
|
||||
type: string
|
||||
type:
|
||||
description: Type of namespace controller condition.
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
phase:
|
||||
description: |-
|
||||
Phase is the current lifecycle phase of the namespace.
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
policies:
|
||||
description: Policies are the policy values
|
||||
items:
|
||||
|
|
|
@ -58,6 +58,97 @@ spec:
|
|||
- name
|
||||
type: object
|
||||
type: array
|
||||
namespaces:
|
||||
description: Namespaces are the namespaces
|
||||
items:
|
||||
description: |-
|
||||
Namespace provides a scope for Names.
|
||||
Use of multiple namespaces is optional.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
description: |-
|
||||
Standard object's metadata.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
type: object
|
||||
spec:
|
||||
description: |-
|
||||
Spec defines the behavior of the Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
properties:
|
||||
finalizers:
|
||||
description: |-
|
||||
Finalizers is an opaque list of values that must be empty to permanently remove object from storage.
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
items:
|
||||
description: FinalizerName is the name identifying a finalizer
|
||||
during namespace lifecycle.
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
status:
|
||||
description: |-
|
||||
Status describes the current status of a Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
properties:
|
||||
conditions:
|
||||
description: Represents the latest available observations of
|
||||
a namespace's current state.
|
||||
items:
|
||||
description: NamespaceCondition contains details about state
|
||||
of namespace.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: Last time the condition transitioned from
|
||||
one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: Human-readable message indicating details
|
||||
about last transition.
|
||||
type: string
|
||||
reason:
|
||||
description: Unique, one-word, CamelCase reason for the
|
||||
condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: Status of the condition, one of True, False,
|
||||
Unknown.
|
||||
type: string
|
||||
type:
|
||||
description: Type of namespace controller condition.
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
phase:
|
||||
description: |-
|
||||
Phase is the current lifecycle phase of the namespace.
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
policies:
|
||||
description: Policies are the policy values
|
||||
items:
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/v1alpha1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
|
@ -28,10 +30,12 @@ func (v Variables) NamespaceSelectors() map[string]Labels {
|
|||
return nil
|
||||
}
|
||||
out := map[string]Labels{}
|
||||
if v.values.NamespaceSelectors != nil {
|
||||
for _, n := range v.values.NamespaceSelectors {
|
||||
out[n.Name] = n.Labels
|
||||
}
|
||||
for _, n := range v.values.NamespaceSelectors {
|
||||
out[n.Name] = n.Labels
|
||||
}
|
||||
// Give precedence to namespaces
|
||||
for _, n := range v.values.Namespaces {
|
||||
out[n.Name] = n.Labels
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
|
@ -39,6 +43,33 @@ func (v Variables) NamespaceSelectors() map[string]Labels {
|
|||
return out
|
||||
}
|
||||
|
||||
func (v Variables) Namespace(name string) *corev1.Namespace {
|
||||
if v.values == nil {
|
||||
return nil
|
||||
}
|
||||
// Give precedence to namespaces
|
||||
for _, n := range v.values.Namespaces {
|
||||
if n.Name == name {
|
||||
return &n
|
||||
}
|
||||
}
|
||||
for _, n := range v.values.NamespaceSelectors {
|
||||
if n.Name == name {
|
||||
return &corev1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1.SchemeGroupVersion.String(),
|
||||
Kind: "Namespace",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: n.Labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v Variables) ComputeVariables(s *store.Store, policy, resource, kind string, kindMap sets.Set[string], variables ...string) (map[string]interface{}, error) {
|
||||
resourceValues := map[string]interface{}{}
|
||||
// first apply global values
|
||||
|
|
|
@ -1046,6 +1046,19 @@ map[string]interface{}
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>namespaces</code><br/>
|
||||
<em>
|
||||
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#namespace-v1-core">
|
||||
[]Kubernetes core/v1.Namespace
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Namespaces are the namespaces</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>subresources</code><br/>
|
||||
<em>
|
||||
<a href="#cli.kyverno.io/v1alpha1.Subresource">
|
||||
|
|
|
@ -2080,6 +2080,35 @@ This is DEPRECATED, Use <code>patchedResources</code> instead.</p>
|
|||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>namespaces</code>
|
||||
|
||||
<span style="color:blue;"> *</span>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
|
||||
|
||||
<span style="font-family: monospace">[]core/v1.Namespace</span>
|
||||
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<p>Namespaces are the namespaces</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td><code>subresources</code>
|
||||
|
||||
|
|
|
@ -8,14 +8,15 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/cel/policy"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
type EngineRequest struct {
|
||||
Resource *unstructured.Unstructured
|
||||
NamespaceLabels map[string]map[string]string
|
||||
Resource *unstructured.Unstructured
|
||||
}
|
||||
|
||||
type EngineResponse struct {
|
||||
|
@ -86,13 +87,17 @@ type Engine interface {
|
|||
Handle(context.Context, EngineRequest, ...policy.CompiledPolicy) (EngineResponse, error)
|
||||
}
|
||||
|
||||
type NamespaceResolver = func(string) *corev1.Namespace
|
||||
|
||||
type engine struct {
|
||||
provider Provider
|
||||
nsResolver NamespaceResolver
|
||||
provider Provider
|
||||
}
|
||||
|
||||
func NewEngine(provider Provider) *engine {
|
||||
func NewEngine(provider Provider, nsResolver NamespaceResolver) *engine {
|
||||
return &engine{
|
||||
provider: provider,
|
||||
nsResolver: nsResolver,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,15 +109,27 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo
|
|||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
// resolve namespace
|
||||
var namespace *unstructured.Unstructured
|
||||
if ns := request.Resource.GetNamespace(); ns != "" {
|
||||
coreNs := e.nsResolver(ns)
|
||||
if coreNs != nil {
|
||||
ns, err := kubeutils.ObjToUnstructured(coreNs)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
namespace = ns
|
||||
}
|
||||
}
|
||||
for _, policy := range policies {
|
||||
response.Policies = append(response.Policies, e.handlePolicy(ctx, policy, request))
|
||||
response.Policies = append(response.Policies, e.handlePolicy(ctx, policy, request.Resource, namespace))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (e *engine) handlePolicy(ctx context.Context, policy policy.CompiledPolicy, request EngineRequest) PolicyResponse {
|
||||
func (e *engine) handlePolicy(ctx context.Context, policy policy.CompiledPolicy, resource *unstructured.Unstructured, namespace *unstructured.Unstructured) PolicyResponse {
|
||||
var rules []engineapi.RuleResponse
|
||||
ok, err := policy.Evaluate(ctx, request.Resource, request.NamespaceLabels)
|
||||
ok, err := policy.Evaluate(ctx, resource, namespace)
|
||||
// TODO: evaluation should be per rule
|
||||
if err != nil {
|
||||
rules = handlers.WithResponses(engineapi.RuleError("todo", engineapi.Validation, "failed to load context", err, nil))
|
||||
|
|
|
@ -24,12 +24,17 @@ type CompiledPolicy struct {
|
|||
func (p *CompiledPolicy) Evaluate(
|
||||
ctx context.Context,
|
||||
resource *unstructured.Unstructured,
|
||||
namespaceLabels map[string]map[string]string,
|
||||
namespace *unstructured.Unstructured,
|
||||
) (bool, error) {
|
||||
var nsData map[string]any
|
||||
if namespace != nil {
|
||||
nsData = namespace.UnstructuredContent()
|
||||
}
|
||||
matchConditions := func() (bool, error) {
|
||||
var errs []error
|
||||
data := map[string]any{
|
||||
ObjectKey: resource.UnstructuredContent(),
|
||||
NamespaceObjectKey: nsData,
|
||||
ObjectKey: resource.UnstructuredContent(),
|
||||
}
|
||||
for _, matchCondition := range p.matchConditions {
|
||||
// evaluate the condition
|
||||
|
@ -63,8 +68,9 @@ func (p *CompiledPolicy) Evaluate(
|
|||
variables := func() map[string]any {
|
||||
vars := lazy.NewMapValue(VariablesType)
|
||||
data := map[string]any{
|
||||
ObjectKey: resource.UnstructuredContent(),
|
||||
VariablesKey: vars,
|
||||
NamespaceObjectKey: nsData,
|
||||
ObjectKey: resource.UnstructuredContent(),
|
||||
VariablesKey: vars,
|
||||
}
|
||||
for name, variable := range p.variables {
|
||||
vars.Append(name, func(*lazy.MapValue) ref.Val {
|
||||
|
|
Loading…
Add table
Reference in a new issue