Skip to content

Commit 1ac6810

Browse files
committed
Add unit tests verifying PR kubernetes-sigs#3193
1 parent 7606727 commit 1ac6810

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

hack/test-all.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ if [[ -n ${ARTIFACTS:-} ]]; then
2525
fi
2626

2727
result=0
28-
go test -v -race ${P_FLAG} ${MOD_OPT} ./... --ginkgo.fail-fast ${GINKGO_ARGS} || result=$?
28+
go test -v -race ${P_FLAG} ${MOD_OPT} ./pkg/controller/controllerutil ${GINKGO_ARGS} || result=$?
2929

3030
if [[ -n ${ARTIFACTS:-} ]]; then
3131
mkdir -p ${ARTIFACTS}

pkg/controller/controllerutil/controllerutil_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3030
"k8s.io/apimachinery/pkg/runtime"
3131
"k8s.io/apimachinery/pkg/types"
32+
"k8s.io/apimachinery/pkg/util/intstr"
3233
"k8s.io/client-go/kubernetes/scheme"
3334
"k8s.io/utils/ptr"
3435
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -454,6 +455,9 @@ var _ = Describe("Controllerutil", func() {
454455
var deplKey types.NamespacedName
455456
var specr controllerutil.MutateFn
456457

458+
var deplSpecWithDefaults appsv1.DeploymentSpec
459+
var specrWithDefaultsr controllerutil.MutateFn
460+
457461
BeforeEach(func() {
458462
deploy = &appsv1.Deployment{
459463
ObjectMeta: metav1.ObjectMeta{
@@ -483,12 +487,59 @@ var _ = Describe("Controllerutil", func() {
483487
},
484488
}
485489

490+
// deployment with all defaults set
491+
deplSpecWithDefaults = appsv1.DeploymentSpec{
492+
Selector: &metav1.LabelSelector{
493+
MatchLabels: map[string]string{"foo": "bar"},
494+
},
495+
Replicas: ptr.To(int32(1)),
496+
Template: corev1.PodTemplateSpec{
497+
ObjectMeta: metav1.ObjectMeta{
498+
Labels: map[string]string{
499+
"foo": "bar",
500+
},
501+
},
502+
Spec: corev1.PodSpec{
503+
RestartPolicy: corev1.RestartPolicyAlways,
504+
TerminationGracePeriodSeconds: ptr.To(int64(30)),
505+
DNSPolicy: corev1.DNSClusterFirst,
506+
SecurityContext: &corev1.PodSecurityContext{},
507+
SchedulerName: "default-scheduler",
508+
Containers: []corev1.Container{
509+
{
510+
Name: "busybox",
511+
Image: "busybox",
512+
TerminationMessagePath: "/dev/termination-log",
513+
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
514+
ImagePullPolicy: corev1.PullAlways,
515+
},
516+
},
517+
},
518+
},
519+
Strategy: appsv1.DeploymentStrategy{
520+
Type: appsv1.RollingUpdateDeploymentStrategyType,
521+
RollingUpdate: &appsv1.RollingUpdateDeployment{
522+
MaxUnavailable: &intstr.IntOrString{
523+
Type: intstr.String,
524+
StrVal: "25%",
525+
},
526+
MaxSurge: &intstr.IntOrString{
527+
Type: intstr.String,
528+
StrVal: "25%",
529+
},
530+
},
531+
},
532+
RevisionHistoryLimit: ptr.To(int32(10)),
533+
ProgressDeadlineSeconds: ptr.To(int32(600)),
534+
}
535+
486536
deplKey = types.NamespacedName{
487537
Name: deploy.Name,
488538
Namespace: deploy.Namespace,
489539
}
490540

491541
specr = deploymentSpecr(deploy, deplSpec)
542+
specrWithDefaultsr = deploymentSpecr(deploy, deplSpecWithDefaults)
492543
})
493544

494545
It("creates a new object if one doesn't exists", func() {
@@ -543,6 +594,59 @@ var _ = Describe("Controllerutil", func() {
543594
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
544595
})
545596

597+
// The next two tests verify that CreateOrUpdate will always report an update unless the
598+
// object has *all* of its defaults set in the mutate function.
599+
600+
It("is idempotent if all defaults are set", func() {
601+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specrWithDefaultsr)
602+
603+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
604+
Expect(err).NotTo(HaveOccurred())
605+
606+
Expect(deploy.Spec).To(BeEquivalentTo(deplSpecWithDefaults))
607+
608+
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specrWithDefaultsr)
609+
By("returning no error")
610+
Expect(err).NotTo(HaveOccurred())
611+
612+
By("returning OperationResultNone")
613+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
614+
})
615+
616+
It("is idempotent even if defaults aren't set", func() {
617+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)
618+
619+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
620+
Expect(err).NotTo(HaveOccurred())
621+
622+
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)
623+
By("returning no error")
624+
Expect(err).NotTo(HaveOccurred())
625+
626+
By("returning OperationResultNone")
627+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
628+
})
629+
630+
// The following test verifies that CreateOrUpdate will overwrite any changes made outside of
631+
// the mutate function. This is working correctly, but needs to be documented.
632+
633+
It("modifies objects that had non-identifier values erroneously pre-set", func() {
634+
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)
635+
636+
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
637+
Expect(err).NotTo(HaveOccurred())
638+
639+
// Change the object outside of the mutate function. Despite deploymentIdentity being
640+
// a literal no-op, this test fails because Replicas will be overwritten.
641+
deploy.Spec.Replicas = ptr.To(int32(5))
642+
_, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentIdentity)
643+
// This also fails, but that should be self-evident, as specr replaces the entire Spec.
644+
// _, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)
645+
646+
Expect(err).NotTo(HaveOccurred())
647+
Expect(deploy.Spec.Replicas).To(HaveValue(BeEquivalentTo(int32(5))))
648+
})
649+
546650
It("errors when MutateFn changes object name on creation", func() {
547651
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, func() error {
548652
Expect(specr()).To(Succeed())

0 commit comments

Comments
 (0)