1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: add cli package to load policy exceptions (#8508)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-09-22 11:53:19 +02:00 committed by GitHub
parent d4d5d751b1
commit 21e044eb1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1178 additions and 22 deletions

View file

@ -539,6 +539,7 @@ codegen-cli-crds: codegen-crds-kyverno ## Copy generated CRDs to embed in the CL
@rm -rf cmd/cli/kubectl-kyverno/data/crds && mkdir -p cmd/cli/kubectl-kyverno/data/crds @rm -rf cmd/cli/kubectl-kyverno/data/crds && mkdir -p cmd/cli/kubectl-kyverno/data/crds
@cp config/crds/kyverno.io_clusterpolicies.yaml cmd/cli/kubectl-kyverno/data/crds @cp config/crds/kyverno.io_clusterpolicies.yaml cmd/cli/kubectl-kyverno/data/crds
@cp config/crds/kyverno.io_policies.yaml cmd/cli/kubectl-kyverno/data/crds @cp config/crds/kyverno.io_policies.yaml cmd/cli/kubectl-kyverno/data/crds
@cp config/crds/kyverno.io_policyexceptions.yaml cmd/cli/kubectl-kyverno/data/crds
@cp cmd/cli/kubectl-kyverno/config/crds/* cmd/cli/kubectl-kyverno/data/crds @cp cmd/cli/kubectl-kyverno/config/crds/* cmd/cli/kubectl-kyverno/data/crds
.PHONY: codegen-docs-all .PHONY: codegen-docs-all

View file

@ -0,0 +1,42 @@
apiVersion: kyverno.io/v2alpha1
kind: PolicyException
metadata:
name: delta-exception
namespace: delta
spec:
exceptions:
- policyName: disallow-host-namespaces
ruleNames:
- host-namespaces
- autogen-host-namespaces
match:
any:
- resources:
kinds:
- Pod
- Deployment
namespaces:
- delta
names:
- important-tool*
---
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: require-ns-purpose-label
namespace: test
spec:
validationFailureAction: Enforce
rules:
- name: require-ns-purpose-label
match:
any:
- resources:
kinds:
- Namespace
validate:
message: "You must have label 'purpose' with value 'production' set on all new namespaces."
pattern:
metadata:
labels:
purpose: production

View file

@ -0,0 +1,21 @@
apiVersion: kyverno.io/v2alpha1
kind: PolicyException
metadata:
name: delta-exception
namespace: delta
spec:
exceptions:
- policyName: disallow-host-namespaces
ruleNames:
- host-namespaces
- autogen-host-namespaces
match:
any:
- resources:
kinds:
- Pod
- Deployment
namespaces:
- delta
names:
- important-tool*

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
package exception
import (
"fmt"
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/data"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/convert"
resourceloader "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/loader"
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
)
var (
factory, _ = resourceloader.New(openapiclient.NewComposite(openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder)))
exceptionV1 = schema.GroupVersion(kyvernov2alpha1.GroupVersion).WithKind("PolicyException")
exceptionV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("PolicyException")
)
func Load(content []byte) ([]*kyvernov2alpha1.PolicyException, error) {
documents, err := yamlutils.SplitDocuments(content)
if err != nil {
return nil, err
}
var exceptions []*kyvernov2alpha1.PolicyException
for _, document := range documents {
gvk, untyped, err := factory.Load(document)
if err != nil {
return nil, err
}
switch gvk {
case exceptionV1, exceptionV2:
exception, err := convert.To[kyvernov2alpha1.PolicyException](untyped)
if err != nil {
return nil, err
}
exceptions = append(exceptions, exception)
default:
return nil, fmt.Errorf("policy exception type not supported %s", gvk)
}
}
return exceptions, nil
}

View file

@ -0,0 +1,41 @@
package exception
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func Test_Load(t *testing.T) {
tests := []struct {
name string
policies string
wantLoaded int
wantErr bool
}{{
name: "not a policy exception",
policies: "../_testdata/resources/namespace.yaml",
wantErr: true,
}, {
name: "policy exception",
policies: "../_testdata/exceptions/exception.yaml",
wantLoaded: 1,
}, {
name: "policy exception and policy",
policies: "../_testdata/exceptions/exception-and-policy.yaml",
wantErr: true,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bytes, err := os.ReadFile(tt.policies)
require.NoError(t, err)
require.NoError(t, err)
if res, err := Load(bytes); (err != nil) != tt.wantErr {
t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
} else if len(res) != tt.wantLoaded {
t.Errorf("Load() loaded amount = %v, wantLoaded %v", len(res), tt.wantLoaded)
}
})
}
}

View file

@ -42,15 +42,9 @@ func TestNew(t *testing.T) {
} }
}(), }(),
}, { }, {
name: "invalid local", name: "invalid local",
client: openapiclient.NewLocalSchemaFiles(data.Crds(), "blam"), client: openapiclient.NewLocalCRDFiles(data.Crds(), "blam"),
want: func() Loader { wantErr: true,
validator, err := validator.New(openapiclient.NewLocalSchemaFiles(data.Crds(), "blam"))
require.NoError(t, err)
return &loader{
validator: validator,
}
}(),
}, { }, {
name: "composite - no clients", name: "composite - no clients",
client: openapiclient.NewComposite(), client: openapiclient.NewComposite(),
@ -70,15 +64,9 @@ func TestNew(t *testing.T) {
client: openapiclient.NewComposite(openapiclient.NewHardcodedBuiltins("1.27"), errClient{}), client: openapiclient.NewComposite(openapiclient.NewHardcodedBuiltins("1.27"), errClient{}),
wantErr: true, wantErr: true,
}, { }, {
name: "composite - invalid local", name: "composite - invalid local",
client: openapiclient.NewComposite(openapiclient.NewLocalSchemaFiles(data.Crds(), "blam")), client: openapiclient.NewComposite(openapiclient.NewLocalCRDFiles(data.Crds(), "blam")),
want: func() Loader { wantErr: true,
validator, err := validator.New(openapiclient.NewComposite(openapiclient.NewLocalSchemaFiles(data.Crds(), "blam")))
require.NoError(t, err)
return &loader{
validator: validator,
}
}(),
}} }}
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -123,16 +111,16 @@ func Test_loader_Load(t *testing.T) {
wantErr bool wantErr bool
}{{ }{{
name: "nil", name: "nil",
loader: newLoader(openapiclient.NewLocalSchemaFiles(data.Crds(), "schemas")), loader: newLoader(openapiclient.NewLocalCRDFiles(data.Crds(), "crds")),
wantErr: true, wantErr: true,
}, { }, {
name: "empty GVK", name: "empty GVK",
loader: newLoader(openapiclient.NewLocalSchemaFiles(data.Crds(), "schemas")), loader: newLoader(openapiclient.NewLocalCRDFiles(data.Crds(), "crds")),
document: []byte(`foo: bar`), document: []byte(`foo: bar`),
wantErr: true, wantErr: true,
}, { }, {
name: "not yaml", name: "not yaml",
loader: newLoader(openapiclient.NewLocalSchemaFiles(data.Crds(), "schemas")), loader: newLoader(openapiclient.NewLocalCRDFiles(data.Crds(), "crds")),
document: []byte(` document: []byte(`
foo foo
bar bar
@ -140,7 +128,7 @@ func Test_loader_Load(t *testing.T) {
wantErr: true, wantErr: true,
}, { }, {
name: "unknown GVK", name: "unknown GVK",
loader: newLoader(openapiclient.NewLocalSchemaFiles(data.Crds(), "schemas")), loader: newLoader(openapiclient.NewLocalCRDFiles(data.Crds(), "crds")),
document: loadFile("../../_testdata/resources/namespace.yaml"), document: loadFile("../../_testdata/resources/namespace.yaml"),
wantErr: true, wantErr: true,
}, { }, {