@@ -17,6 +17,7 @@ limitations under the License.
17
17
package controllers
18
18
19
19
import (
20
+ "crypto/sha1"
20
21
"fmt"
21
22
"reflect"
22
23
"testing"
@@ -34,6 +35,7 @@ import (
34
35
addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
35
36
"sigs.k8s.io/cluster-api/internal/test/envtest"
36
37
"sigs.k8s.io/cluster-api/util"
38
+ "sigs.k8s.io/cluster-api/util/conditions"
37
39
)
38
40
39
41
const (
@@ -724,6 +726,195 @@ metadata:
724
726
t .Log ("Checking resource ConfigMap 2 has been updated" )
725
727
g .Eventually (configMapHasBeenUpdated (env , resourceConfigMap2Key , resourceConfigMap2 ), timeout ).Should (Succeed ())
726
728
})
729
+
730
+ t .Run ("Should reconcile a ClusterResourceSet with ApplyOnce strategy even when one of the resources already exist" , func (t * testing.T ) {
731
+ g := NewWithT (t )
732
+ ns := setup (t , g )
733
+ defer teardown (t , g , ns )
734
+
735
+ t .Log ("Updating the cluster with labels" )
736
+ testCluster .SetLabels (labels )
737
+ g .Expect (env .Update (ctx , testCluster )).To (Succeed ())
738
+
739
+ t .Log ("Creating resource CM before creating CRS" )
740
+ // This CM is defined in the data of "configmapName", which is included in the
741
+ // CRS we will create in this test
742
+ resourceConfigMap1 := configMap (
743
+ resourceConfigMap1Name ,
744
+ resourceConfigMapsNamespace ,
745
+ map [string ]string {
746
+ "created" : "before CRS" ,
747
+ },
748
+ )
749
+ g .Expect (env .Create (ctx , resourceConfigMap1 )).To (Succeed ())
750
+
751
+ t .Log ("Creating a ClusterResourceSet instance that has same labels as selector" )
752
+ clusterResourceSet := & addonsv1.ClusterResourceSet {
753
+ ObjectMeta : metav1.ObjectMeta {
754
+ Name : clusterResourceSetName ,
755
+ Namespace : ns .Name ,
756
+ },
757
+ Spec : addonsv1.ClusterResourceSetSpec {
758
+ Strategy : string (addonsv1 .ClusterResourceSetStrategyApplyOnce ),
759
+ ClusterSelector : metav1.LabelSelector {
760
+ MatchLabels : labels ,
761
+ },
762
+ Resources : []addonsv1.ResourceRef {{Name : configmapName , Kind : "ConfigMap" }, {Name : secretName , Kind : "Secret" }},
763
+ },
764
+ }
765
+
766
+ g .Expect (env .Create (ctx , clusterResourceSet )).To (Succeed ())
767
+
768
+ t .Log ("Checking resource ConfigMap 1 hasn't been updated" )
769
+ resourceConfigMap1Key := client.ObjectKey {
770
+ Namespace : resourceConfigMapsNamespace ,
771
+ Name : resourceConfigMap1Name ,
772
+ }
773
+ g .Eventually (configMapHasBeenUpdated (env , resourceConfigMap1Key , resourceConfigMap1 ), timeout ).Should (Succeed ())
774
+
775
+ t .Log ("Verifying resource ConfigMap 2 has been created" )
776
+ resourceConfigMap2Key := client.ObjectKey {
777
+ Namespace : resourceConfigMapsNamespace ,
778
+ Name : resourceConfigMap2Name ,
779
+ }
780
+ g .Eventually (func () error {
781
+ cm := & corev1.ConfigMap {}
782
+ return env .Get (ctx , resourceConfigMap2Key , cm )
783
+ }, timeout ).Should (Succeed ())
784
+ })
785
+
786
+ t .Run ("Should reconcile a ClusterResourceSet with ApplyOnce strategy even when there is an error, after the error has been corrected" , func (t * testing.T ) {
787
+ // To trigger an error in the middle of the reconciliation, we'll define a an object in a namespace that doesn't yet exist.
788
+ // We'll expect the CRS to reconcile all other objects except that one and bubble up the error.
789
+ // Once that happens, we'll go ahead a create the namespace. Then we'll expect the CRS to, eventually, create that remaining object.
790
+
791
+ g := NewWithT (t )
792
+ ns := setup (t , g )
793
+ defer teardown (t , g , ns )
794
+
795
+ t .Log ("Updating the cluster with labels" )
796
+ testCluster .SetLabels (labels )
797
+ g .Expect (env .Update (ctx , testCluster )).To (Succeed ())
798
+
799
+ t .Log ("Updating the test config map with the missing namespace resource" )
800
+ missingNamespace := randomNamespaceForTest (t )
801
+
802
+ resourceConfigMap1 := configMap (
803
+ resourceConfigMap1Name ,
804
+ resourceConfigMapsNamespace ,
805
+ map [string ]string {
806
+ "my_new_config" : "some_value" ,
807
+ },
808
+ )
809
+
810
+ resourceConfigMap1Content , err := yaml .Marshal (resourceConfigMap1 )
811
+ g .Expect (err ).NotTo (HaveOccurred ())
812
+
813
+ resourceConfigMapWithMissingNamespace := configMap (
814
+ "cm-missing-namespace" ,
815
+ missingNamespace ,
816
+ map [string ]string {
817
+ "my_new_config" : "this is all new" ,
818
+ },
819
+ )
820
+
821
+ resourceConfigMapMissingNamespaceContent , err := yaml .Marshal (resourceConfigMapWithMissingNamespace )
822
+ g .Expect (err ).NotTo (HaveOccurred ())
823
+
824
+ testConfigmap := configMap (
825
+ configmapName ,
826
+ ns .Name ,
827
+ map [string ]string {
828
+ "cm" : string (resourceConfigMap1Content ),
829
+ "problematic_cm" : string (resourceConfigMapMissingNamespaceContent ),
830
+ },
831
+ )
832
+
833
+ g .Expect (env .Update (ctx , testConfigmap )).To (Succeed ())
834
+
835
+ t .Log ("Creating a ClusterResourceSet instance that has same labels as selector" )
836
+ clusterResourceSet := & addonsv1.ClusterResourceSet {
837
+ ObjectMeta : metav1.ObjectMeta {
838
+ Name : clusterResourceSetName ,
839
+ Namespace : ns .Name ,
840
+ },
841
+ Spec : addonsv1.ClusterResourceSetSpec {
842
+ Strategy : string (addonsv1 .ClusterResourceSetStrategyApplyOnce ),
843
+ ClusterSelector : metav1.LabelSelector {
844
+ MatchLabels : labels ,
845
+ },
846
+ Resources : []addonsv1.ResourceRef {{Name : testConfigmap .Name , Kind : "ConfigMap" }, {Name : secretName , Kind : "Secret" }},
847
+ },
848
+ }
849
+
850
+ g .Expect (env .Create (ctx , clusterResourceSet )).To (Succeed ())
851
+
852
+ t .Log ("Verifying resource ConfigMap 1 has been created" )
853
+ resourceConfigMap1Key := client .ObjectKeyFromObject (resourceConfigMap1 )
854
+ g .Eventually (configMapHasBeenUpdated (env , resourceConfigMap1Key , resourceConfigMap1 ), timeout ).Should (Succeed ())
855
+
856
+ t .Log ("Verifying resource ConfigMap 2 has been created" )
857
+ resourceConfigMap2Key := client.ObjectKey {
858
+ Namespace : resourceConfigMapsNamespace ,
859
+ Name : resourceConfigMap2Name ,
860
+ }
861
+ g .Eventually (func () error {
862
+ cm := & corev1.ConfigMap {}
863
+ return env .Get (ctx , resourceConfigMap2Key , cm )
864
+ }, timeout ).Should (Succeed ())
865
+
866
+ t .Log ("Verifying CRS Binding failed marked the resource as not applied" )
867
+ g .Eventually (func (g Gomega ) {
868
+ clusterResourceSetBindingKey := client.ObjectKey {
869
+ Namespace : testCluster .Namespace ,
870
+ Name : testCluster .Name ,
871
+ }
872
+ binding := & addonsv1.ClusterResourceSetBinding {}
873
+ g .Expect (env .Get (ctx , clusterResourceSetBindingKey , binding )).To (Succeed ())
874
+
875
+ g .Expect (binding .Spec .Bindings ).To (HaveLen (1 ))
876
+ g .Expect (binding .Spec .Bindings [0 ].Resources ).To (HaveLen (2 ))
877
+
878
+ for _ , r := range binding .Spec .Bindings [0 ].Resources {
879
+ switch r .ResourceRef .Name {
880
+ case testConfigmap .Name :
881
+ g .Expect (r .Applied ).To (BeFalse (), "test-configmap should be not applied bc of missing namespace" )
882
+ case secretName :
883
+ g .Expect (r .Applied ).To (BeTrue (), "test-secret should be applied" )
884
+ }
885
+ }
886
+ }, timeout ).Should (Succeed ())
887
+
888
+ t .Log ("Verifying CRS has a false ResourcesApplied condition" )
889
+ g .Eventually (func (g Gomega ) {
890
+ clusterResourceSetKey := client .ObjectKeyFromObject (clusterResourceSet )
891
+ crs := & addonsv1.ClusterResourceSet {}
892
+ g .Expect (env .Get (ctx , clusterResourceSetKey , crs )).To (Succeed ())
893
+
894
+ appliedCondition := conditions .Get (crs , addonsv1 .ResourcesAppliedCondition )
895
+ g .Expect (appliedCondition ).NotTo (BeNil ())
896
+ g .Expect (appliedCondition .Status ).To (Equal (corev1 .ConditionFalse ))
897
+ g .Expect (appliedCondition .Reason ).To (Equal (addonsv1 .ApplyFailedReason ))
898
+ g .Expect (appliedCondition .Message ).To (ContainSubstring ("creating object /v1, Kind=ConfigMap %s/cm-missing-namespace" , missingNamespace ))
899
+ }, timeout ).Should (Succeed ())
900
+
901
+ t .Log ("Creating missing namespace" )
902
+ missingNs := & corev1.Namespace {
903
+ ObjectMeta : metav1.ObjectMeta {
904
+ Name : missingNamespace ,
905
+ },
906
+ }
907
+ g .Expect (env .Create (ctx , missingNs )).To (Succeed ())
908
+
909
+ t .Log ("Verifying CRS Binding has all resources applied" )
910
+ g .Eventually (clusterResourceSetBindingReady (env , testCluster ), timeout ).Should (BeTrue ())
911
+
912
+ t .Log ("Verifying resource ConfigMap with previsouly missing namespace has been created" )
913
+ g .Eventually (configMapHasBeenUpdated (env , client .ObjectKeyFromObject (resourceConfigMapWithMissingNamespace ), resourceConfigMapWithMissingNamespace ), timeout ).Should (Succeed ())
914
+
915
+ g .Expect (env .Delete (ctx , resourceConfigMapWithMissingNamespace )).To (Succeed ())
916
+ g .Expect (env .Delete (ctx , missingNs )).To (Succeed ())
917
+ })
727
918
}
728
919
729
920
func clusterResourceSetBindingReady (env * envtest.Environment , cluster * clusterv1.Cluster ) func () bool {
@@ -786,3 +977,10 @@ func configMap(name, namespace string, data map[string]string) *corev1.ConfigMap
786
977
Data : data ,
787
978
}
788
979
}
980
+
981
+ func randomNamespaceForTest (t testing.TB ) string {
982
+ h := sha1 .New ()
983
+ h .Write ([]byte (t .Name ()))
984
+ testNameHash := fmt .Sprintf ("%x" , h .Sum (nil ))
985
+ return "ns-" + testNameHash [:7 ] + "-" + util .RandomString (6 )
986
+ }
0 commit comments