Skip to content

Commit 108d7e8

Browse files
authored
Update e2e tests for boxcutter (#2136)
Modify the `RecoversFromInitialInstallFailedWhenFailureFixed` test to instead not create the namespace and service account, instead of having bad service account. Boxcutter will ignore the service account. Add `RecoversFromExsitingDeploymentWhenFailureFixed` test to ensure we don't adopt, and instead fail when there's an existing resource that matches the bundle (i.e. don't adopt), Signed-off-by: Todd Short <[email protected]>
1 parent 37ace90 commit 108d7e8

File tree

1 file changed

+175
-57
lines changed

1 file changed

+175
-57
lines changed

test/e2e/cluster_extension_install_test.go

Lines changed: 175 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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

206208
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+
}
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

236248
func validateCatalogUnpack(t *testing.T) {
@@ -292,35 +304,42 @@ func ensureNoExtensionResources(t *testing.T, clusterExtensionName string) {
292304
}
293305

294306
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+
}
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

326345
func 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

Comments
 (0)