Skip to content

Commit f8269e5

Browse files
authored
route: refactor route package to use common builder (#1297)
This commit switches the route package to use the common EmbeddableBuilder as the basis for the Builder struct in the package. This provides eliminates the need to have specific methods for common operations like Get, Create, and Delete. This package is a good example for future refactors, since it includes the EmbeddableBuilder, several mixins, specific methods which cannot be common functions, and a NewBuilder with special parameters. In addition to the refactor to use the common package, this commit improves the unit tests even for functions which are not part of the common builder. Additional cases have been added to improve overall coverage. Assisted-by: Cursor
1 parent cb85adc commit f8269e5

3 files changed

Lines changed: 218 additions & 373 deletions

File tree

pkg/clients/clients.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,6 @@ func GetModifiableTestClients(tcp TestClientParams) (*Settings, *fakeRuntimeClie
282282
genericClientObjects = append(genericClientObjects, v)
283283
case *operatorv1.OpenShiftAPIServer:
284284
genericClientObjects = append(genericClientObjects, v)
285-
case *routev1.Route:
286-
genericClientObjects = append(genericClientObjects, v)
287285
case *configV1.Node:
288286
genericClientObjects = append(genericClientObjects, v)
289287
case *operatorv1.IngressController:

pkg/route/route.go

Lines changed: 42 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
package route
22

33
import (
4+
"context"
45
"fmt"
56

67
"slices"
78

89
routev1 "github.com/openshift/api/route/v1"
910
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/clients"
10-
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/internal/logging"
11-
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/msg"
12-
k8serrors "k8s.io/apimachinery/pkg/api/errors"
13-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/internal/common"
12+
"k8s.io/apimachinery/pkg/runtime/schema"
1413
"k8s.io/apimachinery/pkg/util/intstr"
1514
"k8s.io/klog/v2"
16-
17-
goclient "sigs.k8s.io/controller-runtime/pkg/client"
1815
)
1916

2017
// Builder provides struct for route object containing connection to the cluster and the route definitions.
2118
type Builder struct {
22-
// Route definition. Used to create a route object
23-
Definition *routev1.Route
24-
// Created route object
25-
Object *routev1.Route
26-
// Used in functions that define or mutate the route definition.
27-
// errorMsg is processed before the route object is created
28-
errorMsg string
29-
apiClient goclient.Client
19+
common.EmbeddableBuilder[routev1.Route, *routev1.Route]
20+
common.EmbeddableCreator[routev1.Route, Builder, *routev1.Route, *Builder]
21+
common.EmbeddableDeleteReturner[routev1.Route, Builder, *routev1.Route, *Builder]
22+
}
23+
24+
// AttachMixins attaches the mixins to the builder. This is called automatically when the builder is initialized.
25+
func (builder *Builder) AttachMixins() {
26+
builder.EmbeddableCreator.SetBase(builder)
27+
builder.EmbeddableDeleteReturner.SetBase(builder)
28+
}
29+
30+
// GetGVK returns the GVK for the Route resource.
31+
func (builder *Builder) GetGVK() schema.GroupVersionKind {
32+
return routev1.GroupVersion.WithKind("Route")
3033
}
3134

3235
// NewBuilder creates a new instance of Builder.
@@ -35,99 +38,38 @@ func NewBuilder(apiClient *clients.Settings, name, nsname, serviceName string) *
3538
"Initializing new route structure with the following params: name: %s, namespace: %s, serviceName: %s",
3639
name, nsname, serviceName)
3740

38-
if apiClient == nil {
39-
klog.V(100).Info("The apiClient is nil")
40-
41-
return nil
42-
}
43-
44-
builder := &Builder{
45-
apiClient: apiClient.Client,
46-
Definition: &routev1.Route{
47-
ObjectMeta: metav1.ObjectMeta{
48-
Name: name,
49-
Namespace: nsname,
50-
},
51-
Spec: routev1.RouteSpec{
52-
To: routev1.RouteTargetReference{
53-
Kind: "Service",
54-
Name: serviceName,
55-
},
56-
},
57-
},
58-
}
59-
60-
if name == "" {
61-
klog.V(100).Info("The name of the route is empty")
62-
63-
builder.errorMsg = "route 'name' cannot be empty"
64-
65-
return builder
66-
}
67-
68-
if nsname == "" {
69-
klog.V(100).Info("The namespace of the route is empty")
70-
71-
builder.errorMsg = "route 'nsname' cannot be empty"
72-
41+
builder := common.NewNamespacedBuilder[routev1.Route, Builder](apiClient, routev1.AddToScheme, name, nsname)
42+
if builder.GetError() != nil {
7343
return builder
7444
}
7545

7646
if serviceName == "" {
7747
klog.V(100).Info("The serviceName of the route is empty")
7848

79-
builder.errorMsg = "route 'serviceName' cannot be empty"
49+
builder.SetError(fmt.Errorf("route 'serviceName' cannot be empty"))
8050

8151
return builder
8252
}
8353

54+
builder.Definition.Spec.To = routev1.RouteTargetReference{
55+
Kind: "Service",
56+
Name: serviceName,
57+
}
58+
8459
return builder
8560
}
8661

8762
// Pull loads existing route from cluster.
8863
func Pull(apiClient *clients.Settings, name, nsname string) (*Builder, error) {
8964
klog.V(100).Infof("Pulling existing route name %s under namespace %s from cluster", name, nsname)
9065

91-
if apiClient == nil {
92-
klog.V(100).Info("The apiClient is nil")
93-
94-
return nil, fmt.Errorf("the apiClient cannot be nil")
95-
}
96-
97-
builder := &Builder{
98-
apiClient: apiClient.Client,
99-
Definition: &routev1.Route{
100-
ObjectMeta: metav1.ObjectMeta{
101-
Name: name,
102-
Namespace: nsname,
103-
},
104-
},
105-
}
106-
107-
if name == "" {
108-
klog.V(100).Info("The name of the route is empty")
109-
110-
return nil, fmt.Errorf("route 'name' cannot be empty")
111-
}
112-
113-
if nsname == "" {
114-
klog.V(100).Info("The namespace of the route is empty")
115-
116-
return nil, fmt.Errorf("route 'namespace' cannot be empty")
117-
}
118-
119-
if !builder.Exists() {
120-
return nil, fmt.Errorf("route object %s does not exist in namespace %s", name, nsname)
121-
}
122-
123-
builder.Definition = builder.Object
124-
125-
return builder, nil
66+
return common.PullNamespacedBuilder[routev1.Route, Builder](
67+
context.TODO(), apiClient, routev1.AddToScheme, name, nsname)
12668
}
12769

12870
// WithTargetPortNumber adds a target port to the route by number.
12971
func (builder *Builder) WithTargetPortNumber(port int32) *Builder {
130-
if valid, _ := builder.validate(); !valid {
72+
if err := common.Validate(builder); err != nil {
13173
return builder
13274
}
13375

@@ -145,7 +87,7 @@ func (builder *Builder) WithTargetPortNumber(port int32) *Builder {
14587

14688
// WithTargetPortName adds a target port to the route by name.
14789
func (builder *Builder) WithTargetPortName(portName string) *Builder {
148-
if valid, _ := builder.validate(); !valid {
90+
if err := common.Validate(builder); err != nil {
14991
return builder
15092
}
15193

@@ -155,10 +97,10 @@ func (builder *Builder) WithTargetPortName(portName string) *Builder {
15597
if portName == "" {
15698
klog.V(100).Info("Received empty route portName")
15799

158-
builder.errorMsg = "route target port name cannot be empty string"
100+
builder.SetError(fmt.Errorf("route target port name cannot be empty string"))
159101
}
160102

161-
if builder.errorMsg != "" {
103+
if builder.GetError() != nil {
162104
return builder
163105
}
164106

@@ -173,7 +115,7 @@ func (builder *Builder) WithTargetPortName(portName string) *Builder {
173115

174116
// WithHostDomain adds a route host domain to the route.
175117
func (builder *Builder) WithHostDomain(hostDomain string) *Builder {
176-
if valid, _ := builder.validate(); !valid {
118+
if err := common.Validate(builder); err != nil {
177119
return builder
178120
}
179121

@@ -183,7 +125,7 @@ func (builder *Builder) WithHostDomain(hostDomain string) *Builder {
183125
if hostDomain == "" {
184126
klog.V(100).Info("Received empty route hostDomain")
185127

186-
builder.errorMsg = "route host domain cannot be empty string"
128+
builder.SetError(fmt.Errorf("route host domain cannot be empty string"))
187129

188130
return builder
189131
}
@@ -195,18 +137,17 @@ func (builder *Builder) WithHostDomain(hostDomain string) *Builder {
195137

196138
// WithWildCardPolicy adds the specified wildCardPolicy to the route.
197139
func (builder *Builder) WithWildCardPolicy(wildcardPolicy string) *Builder {
198-
if valid, _ := builder.validate(); !valid {
140+
if err := common.Validate(builder); err != nil {
199141
return builder
200142
}
201143

202144
klog.V(100).Infof("Adding wildcardPolicy %s to route %s in namespace %s",
203145
wildcardPolicy, builder.Definition.Name, builder.Definition.Namespace)
204146

205147
if !slices.Contains(supportedWildCardPolicies(), wildcardPolicy) {
206-
klog.V(100).Infof("Received unsupported route wildcardPolicy, supported policies: %v", supportedWildCardPolicies())
148+
klog.V(100).Infof("Received unsupported route wildcardPolicy, expected one of %v", supportedWildCardPolicies())
207149

208-
builder.errorMsg = fmt.Sprintf("received unsupported route wildcardPolicy: supported policies %v",
209-
supportedWildCardPolicies())
150+
builder.SetError(getUnsupportedWildCardPoliciesError())
210151

211152
return builder
212153
}
@@ -216,129 +157,15 @@ func (builder *Builder) WithWildCardPolicy(wildcardPolicy string) *Builder {
216157
return builder
217158
}
218159

219-
// Exists checks whether the given route exists.
220-
func (builder *Builder) Exists() bool {
221-
if valid, _ := builder.validate(); !valid {
222-
return false
223-
}
224-
225-
klog.V(100).Infof(
226-
"Checking if route %s exists in namespace %s",
227-
builder.Definition.Name, builder.Definition.Namespace)
228-
229-
var err error
230-
231-
builder.Object, err = builder.Get()
232-
233-
return err == nil || !k8serrors.IsNotFound(err)
234-
}
235-
236-
// Get returns route object if found.
237-
func (builder *Builder) Get() (*routev1.Route, error) {
238-
if valid, err := builder.validate(); !valid {
239-
return nil, err
240-
}
241-
242-
klog.V(100).Infof(
243-
"Getting route %s in namespace %s",
244-
builder.Definition.Name, builder.Definition.Namespace)
245-
246-
route := &routev1.Route{}
247-
248-
err := builder.apiClient.Get(logging.DiscardContext(), goclient.ObjectKey{
249-
Name: builder.Definition.Name,
250-
Namespace: builder.Definition.Namespace,
251-
}, route)
252-
if err != nil {
253-
return nil, err
254-
}
255-
256-
return route, nil
257-
}
258-
259-
// Create makes a route according to the route definition and stores the created object in the route builder.
260-
func (builder *Builder) Create() (*Builder, error) {
261-
if valid, err := builder.validate(); !valid {
262-
return builder, err
263-
}
264-
265-
klog.V(100).Infof("Creating the route %s in namespace %s",
266-
builder.Definition.Name, builder.Definition.Namespace)
267-
268-
var err error
269-
if !builder.Exists() {
270-
err = builder.apiClient.Create(logging.DiscardContext(), builder.Definition)
271-
if err == nil {
272-
builder.Object = builder.Definition
273-
}
274-
}
275-
276-
return builder, err
277-
}
278-
279-
// Delete removes the route object and resets the builder object.
280-
func (builder *Builder) Delete() (*Builder, error) {
281-
if valid, err := builder.validate(); !valid {
282-
return builder, err
283-
}
284-
285-
klog.V(100).Infof("Deleting the route %s in namespace %s",
286-
builder.Definition.Name, builder.Definition.Namespace)
287-
288-
if !builder.Exists() {
289-
klog.V(100).Infof("Route %s does not exist in namespace %s",
290-
builder.Definition.Name, builder.Definition.Namespace)
291-
292-
builder.Object = nil
293-
294-
return builder, nil
295-
}
296-
297-
err := builder.apiClient.Delete(logging.DiscardContext(), builder.Definition)
298-
if err != nil {
299-
return builder, fmt.Errorf("cannot delete route: %w", err)
300-
}
301-
302-
builder.Object = nil
303-
304-
return builder, nil
305-
}
306-
307-
// validate will check that the builder and builder definition are properly initialized before
308-
// accessing any member fields.
309-
func (builder *Builder) validate() (bool, error) {
310-
resourceCRD := "Route"
311-
312-
if builder == nil {
313-
klog.V(100).Infof("The %s builder is uninitialized", resourceCRD)
314-
315-
return false, fmt.Errorf("error: received nil %s builder", resourceCRD)
316-
}
317-
318-
if builder.Definition == nil {
319-
klog.V(100).Infof("The %s is undefined", resourceCRD)
320-
321-
return false, fmt.Errorf("%s", msg.UndefinedCrdObjectErrString(resourceCRD))
322-
}
323-
324-
if builder.apiClient == nil {
325-
klog.V(100).Infof("The %s builder apiclient is nil", resourceCRD)
326-
327-
return false, fmt.Errorf("%s builder cannot have nil apiClient", resourceCRD)
328-
}
329-
330-
if builder.errorMsg != "" {
331-
klog.V(100).Infof("The %s builder has error message: %s", resourceCRD, builder.errorMsg)
332-
333-
return false, fmt.Errorf("%s", builder.errorMsg)
334-
}
335-
336-
return true, nil
337-
}
338-
339160
func supportedWildCardPolicies() []string {
340161
return []string{
341162
"Subdomain",
342163
"None",
343164
}
344165
}
166+
167+
func getUnsupportedWildCardPoliciesError() error {
168+
return fmt.Errorf(
169+
"received unsupported route wildcardPolicy: expected one of %v",
170+
supportedWildCardPolicies())
171+
}

0 commit comments

Comments
 (0)