Skip to content

Commit

Permalink
:sparkling: Adding reconcile function for lb targets
Browse files Browse the repository at this point in the history
Loadbalancer targets are now reconciled as well. Before they were not
updated after load balancer creation. They can be both deleted and
created - updating is not supported.
  • Loading branch information
janiskemper authored and batistein committed Jan 14, 2022
1 parent 09fca6b commit d11a9a7
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 22 deletions.
3 changes: 2 additions & 1 deletion api/v1beta1/hetznercluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ type LoadBalancerSpec struct {
Port int `json:"port,omitempty"`

// Defines how traffic will be routed from the Load Balancer to your target server.
Targets []LoadBalancerTargetSpec `json:"extraTarget"`
// +optional
Targets []LoadBalancerTargetSpec `json:"extraTarget,omitempty"`

Region Region `json:"region"`
}
Expand Down
6 changes: 3 additions & 3 deletions api/v1beta1/network_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ type LoadBalancerTargetSpec struct {
// Protocol specifies the supported Loadbalancer Protocol.
// +optional
// +kubebuilder:validation:Enum=http;https;tcp
Protocol string `json:"protocol"`
Protocol string `json:"protocol,omitempty"`

// Equal Source port, defines the incoming port open on the loadbalancer
// +optional
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
ListenPort int `json:"listenPort"`
ListenPort int `json:"listenPort,omitempty"`

// Defines the port on the server
// +optional
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
DestinationPort int `json:"destinationPort"`
DestinationPort int `json:"destinationPort,omitempty"`
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ spec:
- lb31
type: string
required:
- extraTarget
- region
type: object
controlPlaneRegion:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ spec:
- lb31
type: string
required:
- extraTarget
- region
type: object
controlPlaneRegion:
Expand Down
5 changes: 5 additions & 0 deletions pkg/scope/hcloud_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type HCloudClient interface {
AddTargetServerToLoadBalancer(context.Context, hcloud.LoadBalancerAddServerTargetOpts, *hcloud.LoadBalancer) (*hcloud.Action, *hcloud.Response, error)
DeleteTargetServerOfLoadBalancer(context.Context, *hcloud.LoadBalancer, *hcloud.Server) (*hcloud.Action, *hcloud.Response, error)
AddServiceToLoadBalancer(context.Context, *hcloud.LoadBalancer, hcloud.LoadBalancerAddServiceOpts) (*hcloud.Action, *hcloud.Response, error)
DeleteServiceFromLoadBalancer(context.Context, *hcloud.LoadBalancer, int) (*hcloud.Action, *hcloud.Response, error)
ListImages(context.Context, hcloud.ImageListOpts) ([]*hcloud.Image, error)
CreateServer(context.Context, hcloud.ServerCreateOpts) (hcloud.ServerCreateResult, *hcloud.Response, error)
AttachServerToNetwork(context.Context, *hcloud.Server, hcloud.ServerAttachToNetworkOpts) (*hcloud.Action, *hcloud.Response, error)
Expand Down Expand Up @@ -111,6 +112,10 @@ func (c *realHCloudClient) AddServiceToLoadBalancer(ctx context.Context, lb *hcl
return c.client.LoadBalancer.AddService(ctx, lb, opts)
}

func (c *realHCloudClient) DeleteServiceFromLoadBalancer(ctx context.Context, lb *hcloud.LoadBalancer, listenPort int) (*hcloud.Action, *hcloud.Response, error) {
return c.client.LoadBalancer.DeleteService(ctx, lb, listenPort)
}

func (c *realHCloudClient) ListImages(ctx context.Context, opts hcloud.ImageListOpts) ([]*hcloud.Image, error) {
return c.client.Image.AllWithOpts(ctx, opts)
}
Expand Down
76 changes: 60 additions & 16 deletions pkg/services/hcloud/loadbalancer/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
infrav1 "github.com/syself/cluster-api-provider-hetzner/api/v1beta1"
"github.com/syself/cluster-api-provider-hetzner/pkg/scope"
"github.com/syself/cluster-api-provider-hetzner/pkg/utils"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/storage/names"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util/conditions"
Expand Down Expand Up @@ -68,6 +69,11 @@ func (s *Service) Reconcile(ctx context.Context) (err error) {
return errors.Wrap(err, "failed to reconcile network attachement")
}

// reconcile targets
if err := s.reconcileTargets(ctx, lb); err != nil {
return errors.Wrap(err, "failed to reconcile targets")
}

// update current status
lbStatus := s.apiToStatus(lb)
s.scope.HetznerCluster.Status.ControlPlaneLoadBalancer = &lbStatus
Expand Down Expand Up @@ -118,6 +124,60 @@ func (s *Service) reconcileNetworkAttachement(ctx context.Context, lb *hcloud.Lo
return nil
}

func (s *Service) reconcileTargets(ctx context.Context, lb *hcloud.LoadBalancer) error {

// Delete targets which are registered for lb but are not in specs
var multierr []error
for _, target := range lb.Services {
var found bool

// Do nothing for kubeAPI target
if target.ListenPort == int(s.scope.HetznerCluster.Spec.ControlPlaneEndpoint.Port) {
continue
}

for _, targetInSpec := range s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Targets {
if target.ListenPort == targetInSpec.ListenPort {
found = true
}
}

if !found {
_, _, err := s.scope.HCloudClient().DeleteServiceFromLoadBalancer(ctx, lb, target.ListenPort)
if err != nil {
multierr = append(multierr, fmt.Errorf("error adding service to load balancer: %s", err))
}
}
}

// Create targets which are in specs but not registered in Hetzner API
for _, targetInSpec := range s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Targets {
var found bool

for _, target := range lb.Services {
if target.ListenPort == targetInSpec.ListenPort {
found = true
}
}

if !found {
proxyProtocol := false
serviceOpts := hcloud.LoadBalancerAddServiceOpts{
Protocol: hcloud.LoadBalancerServiceProtocol(targetInSpec.Protocol),
ListenPort: &targetInSpec.ListenPort,
DestinationPort: &targetInSpec.DestinationPort,
Proxyprotocol: &proxyProtocol,
}
_, _, err := s.scope.HCloudClient().AddServiceToLoadBalancer(ctx, lb, serviceOpts)
if err != nil {
multierr = append(multierr, fmt.Errorf("error adding service to load balancer: %s", err))
}
}
}

return kerrors.NewAggregate(multierr)
}

func (s *Service) createLoadBalancer(ctx context.Context) (*hcloud.LoadBalancer, error) {
log := ctrl.LoggerFrom(ctx)

Expand Down Expand Up @@ -191,22 +251,6 @@ func (s *Service) createLoadBalancer(ctx context.Context) (*hcloud.LoadBalancer,
return nil, errors.Wrap(err, "error creating load balancer")
}

// If there is more than one service in the specs, add them here one after another
// Adding all at the same time on creation led to an error that the source port is already in use
if len(s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Services) > 1 {
for _, spec := range s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Services[1:] {
serviceOpts := hcloud.LoadBalancerAddServiceOpts{
Protocol: hcloud.LoadBalancerServiceProtocol(spec.Protocol),
ListenPort: &spec.ListenPort,
DestinationPort: &spec.DestinationPort,
Proxyprotocol: &proxyprotocol,
}
_, _, err := s.scope.HCloudClient().AddServiceToLoadBalancer(ctx, res.LoadBalancer, serviceOpts)
if err != nil {
return nil, fmt.Errorf("error adding service to load balancer: %s", err)
}
}
}
record.Eventf(s.scope.HetznerCluster, "CreateLoadBalancer", "Created load balancer")
return res.LoadBalancer, nil
}
Expand Down

0 comments on commit d11a9a7

Please sign in to comment.