Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions hcloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ import (
)

const (
providerName = "hcloud"
apiClientTimeout = 15 * time.Second
providerName = "hcloud"
apiClientTimeout = 15 * time.Second
lbTypeCacheMaxAge = time.Hour
lbTypeCacheDefaultMode = cache.ModeAll
)

// providerVersion is set by the build process using -ldflags -X.
Expand All @@ -54,6 +56,7 @@ type cloud struct {
client *hcloud.Client
robotClient hrobot.RobotClient
serverCache *cache.Cache[hcloud.Server]
lbTypeCache *cache.Cache[hcloud.LoadBalancerType]
cfg config.HCCMConfiguration
recorder record.EventRecorder
networkID int64
Expand Down Expand Up @@ -150,11 +153,13 @@ func NewCloud(cidr string, nodeLister corelisters.NodeLister) (cloudprovider.Int
klog.Infof("Hetzner Cloud k8s cloud controller %s started\n", providerVersion)

serverCache := cache.NewServerCache(client, cfg.ServerCache.Mode, cfg.ServerCache.MaxAge)
lbTypeCache := cache.NewLoadBalancerTypeCache(client, lbTypeCacheDefaultMode, lbTypeCacheMaxAge)
Comment thread
lukasmetzner marked this conversation as resolved.

return &cloud{
client: client,
robotClient: robotClient,
serverCache: serverCache,
lbTypeCache: lbTypeCache,
cfg: cfg,
networkID: networkID,
cidr: cidr,
Expand Down Expand Up @@ -202,6 +207,7 @@ func (c *cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
CertOps: &hcops.CertificateOps{ActionClient: &c.client.Action, CertClient: &c.client.Certificate},
ActionClient: &c.client.Action,
NetworkClient: &c.client.Network,
LBTypeCache: c.lbTypeCache,
NetworkID: c.networkID,
Cfg: c.cfg,
Recorder: c.recorder,
Expand Down
29 changes: 29 additions & 0 deletions internal/cache/lbtypecache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cache

import (
"context"
"time"

"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

func NewLoadBalancerTypeCache(client *hcloud.Client, defaultMode Mode, defaultMaxAge time.Duration) *Cache[hcloud.LoadBalancerType] {
return newCache[hcloud.LoadBalancerType](
func(ctx context.Context, id int64) (*hcloud.LoadBalancerType, error) {
value, _, err := client.LoadBalancerType.GetByID(ctx, id)
return value, err
},
func(ctx context.Context, name string) (*hcloud.LoadBalancerType, error) {
value, _, err := client.LoadBalancerType.GetByName(ctx, name)
return value, err
},
func(ctx context.Context) ([]*hcloud.LoadBalancerType, error) {
values, err := client.LoadBalancerType.All(ctx)
return values, err
},
func(value *hcloud.LoadBalancerType) int64 { return value.ID },
func(value *hcloud.LoadBalancerType) string { return value.Name },
defaultMode,
defaultMaxAge,
)
}
65 changes: 65 additions & 0 deletions internal/cache/lbtypecache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cache

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/mockutil"
)

func TestNewLBTypeCache(t *testing.T) {
testCases := []struct {
name string
mode Mode
requests []mockutil.Request
}{
{
mode: ModeAll,
requests: []mockutil.Request{
{Method: "GET", Path: "/load_balancer_types?page=1&per_page=50", Status: 200, JSONRaw: `{ "load_balancer_types": [{ "id": 1, "name": "lb11" }]}`},
},
},
{
mode: ModeOne,
requests: []mockutil.Request{
{Method: "GET", Path: "/load_balancer_types/1", Status: 200, JSONRaw: `{ "load_balancer_type": { "id": 1, "name": "lb11" }}`},
},
},
{
mode: ModeOff,
requests: []mockutil.Request{
{Method: "GET", Path: "/load_balancer_types/1", Status: 200, JSONRaw: `{ "load_balancer_type": { "id": 1, "name": "lb11" }}`},
{Method: "GET", Path: "/load_balancer_types?name=lb11", Status: 200, JSONRaw: `{ "load_balancer_types": [{ "id": 1, "name": "lb11" }]}`},
},
},
}

for _, tt := range testCases {
t.Run(string(tt.mode), func(t *testing.T) {
server := mockutil.NewServer(t, tt.requests)
client := hcloud.NewClient(hcloud.WithEndpoint(server.Server.URL))

cache := NewLoadBalancerTypeCache(client, tt.mode, 10*time.Second)
require.NotNil(t, cache)
require.NotNil(t, cache.fetchOneByID)
require.NotNil(t, cache.fetchOneByName)
require.NotNil(t, cache.fetchAll)
require.NotNil(t, cache.getID)
require.NotNil(t, cache.getName)

ctx := t.Context()

srv, err := cache.ByID(ctx, int64(1))
require.NoError(t, err)
assert.NotNil(t, srv)

srv, err = cache.ByName(ctx, "lb11")
require.NoError(t, err)
assert.NotNil(t, srv)
})
}
}
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ func Read() (HCCMConfiguration, error) {
errs = append(errs, err)
}

cfg.LoadBalancer.Type = os.Getenv(hcloudLoadBalancersType)
cfg.LoadBalancer.Type = os.Getenv(HcloudLoadBalancersType)

cfg.Network.NameOrID = os.Getenv(hcloudNetwork)
disableAttachedCheck, err := getEnvBool(hcloudNetworkDisableAttachedCheck, false)
Expand Down
4 changes: 2 additions & 2 deletions internal/config/load_balancer_envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ const (
// Type: string
hcloudLoadBalancersPrivateSubnetIPRange = "HCLOUD_LOAD_BALANCERS_PRIVATE_SUBNET_IP_RANGE"

// hcloudLoadBalancersType configures the default Load Balancer type this Load Balancer should be created with.
// HcloudLoadBalancersType configures the default Load Balancer type this Load Balancer should be created with.
//
// Type: string
// Default: lb11
hcloudLoadBalancersType = "HCLOUD_LOAD_BALANCERS_TYPE"
HcloudLoadBalancersType = "HCLOUD_LOAD_BALANCERS_TYPE"

// hcloudLoadBalancersUsesProxyProtocol enables the proxyprotocol for a Load Balancer service by default.
//
Expand Down
93 changes: 78 additions & 15 deletions internal/hcops/load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,30 @@ import (
"k8s.io/klog/v2"

"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/annotation"
"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/cache"
"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/config"
"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/metrics"
"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/providerid"
"github.com/hetznercloud/hcloud-cloud-controller-manager/internal/utils"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/deprecationutil"
)

// LabelServiceUID is a label added to the Hetzner Cloud backend to uniquely
// identify a load balancer managed by Hetzner Cloud Cloud Controller Manager.
const LabelServiceUID = "hcloud-ccm/service-uid"
const (
// LabelServiceUID is a label added to the Hetzner Cloud backend to uniquely
// identify a load balancer managed by Hetzner Cloud Cloud Controller Manager.
LabelServiceUID = "hcloud-ccm/service-uid"

defaultLoadBalancerType = "lb11"
)

// LoadBalancerOps implements all operations regarding Hetzner Cloud Load Balancers.
type LoadBalancerOps struct {
LBClient hcloud.ILoadBalancerClient
ActionClient hcloud.IActionClient
NetworkClient hcloud.INetworkClient
RobotClient hrobot.RobotClient
LBTypeCache *cache.Cache[hcloud.LoadBalancerType]
CertOps *CertificateOps
RetryDelay time.Duration
NetworkID int64
Expand Down Expand Up @@ -109,6 +117,52 @@ func (l *LoadBalancerOps) GetByID(ctx context.Context, id int64) (*hcloud.LoadBa
return lb, nil
}

func (l *LoadBalancerOps) getType(ctx context.Context, svc *corev1.Service) (*hcloud.LoadBalancerType, bool, error) {
var lbTypeName string
var unset bool

if l.Cfg.LoadBalancer.Type != "" {
lbTypeName = l.Cfg.LoadBalancer.Type
}

if v, ok := annotation.LBType.StringFromService(svc); ok {
lbTypeName = v
}

if lbTypeName == "" {
lbTypeName = defaultLoadBalancerType
unset = true
utils.WarnEventLogf(
l.Recorder,
svc,
"LoadBalancerTypeUnconfigured",
"load balancer type unconfigured: this will be required in the future, set it with the annotation %q or cluster-wide with the environment variable %q",
annotation.LBType,
config.HcloudLoadBalancersType,
)
}

lbType, err := l.LBTypeCache.ByName(ctx, lbTypeName)
if err != nil {
return nil, unset, err
}

if lbType == nil {
return nil, unset, fmt.Errorf("load balancer type not found: %s", lbTypeName)
}

if msg, _ := deprecationutil.LoadBalancerTypeMessage(lbType); msg != "" {
utils.WarnEventLogf(
l.Recorder,
svc,
"LoadBalancerTypeDeprecated",
"%s", msg,
)
}

return lbType, unset, nil
}

// Create creates a new Load Balancer using the Hetzner Cloud API.
//
// It adds annotations identifying the HC Load Balancer to svc.
Expand All @@ -125,11 +179,13 @@ func (l *LoadBalancerOps) Create(
LabelServiceUID: string(svc.ObjectMeta.UID),
},
}
if v, ok := annotation.LBType.StringFromService(svc); ok {
opts.LoadBalancerType.Name = v
} else if l.Cfg.LoadBalancer.Type != "" {
opts.LoadBalancerType.Name = l.Cfg.LoadBalancer.Type

lbType, _, err := l.getType(ctx, svc)
if err != nil {
return nil, fmt.Errorf("error getting load balancer type: %w", err)
}
opts.LoadBalancerType = lbType

if l.Cfg.LoadBalancer.Location != "" {
opts.Location = &hcloud.Location{Name: l.Cfg.LoadBalancer.Location}
}
Expand Down Expand Up @@ -395,18 +451,25 @@ func (l *LoadBalancerOps) changeType(ctx context.Context, lb *hcloud.LoadBalance
const op = "hcops/LoadBalancerOps.changeType"
metrics.OperationCalled.WithLabelValues(op).Inc()

lt, ok := annotation.LBType.StringFromService(svc)
if !ok {
if l.Cfg.LoadBalancer.Type == "" {
return false, nil
}
lt = l.Cfg.LoadBalancer.Type
opts := hcloud.LoadBalancerChangeTypeOpts{}

lbType, unset, err := l.getType(ctx, svc)
if err != nil {
return false, fmt.Errorf("error getting load balancer type: %w", err)
}

// If the user removes the annotation, we do not downgrade the Load Balancer
// back to its default value. This could be changed in a next major release.
if unset {
return false, nil
}
if lt == lb.LoadBalancerType.Name {

opts.LoadBalancerType = lbType

if lb.LoadBalancerType.Name == lbType.Name {
return false, nil
}

opts := hcloud.LoadBalancerChangeTypeOpts{LoadBalancerType: &hcloud.LoadBalancerType{Name: lt}}
action, _, err := l.LBClient.ChangeType(ctx, lb, opts)
if err != nil {
return false, fmt.Errorf("%s: %w", op, withInvalidInputFields(err))
Expand Down
Loading
Loading