Skip to content

Commit 2507017

Browse files
authored
🌱 Avoid panic if hetznercluster.spec.controlPlaneEndpoint is not set (#1684)
1 parent cdc0983 commit 2507017

File tree

2 files changed

+54
-15
lines changed

2 files changed

+54
-15
lines changed

pkg/services/hcloud/loadbalancer/loadbalancer.go

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ func NewService(scope *scope.ClusterScope) *Service {
5050
// ErrNoLoadBalancerAvailable indicates that no available load balancer could be fond.
5151
var ErrNoLoadBalancerAvailable = fmt.Errorf("no available load balancer")
5252

53+
// ErrControlPlaneEndpointNotSet indicates that hetznercluster.spec.controlPlaneEndpoint is not set.
54+
var ErrControlPlaneEndpointNotSet = errors.New("hetznercluster.spec.controlPlaneEndpoint is not set")
55+
5356
// Reconcile implements the life cycle of HCloud load balancers.
5457
func (s *Service) Reconcile(ctx context.Context) (reconcile.Result, error) {
5558
// delete the deprecated condition from existing cluster objects
@@ -68,20 +71,37 @@ func (s *Service) Reconcile(ctx context.Context) (reconcile.Result, error) {
6871
}
6972

7073
if lb == nil {
71-
// fixed name is set - we expect a load balancer with this name to exist
72-
7374
if s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Name != nil {
75+
// fixed name is set - we expect a load balancer with this name to exist
7476
lb, err = s.ownExistingLoadBalancer(ctx)
75-
76-
// if load balancer is not found even though we expect it to exist, wait and reconcile until user creates it
77-
if errors.Is(err, ErrNoLoadBalancerAvailable) {
78-
return reconcile.Result{RequeueAfter: 1 * time.Minute}, nil
77+
if err != nil {
78+
// if load balancer is not found even though we expect it to exist, wait and reconcile until user creates it
79+
if errors.Is(err, ErrNoLoadBalancerAvailable) {
80+
return reconcile.Result{RequeueAfter: 1 * time.Minute}, nil
81+
}
82+
return reconcile.Result{}, fmt.Errorf("failed to own existing load balancer (name=%s): %w", *s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Name, err)
7983
}
8084
} else {
8185
lb, err = s.createLoadBalancer(ctx)
82-
}
83-
if err != nil {
84-
return reconcile.Result{}, fmt.Errorf("failed to own/create load balancer: %w", err)
86+
if err != nil {
87+
if errors.Is(err, ErrControlPlaneEndpointNotSet) {
88+
// When an external ControlPlane Provider gets used (Kamaji), it might
89+
// need some time until the endpoint is available.
90+
err = fmt.Errorf("requeue, waiting for control-plane endpoint to be set: %w",
91+
err)
92+
conditions.MarkFalse(
93+
s.scope.HetznerCluster,
94+
infrav1.LoadBalancerReadyCondition,
95+
"MissingControlPlaneEndpoint",
96+
clusterv1.ConditionSeverityWarning,
97+
"%s",
98+
err.Error(),
99+
)
100+
s.scope.Logger.Info(err.Error())
101+
return reconcile.Result{RequeueAfter: 10 * time.Second}, nil
102+
}
103+
return reconcile.Result{}, fmt.Errorf("failed to create load balancer: %w", err)
104+
}
85105
}
86106
}
87107

@@ -287,7 +307,10 @@ func (s *Service) reconcileServices(ctx context.Context, lb *hcloud.LoadBalancer
287307
}
288308

289309
func (s *Service) createLoadBalancer(ctx context.Context) (*hcloud.LoadBalancer, error) {
290-
opts := createOptsFromSpec(s.scope.HetznerCluster)
310+
opts, err := createOptsFromSpec(s.scope.HetznerCluster)
311+
if err != nil {
312+
return nil, err
313+
}
291314
lb, err := s.scope.HCloudClient.CreateLoadBalancer(ctx, opts)
292315
if err != nil {
293316
err = fmt.Errorf("failed to create load balancer: %w", err)
@@ -309,7 +332,7 @@ func (s *Service) createLoadBalancer(ctx context.Context) (*hcloud.LoadBalancer,
309332
return lb, nil
310333
}
311334

312-
func createOptsFromSpec(hc *infrav1.HetznerCluster) hcloud.LoadBalancerCreateOpts {
335+
func createOptsFromSpec(hc *infrav1.HetznerCluster) (hcloud.LoadBalancerCreateOpts, error) {
313336
// gather algorithm type
314337
algorithmType := hc.Spec.ControlPlaneLoadBalancer.Algorithm.HCloudAlgorithmType()
315338

@@ -323,6 +346,10 @@ func createOptsFromSpec(hc *infrav1.HetznerCluster) hcloud.LoadBalancerCreateOpt
323346
network = &hcloud.Network{ID: hc.Status.Network.ID}
324347
}
325348

349+
if hc.Spec.ControlPlaneEndpoint == nil {
350+
return hcloud.LoadBalancerCreateOpts{}, ErrControlPlaneEndpointNotSet
351+
}
352+
326353
listenPort := int(hc.Spec.ControlPlaneEndpoint.Port)
327354
publicInterface := true
328355
return hcloud.LoadBalancerCreateOpts{
@@ -341,7 +368,7 @@ func createOptsFromSpec(hc *infrav1.HetznerCluster) hcloud.LoadBalancerCreateOpt
341368
Proxyprotocol: &proxyprotocol,
342369
},
343370
},
344-
}
371+
}, nil
345372
}
346373

347374
// Delete implements the deletion of HCloud load balancers.

pkg/services/hcloud/loadbalancer/loadbalancer_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package loadbalancer
1818

1919
import (
20+
"errors"
21+
2022
"github.com/go-logr/logr"
2123
"github.com/hetznercloud/hcloud-go/v2/hcloud"
2224
. "github.com/onsi/ginkgo/v2"
@@ -122,7 +124,8 @@ var _ = Describe("createOptsFromSpec", func() {
122124
hetznerCluster.Status.Network = nil
123125
wantCreateOpts.Network = nil
124126

125-
createOpts := createOptsFromSpec(hetznerCluster)
127+
createOpts, err := createOptsFromSpec(hetznerCluster)
128+
Expect(err).To(BeNil())
126129

127130
// ignore random name
128131
createOpts.Name = ""
@@ -131,7 +134,8 @@ var _ = Describe("createOptsFromSpec", func() {
131134
})
132135

133136
It("creates specs for cluster with network", func() {
134-
createOpts := createOptsFromSpec(hetznerCluster)
137+
createOpts, err := createOptsFromSpec(hetznerCluster)
138+
Expect(err).To(BeNil())
135139

136140
// ignore random name
137141
createOpts.Name = ""
@@ -142,7 +146,8 @@ var _ = Describe("createOptsFromSpec", func() {
142146
It("creates specs for cluster without load balancer name set", func() {
143147
hetznerCluster.Spec.ControlPlaneLoadBalancer.Name = nil
144148

145-
createOpts := createOptsFromSpec(hetznerCluster)
149+
createOpts, err := createOptsFromSpec(hetznerCluster)
150+
Expect(err).To(BeNil())
146151

147152
// should generate correct name
148153
Expect(createOpts.Name).To(HavePrefix("hetzner-cluster-kube-apiserver-"))
@@ -152,4 +157,11 @@ var _ = Describe("createOptsFromSpec", func() {
152157
wantCreateOpts.Name = ""
153158
Expect(createOpts).To(Equal(wantCreateOpts))
154159
})
160+
161+
It("returns ErrControlPlaneEndpointNotSet", func() {
162+
hetznerCluster.Spec.ControlPlaneEndpoint = nil
163+
164+
_, err := createOptsFromSpec(hetznerCluster)
165+
Expect(errors.Is(err, ErrControlPlaneEndpointNotSet)).To(BeTrue())
166+
})
155167
})

0 commit comments

Comments
 (0)