Skip to content

Commit 793f821

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

File tree

2 files changed

+247
-0
lines changed

2 files changed

+247
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
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+
// TODO: re-enable this once the available condition is mirrored to the ClusterExtension conditions
86+
//t.Log("By eventually reporting available as True")
87+
//require.EventuallyWithT(t, func(ct *assert.CollectT) {
88+
// require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
89+
// cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
90+
// require.NotNil(ct, cond)
91+
// require.Equal(ct, metav1.ConditionTrue, cond.Status)
92+
// require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
93+
//}, pollDuration, pollInterval)
94+
95+
t.Log("By eventually installing the package successfully")
96+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
97+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
98+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
99+
require.NotNil(ct, cond)
100+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
101+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
102+
require.Contains(ct, cond.Message, "Installed bundle")
103+
require.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
104+
}, pollDuration, pollInterval)
105+
106+
t.Log("Check Deployment Availability Probe")
107+
t.Log("By making the operator pod not ready")
108+
podName := getPodName(t, clusterExtension.Spec.Namespace, client.MatchingLabels{"app": "olme2etest"})
109+
podExec(t, clusterExtension.Spec.Namespace, podName, []string{"rm", "/var/www/ready"})
110+
111+
t.Log("By revision-1 eventually reporting Progressing:False:RolledOut and Available:False:ProbeFailure conditions")
112+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
113+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
114+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
115+
require.NotNil(ct, cond)
116+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
117+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
118+
119+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
120+
require.NotNil(ct, cond)
121+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
122+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbeFailure, cond.Reason)
123+
}, pollDuration, pollInterval)
124+
125+
t.Log("By extension eventually reporting available as False")
126+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
127+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
128+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
129+
require.NotNil(ct, cond)
130+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
131+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbeFailure, cond.Reason)
132+
}, pollDuration, pollInterval)
133+
134+
t.Log("By making the operator pod ready")
135+
podName = getPodName(t, clusterExtension.Spec.Namespace, client.MatchingLabels{"app": "olme2etest"})
136+
podExec(t, clusterExtension.Spec.Namespace, podName, []string{"touch", "/var/www/ready"})
137+
138+
t.Log("By revision-1 eventually reporting Progressing:False:RolledOut and Available:True:ProbesSucceeded conditions")
139+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
140+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
141+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
142+
require.NotNil(ct, cond)
143+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
144+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
145+
146+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
147+
require.NotNil(ct, cond)
148+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
149+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
150+
}, pollDuration, pollInterval)
151+
152+
t.Log("By extension eventually reporting available as True")
153+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
154+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
155+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
156+
require.NotNil(ct, cond)
157+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
158+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
159+
}, pollDuration, pollInterval)
160+
161+
t.Log("Check archiving")
162+
t.Log("By upgrading the cluster extension to v1.2.0")
163+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
164+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
165+
clusterExtension.Spec.Source.Catalog.Version = "1.2.0"
166+
require.NoError(t, c.Update(context.Background(), clusterExtension))
167+
}, pollDuration, pollInterval)
168+
169+
t.Log("By revision-2 eventually reporting Progressing:False:RolledOut and Available:True:ProbesSucceeded conditions")
170+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
171+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-2", clusterExtension.Name)}, &clusterExtensionRevision))
172+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
173+
require.NotNil(ct, cond)
174+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
175+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonRolledOut, cond.Reason)
176+
177+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
178+
require.NotNil(ct, cond)
179+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
180+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
181+
}, pollDuration, pollInterval)
182+
183+
t.Log("By eventually reporting progressing, available, and installed as True")
184+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
185+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
186+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
187+
require.NotNil(ct, cond)
188+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
189+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
190+
191+
cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
192+
require.NotNil(ct, cond)
193+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
194+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason)
195+
196+
cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
197+
require.NotNil(ct, cond)
198+
require.Equal(ct, metav1.ConditionTrue, cond.Status)
199+
require.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
200+
require.Contains(ct, cond.Message, "Installed bundle")
201+
require.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
202+
}, pollDuration, pollInterval)
203+
204+
t.Log("By revision-1 eventually reporting Progressing:False:Archived and Available:Unknown:Archived conditions")
205+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
206+
require.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-1", clusterExtension.Name)}, &clusterExtensionRevision))
207+
cond := apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
208+
require.NotNil(ct, cond)
209+
require.Equal(ct, metav1.ConditionFalse, cond.Status)
210+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason)
211+
212+
cond = apimeta.FindStatusCondition(clusterExtensionRevision.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
213+
require.NotNil(ct, cond)
214+
require.Equal(ct, metav1.ConditionUnknown, cond.Status)
215+
require.Equal(ct, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason)
216+
}, pollDuration, pollInterval)
217+
}
218+
219+
func getPodName(t *testing.T, podNamespace string, matchingLabels client.MatchingLabels) string {
220+
var podList corev1.PodList
221+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
222+
require.NoError(ct, c.List(context.Background(), &podList, client.InNamespace(podNamespace), matchingLabels))
223+
podList.Items = slices.DeleteFunc(podList.Items, func(pod corev1.Pod) bool {
224+
// Ignore terminating pods
225+
return pod.DeletionTimestamp != nil
226+
})
227+
require.Len(ct, podList.Items, 1)
228+
}, pollDuration, pollInterval)
229+
return podList.Items[0].Name
230+
}
231+
232+
func podExec(t *testing.T, podNamespace string, podName string, cmd []string) {
233+
req := cs.CoreV1().RESTClient().Post().Resource("pods").Name(podName).Namespace(podNamespace).SubResource("exec")
234+
req.VersionedParams(&corev1.PodExecOptions{
235+
Command: cmd,
236+
Stdout: true,
237+
}, scheme.ParameterCodec)
238+
exec, err := remotecommand.NewSPDYExecutor(ctrl.GetConfigOrDie(), "POST", req.URL())
239+
require.NoError(t, err)
240+
err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{Stdout: os.Stdout})
241+
require.NoError(t, err)
242+
}

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)