Skip to content

Commit d9d9924

Browse files
author
Per Goncalves da Silva
committed
Add ClusterExtensionRevision e2e
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 0f8fa6e commit d9d9924

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"slices"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
corev1 "k8s.io/api/core/v1"
13+
apimeta "k8s.io/apimachinery/pkg/api/meta"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/client-go/kubernetes/scheme"
17+
"k8s.io/client-go/tools/remotecommand"
18+
ctrl "sigs.k8s.io/controller-runtime"
19+
"sigs.k8s.io/controller-runtime/pkg/client"
20+
21+
ocv1 "github.com/operator-framework/operator-controller/api/v1"
22+
"github.com/operator-framework/operator-controller/internal/operator-controller/features"
23+
. "github.com/operator-framework/operator-controller/internal/shared/util/testutils"
24+
. "github.com/operator-framework/operator-controller/test/helpers"
25+
)
26+
27+
func TestClusterExtensionRevision(t *testing.T) {
28+
SkipIfFeatureGateDisabled(t, string(features.BoxcutterRuntime))
29+
t.Log("When a cluster extension is installed from a catalog")
30+
t.Log("When the extension bundle format is registry+v1")
31+
32+
clusterExtension, extensionCatalog, sa, ns := TestInit(t)
33+
defer TestCleanup(t, extensionCatalog, clusterExtension, sa, ns)
34+
defer CollectTestArtifacts(t, artifactName, c, cfg)
35+
36+
clusterExtension.Spec = ocv1.ClusterExtensionSpec{
37+
Source: ocv1.SourceConfig{
38+
SourceType: "Catalog",
39+
Catalog: &ocv1.CatalogFilter{
40+
PackageName: "test",
41+
Version: "1.0.1",
42+
Selector: &metav1.LabelSelector{
43+
MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name},
44+
},
45+
},
46+
},
47+
Namespace: ns.Name,
48+
ServiceAccount: ocv1.ServiceAccountReference{
49+
Name: sa.Name,
50+
},
51+
}
52+
t.Log("It resolves the specified package with correct bundle path")
53+
t.Log("By creating the ClusterExtension resource")
54+
require.NoError(t, c.Create(context.Background(), clusterExtension))
55+
56+
t.Log("By eventually reporting a successful resolution and bundle path")
57+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
58+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
59+
}, pollDuration, pollInterval)
60+
61+
t.Log("By revision-1 eventually reporting Progressing:False:RolledOut and Available:True:ProbesSucceeded conditions")
62+
var clusterExtensionRevision ocv1.ClusterExtensionRevision
63+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
64+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
65+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
66+
require.NotNil(ct, cond)
67+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
68+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
69+
70+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
71+
require.NotNil(ct, cond)
72+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
73+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
74+
}, pollDuration, pollInterval)
75+
76+
t.Log("By eventually reporting progressing as True")
77+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
78+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
79+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
80+
require.NotNil(ct, cond)
81+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
82+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
83+
}, pollDuration, pollInterval)
84+
85+
t.Log("By eventually installing the package successfully")
86+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
87+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
88+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
89+
require.NotNil(ct, cond)
90+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
91+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
92+
require.Contains(ct, cond.Message, "Installed bundle")
93+
require.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
94+
}, pollDuration, pollInterval)
95+
96+
t.Log("Check Deployment Availability Probe")
97+
t.Log("By making the operator pod not ready")
98+
podName := getPodName(t, clusterExtension.Spec.Namespace, client.MatchingLabels{"app": "olme2etest"})
99+
podExec(t, clusterExtension.Spec.Namespace, podName, []string{"rm", "/var/www/ready"})
100+
101+
t.Log("By revision-1 eventually reporting Progressing:False:RolledOut and Available:False:ProbeFailure conditions")
102+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
103+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
104+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
105+
require.NotNil(ct, cond)
106+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
107+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
108+
109+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
110+
require.NotNil(ct, cond)
111+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
112+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbeFailure, cond.Reason)
113+
}, pollDuration, pollInterval)
114+
115+
t.Log("By making the operator pod ready")
116+
podName = getPodName(t, clusterExtension.Spec.Namespace, client.MatchingLabels{"app": "olme2etest"})
117+
podExec(t, clusterExtension.Spec.Namespace, podName, []string{"touch", "/var/www/ready"})
118+
119+
t.Log("By revision-1 eventually reporting Progressing:False:RolledOut and Available:True:ProbesSucceeded conditions")
120+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
121+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
122+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
123+
require.NotNil(ct, cond)
124+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
125+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
126+
127+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
128+
require.NotNil(ct, cond)
129+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
130+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
131+
}, pollDuration, pollInterval)
132+
133+
t.Log("Check archiving")
134+
t.Log("By upgrading the cluster extension to v1.2.0")
135+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
136+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
137+
clusterExtension.Spec.Source.Catalog.Version = "1.2.0"
138+
require.NoError(t, c.Update(context.Background(), clusterExtension))
139+
}, pollDuration, pollInterval)
140+
141+
t.Log("By revision-2 eventually reporting Progressing:False:RolledOut and Available:True:ProbesSucceeded conditions")
142+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
143+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-2", clusterExtension.Name)}, &clusterExtensionRevision))
144+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
145+
require.NotNil(ct, cond)
146+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
147+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
148+
149+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
150+
require.NotNil(ct, cond)
151+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
152+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
153+
}, pollDuration, pollInterval)
154+
155+
t.Log("By eventually reporting progressing, available, and installed as True")
156+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
157+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
158+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
159+
require.NotNil(ct, cond)
160+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
161+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
162+
163+
cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
164+
require.NotNil(ct, cond)
165+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
166+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
167+
require.Contains(ct, cond.Message, "Installed bundle")
168+
require.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
169+
}, pollDuration, pollInterval)
170+
171+
t.Log("By revision-1 eventually reporting Progressing:False:Archived and Available:Unknown:Archived conditions")
172+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
173+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
174+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
175+
require.NotNil(ct, cond)
176+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
177+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason)
178+
179+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
180+
require.NotNil(ct, cond)
181+
require.Equal(ct, metav1.ConditionUnknown, cond.Status)
182+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason)
183+
}, pollDuration, pollInterval)
184+
}
185+
186+
func getPodName(t *testing.T, podNamespace string, matchingLabels client.MatchingLabels) string {
187+
var podList corev1.PodList
188+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
189+
require.NoError(ct, c.List(context.Background(), &podList, client.InNamespace(podNamespace), matchingLabels))
190+
podList.Items = slices.DeleteFunc(podList.Items, func(pod corev1.Pod) bool {
191+
// Ignore terminating pods
192+
return pod.DeletionTimestamp != nil
193+
})
194+
require.Len(ct, podList.Items, 1)
195+
}, pollDuration, pollInterval)
196+
return podList.Items[0].Name
197+
}
198+
199+
func podExec(t *testing.T, podNamespace string, podName string, cmd []string) {
200+
req := cs.CoreV1().RESTClient().Post().Resource("pods").Name(podName).Namespace(podNamespace).SubResource("exec")
201+
req.VersionedParams(&corev1.PodExecOptions{
202+
Command: cmd,
203+
Stdout: true,
204+
}, scheme.ParameterCodec)
205+
exec, err := remotecommand.NewSPDYExecutor(ctrl.GetConfigOrDie(), "POST", req.URL())
206+
require.NoError(t, err)
207+
err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{Stdout: os.Stdout})
208+
require.NoError(t, err)
209+
}

test/e2e/e2e_suite_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1010
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
11+
"k8s.io/client-go/kubernetes"
1112
"k8s.io/client-go/rest"
1213
ctrl "sigs.k8s.io/controller-runtime"
1314
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -20,6 +21,7 @@ import (
2021
var (
2122
cfg *rest.Config
2223
c client.Client
24+
cs *kubernetes.Clientset
2325
)
2426

2527
const (
@@ -35,6 +37,9 @@ func TestMain(m *testing.M) {
3537
c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
3638
utilruntime.Must(err)
3739

40+
cs, err = kubernetes.NewForConfig(cfg)
41+
utilruntime.Must(err)
42+
3843
res := m.Run()
3944
path := os.Getenv(testSummaryOutputEnvVar)
4045
if path == "" {

0 commit comments

Comments
 (0)