mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-19 01:40:13 +00:00
710 lines
24 KiB
Go
710 lines
24 KiB
Go
/*
|
|
Copyright 2018 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.
|
|
*/
|
|
|
|
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/onsi/ginkgo/v2"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
v1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
storagev1 "k8s.io/api/storage/v1"
|
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/client-go/tools/cache"
|
|
"k8s.io/kubernetes/test/e2e/framework"
|
|
e2etestfiles "k8s.io/kubernetes/test/e2e/framework/testfiles"
|
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
|
)
|
|
|
|
// LoadFromManifests loads .yaml or .json manifest files and returns
|
|
// all items that it finds in them. It supports all items for which
|
|
// there is a factory registered in factories and .yaml files with
|
|
// multiple items separated by "---". Files are accessed via the
|
|
// "testfiles" package, which means they can come from a file system
|
|
// or be built into the binary.
|
|
//
|
|
// LoadFromManifests has some limitations:
|
|
// - aliases are not supported (i.e. use serviceAccountName instead of the deprecated serviceAccount,
|
|
// https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1)
|
|
// and silently ignored
|
|
// - the latest stable API version for each item is used, regardless of what
|
|
// is specified in the manifest files
|
|
func LoadFromManifests(files ...string) ([]interface{}, error) {
|
|
var items []interface{}
|
|
err := visitManifests(func(data []byte) error {
|
|
// Ignore any additional fields for now, just determine what we have.
|
|
var what What
|
|
if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), data, &what); err != nil {
|
|
return fmt.Errorf("decode TypeMeta: %w", err)
|
|
}
|
|
|
|
factory := factories[what]
|
|
if factory == nil {
|
|
return fmt.Errorf("item of type %+v not supported", what)
|
|
}
|
|
|
|
object := factory.New()
|
|
if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), data, object); err != nil {
|
|
return fmt.Errorf("decode %+v: %w", what, err)
|
|
}
|
|
items = append(items, object)
|
|
return nil
|
|
}, files...)
|
|
|
|
return items, err
|
|
}
|
|
|
|
func visitManifests(cb func([]byte) error, files ...string) error {
|
|
for _, fileName := range files {
|
|
data, err := e2etestfiles.Read(fileName)
|
|
if err != nil {
|
|
framework.Failf("reading manifest file: %v", err)
|
|
}
|
|
|
|
// Split at the "---" separator before working on
|
|
// individual item. Only works for .yaml.
|
|
//
|
|
// We need to split ourselves because we need access
|
|
// to each original chunk of data for
|
|
// runtime.DecodeInto. kubectl has its own
|
|
// infrastructure for this, but that is a lot of code
|
|
// with many dependencies.
|
|
items := bytes.Split(data, []byte("\n---"))
|
|
|
|
for _, item := range items {
|
|
if err := cb(item); err != nil {
|
|
return fmt.Errorf("%s: %w", fileName, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PatchItems modifies the given items in place such that each test
|
|
// gets its own instances, to avoid conflicts between different tests
|
|
// and between tests and normal deployments.
|
|
//
|
|
// This is done by:
|
|
// - creating namespaced items inside the test's namespace
|
|
// - changing the name of non-namespaced items like ClusterRole
|
|
//
|
|
// PatchItems has some limitations:
|
|
// - only some common items are supported, unknown ones trigger an error
|
|
// - only the latest stable API version for each item is supported
|
|
func PatchItems(f *framework.Framework, driverNamespace *v1.Namespace, items ...interface{}) error {
|
|
for _, item := range items {
|
|
// Uncomment when debugging the loading and patching of items.
|
|
// Logf("patching original content of %T:\n%s", item, PrettyPrint(item))
|
|
if err := patchItemRecursively(f, driverNamespace, item); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateItems creates the items. Each of them must be an API object
|
|
// of a type that is registered in Factory.
|
|
//
|
|
// It returns either a cleanup function or an error, but never both.
|
|
//
|
|
// Cleaning up after a test can be triggered in two ways:
|
|
// - the test invokes the returned cleanup function,
|
|
// usually in an AfterEach
|
|
// - the test suite terminates, potentially after
|
|
// skipping the test's AfterEach (https://github.com/onsi/ginkgo/issues/222)
|
|
//
|
|
// PatchItems has the some limitations as LoadFromManifests:
|
|
// - only some common items are supported, unknown ones trigger an error
|
|
// - only the latest stable API version for each item is supported
|
|
func CreateItems(ctx context.Context, f *framework.Framework, ns *v1.Namespace, items ...interface{}) error {
|
|
var result error
|
|
for _, item := range items {
|
|
// Each factory knows which item(s) it supports, so try each one.
|
|
done := false
|
|
description := describeItem(item)
|
|
// Uncomment this line to get a full dump of the entire item.
|
|
// description = fmt.Sprintf("%s:\n%s", description, PrettyPrint(item))
|
|
framework.Logf("creating %s", description)
|
|
for _, factory := range factories {
|
|
destructor, err := factory.Create(ctx, f, ns, item)
|
|
if destructor != nil {
|
|
ginkgo.DeferCleanup(framework.IgnoreNotFound(destructor), framework.AnnotatedLocation(fmt.Sprintf("deleting %s", description)))
|
|
}
|
|
if err == nil {
|
|
done = true
|
|
break
|
|
} else if !errors.Is(err, errorItemNotSupported) {
|
|
result = err
|
|
break
|
|
}
|
|
}
|
|
if result == nil && !done {
|
|
result = fmt.Errorf("item of type %T not supported", item)
|
|
break
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// CreateFromManifests is a combination of LoadFromManifests,
|
|
// PatchItems, patching with an optional custom function,
|
|
// and CreateItems.
|
|
func CreateFromManifests(ctx context.Context, f *framework.Framework, driverNamespace *v1.Namespace, patch func(item interface{}) error, files ...string) error {
|
|
items, err := LoadFromManifests(files...)
|
|
if err != nil {
|
|
return fmt.Errorf("CreateFromManifests: %w", err)
|
|
}
|
|
if err := PatchItems(f, driverNamespace, items...); err != nil {
|
|
return err
|
|
}
|
|
if patch != nil {
|
|
for _, item := range items {
|
|
if err := patch(item); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return CreateItems(ctx, f, driverNamespace, items...)
|
|
}
|
|
|
|
// What is a subset of metav1.TypeMeta which (in contrast to
|
|
// metav1.TypeMeta itself) satisfies the runtime.Object interface.
|
|
type What struct {
|
|
Kind string `json:"kind"`
|
|
}
|
|
|
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new What.
|
|
func (in *What) DeepCopy() *What {
|
|
return &What{Kind: in.Kind}
|
|
}
|
|
|
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out.
|
|
func (in *What) DeepCopyInto(out *What) {
|
|
out.Kind = in.Kind
|
|
}
|
|
|
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
func (in *What) DeepCopyObject() runtime.Object {
|
|
return &What{Kind: in.Kind}
|
|
}
|
|
|
|
// GetObjectKind returns the ObjectKind schema
|
|
func (in *What) GetObjectKind() schema.ObjectKind {
|
|
return nil
|
|
}
|
|
|
|
// ItemFactory provides support for creating one particular item.
|
|
// The type gets exported because other packages might want to
|
|
// extend the set of pre-defined factories.
|
|
type ItemFactory interface {
|
|
// New returns a new empty item.
|
|
New() runtime.Object
|
|
|
|
// Create is responsible for creating the item. It returns an
|
|
// error or a cleanup function for the created item.
|
|
// If the item is of an unsupported type, it must return
|
|
// an error that has errorItemNotSupported as cause.
|
|
Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, item interface{}) (func(ctx context.Context) error, error)
|
|
}
|
|
|
|
// describeItem always returns a string that describes the item,
|
|
// usually by calling out to cache.MetaNamespaceKeyFunc which
|
|
// concatenates namespace (if set) and name. If that fails, the entire
|
|
// item gets converted to a string.
|
|
func describeItem(item interface{}) string {
|
|
key, err := cache.MetaNamespaceKeyFunc(item)
|
|
if err == nil && key != "" {
|
|
return fmt.Sprintf("%T: %s", item, key)
|
|
}
|
|
return fmt.Sprintf("%T: %s", item, item)
|
|
}
|
|
|
|
// errorItemNotSupported is the error that Create methods
|
|
// must return or wrap when they don't support the given item.
|
|
var errorItemNotSupported = errors.New("not supported")
|
|
|
|
var factories = map[What]ItemFactory{
|
|
{"ClusterRole"}: &clusterRoleFactory{},
|
|
{"ClusterRoleBinding"}: &clusterRoleBindingFactory{},
|
|
{"CSIDriver"}: &csiDriverFactory{},
|
|
{"DaemonSet"}: &daemonSetFactory{},
|
|
{"ReplicaSet"}: &replicaSetFactory{},
|
|
{"Role"}: &roleFactory{},
|
|
{"RoleBinding"}: &roleBindingFactory{},
|
|
{"Secret"}: &secretFactory{},
|
|
{"Service"}: &serviceFactory{},
|
|
{"ServiceAccount"}: &serviceAccountFactory{},
|
|
{"StatefulSet"}: &statefulSetFactory{},
|
|
{"Deployment"}: &deploymentFactory{},
|
|
{"StorageClass"}: &storageClassFactory{},
|
|
{"CustomResourceDefinition"}: &customResourceDefinitionFactory{},
|
|
}
|
|
|
|
// PatchName makes the name of some item unique by appending the
|
|
// generated unique name.
|
|
func PatchName(f *framework.Framework, item *string) {
|
|
if *item != "" {
|
|
*item = *item + "-" + f.UniqueName
|
|
}
|
|
}
|
|
|
|
// PatchNamespace moves the item into the test's namespace. Not
|
|
// all items can be namespaced. For those, the name also needs to be
|
|
// patched.
|
|
func PatchNamespace(f *framework.Framework, driverNamespace *v1.Namespace, item *string) {
|
|
if driverNamespace != nil {
|
|
*item = driverNamespace.GetName()
|
|
return
|
|
}
|
|
|
|
if f.Namespace != nil {
|
|
*item = f.Namespace.GetName()
|
|
}
|
|
}
|
|
|
|
func patchItemRecursively(f *framework.Framework, driverNamespace *v1.Namespace, item interface{}) error {
|
|
switch item := item.(type) {
|
|
case *rbacv1.Subject:
|
|
PatchNamespace(f, driverNamespace, &item.Namespace)
|
|
case *rbacv1.RoleRef:
|
|
// TODO: avoid hard-coding this special name. Perhaps add a Framework.PredefinedRoles
|
|
// which contains all role names that are defined cluster-wide before the test starts?
|
|
// All those names are exempt from renaming. That list could be populated by querying
|
|
// and get extended by tests.
|
|
if item.Name != "e2e-test-privileged-psp" {
|
|
PatchName(f, &item.Name)
|
|
}
|
|
case *rbacv1.ClusterRole:
|
|
PatchName(f, &item.Name)
|
|
case *rbacv1.Role:
|
|
PatchNamespace(f, driverNamespace, &item.Namespace)
|
|
// Roles are namespaced, but because for RoleRef above we don't
|
|
// know whether the referenced role is a ClusterRole or Role
|
|
// and therefore always renames, we have to do the same here.
|
|
PatchName(f, &item.Name)
|
|
case *storagev1.StorageClass:
|
|
PatchName(f, &item.Name)
|
|
case *storagev1.CSIDriver:
|
|
PatchName(f, &item.Name)
|
|
case *v1.ServiceAccount:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
case *v1.Secret:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
case *rbacv1.ClusterRoleBinding:
|
|
PatchName(f, &item.Name)
|
|
for i := range item.Subjects {
|
|
if err := patchItemRecursively(f, driverNamespace, &item.Subjects[i]); err != nil {
|
|
return fmt.Errorf("%T: %w", f, err)
|
|
}
|
|
}
|
|
if err := patchItemRecursively(f, driverNamespace, &item.RoleRef); err != nil {
|
|
return fmt.Errorf("%T: %w", f, err)
|
|
}
|
|
case *rbacv1.RoleBinding:
|
|
PatchNamespace(f, driverNamespace, &item.Namespace)
|
|
for i := range item.Subjects {
|
|
if err := patchItemRecursively(f, driverNamespace, &item.Subjects[i]); err != nil {
|
|
return fmt.Errorf("%T: %w", f, err)
|
|
}
|
|
}
|
|
if err := patchItemRecursively(f, driverNamespace, &item.RoleRef); err != nil {
|
|
return fmt.Errorf("%T: %w", f, err)
|
|
}
|
|
case *v1.Service:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
case *appsv1.StatefulSet:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
|
|
return err
|
|
}
|
|
if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
|
|
return err
|
|
}
|
|
case *appsv1.Deployment:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
|
|
return err
|
|
}
|
|
if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
|
|
return err
|
|
}
|
|
case *appsv1.DaemonSet:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
|
|
return err
|
|
}
|
|
if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
|
|
return err
|
|
}
|
|
case *appsv1.ReplicaSet:
|
|
PatchNamespace(f, driverNamespace, &item.ObjectMeta.Namespace)
|
|
if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
|
|
return err
|
|
}
|
|
if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
|
|
return err
|
|
}
|
|
case *apiextensionsv1.CustomResourceDefinition:
|
|
// Do nothing. Patching name to all CRDs won't always be the expected behavior.
|
|
default:
|
|
return fmt.Errorf("missing support for patching item of type %T", item)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// The individual factories all follow the same template, but with
|
|
// enough differences in types and functions that copy-and-paste
|
|
// looked like the least dirty approach. Perhaps one day Go will have
|
|
// generics.
|
|
|
|
type serviceAccountFactory struct{}
|
|
|
|
func (f *serviceAccountFactory) New() runtime.Object {
|
|
return &v1.ServiceAccount{}
|
|
}
|
|
|
|
func (*serviceAccountFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*v1.ServiceAccount)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
client := f.ClientSet.CoreV1().ServiceAccounts(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create ServiceAccount: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type clusterRoleFactory struct{}
|
|
|
|
func (f *clusterRoleFactory) New() runtime.Object {
|
|
return &rbacv1.ClusterRole{}
|
|
}
|
|
|
|
func (*clusterRoleFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*rbacv1.ClusterRole)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
framework.Logf("Define cluster role %v", item.GetName())
|
|
client := f.ClientSet.RbacV1().ClusterRoles()
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create ClusterRole: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type clusterRoleBindingFactory struct{}
|
|
|
|
func (f *clusterRoleBindingFactory) New() runtime.Object {
|
|
return &rbacv1.ClusterRoleBinding{}
|
|
}
|
|
|
|
func (*clusterRoleBindingFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*rbacv1.ClusterRoleBinding)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.RbacV1().ClusterRoleBindings()
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create ClusterRoleBinding: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type roleFactory struct{}
|
|
|
|
func (f *roleFactory) New() runtime.Object {
|
|
return &rbacv1.Role{}
|
|
}
|
|
|
|
func (*roleFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*rbacv1.Role)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.RbacV1().Roles(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create Role: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type roleBindingFactory struct{}
|
|
|
|
func (f *roleBindingFactory) New() runtime.Object {
|
|
return &rbacv1.RoleBinding{}
|
|
}
|
|
|
|
func (*roleBindingFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*rbacv1.RoleBinding)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.RbacV1().RoleBindings(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create RoleBinding: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type serviceFactory struct{}
|
|
|
|
func (f *serviceFactory) New() runtime.Object {
|
|
return &v1.Service{}
|
|
}
|
|
|
|
func (*serviceFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*v1.Service)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.CoreV1().Services(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create Service: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type statefulSetFactory struct{}
|
|
|
|
func (f *statefulSetFactory) New() runtime.Object {
|
|
return &appsv1.StatefulSet{}
|
|
}
|
|
|
|
func (*statefulSetFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*appsv1.StatefulSet)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.AppsV1().StatefulSets(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create StatefulSet: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type deploymentFactory struct{}
|
|
|
|
func (f *deploymentFactory) New() runtime.Object {
|
|
return &appsv1.Deployment{}
|
|
}
|
|
|
|
func (*deploymentFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*appsv1.Deployment)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.AppsV1().Deployments(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create Deployment: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type daemonSetFactory struct{}
|
|
|
|
func (f *daemonSetFactory) New() runtime.Object {
|
|
return &appsv1.DaemonSet{}
|
|
}
|
|
|
|
func (*daemonSetFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*appsv1.DaemonSet)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.AppsV1().DaemonSets(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create DaemonSet: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type replicaSetFactory struct{}
|
|
|
|
func (f *replicaSetFactory) New() runtime.Object {
|
|
return &appsv1.ReplicaSet{}
|
|
}
|
|
|
|
func (*replicaSetFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*appsv1.ReplicaSet)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.AppsV1().ReplicaSets(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create ReplicaSet: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type storageClassFactory struct{}
|
|
|
|
func (f *storageClassFactory) New() runtime.Object {
|
|
return &storagev1.StorageClass{}
|
|
}
|
|
|
|
func (*storageClassFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*storagev1.StorageClass)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.StorageV1().StorageClasses()
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create StorageClass: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type csiDriverFactory struct{}
|
|
|
|
func (f *csiDriverFactory) New() runtime.Object {
|
|
return &storagev1.CSIDriver{}
|
|
}
|
|
|
|
func (*csiDriverFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*storagev1.CSIDriver)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.StorageV1().CSIDrivers()
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create CSIDriver: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type secretFactory struct{}
|
|
|
|
func (f *secretFactory) New() runtime.Object {
|
|
return &v1.Secret{}
|
|
}
|
|
|
|
func (*secretFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
item, ok := i.(*v1.Secret)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
client := f.ClientSet.CoreV1().Secrets(ns.Name)
|
|
if _, err := client.Create(ctx, item, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create Secret: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return client.Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
type customResourceDefinitionFactory struct{}
|
|
|
|
func (f *customResourceDefinitionFactory) New() runtime.Object {
|
|
return &apiextensionsv1.CustomResourceDefinition{}
|
|
}
|
|
|
|
func (*customResourceDefinitionFactory) Create(ctx context.Context, f *framework.Framework, ns *v1.Namespace, i interface{}) (func(ctx context.Context) error, error) {
|
|
var err error
|
|
unstructCRD := &unstructured.Unstructured{}
|
|
gvr := schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"}
|
|
|
|
item, ok := i.(*apiextensionsv1.CustomResourceDefinition)
|
|
if !ok {
|
|
return nil, errorItemNotSupported
|
|
}
|
|
|
|
unstructCRD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, err = f.DynamicClient.Resource(gvr).Create(ctx, unstructCRD, metav1.CreateOptions{}); err != nil {
|
|
return nil, fmt.Errorf("create CustomResourceDefinition: %w", err)
|
|
}
|
|
return func(ctx context.Context) error {
|
|
return f.DynamicClient.Resource(gvr).Delete(ctx, item.GetName(), metav1.DeleteOptions{})
|
|
}, nil
|
|
}
|
|
|
|
// PrettyPrint returns a human-readable representation of an item.
|
|
func PrettyPrint(item interface{}) string {
|
|
data, err := json.MarshalIndent(item, "", " ")
|
|
if err == nil {
|
|
return string(data)
|
|
}
|
|
return fmt.Sprintf("%+v", item)
|
|
}
|
|
|
|
// patchContainerImages replaces the specified Container Registry with a custom
|
|
// one provided via the KUBE_TEST_REPO_LIST env variable
|
|
func patchContainerImages(containers []v1.Container) error {
|
|
var err error
|
|
for i, c := range containers {
|
|
containers[i].Image, err = imageutils.ReplaceRegistryInImageURL(c.Image)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|