Skip to content

Commit ddc9f12

Browse files
committed
Add basic DevWorkspaceRouting controller envtests
Signed-off-by: Andrew Obuchowicz <aobuchow@redhat.com>
1 parent 5021177 commit ddc9f12

3 files changed

Lines changed: 284 additions & 2 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) 2019-2023 Red Hat, Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package devworkspacerouting_test
15+
16+
import (
17+
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
18+
"github.com/devfile/devworkspace-operator/pkg/common"
19+
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
)
23+
24+
var _ = Describe("DevWorkspaceRouting Controller", func() {
25+
Context("Basic DevWorkspaceRouting Tests", func() {
26+
It("Gets Preparing status", func() {
27+
By("Creating a new DevWorkspaceRouting object")
28+
dwrNamespacedName := namespacedName(devWorkspaceRoutingName, testNamespace)
29+
createdDWR := createPreparingDWR(testWorkspaceID, devWorkspaceRoutingName)
30+
Eventually(func() bool {
31+
err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR)
32+
return err == nil
33+
}, timeout, interval).Should(BeTrue(), "DevWorkspaceRouting should exist in cluster")
34+
35+
By("Checking DevWorkspaceRouting Status is updated to preparing")
36+
Eventually(func() (phase controllerv1alpha1.DevWorkspaceRoutingPhase, err error) {
37+
if err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR); err != nil {
38+
return "", err
39+
}
40+
return createdDWR.Status.Phase, nil
41+
}, timeout, interval).Should(Equal(controllerv1alpha1.RoutingPreparing), "DevWorkspaceRouting should have Preparing phase")
42+
43+
Expect(createdDWR.Status.Message).ShouldNot(BeNil(), "Status message should be set for DevWorkspaceRoutings in Preparing phase")
44+
45+
deleteDevWorkspaceRouting(devWorkspaceRoutingName)
46+
// Services and Ingresses aren't created since the DWR was stuck in preparing phase, no need to clean them up
47+
})
48+
49+
It("Gets Ready Status on OpenShift", func() {
50+
By("Setting infrastructure to OpenShift")
51+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
52+
53+
By("Creating a new DevWorkspaceRouting object")
54+
dwrNamespacedName := namespacedName(devWorkspaceRoutingName, testNamespace)
55+
createdDWR := createDWR(testWorkspaceID, devWorkspaceRoutingName)
56+
Eventually(func() bool {
57+
err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR)
58+
return err == nil
59+
}, timeout, interval).Should(BeTrue(), "DevWorkspaceRouting should exist in cluster")
60+
61+
By("Checking DevWorkspaceRouting Status is updated to Ready")
62+
Eventually(func() (phase controllerv1alpha1.DevWorkspaceRoutingPhase, err error) {
63+
if err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR); err != nil {
64+
return "", err
65+
}
66+
return createdDWR.Status.Phase, nil
67+
}, timeout, interval).Should(Equal(controllerv1alpha1.RoutingReady), "DevWorkspaceRouting should have Ready phase")
68+
69+
Expect(createdDWR.Status.Message).ShouldNot(BeNil(), "Status message should be set for preparing DevWorkspaceRoutings")
70+
Expect(createdDWR.Status.Message).Should(Equal("DevWorkspaceRouting prepared"), "Status message should indicate that the DevWorkspaceRouting is prepared")
71+
72+
deleteDevWorkspaceRouting(devWorkspaceRoutingName)
73+
deleteService(common.ServiceName(testWorkspaceID), testNamespace)
74+
deleteService(common.EndpointName(discoverableEndpointName), testNamespace)
75+
deleteRoute(exposedEndPointName, testNamespace)
76+
deleteRoute(discoverableEndpointName, testNamespace)
77+
})
78+
79+
It("Gets Ready Status on Kubernetes", func() {
80+
By("Setting infrastructure to Kubernetes")
81+
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
82+
83+
By("Creating a new DevWorkspaceRouting object")
84+
dwrNamespacedName := namespacedName(devWorkspaceRoutingName, testNamespace)
85+
createdDWR := createDWR(testWorkspaceID, devWorkspaceRoutingName)
86+
Eventually(func() bool {
87+
err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR)
88+
return err == nil
89+
}, timeout, interval).Should(BeTrue(), "DevWorkspaceRouting should exist in cluster")
90+
91+
By("Checking DevWorkspaceRouting Status is updated to Ready")
92+
Eventually(func() (phase controllerv1alpha1.DevWorkspaceRoutingPhase, err error) {
93+
if err := k8sClient.Get(ctx, dwrNamespacedName, createdDWR); err != nil {
94+
return "", err
95+
}
96+
return createdDWR.Status.Phase, nil
97+
}, timeout, interval).Should(Equal(controllerv1alpha1.RoutingReady), "DevWorkspaceRouting should have Ready phase")
98+
Expect(createdDWR.Status.Message).ShouldNot(BeNil(), "Status message should be set for preparing DevWorkspaceRoutings")
99+
Expect(createdDWR.Status.Message).Should(Equal("DevWorkspaceRouting prepared"), "Status message should indicate that the DevWorkspaceRouting is prepared")
100+
101+
deleteDevWorkspaceRouting(devWorkspaceRoutingName)
102+
deleteService(common.ServiceName(testWorkspaceID), testNamespace)
103+
deleteService(common.EndpointName(discoverableEndpointName), testNamespace)
104+
deleteIngress(exposedEndPointName, testNamespace)
105+
deleteIngress(discoverableEndpointName, testNamespace)
106+
})
107+
})
108+
})

controllers/controller/devworkspacerouting/suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,4 @@ func setupEnvVars() error {
188188
}
189189

190190
return nil
191-
}
191+
}

controllers/controller/devworkspacerouting/util_test.go

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,180 @@
1313

1414
package devworkspacerouting_test
1515

16+
import (
17+
"time"
18+
19+
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
20+
"github.com/devfile/devworkspace-operator/pkg/common"
21+
"github.com/devfile/devworkspace-operator/pkg/constants"
22+
. "github.com/onsi/gomega"
23+
routeV1 "github.com/openshift/api/route/v1"
24+
crclient "sigs.k8s.io/controller-runtime/pkg/client"
25+
26+
corev1 "k8s.io/api/core/v1"
27+
networkingv1 "k8s.io/api/networking/v1"
28+
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
29+
"k8s.io/apimachinery/pkg/types"
30+
)
31+
1632
const (
17-
testNamespace = "devworkspace-test"
33+
timeout = 10 * time.Second
34+
interval = 250 * time.Millisecond
35+
36+
testNamespace = "devworkspace-test"
37+
devWorkspaceRoutingName = "test-devworkspacerouting"
38+
testWorkspaceID = "test-id"
39+
testMachineName = "test-machine-name"
40+
41+
exposedEndPointName = "test-endpoint"
42+
exposedTargetPort = 7777
43+
discoverableEndpointName = "discoverable-endpoint"
44+
discoverableTargetPort = 7979
45+
nonExposedEndpointName = "non-exposed-endpoint"
46+
nonExposedTargetPort = 8989
1847
)
48+
49+
func createPreparingDWR(workspaceID string, name string) *controllerv1alpha1.DevWorkspaceRouting {
50+
mainAttributes := controllerv1alpha1.Attributes{}
51+
mainAttributes.PutString("type", "main")
52+
exposedEndpoint := controllerv1alpha1.Endpoint{
53+
Name: exposedEndPointName,
54+
Attributes: mainAttributes,
55+
// Target port must be within range 1 and 65535
56+
// Created service will be invalid on cluster and error will be logged
57+
// DWR will continue trying to reconcile however, keeping it stuck in preparing phase
58+
TargetPort: 0,
59+
}
60+
machineEndpointsMap := map[string]controllerv1alpha1.EndpointList{
61+
testMachineName: {
62+
exposedEndpoint,
63+
},
64+
}
65+
66+
dwr := &controllerv1alpha1.DevWorkspaceRouting{
67+
Spec: controllerv1alpha1.DevWorkspaceRoutingSpec{
68+
DevWorkspaceId: workspaceID,
69+
RoutingClass: controllerv1alpha1.DevWorkspaceRoutingBasic,
70+
Endpoints: machineEndpointsMap,
71+
PodSelector: map[string]string{
72+
constants.DevWorkspaceIDLabel: workspaceID,
73+
},
74+
},
75+
}
76+
dwr.SetName(name)
77+
dwr.SetNamespace(testNamespace)
78+
Expect(k8sClient.Create(ctx, dwr)).Should(Succeed())
79+
return dwr
80+
}
81+
82+
func createDWR(workspaceID string, name string) *controllerv1alpha1.DevWorkspaceRouting {
83+
mainAttributes := controllerv1alpha1.Attributes{}
84+
mainAttributes.PutString("type", "main")
85+
discoverableAttributes := controllerv1alpha1.Attributes{}
86+
discoverableAttributes.PutBoolean(string(controllerv1alpha1.DiscoverableAttribute), true)
87+
88+
exposedEndpoint := controllerv1alpha1.Endpoint{
89+
Name: exposedEndPointName,
90+
Exposure: controllerv1alpha1.PublicEndpointExposure,
91+
Attributes: mainAttributes,
92+
TargetPort: exposedTargetPort,
93+
}
94+
nonExposedEndpoint := controllerv1alpha1.Endpoint{
95+
Name: nonExposedEndpointName,
96+
Exposure: controllerv1alpha1.NoneEndpointExposure,
97+
TargetPort: nonExposedTargetPort,
98+
}
99+
discoverableEndpoint := controllerv1alpha1.Endpoint{
100+
Name: discoverableEndpointName,
101+
Exposure: controllerv1alpha1.PublicEndpointExposure,
102+
Attributes: discoverableAttributes,
103+
TargetPort: discoverableTargetPort,
104+
}
105+
machineEndpointsMap := map[string]controllerv1alpha1.EndpointList{
106+
"test-machine-name": {
107+
exposedEndpoint,
108+
nonExposedEndpoint,
109+
discoverableEndpoint,
110+
},
111+
}
112+
113+
dwr := &controllerv1alpha1.DevWorkspaceRouting{
114+
Spec: controllerv1alpha1.DevWorkspaceRoutingSpec{
115+
DevWorkspaceId: workspaceID,
116+
RoutingClass: controllerv1alpha1.DevWorkspaceRoutingBasic,
117+
Endpoints: machineEndpointsMap,
118+
PodSelector: map[string]string{
119+
constants.DevWorkspaceIDLabel: workspaceID,
120+
},
121+
},
122+
}
123+
124+
dwr.SetName(name)
125+
dwr.SetNamespace(testNamespace)
126+
127+
Expect(k8sClient.Create(ctx, dwr)).Should(Succeed())
128+
return dwr
129+
}
130+
131+
func deleteService(serviceName string, namespace string) {
132+
createdService := &corev1.Service{}
133+
serviceNamespacedName := namespacedName(serviceName, namespace)
134+
Eventually(func() bool {
135+
err := k8sClient.Get(ctx, serviceNamespacedName, createdService)
136+
return err == nil
137+
}, timeout, interval).Should(BeTrue(), "Service should exist in cluster")
138+
deleteObject(createdService)
139+
}
140+
141+
func deleteRoute(endpointName string, namespace string) {
142+
createdRoute := routeV1.Route{}
143+
routeNamespacedName := namespacedName(common.RouteName(testWorkspaceID, endpointName), namespace)
144+
Eventually(func() bool {
145+
err := k8sClient.Get(ctx, routeNamespacedName, &createdRoute)
146+
return err == nil
147+
}, timeout, interval).Should(BeTrue(), "Route should exist in cluster")
148+
deleteObject(&createdRoute)
149+
}
150+
151+
func deleteIngress(endpointName string, namespace string) {
152+
createdIngress := networkingv1.Ingress{}
153+
ingressNamespacedName := namespacedName(common.RouteName(testWorkspaceID, endpointName), namespace)
154+
Eventually(func() bool {
155+
err := k8sClient.Get(ctx, ingressNamespacedName, &createdIngress)
156+
return err == nil
157+
}, timeout, interval).Should(BeTrue(), "Ingress should exist in cluster")
158+
deleteObject(&createdIngress)
159+
}
160+
161+
func deleteDevWorkspaceRouting(name string) {
162+
dwrNN := namespacedName(name, testNamespace)
163+
dwr := &controllerv1alpha1.DevWorkspaceRouting{}
164+
dwr.Name = name
165+
dwr.Namespace = testNamespace
166+
// Do nothing if already deleted
167+
err := k8sClient.Delete(ctx, dwr)
168+
if k8sErrors.IsNotFound(err) {
169+
return
170+
}
171+
Expect(err).Should(BeNil())
172+
173+
Eventually(func() bool {
174+
err := k8sClient.Get(ctx, dwrNN, dwr)
175+
return err != nil && k8sErrors.IsNotFound(err)
176+
}, 10*time.Second, 250*time.Millisecond).Should(BeTrue(), "DevWorkspaceRouting not deleted after timeout")
177+
}
178+
179+
func deleteObject(obj crclient.Object) {
180+
Expect(k8sClient.Delete(ctx, obj)).Should(Succeed())
181+
Eventually(func() bool {
182+
err := k8sClient.Get(ctx, namespacedName(obj.GetName(), obj.GetNamespace()), obj)
183+
return k8sErrors.IsNotFound(err)
184+
}, 10*time.Second, 250*time.Millisecond).Should(BeTrue(), "Deleting %s with name %s", obj.GetObjectKind(), obj.GetName())
185+
}
186+
187+
func namespacedName(name, namespace string) types.NamespacedName {
188+
return types.NamespacedName{
189+
Name: name,
190+
Namespace: namespace,
191+
}
192+
}

0 commit comments

Comments
 (0)