Skip to content

Commit 5cf92b2

Browse files
authored
fix: Correctly report failed deploy of ServiceLoadBalancer (#759)
**What problem does this PR solve?**: I found a bug while working on SLB configuration (future PR). Wrote a failing test, and fixed the bug. **Which issue(s) this PR fixes**: Fixes # **How Has This Been Tested?**: <!-- Please describe the tests that you ran to verify your changes. Provide output from the tests and any manual steps needed to replicate the tests. --> **Special notes for your reviewer**: <!-- Use this to provide any additional information to the reviewers. This may include: - Best way to review the PR. - Where the author wants the most review attention on. - etc. -->
1 parent 4048446 commit 5cf92b2

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

pkg/handlers/generic/lifecycle/serviceloadbalancer/handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ func (s *ServiceLoadBalancerHandler) apply(
149149
err,
150150
),
151151
)
152+
return
152153
}
153154

154155
resp.SetStatus(runtimehooksv1.ResponseStatusSuccess)
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright 2024 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package serviceloadbalancer
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"testing"
10+
11+
"github.com/go-logr/logr"
12+
"github.com/google/go-cmp/cmp"
13+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
14+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
15+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
16+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
17+
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
19+
apivariables "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/variables"
20+
)
21+
22+
type fakeServiceLoadBalancerProvider struct {
23+
returnedErr error
24+
}
25+
26+
func (p *fakeServiceLoadBalancerProvider) Apply(
27+
ctx context.Context,
28+
cluster *clusterv1.Cluster,
29+
log logr.Logger,
30+
) error {
31+
return p.returnedErr
32+
}
33+
34+
var testProviderHandlers = map[string]ServiceLoadBalancerProvider{
35+
"test1": &fakeServiceLoadBalancerProvider{},
36+
"test2": &fakeServiceLoadBalancerProvider{},
37+
"broken": &fakeServiceLoadBalancerProvider{
38+
returnedErr: fmt.Errorf("fake error"),
39+
},
40+
}
41+
42+
func testClusterVariable(
43+
t *testing.T,
44+
slb *v1alpha1.ServiceLoadBalancer,
45+
) *clusterv1.ClusterVariable {
46+
t.Helper()
47+
cv, err := apivariables.MarshalToClusterVariable(
48+
"clusterConfig",
49+
&apivariables.ClusterConfigSpec{
50+
Addons: &apivariables.Addons{
51+
GenericAddons: v1alpha1.GenericAddons{
52+
ServiceLoadBalancer: slb,
53+
},
54+
},
55+
},
56+
)
57+
if err != nil {
58+
t.Fatalf("failed to create clusterVariable: %s", err)
59+
}
60+
return cv
61+
}
62+
63+
type testCase struct {
64+
name string
65+
clusterVariable *clusterv1.ClusterVariable
66+
wantStatus runtimehooksv1.ResponseStatus
67+
}
68+
69+
func testCases(t *testing.T) []testCase {
70+
t.Helper()
71+
return []testCase{
72+
{
73+
name: "request is missing serviceLoadBalancer field",
74+
clusterVariable: testClusterVariable(
75+
t,
76+
nil,
77+
),
78+
wantStatus: runtimehooksv1.ResponseStatus(""), // Neither success, nor failure.
79+
},
80+
{
81+
name: "request is malformed",
82+
clusterVariable: &clusterv1.ClusterVariable{
83+
Name: "clusterConfig",
84+
Value: apiextensionsv1.JSON{
85+
Raw: []byte("{\"addons\":{\"serviceLoadBalancer\":{\"provider\": %%% }}}"),
86+
},
87+
},
88+
wantStatus: runtimehooksv1.ResponseStatusFailure,
89+
},
90+
{
91+
name: "provider is not known",
92+
clusterVariable: testClusterVariable(
93+
t,
94+
&v1alpha1.ServiceLoadBalancer{
95+
Provider: "unknown",
96+
},
97+
),
98+
wantStatus: runtimehooksv1.ResponseStatusFailure,
99+
},
100+
{
101+
name: "provider is known, deploy succeeds",
102+
clusterVariable: testClusterVariable(
103+
t,
104+
&v1alpha1.ServiceLoadBalancer{
105+
Provider: "test1",
106+
},
107+
),
108+
wantStatus: runtimehooksv1.ResponseStatusSuccess,
109+
},
110+
{
111+
name: "provider is known, deploy fails",
112+
clusterVariable: testClusterVariable(
113+
t,
114+
&v1alpha1.ServiceLoadBalancer{
115+
Provider: "broken",
116+
},
117+
),
118+
wantStatus: runtimehooksv1.ResponseStatusFailure,
119+
},
120+
}
121+
}
122+
123+
func TestAfterControlPlaneInitialized(t *testing.T) {
124+
for _, tt := range testCases(t) {
125+
t.Run(tt.name, func(t *testing.T) {
126+
ctx := context.Background()
127+
client := fake.NewClientBuilder().Build()
128+
handler := New(client, testProviderHandlers)
129+
resp := &runtimehooksv1.AfterControlPlaneInitializedResponse{}
130+
131+
req := &runtimehooksv1.AfterControlPlaneInitializedRequest{
132+
Cluster: clusterv1.Cluster{
133+
Spec: clusterv1.ClusterSpec{
134+
Topology: &clusterv1.Topology{
135+
Variables: []clusterv1.ClusterVariable{
136+
*tt.clusterVariable,
137+
},
138+
},
139+
},
140+
},
141+
}
142+
143+
handler.AfterControlPlaneInitialized(ctx, req, resp)
144+
if diff := cmp.Diff(tt.wantStatus, resp.Status); diff != "" {
145+
t.Errorf(
146+
"response Status mismatch (-want +got):\n%s. Message: %s",
147+
diff,
148+
resp.Message,
149+
)
150+
}
151+
})
152+
}
153+
}
154+
155+
func TestBeforeClusterUpgrade(t *testing.T) {
156+
for _, tt := range testCases(t) {
157+
t.Run(tt.name, func(t *testing.T) {
158+
ctx := context.Background()
159+
client := fake.NewClientBuilder().Build()
160+
handler := New(client, testProviderHandlers)
161+
resp := &runtimehooksv1.BeforeClusterUpgradeResponse{}
162+
163+
req := &runtimehooksv1.BeforeClusterUpgradeRequest{
164+
Cluster: clusterv1.Cluster{
165+
Spec: clusterv1.ClusterSpec{
166+
Topology: &clusterv1.Topology{
167+
Variables: []clusterv1.ClusterVariable{
168+
*tt.clusterVariable,
169+
},
170+
},
171+
},
172+
},
173+
}
174+
175+
handler.BeforeClusterUpgrade(ctx, req, resp)
176+
if diff := cmp.Diff(tt.wantStatus, resp.Status); diff != "" {
177+
t.Errorf(
178+
"response Status mismatch (-want +got):\n%s. Message: %s",
179+
diff,
180+
resp.Message,
181+
)
182+
}
183+
})
184+
}
185+
}

0 commit comments

Comments
 (0)