kubeadm: generalise CreateOrUpdate etc.

This uses generics to generalise the various CreateOrUpdate,
CreateOrRetain etc. functions. Where appropriate, the context is added
as an initial argument to the new functions.

ConfigMapMutator isn't used anywhere else, so it's dropped in favour
of the private objectMutator added in this commit.

Signed-off-by: Stephen Kitt <skitt@redhat.com>
This commit is contained in:
Stephen Kitt 2023-10-08 22:16:45 -04:00
parent 4a0b0365ef
commit db4c509e71
No known key found for this signature in database
GPG Key ID: 1CC5FA453662A71D
2 changed files with 318 additions and 901 deletions

View File

@ -28,6 +28,7 @@ import (
rbac "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait"
@ -37,28 +38,40 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
// ConfigMapMutator is a function that mutates the given ConfigMap and optionally returns an error
type ConfigMapMutator func(*v1.ConfigMap) error
// objectMutator is a function that mutates the given runtime object and optionally returns an error
type objectMutator[T runtime.Object] func(T) error
// apiCallRetryInterval holds a local copy of apiCallRetryInterval for testing purposes
var apiCallRetryInterval = constants.KubernetesAPICallRetryInterval
// TODO: We should invent a dynamic mechanism for this using the dynamic client instead of hard-coding these functions per-type
type kubernetesInterface[T kubernetesObject] interface {
Create(ctx context.Context, obj T, opts metav1.CreateOptions) (T, error)
Get(ctx context.Context, name string, opts metav1.GetOptions) (T, error)
Update(ctx context.Context, obj T, opts metav1.UpdateOptions) (T, error)
}
// CreateOrUpdateConfigMap creates a ConfigMap if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
func CreateOrUpdateConfigMap(client clientset.Interface, cm *v1.ConfigMap) error {
type kubernetesObject interface {
runtime.Object
metav1.Object
}
// CreateOrUpdate creates a runtime object if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
func CreateOrUpdate[T kubernetesObject](ctx context.Context, client kubernetesInterface[T], obj T) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
err := wait.PollUntilContextTimeout(ctx,
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
// This uses a background context for API calls to avoid confusing callers that don't
// expect context-related errors.
ctx := context.Background()
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(ctx, cm, metav1.CreateOptions{}); err != nil {
if _, err := client.Create(ctx, obj, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create ConfigMap")
lastError = errors.Wrapf(err, "unable to create %T", obj)
return false, nil
}
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Update(ctx, cm, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update ConfigMap")
if _, err := client.Update(ctx, obj, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrapf(err, "unable to update %T", obj)
return false, nil
}
}
@ -70,19 +83,30 @@ func CreateOrUpdateConfigMap(client clientset.Interface, cm *v1.ConfigMap) error
return lastError
}
// CreateOrMutateConfigMap tries to create the ConfigMap provided as cm. If the resource exists already, the latest version will be fetched from
// the cluster and mutator callback will be called on it, then an Update of the mutated ConfigMap will be performed. This function is resilient
// to conflicts, and a retry will be issued if the ConfigMap was modified on the server between the refresh and the update (while the mutation was
// taking place)
func CreateOrMutateConfigMap(client clientset.Interface, cm *v1.ConfigMap, mutator ConfigMapMutator) error {
// CreateOrUpdateConfigMap creates a ConfigMap if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateConfigMap(client clientset.Interface, cm *v1.ConfigMap) error {
return CreateOrUpdate(context.Background(), client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace), cm)
}
// CreateOrMutate tries to create the provided object. If the resource exists already, the latest version will be fetched from
// the cluster and mutator callback will be called on it, then an Update of the mutated object will be performed. This function is resilient
// to conflicts, and a retry will be issued if the object was modified on the server between the refresh and the update (while the mutation was
// taking place).
func CreateOrMutate[T kubernetesObject](ctx context.Context, client kubernetesInterface[T], obj T, mutator objectMutator[T]) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
err := wait.PollUntilContextTimeout(ctx,
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(context.Background(), cm, metav1.CreateOptions{}); err != nil {
// This uses a background context for API calls to avoid confusing callers that don't
// expect context-related errors.
ctx := context.Background()
if _, err := client.Create(ctx, obj, metav1.CreateOptions{}); err != nil {
lastError = err
if apierrors.IsAlreadyExists(err) {
lastError = mutateConfigMap(client, metav1.ObjectMeta{Namespace: cm.ObjectMeta.Namespace, Name: cm.ObjectMeta.Name}, mutator)
lastError = mutate(ctx, client, metav1.ObjectMeta{Namespace: obj.GetNamespace(), Name: obj.GetName()}, mutator)
return lastError == nil, nil
}
return false, nil
@ -95,273 +119,138 @@ func CreateOrMutateConfigMap(client clientset.Interface, cm *v1.ConfigMap, mutat
return lastError
}
// mutateConfigMap takes a ConfigMap Object Meta (namespace and name), retrieves the resource from the server and tries to mutate it
// by calling to the mutator callback, then an Update of the mutated ConfigMap will be performed. This function is resilient
// CreateOrMutateConfigMap tries to create the ConfigMap provided as cm. If the resource exists already, the latest version will be fetched from
// the cluster and mutator callback will be called on it, then an Update of the mutated ConfigMap will be performed. This function is resilient
// to conflicts, and a retry will be issued if the ConfigMap was modified on the server between the refresh and the update (while the mutation was
// taking place).
func mutateConfigMap(client clientset.Interface, meta metav1.ObjectMeta, mutator ConfigMapMutator) error {
ctx := context.Background()
configMap, err := client.CoreV1().ConfigMaps(meta.Namespace).Get(ctx, meta.Name, metav1.GetOptions{})
//
// Deprecated: use CreateOrMutate() instead.
func CreateOrMutateConfigMap(client clientset.Interface, cm *v1.ConfigMap, mutator objectMutator[*v1.ConfigMap]) error {
return CreateOrMutate(context.Background(), client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace), cm, mutator)
}
// mutate takes an Object Meta (namespace and name), retrieves the resource from the server and tries to mutate it
// by calling to the mutator callback, then an Update of the mutated object will be performed. This function is resilient
// to conflicts, and a retry will be issued if the object was modified on the server between the refresh and the update (while the mutation was
// taking place).
func mutate[T kubernetesObject](ctx context.Context, client kubernetesInterface[T], meta metav1.ObjectMeta, mutator objectMutator[T]) error {
obj, err := client.Get(ctx, meta.Name, metav1.GetOptions{})
if err != nil {
return errors.Wrap(err, "unable to get ConfigMap")
return errors.Wrapf(err, "unable to get %T", obj)
}
if err = mutator(configMap); err != nil {
return errors.Wrap(err, "unable to mutate ConfigMap")
if err = mutator(obj); err != nil {
return errors.Wrapf(err, "unable to mutate %T", obj)
}
_, err = client.CoreV1().ConfigMaps(configMap.ObjectMeta.Namespace).Update(ctx, configMap, metav1.UpdateOptions{})
_, err = client.Update(ctx, obj, metav1.UpdateOptions{})
return err
}
// CreateOrRetainConfigMap creates a ConfigMap if the target resource doesn't exist. If the resource exists already, this function will retain the resource instead.
// CreateOrRetain creates a runtime object if the target resource doesn't exist.
// If the resource exists already, this function will retain the resource instead.
func CreateOrRetain[T kubernetesObject](ctx context.Context, client kubernetesInterface[T], obj T) error {
var lastError error
err := wait.PollUntilContextTimeout(ctx,
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
// This uses a background context for API calls to avoid confusing callers that don't
// expect context-related errors.
ctx := context.Background()
if _, err := client.Get(ctx, obj.GetName(), metav1.GetOptions{}); err != nil {
if !apierrors.IsNotFound(err) {
lastError = errors.Wrapf(err, "unable to get %T", obj)
return false, nil
}
if _, err := client.Create(ctx, obj, metav1.CreateOptions{}); err != nil {
lastError = errors.Wrapf(err, "unable to create %T", obj)
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
}
// CreateOrRetainConfigMap creates a ConfigMap if the target resource doesn't exist.
// If the resource exists already, this function will retain the resource instead.
//
// Deprecated: use CreateOrRetain() instead.
func CreateOrRetainConfigMap(client clientset.Interface, cm *v1.ConfigMap, configMapName string) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Get(ctx, configMapName, metav1.GetOptions{}); err != nil {
if !apierrors.IsNotFound(err) {
lastError = errors.Wrap(err, "unable to get ConfigMap")
return false, nil
}
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(ctx, cm, metav1.CreateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to create ConfigMap")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrRetain(context.Background(), client.CoreV1().ConfigMaps(cm.Namespace), cm)
}
// CreateOrUpdateSecret creates a Secret if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateSecret creates a Secret if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateSecret(client clientset.Interface, secret *v1.Secret) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.CoreV1().Secrets(secret.ObjectMeta.Namespace).Create(ctx, secret, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create Secret")
return false, nil
}
if _, err := client.CoreV1().Secrets(secret.ObjectMeta.Namespace).Update(ctx, secret, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update Secret")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.CoreV1().Secrets(secret.Namespace), secret)
}
// CreateOrUpdateServiceAccount creates a ServiceAccount if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateServiceAccount creates a ServiceAccount if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAccount) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.CoreV1().ServiceAccounts(sa.ObjectMeta.Namespace).Create(ctx, sa, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create ServicAccount")
return false, nil
}
if _, err := client.CoreV1().ServiceAccounts(sa.ObjectMeta.Namespace).Update(ctx, sa, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update ServicAccount")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.CoreV1().ServiceAccounts(sa.Namespace), sa)
}
// CreateOrUpdateDeployment creates a Deployment if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateDeployment creates a Deployment if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateDeployment(client clientset.Interface, deploy *apps.Deployment) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Create(ctx, deploy, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create Deployment")
return false, nil
}
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Update(ctx, deploy, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update Deployment")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.AppsV1().Deployments(deploy.Namespace), deploy)
}
// CreateOrRetainDeployment creates a Deployment if the target resource doesn't exist. If the resource exists already, this function will retain the resource instead.
// CreateOrRetainDeployment creates a Deployment if the target resource doesn't exist.
// If the resource exists already, this function will retain the resource instead.
//
// Deprecated: use CreateOrRetain() instead.
func CreateOrRetainDeployment(client clientset.Interface, deploy *apps.Deployment, deployName string) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Get(ctx, deployName, metav1.GetOptions{}); err != nil {
if !apierrors.IsNotFound(err) {
lastError = errors.Wrap(err, "unable to get Deployment")
return false, nil
}
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Create(ctx, deploy, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create Deployment")
return false, nil
}
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrRetain(context.Background(), client.AppsV1().Deployments(deploy.Namespace), deploy)
}
// CreateOrUpdateDaemonSet creates a DaemonSet if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateDaemonSet creates a DaemonSet if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateDaemonSet(client clientset.Interface, ds *apps.DaemonSet) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Create(ctx, ds, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create DaemonSet")
return false, nil
}
if _, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Update(ctx, ds, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update DaemonSet")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.AppsV1().DaemonSets(ds.Namespace), ds)
}
// CreateOrUpdateRole creates a Role if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateRole creates a Role if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateRole(client clientset.Interface, role *rbac.Role) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.RbacV1().Roles(role.ObjectMeta.Namespace).Create(ctx, role, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create Role")
return false, nil
}
if _, err := client.RbacV1().Roles(role.ObjectMeta.Namespace).Update(ctx, role, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update Role")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.RbacV1().Roles(role.Namespace), role)
}
// CreateOrUpdateRoleBinding creates a RoleBinding if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateRoleBinding creates a RoleBinding if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateRoleBinding(client clientset.Interface, roleBinding *rbac.RoleBinding) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Create(ctx, roleBinding, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create RoleBinding")
return false, nil
}
if _, err := client.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Update(ctx, roleBinding, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update RoleBinding")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.RbacV1().RoleBindings(roleBinding.Namespace), roleBinding)
}
// CreateOrUpdateClusterRole creates a ClusterRole if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateClusterRole creates a ClusterRole if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateClusterRole(client clientset.Interface, clusterRole *rbac.ClusterRole) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create ClusterRole")
return false, nil
}
if _, err := client.RbacV1().ClusterRoles().Update(ctx, clusterRole, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update ClusterRole")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.RbacV1().ClusterRoles(), clusterRole)
}
// CreateOrUpdateClusterRoleBinding creates a ClusterRoleBinding if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
// CreateOrUpdateClusterRoleBinding creates a ClusterRoleBinding if the target resource doesn't exist.
// If the resource exists already, this function will update the resource instead.
//
// Deprecated: use CreateOrUpdate() instead.
func CreateOrUpdateClusterRoleBinding(client clientset.Interface, clusterRoleBinding *rbac.ClusterRoleBinding) error {
var lastError error
err := wait.PollUntilContextTimeout(context.Background(),
apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
true, func(_ context.Context) (bool, error) {
ctx := context.Background()
if _, err := client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
lastError = errors.Wrap(err, "unable to create ClusterRoleBinding")
return false, nil
}
if _, err := client.RbacV1().ClusterRoleBindings().Update(ctx, clusterRoleBinding, metav1.UpdateOptions{}); err != nil {
lastError = errors.Wrap(err, "unable to update ClusterRoleBinding")
return false, nil
}
}
return true, nil
})
if err == nil {
return nil
}
return lastError
return CreateOrUpdate(context.Background(), client.RbacV1().ClusterRoleBindings(), clusterRoleBinding)
}
// PatchNodeOnce executes patchFn on the node object found by the node name.

View File

@ -18,6 +18,7 @@ package apiclient
import (
"context"
"fmt"
"os"
"testing"
"time"
@ -32,6 +33,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
clientsetfake "k8s.io/client-go/kubernetes/fake"
clientgotesting "k8s.io/client-go/testing"
@ -56,49 +58,49 @@ func TestMain(m *testing.M) {
os.Exit(exitVal)
}
func TestCreateOrUpdateConfigMap(t *testing.T) {
func testCreateOrUpdate[T kubernetesObject](t *testing.T, resource, resources string, empty T, clientBuilder func(kubernetes.Interface, T) kubernetesInterface[T]) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
nameFormat string
setupClient func(*clientsetfake.Clientset, string)
expectedError bool
}{
{
name: "create configmap success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
nameFormat: "create %s success",
setupClient: func(client *clientsetfake.Clientset, resources string) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create configmap returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
nameFormat: "create %s returns error",
setupClient: func(client *clientsetfake.Clientset, resources string) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "configmap exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
nameFormat: "%s exists, update it",
setupClient: func(client *clientsetfake.Clientset, resources string) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
client.PrependReactor("update", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "configmap exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
nameFormat: "%s exists, update error",
setupClient: func(client *clientsetfake.Clientset, resources string) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
client.PrependReactor("update", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
@ -107,10 +109,103 @@ func TestCreateOrUpdateConfigMap(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Run(fmt.Sprintf(tc.nameFormat, resource), func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client, resources)
err := CreateOrUpdate(context.Background(), clientBuilder(client, empty), empty)
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
})
}
}
func TestCreateOrUpdateConfigMap(t *testing.T) {
testCreateOrUpdate(t, "configmap", "configmaps", &v1.ConfigMap{},
func(client kubernetes.Interface, obj *v1.ConfigMap) kubernetesInterface[*v1.ConfigMap] {
return client.CoreV1().ConfigMaps(obj.ObjectMeta.Namespace)
})
}
func testCreateOrMutate[T kubernetesObject](t *testing.T, resource, resources string, empty T, clientBuilder func(kubernetes.Interface, T) kubernetesInterface[T]) {
tests := []struct {
nameFormat string
setupClient func(*clientsetfake.Clientset)
mutator objectMutator[T]
expectedError bool
}{
{
nameFormat: "create %s",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
client.PrependReactor("update", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
nameFormat: "create %s error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
nameFormat: "%s exists, mutate returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, empty, nil
})
},
mutator: func(T) error { return errors.New("") },
expectedError: true,
},
{
nameFormat: "%s exists, get returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
nameFormat: "%s exists, mutate returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, empty, nil
})
client.PrependReactor("update", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
mutator: func(T) error { return nil },
expectedError: true,
},
}
for _, tc := range tests {
t.Run(fmt.Sprintf(tc.nameFormat, resource), func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateConfigMap(client, &v1.ConfigMap{})
err := CreateOrMutate[T](context.Background(), clientBuilder(client, empty), empty, tc.mutator)
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
@ -119,84 +214,67 @@ func TestCreateOrUpdateConfigMap(t *testing.T) {
}
func TestCreateOrMutateConfigMap(t *testing.T) {
testCreateOrMutate(t, "configmap", "configmaps", &v1.ConfigMap{},
func(client kubernetes.Interface, obj *v1.ConfigMap) kubernetesInterface[*v1.ConfigMap] {
return client.CoreV1().ConfigMaps(obj.ObjectMeta.Namespace)
})
}
func testCreateOrRetain[T kubernetesObject](t *testing.T, resource, resources string, empty T, clientBuilder func(kubernetes.Interface, T) kubernetesInterface[T]) {
tests := []struct {
name string
nameFormat string
setupClient func(*clientsetfake.Clientset)
mutator func(*v1.ConfigMap) error
expectedError bool
}{
{
name: "create configmap",
nameFormat: "%s exists",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, empty, nil
})
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
},
expectedError: false,
},
{
nameFormat: "%s get returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
client.PrependReactor("update", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
},
expectedError: true,
},
{
nameFormat: "%s is not found, create it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create configmap error",
nameFormat: "%s is not found, create returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
client.PrependReactor("get", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", resources, func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
name: "configmap exists, mutate returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, &v1.ConfigMap{}, nil
})
},
mutator: func(*v1.ConfigMap) error { return errors.New("") },
expectedError: true,
},
{
name: "configmap exists, get returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
name: "configmap exists, mutate returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, &v1.ConfigMap{}, nil
})
client.PrependReactor("update", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
mutator: func(*v1.ConfigMap) error { return nil },
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Run(fmt.Sprintf(tc.nameFormat, resource), func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrMutateConfigMap(client, &v1.ConfigMap{}, tc.mutator)
err := CreateOrRetain[T](context.Background(), clientBuilder(client, empty), empty)
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
@ -205,623 +283,73 @@ func TestCreateOrMutateConfigMap(t *testing.T) {
}
func TestCreateOrRetainConfigMap(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "configmap exists",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, &v1.ConfigMap{}, nil
})
},
expectedError: false,
},
{
name: "configmap get returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
name: "configmap is not found, create it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "configmap is not found, create returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", "configmaps", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrRetainConfigMap(client, &v1.ConfigMap{}, "some-cm")
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrRetain(t, "configmap", "configmaps", &v1.ConfigMap{},
func(client kubernetes.Interface, obj *v1.ConfigMap) kubernetesInterface[*v1.ConfigMap] {
return client.CoreV1().ConfigMaps(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateSecret(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create secret success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create secret returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "secret exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "secret exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "secrets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateSecret(client, &v1.Secret{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "secret", "secrets", &v1.Secret{},
func(client kubernetes.Interface, obj *v1.Secret) kubernetesInterface[*v1.Secret] {
return client.CoreV1().Secrets(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateServiceAccount(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create serviceaccount success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create serviceaccount returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "serviceaccount exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "serviceaccount exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "serviceaccounts", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateServiceAccount(client, &v1.ServiceAccount{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "serviceaccount", "serviceaccounts", &v1.ServiceAccount{},
func(client kubernetes.Interface, obj *v1.ServiceAccount) kubernetesInterface[*v1.ServiceAccount] {
return client.CoreV1().ServiceAccounts(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateDeployment(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create deployment success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create deployment returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "deployment exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "deployment exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateDeployment(client, &apps.Deployment{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "deployment", "deployments", &apps.Deployment{},
func(client kubernetes.Interface, obj *apps.Deployment) kubernetesInterface[*apps.Deployment] {
return client.AppsV1().Deployments(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrRetainDeployment(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "deployment exists",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, &apps.Deployment{}, nil
})
},
expectedError: false,
},
{
name: "deployment get returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
{
name: "deployment is not found, create it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "deployment is not found, create returns an error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("get", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "name")
})
client.PrependReactor("create", "deployments", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrRetainDeployment(client, &apps.Deployment{}, "some-deployment")
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrRetain(t, "deployment", "deployments", &apps.Deployment{},
func(client kubernetes.Interface, obj *apps.Deployment) kubernetesInterface[*apps.Deployment] {
return client.AppsV1().Deployments(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateDaemonSet(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create daemonset success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create daemonset returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "daemonset exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "daemonset exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "daemonsets", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateDaemonSet(client, &apps.DaemonSet{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "daemonset", "daemonsets", &apps.DaemonSet{},
func(client kubernetes.Interface, obj *apps.DaemonSet) kubernetesInterface[*apps.DaemonSet] {
return client.AppsV1().DaemonSets(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateRole(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create role success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create role returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "role exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "role exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "roles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateRole(client, &rbac.Role{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "role", "roles", &rbac.Role{},
func(client kubernetes.Interface, obj *rbac.Role) kubernetesInterface[*rbac.Role] {
return client.RbacV1().Roles(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateRoleBindings(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create rolebinding success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create rolebinding returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "rolebinding exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "rolebinding exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "rolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateRoleBinding(client, &rbac.RoleBinding{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "rolebinding", "rolebindings", &rbac.RoleBinding{},
func(client kubernetes.Interface, obj *rbac.RoleBinding) kubernetesInterface[*rbac.RoleBinding] {
return client.RbacV1().RoleBindings(obj.ObjectMeta.Namespace)
})
}
}
func TestCreateOrUpdateClusterRole(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create clusterrole success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create clusterrole returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "clusterrole exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "clusterrole exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "clusterroles", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateClusterRole(client, &rbac.ClusterRole{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "clusterrole", "clusterroles", &rbac.ClusterRole{},
func(client kubernetes.Interface, obj *rbac.ClusterRole) kubernetesInterface[*rbac.ClusterRole] {
return client.RbacV1().ClusterRoles()
})
}
}
func TestCreateOrUpdateClusterRoleBindings(t *testing.T) {
tests := []struct {
name string
setupClient func(*clientsetfake.Clientset)
expectedError bool
}{
{
name: "create clusterrolebinding success",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "create clusterrolebinding returns error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unknown error")
})
},
expectedError: true,
},
{
name: "clusterrolebinding exists, update it",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
},
expectedError: false,
},
{
name: "clusterrolebinding exists, update error",
setupClient: func(client *clientsetfake.Clientset) {
client.PrependReactor("create", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, apierrors.NewAlreadyExists(schema.GroupResource{}, "name")
})
client.PrependReactor("update", "clusterrolebindings", func(clientgotesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("")
})
},
expectedError: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset()
tc.setupClient(client)
err := CreateOrUpdateClusterRoleBinding(client, &rbac.ClusterRoleBinding{})
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
}
testCreateOrUpdate(t, "clusterrolebinding", "clusterrolebindings", &rbac.ClusterRoleBinding{},
func(client kubernetes.Interface, obj *rbac.ClusterRoleBinding) kubernetesInterface[*rbac.ClusterRoleBinding] {
return client.RbacV1().ClusterRoleBindings()
})
}
}
func TestPatchNodeOnce(t *testing.T) {