Skip to content

Commit fbe6ef4

Browse files
committed
test: add spec hash tests
1 parent e602639 commit fbe6ef4

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

internal/controller/genericprovider_controller_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/runtime"
2727
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
28+
"k8s.io/utils/pointer"
2829
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2930
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
3031
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -462,6 +463,181 @@ func TestNewGenericProviderList(t *testing.T) {
462463
}
463464
}
464465

466+
func TestProviderSpecChanges(t *testing.T) {
467+
testCases := []struct {
468+
name string
469+
spec operatorv1.ProviderSpec
470+
updatedSpec operatorv1.ProviderSpec
471+
expectError bool
472+
}{
473+
{
474+
name: "same spec, hash annotation doesn't change",
475+
spec: operatorv1.ProviderSpec{
476+
Version: testCurrentVersion,
477+
FetchConfig: &operatorv1.FetchConfiguration{
478+
Selector: &metav1.LabelSelector{
479+
MatchLabels: map[string]string{
480+
"test": "dummy-config",
481+
},
482+
},
483+
},
484+
},
485+
updatedSpec: operatorv1.ProviderSpec{
486+
Version: testCurrentVersion,
487+
FetchConfig: &operatorv1.FetchConfiguration{
488+
Selector: &metav1.LabelSelector{
489+
MatchLabels: map[string]string{
490+
"test": "dummy-config",
491+
},
492+
},
493+
},
494+
},
495+
},
496+
{
497+
name: "add more replicas, hash annotation is updated",
498+
spec: operatorv1.ProviderSpec{
499+
Version: testCurrentVersion,
500+
FetchConfig: &operatorv1.FetchConfiguration{
501+
Selector: &metav1.LabelSelector{
502+
MatchLabels: map[string]string{
503+
"test": "dummy-config",
504+
},
505+
},
506+
},
507+
},
508+
updatedSpec: operatorv1.ProviderSpec{
509+
Version: testCurrentVersion,
510+
Deployment: &operatorv1.DeploymentSpec{
511+
Replicas: pointer.Int(2),
512+
},
513+
FetchConfig: &operatorv1.FetchConfiguration{
514+
Selector: &metav1.LabelSelector{
515+
MatchLabels: map[string]string{
516+
"test": "dummy-config",
517+
},
518+
},
519+
},
520+
},
521+
},
522+
{
523+
name: "upgrade to a non-existent version, hash annotation is empty",
524+
expectError: true,
525+
spec: operatorv1.ProviderSpec{
526+
Version: testCurrentVersion,
527+
FetchConfig: &operatorv1.FetchConfiguration{
528+
Selector: &metav1.LabelSelector{
529+
MatchLabels: map[string]string{
530+
"test": "dummy-config",
531+
},
532+
},
533+
},
534+
},
535+
updatedSpec: operatorv1.ProviderSpec{
536+
Version: "10000.0.0-NONEXISTENT",
537+
Deployment: &operatorv1.DeploymentSpec{
538+
Replicas: pointer.Int(2),
539+
},
540+
FetchConfig: &operatorv1.FetchConfiguration{
541+
Selector: &metav1.LabelSelector{
542+
MatchLabels: map[string]string{
543+
"test": "dummy-config",
544+
},
545+
},
546+
},
547+
},
548+
},
549+
}
550+
551+
for _, tc := range testCases {
552+
t.Run(tc.name, func(t *testing.T) {
553+
g := NewWithT(t)
554+
555+
specHash, err := calculateHash(tc.spec)
556+
g.Expect(err).ToNot(HaveOccurred())
557+
558+
updatedSpecHash, err := calculateHash(tc.spec)
559+
g.Expect(err).ToNot(HaveOccurred())
560+
561+
provider := &genericprovider.CoreProviderWrapper{
562+
CoreProvider: &operatorv1.CoreProvider{
563+
ObjectMeta: metav1.ObjectMeta{
564+
Name: "cluster-api",
565+
},
566+
Spec: operatorv1.CoreProviderSpec{
567+
ProviderSpec: tc.spec,
568+
},
569+
},
570+
}
571+
572+
namespace := "test-provider-spec-changes"
573+
574+
t.Log("Ensure namespace exists", namespace)
575+
g.Expect(env.EnsureNamespaceExists(ctx, namespace)).To(Succeed())
576+
577+
g.Expect(env.CreateAndWait(ctx, dummyConfigMap(namespace, testCurrentVersion))).To(Succeed())
578+
579+
provider.SetNamespace(namespace)
580+
t.Log("creating test provider", provider.GetName())
581+
g.Expect(env.CreateAndWait(ctx, provider.GetObject())).To(Succeed())
582+
583+
g.Eventually(generateExpectedResultChecker(provider, specHash, corev1.ConditionTrue), timeout).Should(BeEquivalentTo(true))
584+
585+
// Change provider spec
586+
provider.SetSpec(tc.updatedSpec)
587+
588+
// Set a label to ensure that provider was changed
589+
labels := provider.GetLabels()
590+
if labels == nil {
591+
labels = map[string]string{}
592+
}
593+
labels["my-label"] = "some-value"
594+
provider.SetLabels(labels)
595+
596+
g.Expect(env.Client.Update(ctx, provider.GetObject())).To(Succeed())
597+
598+
if !tc.expectError {
599+
g.Eventually(generateExpectedResultChecker(provider, updatedSpecHash, corev1.ConditionTrue), timeout).Should(BeEquivalentTo(true))
600+
} else {
601+
g.Eventually(generateExpectedResultChecker(provider, "", corev1.ConditionFalse), timeout).Should(BeEquivalentTo(true))
602+
}
603+
604+
// Clean up
605+
objs := []client.Object{provider.GetObject()}
606+
objs = append(objs, &corev1.ConfigMap{
607+
ObjectMeta: metav1.ObjectMeta{
608+
Name: testCurrentVersion,
609+
Namespace: namespace,
610+
},
611+
})
612+
613+
g.Expect(env.CleanupAndWait(ctx, objs...)).To(Succeed())
614+
})
615+
}
616+
}
617+
618+
func generateExpectedResultChecker(provider *genericprovider.CoreProviderWrapper, specHash string, condStatus corev1.ConditionStatus) func() bool {
619+
return func() bool {
620+
if err := env.Get(ctx, client.ObjectKeyFromObject(provider.GetObject()), provider.GetObject()); err != nil {
621+
return false
622+
}
623+
624+
// In case of error we don't want the spec annotation to be updated
625+
if provider.GetAnnotations()[appliedSpecHashAnnotation] != specHash {
626+
return false
627+
}
628+
629+
for _, cond := range provider.GetStatus().Conditions {
630+
if cond.Type == operatorv1.ProviderInstalledCondition {
631+
if cond.Status == condStatus {
632+
return true
633+
}
634+
}
635+
}
636+
637+
return false
638+
}
639+
}
640+
465641
func setupScheme() *runtime.Scheme {
466642
scheme := runtime.NewScheme()
467643
utilruntime.Must(corev1.AddToScheme(scheme))

0 commit comments

Comments
 (0)