Skip to content

Commit 02f2d2b

Browse files
authored
feat(loadbalancer): floating IP addresses (#2)
1 parent 7031553 commit 02f2d2b

15 files changed

Lines changed: 189 additions & 51 deletions

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
FROM alpine:3.22
1+
FROM alpine:3.23
22
RUN apk add --no-cache curl tini
33
COPY cloud-controller-manager /usr/local/bin/
44
ENTRYPOINT ["/sbin/tini", "--"]
5-
CMD ["cloud-controller-manager"]
5+
CMD ["cloud-controller-manager"]

Dockerfile.builder

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM golang:1.25-alpine AS builder
2+
3+
WORKDIR /workspace
4+
COPY go.mod go.mod
5+
COPY go.sum go.sum
6+
RUN go mod download
7+
COPY ./ ./
8+
ARG GOPROXY
9+
ARG OS
10+
ARG ARCH
11+
ARG ARM
12+
ARG LDFLAGS
13+
RUN CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH} GOARM=${ARM} GOPROXY=${GOPROXY} \
14+
go build \
15+
-ldflags="-extldflags '-static' ${LDFLAGS}" \
16+
-o=cloud-controller-manager \
17+
github.com/UpCloudLtd/upcloud-cloud-controller-manager/cmd/upcloud-cloud-controller-manager
18+
19+
FROM alpine:3.23
20+
RUN apk add --no-cache curl tini
21+
WORKDIR /
22+
COPY --from=builder /workspace/cloud-controller-manager /usr/local/bin/
23+
ENTRYPOINT ["/sbin/tini", "--"]
24+
CMD ["cloud-controller-manager"]

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ ifeq ($(GOPROXY),)
99
GOPROXY := https://proxy.golang.org
1010
endif
1111
export GOPROXY
12-
13-
export LDFLAGS := "-w -s -X 'k8s.io/component-base/version/verflag.programName=UpCloud cloud controller manager' $(shell scripts/version.sh "ldflags")"
12+
LDFLAGS ?= ""
1413
## --------------------------------------
1514
## Binaries
1615
## --------------------------------------
1716

1817
.PHONY: manager
1918
manager: ## Build cloud controller manager binary in local environment
2019
CGO_ENABLED=0 GOPROXY=$(GOPROXY) go build -ldflags $(LDFLAGS) -o $(BIN_DIR)/cloud-controller-manager github.com/UpCloudLtd/upcloud-cloud-controller-manager/cmd/upcloud-cloud-controller-manager
20+
21+
.PHONY: test
22+
test:
23+
go test -race ./...

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module github.com/UpCloudLtd/upcloud-cloud-controller-manager
22

3-
go 1.24.6
3+
go 1.25
44

55
require (
6-
github.com/UpCloudLtd/upcloud-go-api/v8 v8.20.0
6+
github.com/UpCloudLtd/upcloud-go-api/v8 v8.35.0
77
github.com/google/uuid v1.6.0
8-
github.com/stretchr/testify v1.10.0
8+
github.com/stretchr/testify v1.11.1
99
gopkg.in/yaml.v2 v2.4.0
1010
k8s.io/api v0.31.10
1111
k8s.io/apimachinery v0.31.10

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
22
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
33
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
44
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
5-
github.com/UpCloudLtd/upcloud-go-api/v8 v8.20.0 h1:nx1lbPwbqRPTNCZx437a7PrmyQkVXkgnK3hYZi9QgPc=
6-
github.com/UpCloudLtd/upcloud-go-api/v8 v8.20.0/go.mod h1:ImDdnWfVVM6WCRTrskGhAw2W1cRwu5IEkBw+9UCzAv8=
5+
github.com/UpCloudLtd/upcloud-go-api/v8 v8.35.0 h1:AIt07ExXzCaC9YVszkVPT+CteoyXldw0C8DGUMxtjD4=
6+
github.com/UpCloudLtd/upcloud-go-api/v8 v8.35.0/go.mod h1:sxG94uNhC31OQH+zK0RhZjVj+PdkhObsNAt5bvq2J8c=
77
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
88
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
99
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
@@ -162,8 +162,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
162162
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
163163
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
164164
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
165-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
166-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
165+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
166+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
167167
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
168168
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
169169
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=

internal/loadbalancer/compare.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ func createLoadBalancerRequestsEqual(r1, r2 *request.CreateLoadBalancerRequest)
7272
if !reflect.DeepEqual(r1.Backends, r2.Backends) {
7373
return fieldValueNotEqualError("backends")
7474
}
75+
if !reflect.DeepEqual(r1.IPAddresses, r2.IPAddresses) {
76+
return fieldValueNotEqualError("IP addresses")
77+
}
7578

7679
return nil
7780
}
@@ -120,6 +123,9 @@ func sortCreateLoadBalancerRequestSlices(r *request.CreateLoadBalancerRequest) {
120123
slices.SortFunc(r.Backends, func(a, b request.LoadBalancerBackend) int {
121124
return strings.Compare(a.Name, b.Name)
122125
})
126+
slices.SortFunc(r.IPAddresses, func(a, b request.LoadBalancerIPAddress) int {
127+
return strings.Compare(a.NetworkName, b.NetworkName)
128+
})
123129
}
124130

125131
func lengthNotEqualError(field string) error {

internal/loadbalancer/compare_internal_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ func TestCreateLoadBalancerRequestsEqual(t *testing.T) {
5252
r2.Name = "test-1"
5353
require.Equal(t, fieldValueNotEqualError("name"), createLoadBalancerRequestsEqual(&r1, &r2))
5454
})
55+
t.Run("IPAddresses", func(t *testing.T) {
56+
t.Parallel()
57+
r1 := createRequest(id)
58+
r2 := createRequest(id)
59+
r2.IPAddresses[0].Address = "0.0.0.1"
60+
require.Equal(t, fieldValueNotEqualError("IP addresses"), createLoadBalancerRequestsEqual(&r1, &r2))
61+
})
5562
}
5663

5764
func createRequest(id uuid.UUID) request.CreateLoadBalancerRequest {
@@ -68,6 +75,7 @@ func createRequest(id uuid.UUID) request.CreateLoadBalancerRequest {
6875
Backends: backends(id),
6976
Resolvers: resolvers(),
7077
Labels: upcloudLabels(id),
78+
IPAddresses: ipAddresses(),
7179
}
7280
}
7381

@@ -192,3 +200,12 @@ func upcloudLabels(id uuid.UUID) []upcloud.Label {
192200
},
193201
}
194202
}
203+
204+
func ipAddresses() []request.LoadBalancerIPAddress {
205+
return []request.LoadBalancerIPAddress{
206+
{
207+
NetworkName: networkNamePublic,
208+
Address: "0.0.0.0",
209+
},
210+
}
211+
}

internal/loadbalancer/const.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ const (
3232
// ServiceExternalTrafficPolicyLabelKey is a key for label that should store external traffic policy type as a value.
3333
serviceExternalTrafficPolicyLabel string = "ccm_external_traffic_policy"
3434

35-
changesDetectedEventType string = "ChangesDetected"
36-
noChangesDetectedEventType string = "NoChangesDetected"
37-
newLoadBalancerEventType string = "NewLoadBalancer"
38-
updateLoadBalancerEventType string = "UpdateLoadBalancer"
39-
deleteLoadBalancerEventType string = "DeleteLoadBalancer"
40-
nodeCountLimitReached string = "NodeCountLimitReached"
35+
changesDetectedEventType string = "ChangesDetected"
36+
noChangesDetectedEventType string = "NoChangesDetected"
37+
newLoadBalancerEventType string = "NewLoadBalancer"
38+
nodeCountLimitReached string = "NodeCountLimitReached"
4139

4240
loadBalancerNameMaxLength int = 64
4341
loadBalancerIDMaxLength int = 36

internal/loadbalancer/loadbalancer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (m *manager) ensureNewLoadBalancer(ctx context.Context, clusterName string,
138138
m.eventRecorder.Event(service, v1.EventTypeNormal, newLoadBalancerEventType, "Creating load balancer")
139139
lb, err := m.svc.CreateLoadBalancer(ctx, service, nodes, clusterName)
140140
// Patch service object as soon as we have LB UUID so that we don't loose reference.
141-
if lb.UUID != "" && !serviceHasAnnotation(service, loadBalancerIDAnnotation) {
141+
if lb != nil && lb.UUID != "" && !serviceHasAnnotation(service, loadBalancerIDAnnotation) {
142142
modifiedService := service.DeepCopy()
143143
updateServiceAnnotations(modifiedService, lb)
144144
if perr := m.patchService(ctx, service, modifiedService); perr != nil {

internal/loadbalancer/loadbalancer_request.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type replaceLoadBalancerRequest struct {
2828
Labels []upcloud.Label `json:"labels,omitempty"`
2929
MaintenanceDOW upcloud.LoadBalancerMaintenanceDOW `json:"maintenance_dow,omitempty"`
3030
MaintenanceTime string `json:"maintenance_time,omitempty"`
31+
IPAddresses []request.LoadBalancerIPAddress `json:"ip_addresses"`
3132
}
3233

3334
func (r *replaceLoadBalancerRequest) RequestURL() string {
@@ -60,7 +61,8 @@ func createLoadBalancerRequest(service *v1.Service, nodes []*v1.Node, plan upclo
6061
},
6162
},
6263
ConfiguredStatus: upcloud.LoadBalancerConfiguredStatusStarted,
63-
Resolvers: []request.LoadBalancerResolver{},
64+
Resolvers: make([]request.LoadBalancerResolver, 0),
65+
IPAddresses: make([]request.LoadBalancerIPAddress, 0),
6466
}
6567
r.Frontends = make([]request.LoadBalancerFrontend, len(service.Spec.Ports))
6668
r.Backends = make([]request.LoadBalancerBackend, len(service.Spec.Ports))
@@ -220,6 +222,13 @@ func loadBalancerToCreateRequest(lb *upcloud.LoadBalancer) *request.CreateLoadBa
220222
CacheInvalid: lb.Resolvers[i].CacheInvalid,
221223
}
222224
}
225+
ipAddresses := make([]request.LoadBalancerIPAddress, len(lb.IPAddresses))
226+
for i := range ipAddresses {
227+
ipAddresses[i] = request.LoadBalancerIPAddress{
228+
NetworkName: lb.IPAddresses[i].NetworkName,
229+
Address: lb.IPAddresses[i].Address,
230+
}
231+
}
223232
return &request.CreateLoadBalancerRequest{
224233
Name: lb.Name,
225234
Plan: lb.Plan,
@@ -233,5 +242,6 @@ func loadBalancerToCreateRequest(lb *upcloud.LoadBalancer) *request.CreateLoadBa
233242
Labels: lb.Labels,
234243
MaintenanceDOW: lb.MaintenanceDOW,
235244
MaintenanceTime: lb.MaintenanceTime,
245+
IPAddresses: ipAddresses,
236246
}
237247
}

0 commit comments

Comments
 (0)