mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
feat: add e2e framework and verify image new test (#4094)
Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
parent
91ce9f9abd
commit
e1db7c9814
15 changed files with 378 additions and 0 deletions
92
test/e2e/framework/client/client.go
Normal file
92
test/e2e/framework/client/client.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kyverno/kyverno/test/e2e"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/id"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/resource"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/utils"
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
CreateResource(resource.Resource) *unstructured.Unstructured
|
||||||
|
GetResource(id.Id) *unstructured.Unstructured
|
||||||
|
DeleteResource(id.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
t *testing.T
|
||||||
|
client *e2e.E2EClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(t *testing.T) Client {
|
||||||
|
c, err := e2e.NewE2EClient()
|
||||||
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
|
return &client{t, c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) CreateResource(resource resource.Resource) *unstructured.Unstructured {
|
||||||
|
u := resource.Unstructured()
|
||||||
|
ginkgo.By(fmt.Sprintf("Creating %s : %s", resource.Gvr(), utils.Key(u)))
|
||||||
|
var err error
|
||||||
|
if u.GetNamespace() != "" {
|
||||||
|
u, err = c.client.CreateNamespacedResource(resource.Gvr(), u.GetNamespace(), u)
|
||||||
|
} else {
|
||||||
|
u, err = c.client.CreateClusteredResource(resource.Gvr(), u)
|
||||||
|
}
|
||||||
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
|
c.t.Cleanup(func() {
|
||||||
|
c.DeleteResource(id.New(resource.Gvr(), u.GetNamespace(), u.GetName()))
|
||||||
|
})
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) DeleteResource(id id.Id) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Deleting %s : %s", id.GetGvr(), utils.Key(id)))
|
||||||
|
var err error
|
||||||
|
if id.IsClustered() {
|
||||||
|
err = c.client.DeleteClusteredResource(id.GetGvr(), id.GetName())
|
||||||
|
} else {
|
||||||
|
err = c.client.DeleteNamespacedResource(id.GetGvr(), id.GetNamespace(), id.GetName())
|
||||||
|
}
|
||||||
|
if !apierrors.IsNotFound(err) {
|
||||||
|
err = e2e.GetWithRetry(1*time.Second, 15, func() error {
|
||||||
|
if id.IsClustered() {
|
||||||
|
_, err = c.client.GetClusteredResource(id.GetGvr(), id.GetName())
|
||||||
|
} else {
|
||||||
|
_, err = c.client.GetNamespacedResource(id.GetGvr(), id.GetNamespace(), id.GetName())
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("resource still exists: %s, %s", id.GetGvr(), utils.Key(id))
|
||||||
|
}
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetResource(id id.Id) *unstructured.Unstructured {
|
||||||
|
ginkgo.By(fmt.Sprintf("Getting %s : %s", id.GetGvr(), utils.Key(id)))
|
||||||
|
var u *unstructured.Unstructured
|
||||||
|
err := e2e.GetWithRetry(1*time.Second, 15, func() error {
|
||||||
|
var err error
|
||||||
|
if id.IsClustered() {
|
||||||
|
u, err = c.client.GetClusteredResource(id.GetGvr(), id.GetName())
|
||||||
|
} else {
|
||||||
|
u, err = c.client.GetNamespacedResource(id.GetGvr(), id.GetNamespace(), id.GetName())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||||
|
return u
|
||||||
|
}
|
27
test/e2e/framework/framework.go
Normal file
27
test/e2e/framework/framework.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package framework
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/client"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/step"
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup(t *testing.T) {
|
||||||
|
gomega.RegisterTestingT(t)
|
||||||
|
if os.Getenv("E2E") == "" {
|
||||||
|
t.Skip("Skipping E2E Test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(t *testing.T, steps ...step.Step) {
|
||||||
|
ginkgo.By("Creating client ...")
|
||||||
|
client := client.New(t)
|
||||||
|
for _, step := range steps {
|
||||||
|
step(client)
|
||||||
|
}
|
||||||
|
ginkgo.By("Cleaning up ...")
|
||||||
|
}
|
14
test/e2e/framework/gvr/gvr.go
Normal file
14
test/e2e/framework/gvr/gvr.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package gvr
|
||||||
|
|
||||||
|
import "github.com/kyverno/kyverno/test/e2e"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ClusterPolicy = e2e.GetGVR("kyverno.io", "v1", "clusterpolicies")
|
||||||
|
Namespace = e2e.GetGVR("", "v1", "namespaces")
|
||||||
|
ClusterRole = e2e.GetGVR("rbac.authorization.k8s.io", "v1", "clusterroles")
|
||||||
|
ClusterRoleBinding = e2e.GetGVR("rbac.authorization.k8s.io", "v1", "clusterrolebindings")
|
||||||
|
Role = e2e.GetGVR("rbac.authorization.k8s.io", "v1", "roles")
|
||||||
|
RoleBinding = e2e.GetGVR("rbac.authorization.k8s.io", "v1", "rolebindings")
|
||||||
|
ConfigMap = e2e.GetGVR("", "v1", "configmaps")
|
||||||
|
NetworkPolicy = e2e.GetGVR("networking.k8s.io", "v1", "networkpolicies")
|
||||||
|
)
|
26
test/e2e/framework/id/clustered.go
Normal file
26
test/e2e/framework/id/clustered.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package id
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/gvr"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Clustered(gvr schema.GroupVersionResource, name string) Id {
|
||||||
|
return New(gvr, "", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterPolicy(name string) Id {
|
||||||
|
return Clustered(gvr.ClusterPolicy, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterRole(name string) Id {
|
||||||
|
return Clustered(gvr.ClusterRole, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterRoleBinding(name string) Id {
|
||||||
|
return Clustered(gvr.ClusterRoleBinding, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Namespace(name string) Id {
|
||||||
|
return Clustered(gvr.Namespace, name)
|
||||||
|
}
|
18
test/e2e/framework/id/id.go
Normal file
18
test/e2e/framework/id/id.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package id
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Id struct {
|
||||||
|
gvr schema.GroupVersionResource
|
||||||
|
ns string
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(gvr schema.GroupVersionResource, ns, name string) Id { return Id{gvr, ns, name} }
|
||||||
|
func (r Id) GetGvr() schema.GroupVersionResource { return r.gvr }
|
||||||
|
func (r Id) GetNamespace() string { return r.ns }
|
||||||
|
func (r Id) GetName() string { return r.name }
|
||||||
|
func (r Id) IsClustered() bool { return r.ns == "" }
|
||||||
|
func (r Id) IsNamespaced() bool { return !r.IsClustered() }
|
26
test/e2e/framework/resource/clustered.go
Normal file
26
test/e2e/framework/resource/clustered.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/gvr"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Clustered(gvr schema.GroupVersionResource, raw []byte) Resource {
|
||||||
|
return Resource{gvr, "", raw}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterPolicy(raw []byte) Resource {
|
||||||
|
return Clustered(gvr.ClusterPolicy, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterRole(raw []byte) Resource {
|
||||||
|
return Clustered(gvr.ClusterRole, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClusterRoleBinding(raw []byte) Resource {
|
||||||
|
return Clustered(gvr.ClusterRoleBinding, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Namespace(raw []byte) Resource {
|
||||||
|
return Clustered(gvr.Namespace, raw)
|
||||||
|
}
|
22
test/e2e/framework/resource/namespaced.go
Normal file
22
test/e2e/framework/resource/namespaced.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/gvr"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Namespaced(gvr schema.GroupVersionResource, ns string, raw []byte) Resource {
|
||||||
|
return Resource{gvr, ns, raw}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Role(ns string, raw []byte) Resource {
|
||||||
|
return Namespaced(gvr.Role, ns, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoleBinding(ns string, raw []byte) Resource {
|
||||||
|
return Namespaced(gvr.RoleBinding, ns, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigMap(ns string, raw []byte) Resource {
|
||||||
|
return Namespaced(gvr.ConfigMap, ns, raw)
|
||||||
|
}
|
32
test/e2e/framework/resource/resource.go
Normal file
32
test/e2e/framework/resource/resource.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
gvr schema.GroupVersionResource
|
||||||
|
ns string
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) Gvr() schema.GroupVersionResource { return r.gvr }
|
||||||
|
func (r *Resource) Namespace() string { return r.ns }
|
||||||
|
func (r *Resource) Data() []byte { return r.data }
|
||||||
|
func (r *Resource) IsClustered() bool { return r.ns == "" }
|
||||||
|
func (r *Resource) IsNamespaced() bool { return !r.IsClustered() }
|
||||||
|
|
||||||
|
func (r *Resource) Unstructured() *unstructured.Unstructured {
|
||||||
|
var u unstructured.Unstructured
|
||||||
|
gomega.Expect(yaml.Unmarshal(r.data, &u)).To(gomega.Succeed())
|
||||||
|
// TODO: set namespace ?
|
||||||
|
// TODO: ensure GV(R/K) ?
|
||||||
|
return &u
|
||||||
|
}
|
||||||
|
|
||||||
|
func Resources(resources ...Resource) []Resource {
|
||||||
|
return resources
|
||||||
|
}
|
12
test/e2e/framework/step/by.go
Normal file
12
test/e2e/framework/step/by.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package step
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/client"
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func By(message string) Step {
|
||||||
|
return func(client.Client) {
|
||||||
|
ginkgo.By(message)
|
||||||
|
}
|
||||||
|
}
|
23
test/e2e/framework/step/expect.go
Normal file
23
test/e2e/framework/step/expect.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package step
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/client"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/id"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/utils"
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourceExpectation func(*unstructured.Unstructured)
|
||||||
|
|
||||||
|
func ExpectResource(id id.Id, expectations ...ResourceExpectation) Step {
|
||||||
|
return func(client client.Client) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Checking resource expectations (%s : %s) ...", id.GetGvr(), utils.Key(id)))
|
||||||
|
resource := client.GetResource(id)
|
||||||
|
for _, expectation := range expectations {
|
||||||
|
expectation(resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
test/e2e/framework/step/policy.go
Normal file
17
test/e2e/framework/step/policy.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package step
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/common"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/client"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/resource"
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateClusterPolicy(data []byte) Step {
|
||||||
|
return func(client client.Client) {
|
||||||
|
ginkgo.By("Creating cluster policy ...")
|
||||||
|
policy := client.CreateResource(resource.ClusterPolicy(data))
|
||||||
|
gomega.Expect(common.PolicyCreated(policy.GetName())).To(gomega.Succeed())
|
||||||
|
}
|
||||||
|
}
|
7
test/e2e/framework/step/step.go
Normal file
7
test/e2e/framework/step/step.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package step
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Step func(client.Client)
|
14
test/e2e/framework/utils/named.go
Normal file
14
test/e2e/framework/utils/named.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
type Named interface {
|
||||||
|
GetNamespace() string
|
||||||
|
GetName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Key(obj Named) string {
|
||||||
|
n, ns := obj.GetName(), obj.GetNamespace()
|
||||||
|
if ns == "" {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return ns + "/" + n
|
||||||
|
}
|
|
@ -203,3 +203,27 @@ spec:
|
||||||
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
|
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
|
||||||
-----END PUBLIC KEY-----
|
-----END PUBLIC KEY-----
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
var cpolVerifyImages = []byte(`
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: verify-images
|
||||||
|
spec:
|
||||||
|
validationFailureAction: enforce
|
||||||
|
rules:
|
||||||
|
- name: check-image-sig
|
||||||
|
match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
verifyImages:
|
||||||
|
- image: "harbor2.zoller.com/cosign/*"
|
||||||
|
mutateDigest: false
|
||||||
|
key: |-
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpNlOGZ323zMlhs4bcKSpAKQvbcWi
|
||||||
|
5ZLRmijm6SqXDy0Fp0z0Eal+BekFnLzs8rUXUaXlhZ3hNudlgFJH+nFNMw==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
`)
|
||||||
|
|
|
@ -8,8 +8,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kyverno/kyverno/test/e2e"
|
"github.com/kyverno/kyverno/test/e2e"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/id"
|
||||||
|
"github.com/kyverno/kyverno/test/e2e/framework/step"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -149,3 +153,23 @@ func TestImageVerify(t *testing.T) {
|
||||||
e2eClient.DeleteClusteredResource(crdGVR, crdName)
|
e2eClient.DeleteClusteredResource(crdGVR, crdName)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_BoolFields(t *testing.T) {
|
||||||
|
framework.Setup(t)
|
||||||
|
framework.Run(t,
|
||||||
|
step.CreateClusterPolicy(cpolVerifyImages),
|
||||||
|
step.By("Checking spec.rules[0].verifyImages[0].mutateDigest is false ..."),
|
||||||
|
step.ExpectResource(id.ClusterPolicy("verify-images"), func(resource *unstructured.Unstructured) {
|
||||||
|
rules, found, err := unstructured.NestedSlice(resource.UnstructuredContent(), "spec", "rules")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(found).To(BeTrue())
|
||||||
|
verifyImages, found, err := unstructured.NestedSlice(rules[0].(map[string]interface{}), "verifyImages")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(found).To(BeTrue())
|
||||||
|
mutateDigest, found, err := unstructured.NestedBool(verifyImages[0].(map[string]interface{}), "mutateDigest")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(found).To(BeTrue())
|
||||||
|
Expect(mutateDigest).To(BeFalse())
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue