Skip to content

Commit db765c3

Browse files
committed
Update e2e tests for boxcutter
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 5970a0d commit db765c3

File tree

1 file changed

+180
-52
lines changed

1 file changed

+180
-52
lines changed

test/e2e/cluster_extension_install_test.go

Lines changed: 180 additions & 52 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
209+
ce, cc := testInitClusterExtensionClusterCatalog(t)
210+
sa, ns := testInitServiceAccountNamespace(t, ce.Name)
211+
return ce, cc, sa, ns
212+
}
208213

209-
clusterExtensionName := fmt.Sprintf("clusterextension-%s", rand.String(8))
214+
func testInitClusterExtensionClusterCatalog(t *testing.T) (*ocv1.ClusterExtension, *ocv1.ClusterCatalog) {
215+
ceName := fmt.Sprintf("clusterextension-%s", rand.String(8))
210216

211-
ns, err := createNamespace(context.Background(), clusterExtensionName)
212-
require.NoError(t, err)
213-
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,21 +901,95 @@ 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,
911+
defer testCleanup(t, extensionCatalog, clusterExtension, nil, nil)
912+
defer utils.CollectTestArtifacts(t, artifactName, c, cfg)
913+
914+
clusterExtension.Spec = ocv1.ClusterExtensionSpec{
915+
Source: ocv1.SourceConfig{
916+
SourceType: "Catalog",
917+
Catalog: &ocv1.CatalogFilter{
918+
PackageName: "test",
919+
Selector: &metav1.LabelSelector{
920+
MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name},
921+
},
922+
},
923+
},
924+
Namespace: clusterExtension.Name,
925+
ServiceAccount: ocv1.ServiceAccountReference{
926+
Name: clusterExtension.Name,
896927
},
897928
}
898-
err := c.Create(context.Background(), sa)
899-
require.NoError(t, err)
929+
930+
t.Log("It resolves the specified package with correct bundle path")
931+
t.Log("By creating the ClusterExtension resource")
932+
require.NoError(t, c.Create(context.Background(), clusterExtension))
933+
934+
t.Log("By eventually reporting a successful resolution and bundle path")
935+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
936+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
937+
}, pollDuration, pollInterval)
938+
939+
t.Log("By eventually reporting Progressing == True with Reason Retrying")
940+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
941+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
942+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
943+
require.NotNil(ct, cond)
944+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
945+
require.Equal(ct, ocv1.ReasonRetrying, cond.Reason)
946+
}, pollDuration, pollInterval)
947+
948+
t.Log("By eventually failing to install the package successfully due to no namespace")
949+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
950+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
951+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
952+
require.NotNil(ct, cond)
953+
require.Equal(ct, metav1.ConditionUnknown, cond.Status)
954+
require.Equal(ct, ocv1.ReasonFailed, cond.Reason)
955+
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))
956+
}, pollDuration, pollInterval)
957+
958+
t.Log("By creating the Namespace and ServiceAccount")
959+
sa, ns := testInitServiceAccountNamespace(t, clusterExtension.Name)
960+
defer testCleanup(t, nil, nil, sa, ns)
961+
962+
// NOTE: In order to ensure predictable results we need to ensure we have a single
963+
// known failure with a singular fix operation. Additionally, due to the exponential
964+
// backoff of this eventually check we MUST ensure we do not touch the ClusterExtension
965+
// after creating and binding the needed permissions to the ServiceAccount.
966+
t.Log("By eventually installing the package successfully")
967+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
968+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
969+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
970+
require.NotNil(ct, cond)
971+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
972+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
973+
require.Contains(ct, cond.Message, "Installed bundle")
974+
require.NotEmpty(ct, clusterExtension.Status.Install)
975+
}, pollDuration, pollInterval)
976+
977+
t.Log("By eventually reporting Progressing == True with Reason Success")
978+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
979+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
980+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
981+
require.NotNil(ct, cond)
982+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
983+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
984+
}, pollDuration, pollInterval)
985+
}
986+
987+
func TestClusterExtensionRecoversFromExistingDeploymentWhenFailureFixed(t *testing.T) {
988+
t.Log("When a cluster extension is installed from a catalog")
989+
t.Log("When the extension bundle format is registry+v1")
990+
991+
clusterExtension, extensionCatalog, sa, ns := testInit(t)
992+
900993
defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns)
901994
defer utils.CollectTestArtifacts(t, artifactName, c, cfg)
902995

@@ -910,11 +1003,46 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
9101003
},
9111004
},
9121005
},
913-
Namespace: ns.Name,
1006+
Namespace: clusterExtension.Name,
9141007
ServiceAccount: ocv1.ServiceAccountReference{
915-
Name: sa.Name,
1008+
Name: clusterExtension.Name,
1009+
},
1010+
}
1011+
1012+
t.Log("By creating a new Deployment that can not be adopted")
1013+
newDeployment := &appsv1.Deployment{
1014+
ObjectMeta: metav1.ObjectMeta{
1015+
Name: "test-operator",
1016+
Namespace: clusterExtension.Name,
1017+
},
1018+
Spec: appsv1.DeploymentSpec{
1019+
Replicas: ptr.To(int32(1)),
1020+
Selector: &metav1.LabelSelector{
1021+
MatchLabels: map[string]string{"app": "test-operator"},
1022+
},
1023+
Template: corev1.PodTemplateSpec{
1024+
ObjectMeta: metav1.ObjectMeta{
1025+
Labels: map[string]string{"app": "test-operator"},
1026+
},
1027+
Spec: corev1.PodSpec{
1028+
Containers: []corev1.Container{
1029+
{
1030+
Command: []string{"sleep", "1000"},
1031+
Image: "busybox",
1032+
ImagePullPolicy: corev1.PullAlways,
1033+
Name: "busybox",
1034+
SecurityContext: &corev1.SecurityContext{
1035+
RunAsNonRoot: ptr.To(true),
1036+
RunAsUser: ptr.To(int64(1000)),
1037+
},
1038+
},
1039+
},
1040+
},
1041+
},
9161042
},
9171043
}
1044+
require.NoError(t, c.Create(context.Background(), newDeployment))
1045+
9181046
t.Log("It resolves the specified package with correct bundle path")
9191047
t.Log("By creating the ClusterExtension resource")
9201048
require.NoError(t, c.Create(context.Background(), clusterExtension))
@@ -933,18 +1061,18 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
9331061
require.Equal(ct, ocv1.ReasonRetrying, cond.Reason)
9341062
}, pollDuration, pollInterval)
9351063

936-
t.Log("By eventually failing to install the package successfully due to insufficient ServiceAccount permissions")
1064+
t.Log("By eventually failing to install the package successfully due to no adoption support")
9371065
require.EventuallyWithT(t, func(ct *assert.CollectT) {
9381066
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
9391067
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
9401068
require.NotNil(ct, cond)
9411069
require.Equal(ct, metav1.ConditionFalse, cond.Status)
9421070
require.Equal(ct, ocv1.ReasonFailed, cond.Reason)
943-
require.Equal(ct, "No bundle installed", cond.Message)
1071+
require.Contains(ct, cond.Message, "No bundle installed")
9441072
}, pollDuration, pollInterval)
9451073

946-
t.Log("By fixing the ServiceAccount permissions")
947-
require.NoError(t, createClusterRoleAndBindingForSA(context.Background(), name, sa, clusterExtension.Name))
1074+
t.Log("By deleting the new Deployment")
1075+
require.NoError(t, c.Delete(context.Background(), newDeployment))
9481076

9491077
// NOTE: In order to ensure predictable results we need to ensure we have a single
9501078
// known failure with a singular fix operation. Additionally, due to the exponential

0 commit comments

Comments
 (0)