@@ -10,6 +10,7 @@ import (
1010 "github.com/google/go-containerregistry/pkg/crane"
1111 "github.com/stretchr/testify/assert"
1212 "github.com/stretchr/testify/require"
13+ appsv1 "k8s.io/api/apps/v1"
1314 corev1 "k8s.io/api/core/v1"
1415 networkingv1 "k8s.io/api/networking/v1"
1516 rbacv1 "k8s.io/api/rbac/v1"
@@ -20,6 +21,7 @@ import (
2021 "k8s.io/apimachinery/pkg/labels"
2122 "k8s.io/apimachinery/pkg/types"
2223 "k8s.io/apimachinery/pkg/util/rand"
24+ "k8s.io/utils/ptr"
2325 "sigs.k8s.io/controller-runtime/pkg/client"
2426
2527 ocv1 "github.com/operator-framework/operator-controller/api/v1"
@@ -204,20 +206,32 @@ func createClusterRoleAndBindingForSA(ctx context.Context, name string, sa *core
204206}
205207
206208func 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+ }
210213
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 ) )
213216
214- clusterExtension := & ocv1.ClusterExtension {
217+ ce := & ocv1.ClusterExtension {
215218 ObjectMeta : metav1.ObjectMeta {
216- Name : clusterExtensionName ,
219+ Name : ceName ,
217220 },
218221 }
219222
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 )
221235 require .NoError (t , err )
222236
223237 name := types.NamespacedName {
@@ -228,9 +242,7 @@ func testInit(t *testing.T) (*ocv1.ClusterExtension, *ocv1.ClusterCatalog, *core
228242 sa , err := createServiceAccount (context .Background (), name , clusterExtensionName )
229243 require .NoError (t , err )
230244
231- validateCatalogUnpack (t )
232-
233- return clusterExtension , extensionCatalog , sa , ns
245+ return sa , ns
234246}
235247
236248func validateCatalogUnpack (t * testing.T ) {
@@ -292,35 +304,42 @@ func ensureNoExtensionResources(t *testing.T, clusterExtensionName string) {
292304}
293305
294306func 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+ }
308315
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+ }
315325
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+ }
317334
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+ }
324343}
325344
326345func TestClusterExtensionInstallRegistry (t * testing.T ) {
@@ -882,22 +901,14 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T
882901 }, pollDuration , pollInterval )
883902}
884903
885- func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed (t * testing.T ) {
904+ func TestClusterExtensionRecoversFromNoNamespaceWhenFailureFixed (t * testing.T ) {
886905 t .Log ("When a cluster extension is installed from a catalog" )
887906 t .Log ("When the extension bundle format is registry+v1" )
888907
889- clusterExtension , extensionCatalog , _ , ns := testInit (t )
908+ t .Log ("By not creating the Namespace and ServiceAccount" )
909+ clusterExtension , extensionCatalog := testInitClusterExtensionClusterCatalog (t )
890910
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 )
901912 defer utils .CollectTestArtifacts (t , artifactName , c , cfg )
902913
903914 clusterExtension .Spec = ocv1.ClusterExtensionSpec {
@@ -910,20 +921,127 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
910921 },
911922 },
912923 },
913- Namespace : ns .Name ,
924+ Namespace : clusterExtension .Name ,
914925 ServiceAccount : ocv1.ServiceAccountReference {
915- Name : sa .Name ,
926+ Name : clusterExtension .Name ,
916927 },
917928 }
929+
918930 t .Log ("It resolves the specified package with correct bundle path" )
919931 t .Log ("By creating the ClusterExtension resource" )
920932 require .NoError (t , c .Create (context .Background (), clusterExtension ))
921933
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" )
923944 require .EventuallyWithT (t , func (ct * assert.CollectT ) {
924945 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 ))
925951 }, pollDuration , pollInterval )
926952
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+
9271045 t .Log ("By eventually reporting Progressing == True with Reason Retrying" )
9281046 require .EventuallyWithT (t , func (ct * assert.CollectT ) {
9291047 require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
@@ -933,23 +1051,23 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
9331051 require .Equal (ct , ocv1 .ReasonRetrying , cond .Reason )
9341052 }, pollDuration , pollInterval )
9351053
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 " )
9371055 require .EventuallyWithT (t , func (ct * assert.CollectT ) {
9381056 require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
9391057 cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
9401058 require .NotNil (ct , cond )
9411059 require .Equal (ct , metav1 .ConditionFalse , cond .Status )
9421060 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" )
9441062 }, pollDuration , pollInterval )
9451063
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 ))
9481066
9491067 // NOTE: In order to ensure predictable results we need to ensure we have a single
9501068 // known failure with a singular fix operation. Additionally, due to the exponential
9511069 // 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 .
9531071 t .Log ("By eventually installing the package successfully" )
9541072 require .EventuallyWithT (t , func (ct * assert.CollectT ) {
9551073 require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
0 commit comments