crossplane-runtime/pkg/meta/meta_test.go

1227 lines
27 KiB
Go

/*
Copyright 2019 The Crossplane 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 meta
import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/test"
)
const (
group = "coolstuff"
version = "v1"
groupVersion = group + "/" + version
kind = "coolresource"
namespace = "coolns"
name = "cool"
uid = types.UID("definitely-a-uuid")
)
func TestReferenceTo(t *testing.T) {
type args struct {
o metav1.Object
of schema.GroupVersionKind
}
tests := map[string]struct {
args
want *corev1.ObjectReference
}{
"WithTypeMeta": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
UID: uid,
},
},
of: schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
},
},
want: &corev1.ObjectReference{
APIVersion: groupVersion,
Kind: kind,
Namespace: namespace,
Name: name,
UID: uid,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := ReferenceTo(tc.o, tc.of)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("ReferenceTo(): -want, +got:\n%s", diff)
}
})
}
}
func TestTypedReferenceTo(t *testing.T) {
type args struct {
o metav1.Object
of schema.GroupVersionKind
}
tests := map[string]struct {
args
want *xpv1.TypedReference
}{
"WithTypeMeta": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
UID: uid,
},
},
of: schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
},
},
want: &xpv1.TypedReference{
APIVersion: groupVersion,
Kind: kind,
Name: name,
UID: uid,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := TypedReferenceTo(tc.o, tc.of)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("TypedReferenceTo(): -want, +got:\n%s", diff)
}
})
}
}
func TestAsOwner(t *testing.T) {
tests := map[string]struct {
r *xpv1.TypedReference
want metav1.OwnerReference
}{
"Successful": {
r: &xpv1.TypedReference{
APIVersion: groupVersion,
Kind: kind,
Name: name,
UID: uid,
},
want: metav1.OwnerReference{
APIVersion: groupVersion,
Kind: kind,
Name: name,
UID: uid,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := AsOwner(tc.r)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("AsOwner(): -want, +got:\n%s", diff)
}
})
}
}
func TestAsController(t *testing.T) {
flag := true
tests := map[string]struct {
r *xpv1.TypedReference
want metav1.OwnerReference
}{
"Successful": {
r: &xpv1.TypedReference{
APIVersion: groupVersion,
Kind: kind,
Name: name,
UID: uid,
},
want: metav1.OwnerReference{
APIVersion: groupVersion,
Kind: kind,
Name: name,
UID: uid,
Controller: &flag,
BlockOwnerDeletion: &flag,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := AsController(tc.r)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("AsController(): -want, +got:\n%s", diff)
}
})
}
}
func TestHaveSameController(t *testing.T) {
controller := true
controllerA := metav1.OwnerReference{
UID: uid,
Controller: &controller,
}
controllerB := metav1.OwnerReference{
UID: types.UID("a-different-uuid"),
Controller: &controller,
}
cases := map[string]struct {
a metav1.Object
b metav1.Object
want bool
}{
"SameController": {
a: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerA},
},
},
b: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerA},
},
},
want: true,
},
"AHasNoController": {
a: &corev1.Pod{},
b: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerB},
},
},
want: false,
},
"BHasNoController": {
a: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerA},
},
},
b: &corev1.Pod{},
want: false,
},
"ControllersDiffer": {
a: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerA},
},
},
b: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{controllerB},
},
},
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := HaveSameController(tc.a, tc.b)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("HaveSameController(...): -want, +got:\n%s", diff)
}
})
}
}
func TestNamespacedNameOf(t *testing.T) {
cases := map[string]struct {
r *corev1.ObjectReference
want types.NamespacedName
}{
"Success": {
r: &corev1.ObjectReference{Namespace: namespace, Name: name},
want: types.NamespacedName{Namespace: namespace, Name: name},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := NamespacedNameOf(tc.r)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("NamespacedNameOf(...): -want, +got:\n%s", diff)
}
})
}
}
func TestAddOwnerReference(t *testing.T) {
owner := metav1.OwnerReference{UID: uid}
other := metav1.OwnerReference{UID: "a-different-uuid"}
ctrlr := metav1.OwnerReference{UID: uid, Controller: func() *bool { c := true; return &c }()}
type args struct {
o metav1.Object
r metav1.OwnerReference
}
cases := map[string]struct {
args args
want []metav1.OwnerReference
}{
"NoExistingOwners": {
args: args{
o: &corev1.Pod{},
r: owner,
},
want: []metav1.OwnerReference{owner},
},
"UpdateExistingOwner": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{ctrlr},
},
},
r: owner,
},
want: []metav1.OwnerReference{owner},
},
"OwnedByAnotherObject": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{other},
},
},
r: owner,
},
want: []metav1.OwnerReference{other, owner},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
AddOwnerReference(tc.args.o, tc.args.r)
got := tc.args.o.GetOwnerReferences()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetOwnerReferences(...): -want, +got:\n%s", diff)
}
})
}
}
func TestAddControllerReference(t *testing.T) {
owner := metav1.OwnerReference{UID: uid}
other := metav1.OwnerReference{UID: "a-different-uuid"}
ctrlr := metav1.OwnerReference{UID: uid, Controller: func() *bool { c := true; return &c }()}
otrlr := metav1.OwnerReference{
Kind: "lame",
Name: "othercontroller",
UID: "a-different-uuid",
Controller: func() *bool { c := true; return &c }(),
}
type args struct {
o metav1.Object
r metav1.OwnerReference
}
type want struct {
owners []metav1.OwnerReference
err error
}
cases := map[string]struct {
args args
want want
}{
"NoExistingOwners": {
args: args{
o: &corev1.Pod{},
r: owner,
},
want: want{
owners: []metav1.OwnerReference{owner},
},
},
"UpdateExistingOwner": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{ctrlr},
},
},
r: owner,
},
want: want{
owners: []metav1.OwnerReference{owner},
},
},
"OwnedByAnotherObject": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{other},
},
},
r: owner,
},
want: want{
owners: []metav1.OwnerReference{other, owner},
},
},
"ControlledByAnotherObject": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
OwnerReferences: []metav1.OwnerReference{otrlr},
},
},
r: owner,
},
want: want{
owners: []metav1.OwnerReference{otrlr},
err: errors.Errorf("%s is already controlled by %s %s (UID %s)", name, otrlr.Kind, otrlr.Name, otrlr.UID),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
err := AddControllerReference(tc.args.o, tc.args.r)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("AddControllerReference(...): -want error, +got error:\n%s", diff)
}
got := tc.args.o.GetOwnerReferences()
if diff := cmp.Diff(tc.want.owners, got); diff != "" {
t.Errorf("tc.args.o.GetOwnerReferences(...): -want, +got:\n%s", diff)
}
})
}
}
func TestAddFinalizer(t *testing.T) {
finalizer := "fin"
funalizer := "fun"
type args struct {
o metav1.Object
finalizer string
}
cases := map[string]struct {
args args
want []string
}{
"NoExistingFinalizers": {
args: args{
o: &corev1.Pod{},
finalizer: finalizer,
},
want: []string{finalizer},
},
"FinalizerAlreadyExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{finalizer},
},
},
finalizer: finalizer,
},
want: []string{finalizer},
},
"AnotherFinalizerExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{funalizer},
},
},
finalizer: finalizer,
},
want: []string{funalizer, finalizer},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
AddFinalizer(tc.args.o, tc.args.finalizer)
got := tc.args.o.GetFinalizers()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetFinalizers(...): -want, +got:\n%s", diff)
}
})
}
}
func TestRemoveFinalizer(t *testing.T) {
finalizer := "fin"
funalizer := "fun"
type args struct {
o metav1.Object
finalizer string
}
cases := map[string]struct {
args args
want []string
}{
"NoExistingFinalizers": {
args: args{
o: &corev1.Pod{},
finalizer: finalizer,
},
want: nil,
},
"FinalizerExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{finalizer},
},
},
finalizer: finalizer,
},
want: []string{},
},
"AnotherFinalizerExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{finalizer, funalizer},
},
},
finalizer: finalizer,
},
want: []string{funalizer},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
RemoveFinalizer(tc.args.o, tc.args.finalizer)
got := tc.args.o.GetFinalizers()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetFinalizers(...): -want, +got:\n%s", diff)
}
})
}
}
func TestFinalizerExists(t *testing.T) {
finalizer := "fin"
funalizer := "fun"
type args struct {
o metav1.Object
finalizer string
}
cases := map[string]struct {
args args
want bool
}{
"NoExistingFinalizers": {
args: args{
o: &corev1.Pod{},
finalizer: finalizer,
},
want: false,
},
"FinalizerExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{finalizer},
},
},
finalizer: finalizer,
},
want: true,
},
"AnotherFinalizerExists": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Finalizers: []string{funalizer},
},
},
finalizer: finalizer,
},
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
if diff := cmp.Diff(tc.want, FinalizerExists(tc.args.o, tc.args.finalizer)); diff != "" {
t.Errorf("tc.args.o.GetFinalizers(...): -want, +got:\n%s", diff)
}
})
}
}
func TestAddLabels(t *testing.T) {
key, value := "key", "value"
existingKey, existingValue := "ekey", "evalue"
type args struct {
o metav1.Object
labels map[string]string
}
cases := map[string]struct {
args args
want map[string]string
}{
"ExistingLabels": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
existingKey: existingValue,
},
},
},
labels: map[string]string{key: value},
},
want: map[string]string{
existingKey: existingValue,
key: value,
},
},
"NoExistingLabels": {
args: args{
o: &corev1.Pod{},
labels: map[string]string{key: value},
},
want: map[string]string{key: value},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
AddLabels(tc.args.o, tc.args.labels)
got := tc.args.o.GetLabels()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetLabels(...): -want, +got:\n%s", diff)
}
})
}
}
func TestRemoveLabels(t *testing.T) {
keyA, valueA := "keyA", "valueA"
keyB, valueB := "keyB", "valueB"
type args struct {
o metav1.Object
labels []string
}
cases := map[string]struct {
args args
want map[string]string
}{
"ExistingLabels": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
keyA: valueA,
keyB: valueB,
},
},
},
labels: []string{keyA},
},
want: map[string]string{keyB: valueB},
},
"NoExistingLabels": {
args: args{
o: &corev1.Pod{},
labels: []string{keyA},
},
want: nil,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
RemoveLabels(tc.args.o, tc.args.labels...)
got := tc.args.o.GetLabels()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetLabels(...): -want, +got:\n%s", diff)
}
})
}
}
func TestAddAnnotations(t *testing.T) {
key, value := "key", "value"
existingKey, existingValue := "ekey", "evalue"
type args struct {
o metav1.Object
annotations map[string]string
}
cases := map[string]struct {
args args
want map[string]string
}{
"ExistingAnnotations": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
existingKey: existingValue,
},
},
},
annotations: map[string]string{key: value},
},
want: map[string]string{
existingKey: existingValue,
key: value,
},
},
"NoExistingAnnotations": {
args: args{
o: &corev1.Pod{},
annotations: map[string]string{key: value},
},
want: map[string]string{key: value},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
AddAnnotations(tc.args.o, tc.args.annotations)
got := tc.args.o.GetAnnotations()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetAnnotations(...): -want, +got:\n%s", diff)
}
})
}
}
func TestRemoveAnnotations(t *testing.T) {
keyA, valueA := "keyA", "valueA"
keyB, valueB := "keyB", "valueB"
type args struct {
o metav1.Object
annotations []string
}
cases := map[string]struct {
args args
want map[string]string
}{
"ExistingAnnotations": {
args: args{
o: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
keyA: valueA,
keyB: valueB,
},
},
},
annotations: []string{keyA},
},
want: map[string]string{keyB: valueB},
},
"NoExistingAnnotations": {
args: args{
o: &corev1.Pod{},
annotations: []string{keyA},
},
want: nil,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
RemoveAnnotations(tc.args.o, tc.args.annotations...)
got := tc.args.o.GetAnnotations()
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.args.o.GetAnnotations(...): -want, +got:\n%s", diff)
}
})
}
}
func TestWasDeleted(t *testing.T) {
now := metav1.Now()
cases := map[string]struct {
o metav1.Object
want bool
}{
"ObjectWasDeleted": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: &now}},
want: true,
},
"ObjectWasNotDeleted": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: nil}},
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := WasDeleted(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("WasDeleted(...): -want, +got:\n%s", diff)
}
})
}
}
func TestWasCreated(t *testing.T) {
now := metav1.Now()
zero := metav1.Time{}
cases := map[string]struct {
o metav1.Object
want bool
}{
"ObjectWasCreated": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{CreationTimestamp: now}},
want: true,
},
"ObjectWasNotCreated": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{CreationTimestamp: zero}},
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := WasCreated(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("WasCreated(...): -want, +got:\n%s", diff)
}
})
}
}
func TestGetExternalName(t *testing.T) {
cases := map[string]struct {
o metav1.Object
want string
}{
"ExternalNameExists": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalName: name}}},
want: name,
},
"NoExternalName": {
o: &corev1.Pod{},
want: "",
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := GetExternalName(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("GetExternalName(...): -want, +got:\n%s", diff)
}
})
}
}
func TestSetExternalName(t *testing.T) {
cases := map[string]struct {
o metav1.Object
name string
want metav1.Object
}{
"SetsTheCorrectKey": {
o: &corev1.Pod{},
name: name,
want: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalName: name}}},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
SetExternalName(tc.o, tc.name)
if diff := cmp.Diff(tc.want, tc.o); diff != "" {
t.Errorf("SetExternalName(...): -want, +got:\n%s", diff)
}
})
}
}
func TestGetExternalCreatePending(t *testing.T) {
now := time.Now().Round(time.Second)
cases := map[string]struct {
o metav1.Object
want time.Time
}{
"ExternalCreatePendingExists": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreatePending: now.Format(time.RFC3339)}}},
want: now,
},
"NoExternalCreatePending": {
o: &corev1.Pod{},
want: time.Time{},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := GetExternalCreatePending(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("GetExternalCreatePending(...): -want, +got:\n%s", diff)
}
})
}
}
func TestSetExternalCreatePending(t *testing.T) {
now := time.Now()
cases := map[string]struct {
o metav1.Object
t time.Time
want metav1.Object
}{
"SetsTheCorrectKey": {
o: &corev1.Pod{},
t: now,
want: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreatePending: now.Format(time.RFC3339)}}},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
SetExternalCreatePending(tc.o, tc.t)
if diff := cmp.Diff(tc.want, tc.o); diff != "" {
t.Errorf("SetExternalCreatePending(...): -want, +got:\n%s", diff)
}
})
}
}
func TestGetExternalCreateSucceeded(t *testing.T) {
now := time.Now().Round(time.Second)
cases := map[string]struct {
o metav1.Object
want time.Time
}{
"ExternalCreateTimeExists": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateSucceeded: now.Format(time.RFC3339)}}},
want: now,
},
"NoExternalCreateTime": {
o: &corev1.Pod{},
want: time.Time{},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := GetExternalCreateSucceeded(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("GetExternalCreateSucceeded(...): -want, +got:\n%s", diff)
}
})
}
}
func TestSetExternalCreateSucceeded(t *testing.T) {
now := time.Now()
cases := map[string]struct {
o metav1.Object
t time.Time
want metav1.Object
}{
"SetsTheCorrectKey": {
o: &corev1.Pod{},
t: now,
want: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateSucceeded: now.Format(time.RFC3339)}}},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
SetExternalCreateSucceeded(tc.o, tc.t)
if diff := cmp.Diff(tc.want, tc.o); diff != "" {
t.Errorf("SetExternalCreateSucceeded(...): -want, +got:\n%s", diff)
}
})
}
}
func TestGetExternalCreateFailed(t *testing.T) {
now := time.Now().Round(time.Second)
cases := map[string]struct {
o metav1.Object
want time.Time
}{
"ExternalCreateFailedExists": {
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateFailed: now.Format(time.RFC3339)}}},
want: now,
},
"NoExternalCreateFailed": {
o: &corev1.Pod{},
want: time.Time{},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := GetExternalCreateFailed(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("GetExternalCreateFailed(...): -want, +got:\n%s", diff)
}
})
}
}
func TestSetExternalCreateFailed(t *testing.T) {
now := time.Now()
cases := map[string]struct {
o metav1.Object
t time.Time
want metav1.Object
}{
"SetsTheCorrectKey": {
o: &corev1.Pod{},
t: now,
want: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{AnnotationKeyExternalCreateFailed: now.Format(time.RFC3339)}}},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
SetExternalCreateFailed(tc.o, tc.t)
if diff := cmp.Diff(tc.want, tc.o); diff != "" {
t.Errorf("SetExternalCreateFailed(...): -want, +got:\n%s", diff)
}
})
}
}
func TestExternalCreateSucceededDuring(t *testing.T) {
type args struct {
o metav1.Object
d time.Duration
}
cases := map[string]struct {
args args
want bool
}{
"NotYetSuccessfullyCreated": {
args: args{
o: &corev1.Pod{},
d: 1 * time.Minute,
},
want: false,
},
"SuccessfullyCreatedTooLongAgo": {
args: args{
o: func() metav1.Object {
o := &corev1.Pod{}
t := time.Now().Add(-2 * time.Minute)
SetExternalCreateSucceeded(o, t)
return o
}(),
d: 1 * time.Minute,
},
want: false,
},
"SuccessfullyCreatedWithinDuration": {
args: args{
o: func() metav1.Object {
o := &corev1.Pod{}
t := time.Now().Add(-30 * time.Second)
SetExternalCreateSucceeded(o, t)
return o
}(),
d: 1 * time.Minute,
},
want: true,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := ExternalCreateSucceededDuring(tc.args.o, tc.args.d)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("ExternalCreateSucceededDuring(...): -want, +got:\n%s", diff)
}
})
}
}
func TestExternalCreateIncomplete(t *testing.T) {
now := time.Now().Format(time.RFC3339)
earlier := time.Now().Add(-1 * time.Second).Format(time.RFC3339)
evenEarlier := time.Now().Add(-1 * time.Minute).Format(time.RFC3339)
cases := map[string]struct {
reason string
o metav1.Object
want bool
}{
"CreateNeverPending": {
reason: "If we've never called Create it can't be incomplete.",
o: &corev1.Pod{},
want: false,
},
"CreateSucceeded": {
reason: "If Create succeeded since it was pending, it's complete.",
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
AnnotationKeyExternalCreateFailed: evenEarlier,
AnnotationKeyExternalCreatePending: earlier,
AnnotationKeyExternalCreateSucceeded: now,
}}},
want: false,
},
"CreateFailed": {
reason: "If Create failed since it was pending, it's complete.",
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
AnnotationKeyExternalCreateSucceeded: evenEarlier,
AnnotationKeyExternalCreatePending: earlier,
AnnotationKeyExternalCreateFailed: now,
}}},
want: false,
},
"CreateNeverCompleted": {
reason: "If Create was pending but never succeeded or failed, it's incomplete.",
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
AnnotationKeyExternalCreatePending: earlier,
}}},
want: true,
},
"RecreateNeverCompleted": {
reason: "If Create is pending and there's an older success we're probably trying to recreate a deleted external resource, and it's incomplete.",
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
AnnotationKeyExternalCreateSucceeded: earlier,
AnnotationKeyExternalCreatePending: now,
}}},
want: true,
},
"RetryNeverCompleted": {
reason: "If Create is pending and there's an older failure we're probably trying to recreate a deleted external resource, and it's incomplete.",
o: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
AnnotationKeyExternalCreateFailed: earlier,
AnnotationKeyExternalCreatePending: now,
}}},
want: true,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := ExternalCreateIncomplete(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("ExternalCreateIncomplete(...): -want, +got:\n%s", diff)
}
})
}
}
func TestIsPaused(t *testing.T) {
cases := map[string]struct {
o metav1.Object
want bool
}{
"HasPauseAnnotationSetTrue": {
o: func() metav1.Object {
p := &corev1.Pod{}
p.SetAnnotations(map[string]string{
AnnotationKeyReconciliationPaused: "true",
})
return p
}(),
want: true,
},
"NoPauseAnnotation": {
o: &corev1.Pod{},
want: false,
},
"HasEmptyPauseAnnotation": {
o: func() metav1.Object {
p := &corev1.Pod{}
p.SetAnnotations(map[string]string{
AnnotationKeyReconciliationPaused: "",
})
return p
}(),
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := IsPaused(tc.o)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("IsPaused(...): -want, +got:\n%s", diff)
}
})
}
}