@@ -10,6 +10,7 @@ import (
10
10
"github.com/google/go-containerregistry/pkg/crane"
11
11
"github.com/stretchr/testify/assert"
12
12
"github.com/stretchr/testify/require"
13
+ appsv1 "k8s.io/api/apps/v1"
13
14
corev1 "k8s.io/api/core/v1"
14
15
networkingv1 "k8s.io/api/networking/v1"
15
16
rbacv1 "k8s.io/api/rbac/v1"
@@ -20,6 +21,7 @@ import (
20
21
"k8s.io/apimachinery/pkg/labels"
21
22
"k8s.io/apimachinery/pkg/types"
22
23
"k8s.io/apimachinery/pkg/util/rand"
24
+ "k8s.io/utils/ptr"
23
25
"sigs.k8s.io/controller-runtime/pkg/client"
24
26
25
27
ocv1 "github.com/operator-framework/operator-controller/api/v1"
@@ -204,20 +206,32 @@ func createClusterRoleAndBindingForSA(ctx context.Context, name string, sa *core
204
206
}
205
207
206
208
func testInit (t * testing.T ) (* ocv1.ClusterExtension , * ocv1.ClusterCatalog , * corev1.ServiceAccount , * corev1.Namespace ) {
207
- var err error
208
-
209
- clusterExtensionName := fmt .Sprintf ("clusterextension-%s" , rand .String (8 ))
209
+ ce , cc := testInitClusterExtensionClusterCatalog (t )
210
+ sa , ns := testInitServiceAccountNamespace (t , ce .Name )
211
+ return ce , cc , sa , ns
212
+ }
210
213
211
- ns , err := createNamespace ( context . Background (), clusterExtensionName )
212
- require . NoError ( t , err )
214
+ func testInitClusterExtensionClusterCatalog ( t * testing. T ) ( * ocv1. ClusterExtension , * ocv1. ClusterCatalog ) {
215
+ ceName := fmt . Sprintf ( "clusterextension-%s" , rand . String ( 8 ) )
213
216
214
- clusterExtension := & ocv1.ClusterExtension {
217
+ ce := & ocv1.ClusterExtension {
215
218
ObjectMeta : metav1.ObjectMeta {
216
- Name : clusterExtensionName ,
219
+ Name : ceName ,
217
220
},
218
221
}
219
222
220
- extensionCatalog , err := createTestCatalog (context .Background (), testCatalogName , os .Getenv (testCatalogRefEnvVar ))
223
+ cc , err := createTestCatalog (context .Background (), testCatalogName , os .Getenv (testCatalogRefEnvVar ))
224
+ require .NoError (t , err )
225
+
226
+ validateCatalogUnpack (t )
227
+
228
+ return ce , cc
229
+ }
230
+
231
+ func testInitServiceAccountNamespace (t * testing.T , clusterExtensionName string ) (* corev1.ServiceAccount , * corev1.Namespace ) {
232
+ var err error
233
+
234
+ ns , err := createNamespace (context .Background (), clusterExtensionName )
221
235
require .NoError (t , err )
222
236
223
237
name := types.NamespacedName {
@@ -228,9 +242,7 @@ func testInit(t *testing.T) (*ocv1.ClusterExtension, *ocv1.ClusterCatalog, *core
228
242
sa , err := createServiceAccount (context .Background (), name , clusterExtensionName )
229
243
require .NoError (t , err )
230
244
231
- validateCatalogUnpack (t )
232
-
233
- return clusterExtension , extensionCatalog , sa , ns
245
+ return sa , ns
234
246
}
235
247
236
248
func validateCatalogUnpack (t * testing.T ) {
@@ -292,35 +304,42 @@ func ensureNoExtensionResources(t *testing.T, clusterExtensionName string) {
292
304
}
293
305
294
306
func testCleanup (t * testing.T , cat * ocv1.ClusterCatalog , clusterExtension * ocv1.ClusterExtension , sa * corev1.ServiceAccount , ns * corev1.Namespace ) {
295
- t .Logf ("By deleting ClusterCatalog %q" , cat .Name )
296
- require .NoError (t , c .Delete (context .Background (), cat ))
297
- require .Eventually (t , func () bool {
298
- err := c .Get (context .Background (), types.NamespacedName {Name : cat .Name }, & ocv1.ClusterCatalog {})
299
- return errors .IsNotFound (err )
300
- }, pollDuration , pollInterval )
301
-
302
- t .Logf ("By deleting ClusterExtension %q" , clusterExtension .Name )
303
- require .NoError (t , c .Delete (context .Background (), clusterExtension ))
304
- require .Eventually (t , func () bool {
305
- err := c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, & ocv1.ClusterExtension {})
306
- return errors .IsNotFound (err )
307
- }, pollDuration , pollInterval )
307
+ if cat != nil {
308
+ t .Logf ("By deleting ClusterCatalog %q" , cat .Name )
309
+ require .NoError (t , c .Delete (context .Background (), cat ))
310
+ require .Eventually (t , func () bool {
311
+ err := c .Get (context .Background (), types.NamespacedName {Name : cat .Name }, & ocv1.ClusterCatalog {})
312
+ return errors .IsNotFound (err )
313
+ }, pollDuration , pollInterval )
314
+ }
308
315
309
- t .Logf ("By deleting ServiceAccount %q" , sa .Name )
310
- require .NoError (t , c .Delete (context .Background (), sa ))
311
- require .Eventually (t , func () bool {
312
- err := c .Get (context .Background (), types.NamespacedName {Name : sa .Name , Namespace : sa .Namespace }, & corev1.ServiceAccount {})
313
- return errors .IsNotFound (err )
314
- }, pollDuration , pollInterval )
316
+ if clusterExtension != nil {
317
+ t .Logf ("By deleting ClusterExtension %q" , clusterExtension .Name )
318
+ require .NoError (t , c .Delete (context .Background (), clusterExtension ))
319
+ require .Eventually (t , func () bool {
320
+ err := c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, & ocv1.ClusterExtension {})
321
+ return errors .IsNotFound (err )
322
+ }, pollDuration , pollInterval )
323
+ ensureNoExtensionResources (t , clusterExtension .Name )
324
+ }
315
325
316
- ensureNoExtensionResources (t , clusterExtension .Name )
326
+ if sa != nil {
327
+ t .Logf ("By deleting ServiceAccount %q" , sa .Name )
328
+ require .NoError (t , c .Delete (context .Background (), sa ))
329
+ require .Eventually (t , func () bool {
330
+ err := c .Get (context .Background (), types.NamespacedName {Name : sa .Name , Namespace : sa .Namespace }, & corev1.ServiceAccount {})
331
+ return errors .IsNotFound (err )
332
+ }, pollDuration , pollInterval )
333
+ }
317
334
318
- t .Logf ("By deleting Namespace %q" , ns .Name )
319
- require .NoError (t , c .Delete (context .Background (), ns ))
320
- require .Eventually (t , func () bool {
321
- err := c .Get (context .Background (), types.NamespacedName {Name : ns .Name }, & corev1.Namespace {})
322
- return errors .IsNotFound (err )
323
- }, pollDuration , pollInterval )
335
+ if ns != nil {
336
+ t .Logf ("By deleting Namespace %q" , ns .Name )
337
+ require .NoError (t , c .Delete (context .Background (), ns ))
338
+ require .Eventually (t , func () bool {
339
+ err := c .Get (context .Background (), types.NamespacedName {Name : ns .Name }, & corev1.Namespace {})
340
+ return errors .IsNotFound (err )
341
+ }, pollDuration , pollInterval )
342
+ }
324
343
}
325
344
326
345
func TestClusterExtensionInstallRegistry (t * testing.T ) {
@@ -882,22 +901,14 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T
882
901
}, pollDuration , pollInterval )
883
902
}
884
903
885
- func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed (t * testing.T ) {
904
+ func TestClusterExtensionRecoversFromNoNamespaceWhenFailureFixed (t * testing.T ) {
886
905
t .Log ("When a cluster extension is installed from a catalog" )
887
906
t .Log ("When the extension bundle format is registry+v1" )
888
907
889
- clusterExtension , extensionCatalog , _ , ns := testInit (t )
908
+ t .Log ("By not creating the Namespace and ServiceAccount" )
909
+ clusterExtension , extensionCatalog := testInitClusterExtensionClusterCatalog (t )
890
910
891
- name := rand .String (10 )
892
- sa := & corev1.ServiceAccount {
893
- ObjectMeta : metav1.ObjectMeta {
894
- Name : name ,
895
- Namespace : ns .Name ,
896
- },
897
- }
898
- err := c .Create (context .Background (), sa )
899
- require .NoError (t , err )
900
- defer testCleanup (t , extensionCatalog , clusterExtension , sa , ns )
911
+ defer testCleanup (t , extensionCatalog , clusterExtension , nil , nil )
901
912
defer utils .CollectTestArtifacts (t , artifactName , c , cfg )
902
913
903
914
clusterExtension .Spec = ocv1.ClusterExtensionSpec {
@@ -910,20 +921,127 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
910
921
},
911
922
},
912
923
},
913
- Namespace : ns .Name ,
924
+ Namespace : clusterExtension .Name ,
914
925
ServiceAccount : ocv1.ServiceAccountReference {
915
- Name : sa .Name ,
926
+ Name : clusterExtension .Name ,
916
927
},
917
928
}
929
+
918
930
t .Log ("It resolves the specified package with correct bundle path" )
919
931
t .Log ("By creating the ClusterExtension resource" )
920
932
require .NoError (t , c .Create (context .Background (), clusterExtension ))
921
933
922
- t .Log ("By eventually reporting a successful resolution and bundle path" )
934
+ t .Log ("By eventually reporting Progressing == True with Reason Retrying" )
935
+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
936
+ require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
937
+ cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeProgressing )
938
+ require .NotNil (ct , cond )
939
+ require .Equal (ct , metav1 .ConditionTrue , cond .Status )
940
+ require .Equal (ct , ocv1 .ReasonRetrying , cond .Reason )
941
+ }, pollDuration , pollInterval )
942
+
943
+ t .Log ("By eventually failing to install the package successfully due to no namespace" )
923
944
require .EventuallyWithT (t , func (ct * assert.CollectT ) {
924
945
require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
946
+ cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
947
+ require .NotNil (ct , cond )
948
+ require .Equal (ct , metav1 .ConditionUnknown , cond .Status )
949
+ require .Equal (ct , ocv1 .ReasonFailed , cond .Reason )
950
+ require .Contains (ct , cond .Message , fmt .Sprintf ("service account %q not found in namespace %q: unable to authenticate with the Kubernetes cluster." , clusterExtension .Name , clusterExtension .Name ))
925
951
}, pollDuration , pollInterval )
926
952
953
+ t .Log ("By creating the Namespace and ServiceAccount" )
954
+ sa , ns := testInitServiceAccountNamespace (t , clusterExtension .Name )
955
+ defer testCleanup (t , nil , nil , sa , ns )
956
+
957
+ // NOTE: In order to ensure predictable results we need to ensure we have a single
958
+ // known failure with a singular fix operation. Additionally, due to the exponential
959
+ // backoff of this eventually check we MUST ensure we do not touch the ClusterExtension
960
+ // after creating int the Namespace and ServiceAccount.
961
+ t .Log ("By eventually installing the package successfully" )
962
+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
963
+ require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
964
+ cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
965
+ require .NotNil (ct , cond )
966
+ require .Equal (ct , metav1 .ConditionTrue , cond .Status )
967
+ require .Equal (ct , ocv1 .ReasonSucceeded , cond .Reason )
968
+ require .Contains (ct , cond .Message , "Installed bundle" )
969
+ require .NotEmpty (ct , clusterExtension .Status .Install )
970
+ }, pollDuration , pollInterval )
971
+
972
+ t .Log ("By eventually reporting Progressing == True with Reason Success" )
973
+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
974
+ require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
975
+ cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeProgressing )
976
+ require .NotNil (ct , cond )
977
+ require .Equal (ct , metav1 .ConditionTrue , cond .Status )
978
+ require .Equal (ct , ocv1 .ReasonSucceeded , cond .Reason )
979
+ }, pollDuration , pollInterval )
980
+ }
981
+
982
+ func TestClusterExtensionRecoversFromExistingDeploymentWhenFailureFixed (t * testing.T ) {
983
+ t .Log ("When a cluster extension is installed from a catalog" )
984
+ t .Log ("When the extension bundle format is registry+v1" )
985
+
986
+ clusterExtension , extensionCatalog , sa , ns := testInit (t )
987
+
988
+ defer testCleanup (t , extensionCatalog , clusterExtension , sa , ns )
989
+ defer utils .CollectTestArtifacts (t , artifactName , c , cfg )
990
+
991
+ clusterExtension .Spec = ocv1.ClusterExtensionSpec {
992
+ Source : ocv1.SourceConfig {
993
+ SourceType : "Catalog" ,
994
+ Catalog : & ocv1.CatalogFilter {
995
+ PackageName : "test" ,
996
+ Selector : & metav1.LabelSelector {
997
+ MatchLabels : map [string ]string {"olm.operatorframework.io/metadata.name" : extensionCatalog .Name },
998
+ },
999
+ },
1000
+ },
1001
+ Namespace : clusterExtension .Name ,
1002
+ ServiceAccount : ocv1.ServiceAccountReference {
1003
+ Name : clusterExtension .Name ,
1004
+ },
1005
+ }
1006
+
1007
+ t .Log ("By creating a new Deployment that can not be adopted" )
1008
+ newDeployment := & appsv1.Deployment {
1009
+ ObjectMeta : metav1.ObjectMeta {
1010
+ Name : "test-operator" ,
1011
+ Namespace : clusterExtension .Name ,
1012
+ },
1013
+ Spec : appsv1.DeploymentSpec {
1014
+ Replicas : ptr .To (int32 (1 )),
1015
+ Selector : & metav1.LabelSelector {
1016
+ MatchLabels : map [string ]string {"app" : "test-operator" },
1017
+ },
1018
+ Template : corev1.PodTemplateSpec {
1019
+ ObjectMeta : metav1.ObjectMeta {
1020
+ Labels : map [string ]string {"app" : "test-operator" },
1021
+ },
1022
+ Spec : corev1.PodSpec {
1023
+ Containers : []corev1.Container {
1024
+ {
1025
+ Command : []string {"sleep" , "1000" },
1026
+ Image : "busybox" ,
1027
+ ImagePullPolicy : corev1 .PullAlways ,
1028
+ Name : "busybox" ,
1029
+ SecurityContext : & corev1.SecurityContext {
1030
+ RunAsNonRoot : ptr .To (true ),
1031
+ RunAsUser : ptr .To (int64 (1000 )),
1032
+ },
1033
+ },
1034
+ },
1035
+ },
1036
+ },
1037
+ },
1038
+ }
1039
+ require .NoError (t , c .Create (context .Background (), newDeployment ))
1040
+
1041
+ t .Log ("It resolves the specified package with correct bundle path" )
1042
+ t .Log ("By creating the ClusterExtension resource" )
1043
+ require .NoError (t , c .Create (context .Background (), clusterExtension ))
1044
+
927
1045
t .Log ("By eventually reporting Progressing == True with Reason Retrying" )
928
1046
require .EventuallyWithT (t , func (ct * assert.CollectT ) {
929
1047
require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
@@ -933,23 +1051,23 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
933
1051
require .Equal (ct , ocv1 .ReasonRetrying , cond .Reason )
934
1052
}, pollDuration , pollInterval )
935
1053
936
- t .Log ("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions " )
1054
+ t .Log ("By eventually failing to install the package successfully due to no adoption support " )
937
1055
require .EventuallyWithT (t , func (ct * assert.CollectT ) {
938
1056
require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
939
1057
cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
940
1058
require .NotNil (ct , cond )
941
1059
require .Equal (ct , metav1 .ConditionFalse , cond .Status )
942
1060
require .Equal (ct , ocv1 .ReasonFailed , cond .Reason )
943
- require .Equal (ct , "No bundle installed" , cond . Message )
1061
+ require .Contains (ct , cond . Message , "No bundle installed" )
944
1062
}, pollDuration , pollInterval )
945
1063
946
- t .Log ("By fixing the ServiceAccount permissions " )
947
- require .NoError (t , createClusterRoleAndBindingForSA (context .Background (), name , sa , clusterExtension . Name ))
1064
+ t .Log ("By deleting the new Deployment " )
1065
+ require .NoError (t , c . Delete (context .Background (), newDeployment ))
948
1066
949
1067
// NOTE: In order to ensure predictable results we need to ensure we have a single
950
1068
// known failure with a singular fix operation. Additionally, due to the exponential
951
1069
// backoff of this eventually check we MUST ensure we do not touch the ClusterExtension
952
- // after creating and binding the needed permissions to the ServiceAccount .
1070
+ // after deleting the Deployment .
953
1071
t .Log ("By eventually installing the package successfully" )
954
1072
require .EventuallyWithT (t , func (ct * assert.CollectT ) {
955
1073
require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
0 commit comments