Skip to content

Commit d1c0fba

Browse files
Menekse CeylanMenekse Ceylan
authored andcommitted
added labels upon creation of alb and storing certificate
1 parent c7749a6 commit d1c0fba

5 files changed

Lines changed: 128 additions & 56 deletions

File tree

pkg/alb/ingress/alb_spec.go

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ import (
1717
certsdk "github.com/stackitcloud/stackit-sdk-go/services/certificates/v2api"
1818
)
1919

20+
const (
21+
// prefixCustomerLabel is the api prefix for all custom labels
22+
prefixCustomerLabel = "lb.customer.label/"
23+
24+
// LabelIngressClassUID is the unique key that identifies resources
25+
// owned by a specific IngressClass.
26+
LabelIngressClassUID = prefixCustomerLabel + "ingress-class-uid"
27+
)
28+
2029
func (r *IngressClassReconciler) getAlbSpecForIngressClass(ctx context.Context, class *networkingv1.IngressClass) (*albsdk.CreateLoadBalancerPayload, []errorEvents, error) {
2130
ingresses, err := r.getIngressesForIngressClass(ctx, class)
2231
if err != nil {
@@ -45,7 +54,7 @@ func (r *IngressClassReconciler) getAlbSpecForIngresses(ctx context.Context, cla
4554
errorList = append(errorList, listenerMergeError...)
4655
}
4756

48-
certNameToId, certificateErrorEvents := r.applyCertificates(ctx, certificates)
57+
certNameToId, certificateErrorEvents := r.applyCertificates(ctx, class, certificates)
4958
errorList = append(errorList, certificateErrorEvents...)
5059

5160
alb, albSpecErrorList, err := r.getAlbSpecForResources(ctx, class, listeners, targetPools, certNameToId)
@@ -60,83 +69,105 @@ func (r *IngressClassReconciler) getAlbSpecForResources(ctx context.Context, cla
6069
Options: &albsdk.LoadBalancerOptions{},
6170
Networks: []albsdk.Network{
6271
{
63-
NetworkId: new(r.ALBConfig.ApplicationLoadBalancer.NetworkID),
64-
Role: new("ROLE_LISTENERS_AND_TARGETS"),
72+
NetworkId: ptr.To(r.ALBConfig.ApplicationLoadBalancer.NetworkID),
73+
Role: ptr.To("ROLE_LISTENERS_AND_TARGETS"),
6574
},
6675
},
67-
Name: new(string(class.UID)),
68-
DisableTargetSecurityGroupAssignment: new(true),
76+
Name: ptr.To(string(class.UID)),
77+
DisableTargetSecurityGroupAssignment: ptr.To(true),
6978
}
7079

7180
externalAddress := getAnnotation(AnnotationExternalIP, "", class)
7281
if externalAddress != "" {
7382
alb.ExternalAddress = &externalAddress
7483
} else {
75-
alb.Options.EphemeralAddress = new(true)
84+
alb.Options.EphemeralAddress = ptr.To(true)
7685
}
7786

7887
if getAnnotation(AnnotationInternal, false, class) {
79-
alb.Options.PrivateNetworkOnly = new(true)
88+
alb.Options.PrivateNetworkOnly = ptr.To(true)
8089
}
8190

8291
if plan := getAnnotation(AnnotationPlanID, "", class); plan != "" {
8392
alb.PlanId = &plan
8493
}
8594

95+
mergedLabels := make(map[string]string)
96+
97+
// Add user labels, mind the limit
98+
for k, v := range class.Labels {
99+
if len(mergedLabels) < 64 {
100+
mergedLabels[k] = v
101+
}
102+
}
103+
104+
// Merge with existing global config labels
105+
if r.ALBConfig.ApplicationLoadBalancer.ExtraLabels != nil {
106+
for k, v := range r.ALBConfig.ApplicationLoadBalancer.ExtraLabels {
107+
if len(mergedLabels) < 64 {
108+
mergedLabels[k] = v
109+
}
110+
}
111+
}
112+
113+
// Add ownership label
114+
mergedLabels[LabelIngressClassUID] = string(class.UID)
115+
alb.Labels = &mergedLabels
116+
86117
for port, listener := range listeners {
87118
albsdkListener := albsdk.Listener{
88119
Http: nil,
89-
Name: new(listener.name),
90-
Port: new(int32(port)),
91-
Protocol: new(listener.protocol),
120+
Name: ptr.To(listener.name),
121+
Port: ptr.To(int32(port)),
122+
Protocol: ptr.To(listener.protocol),
92123
AdditionalProperties: nil,
93124
}
94125

95126
if listener.wafConfigName != "" {
96-
albsdkListener.WafConfigName = new(listener.wafConfigName)
127+
albsdkListener.WafConfigName = ptr.To(listener.wafConfigName)
97128
}
98129

99130
albsdkHosts := []albsdk.HostConfig{}
100131
for host, hostPaths := range listener.hosts {
101132
albsdkHost := albsdk.HostConfig{
102-
Host: new(host),
133+
Host: ptr.To(host),
103134
}
104135
for path, rule := range hostPaths.path {
105136
albsdkRule := albsdk.Rule{
106-
TargetPool: new(rule.targetPoolName),
107-
WebSocket: new(rule.websocket),
137+
TargetPool: ptr.To(rule.targetPoolName),
138+
WebSocket: ptr.To(rule.websocket),
108139
}
109140

110141
if rule.cookiePersistenceName != "" {
111-
albsdkRule.CookiePersistence = new(albsdk.CookiePersistence{
112-
Name: new(rule.cookiePersistenceName),
113-
Ttl: new(fmt.Sprintf("%ds", rule.cookiePersistenceTtlSeconds)),
142+
albsdkRule.CookiePersistence = ptr.To(albsdk.CookiePersistence{
143+
Name: ptr.To(rule.cookiePersistenceName),
144+
Ttl: ptr.To(fmt.Sprintf("%ds", rule.cookiePersistenceTtlSeconds)),
114145
})
115146
}
116147

117148
switch rule.pathTyp {
118149
case networkingv1.PathTypeExact:
119-
albsdkRule.Path = new(albsdk.Path{
120-
ExactMatch: new(path),
150+
albsdkRule.Path = ptr.To(albsdk.Path{
151+
ExactMatch: ptr.To(path),
121152
})
122153
default:
123-
albsdkRule.Path = new(albsdk.Path{
124-
Prefix: new(path),
154+
albsdkRule.Path = ptr.To(albsdk.Path{
155+
Prefix: ptr.To(path),
125156
})
126157
}
127158

128159
albsdkHost.Rules = append(albsdkHost.Rules, albsdkRule)
129160
}
130161
albsdkHosts = append(albsdkHosts, albsdkHost)
131162

132-
albsdkListener.Http = new(albsdk.ProtocolOptionsHTTP{
163+
albsdkListener.Http = ptr.To(albsdk.ProtocolOptionsHTTP{
133164
Hosts: albsdkHosts,
134165
})
135166
}
136167

137168
if listener.protocol == "PROTOCOL_HTTPS" {
138-
albsdkListener.Https = new(albsdk.ProtocolOptionsHTTPS{
139-
CertificateConfig: new(albsdk.CertificateConfig{
169+
albsdkListener.Https = ptr.To(albsdk.ProtocolOptionsHTTPS{
170+
CertificateConfig: ptr.To(albsdk.CertificateConfig{
140171
CertificateIds: []string{},
141172
}),
142173
})
@@ -167,8 +198,8 @@ func (r *IngressClassReconciler) getAlbSpecForResources(ctx context.Context, cla
167198

168199
for name, targetPool := range targetPools {
169200
albsdkTargetPool := albsdk.TargetPool{
170-
Name: new(name),
171-
TargetPort: new(targetPool.port),
201+
Name: ptr.To(name),
202+
TargetPort: ptr.To(targetPool.port),
172203
Targets: targets,
173204
ActiveHealthCheck: nil, // TODO
174205
}
@@ -379,15 +410,18 @@ func (r *IngressClassReconciler) getCertificateForSecretName(ctx context.Context
379410
}, nil
380411
}
381412

382-
func (r *IngressClassReconciler) applyCertificates(ctx context.Context, certificates albCertificates) (map[string]string, []errorEvents) {
413+
func (r *IngressClassReconciler) applyCertificates(ctx context.Context, class *networkingv1.IngressClass, certificates albCertificates) (map[string]string, []errorEvents) {
383414
errorList := []errorEvents{}
384415
nameToID := map[string]string{}
385416
for name, certificate := range certificates {
386417
createCertificatePayload := &certsdk.CreateCertificatePayload{
387-
Name: new(name),
418+
Name: ptr.To(name),
388419
ProjectId: &r.ALBConfig.Global.ProjectID,
389-
PrivateKey: new(string(certificate.privateKey)),
390-
PublicKey: new(string(certificate.publicKey)),
420+
PrivateKey: ptr.To(string(certificate.privateKey)),
421+
PublicKey: ptr.To(string(certificate.publicKey)),
422+
Labels: &map[string]string{
423+
LabelIngressClassUID: string(class.UID),
424+
},
391425
}
392426
response, err := r.CertificateClient.CreateCertificate(ctx, r.ALBConfig.Global.ProjectID, r.ALBConfig.Global.Region, createCertificatePayload)
393427
if err != nil {

pkg/alb/ingress/alb_spec_test.go

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ingress
33
import (
44
"context"
55
"fmt"
6+
"k8s.io/utils/ptr"
67

78
. "github.com/onsi/ginkgo/v2"
89
. "github.com/onsi/gomega"
@@ -81,49 +82,49 @@ var _ = Describe("Node Controller", func() {
8182
}
8283

8384
albSpec = albsdk.CreateLoadBalancerPayload{
84-
DisableTargetSecurityGroupAssignment: new(true),
85+
DisableTargetSecurityGroupAssignment: ptr.To(true),
8586
Listeners: []albsdk.Listener{
8687
{
87-
Http: new(albsdk.ProtocolOptionsHTTP{
88+
Http: ptr.To(albsdk.ProtocolOptionsHTTP{
8889
Hosts: []albsdk.HostConfig{
8990
{
90-
Host: new("example.com"),
91+
Host: ptr.To("example.com"),
9192
Rules: []albsdk.Rule{
9293
{
93-
Path: new(albsdk.Path{
94-
Prefix: new("/"),
94+
Path: ptr.To(albsdk.Path{
95+
Prefix: ptr.To("/"),
9596
}),
96-
TargetPool: new(fmt.Sprintf("port-%d", service.Spec.Ports[0].NodePort)),
97-
WebSocket: new(false),
97+
TargetPool: ptr.To(fmt.Sprintf("port-%d", service.Spec.Ports[0].NodePort)),
98+
WebSocket: ptr.To(false),
9899
},
99100
},
100101
},
101102
},
102103
}),
103-
Name: new("80-http"),
104-
Port: new(int32(80)),
105-
Protocol: new("PROTOCOL_HTTP"),
104+
Name: ptr.To("80-http"),
105+
Port: ptr.To(int32(80)),
106+
Protocol: ptr.To("PROTOCOL_HTTP"),
106107
},
107108
},
108-
Name: new(string(ingressClass.UID)), //todo
109+
Name: ptr.To(string(ingressClass.UID)), //todo
109110
Networks: []albsdk.Network{
110111
{
111-
NetworkId: new(reconciler.ALBConfig.ApplicationLoadBalancer.NetworkID),
112-
Role: new("ROLE_LISTENERS_AND_TARGETS"),
112+
NetworkId: ptr.To(reconciler.ALBConfig.ApplicationLoadBalancer.NetworkID),
113+
Role: ptr.To("ROLE_LISTENERS_AND_TARGETS"),
113114
},
114115
},
115-
Options: new(albsdk.LoadBalancerOptions{
116-
EphemeralAddress: new(true),
116+
Options: ptr.To(albsdk.LoadBalancerOptions{
117+
EphemeralAddress: ptr.To(true),
117118
}),
118-
// Region: new(reconciler.ALBConfig.Global.Region), why is there a region in spec? TODO
119+
// Region: ptr.To(reconciler.ALBConfig.Global.Region), why is there a region in spec? TODO
119120
TargetPools: []albsdk.TargetPool{
120121
{
121-
Name: new(fmt.Sprintf("port-%d", service.Spec.Ports[0].NodePort)),
122-
TargetPort: new(service.Spec.Ports[0].NodePort),
122+
Name: ptr.To(fmt.Sprintf("port-%d", service.Spec.Ports[0].NodePort)),
123+
TargetPort: ptr.To(service.Spec.Ports[0].NodePort),
123124
Targets: []albsdk.Target{
124125
{
125-
DisplayName: new(node.Name),
126-
Ip: new(node.Status.Addresses[0].Address),
126+
DisplayName: ptr.To(node.Name),
127+
Ip: ptr.To(node.Status.Addresses[0].Address),
127128
},
128129
},
129130
},
@@ -142,6 +143,19 @@ var _ = Describe("Node Controller", func() {
142143
Expect(*spec).To(BeEquivalentTo(albSpec))
143144
})
144145

146+
It("should work with labels", func() {
147+
148+
reconciler.ALBConfig.ApplicationLoadBalancer.ExtraLabels = map[string]string{"managed-by": "alb-ingressClass"}
149+
spec, errorEventList, err := reconciler.getAlbSpecForIngressClass(context.Background(), &ingressClass)
150+
Expect(err).To(Succeed())
151+
Expect(errorEventList).To(BeEmpty())
152+
153+
albSpec.Labels = ptr.To(map[string]string{"managed-by": "alb-ingressClass"})
154+
155+
Expect(spec).ToNot(BeNil())
156+
Expect(*spec).To(BeEquivalentTo(albSpec))
157+
})
158+
145159
It("should work with 2 ingresses different path", func() {
146160
ingress2 := testIngress(&ingressClass, &service)
147161
ingress2.Name = "ingress2"
@@ -152,9 +166,9 @@ var _ = Describe("Node Controller", func() {
152166
albSpec.Listeners[0].Http.Hosts[0].Rules = append(
153167
albSpec.Listeners[0].Http.Hosts[0].Rules,
154168
albsdk.Rule{
155-
Path: new(albsdk.Path{Prefix: new("/foobar")}),
169+
Path: ptr.To(albsdk.Path{Prefix: ptr.To("/foobar")}),
156170
TargetPool: albSpec.Listeners[0].Http.Hosts[0].Rules[0].TargetPool,
157-
WebSocket: new(false),
171+
WebSocket: ptr.To(false),
158172
})
159173

160174
spec, errorEventList, err := reconciler.getAlbSpecForIngressClass(context.Background(), &ingressClass)
@@ -171,7 +185,7 @@ func testIngress(class *networkingv1.IngressClass, service *corev1.Service) netw
171185
return networkingv1.Ingress{
172186
ObjectMeta: metav1.ObjectMeta{Name: "test-ingress"},
173187
Spec: networkingv1.IngressSpec{
174-
IngressClassName: new(class.Name),
188+
IngressClassName: ptr.To(class.Name),
175189
Rules: []networkingv1.IngressRule{
176190
{
177191
Host: "example.com",
@@ -180,7 +194,7 @@ func testIngress(class *networkingv1.IngressClass, service *corev1.Service) netw
180194
Paths: []networkingv1.HTTPIngressPath{
181195
{
182196
Path: "/",
183-
PathType: new(networkingv1.PathTypePrefix),
197+
PathType: ptr.To(networkingv1.PathTypePrefix),
184198
Backend: networkingv1.IngressBackend{
185199
Service: &networkingv1.IngressServiceBackend{
186200
Name: service.Name,

pkg/alb/ingress/certificate.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,21 @@ func (r *IngressClassReconciler) deleteAllCertsForClass(ctx context.Context, cla
1919
return nil // No certificates to clean up
2020
}
2121

22+
// using labels for certificates
23+
targetUID := string(class.UID)
24+
2225
for _, cert := range certificatesList.Items {
23-
if strings.HasPrefix(*cert.Name, shortUUID(string(class.UID))) {
26+
if cert.Labels == nil {
27+
// This part will go away when Labels are supported by Cert API
28+
if strings.HasPrefix(*cert.Name, shortUUID(string(class.UID))) {
29+
err := r.CertificateClient.DeleteCertificate(ctx, r.ALBConfig.Global.ProjectID, r.ALBConfig.Global.Region, *cert.Id)
30+
if err != nil {
31+
return fmt.Errorf("failed to delete orphaned certificate %s: %v", *cert.Name, err)
32+
}
33+
}
34+
}
35+
36+
if val, ok := (*cert.Labels)[LabelIngressClassUID]; ok && val == targetUID {
2437
err := r.CertificateClient.DeleteCertificate(ctx, r.ALBConfig.Global.ProjectID, r.ALBConfig.Global.Region, *cert.Id)
2538
if err != nil {
2639
return fmt.Errorf("failed to delete orphaned certificate %s: %v", *cert.Name, err)

pkg/alb/ingress/update.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"reflect"
78

89
"github.com/stackitcloud/cloud-provider-stackit/pkg/stackit"
910
albsdk "github.com/stackitcloud/stackit-sdk-go/services/alb/v2api"
@@ -161,5 +162,14 @@ func updateNeeded(alb *albsdk.LoadBalancer, albPayload *albsdk.CreateLoadBalance
161162
}
162163
}
163164

165+
// Label comparison
166+
// normalize pointers to prevent nil vs empty map issue
167+
currentLabels := ptr.Deref(alb.Labels, map[string]string{})
168+
desiredLabels := ptr.Deref(albPayload.Labels, map[string]string{})
169+
170+
if !reflect.DeepEqual(currentLabels, desiredLabels) {
171+
return true
172+
}
173+
164174
return false
165175
}

pkg/stackit/config/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ type ALBConfig struct {
4949
ApplicationLoadBalancer ApplicationLoadBalancerOpts `yaml:"applicationLoadBalancer"`
5050
}
5151
type ApplicationLoadBalancerOpts struct {
52-
NetworkID string `yaml:"networkId"`
52+
NetworkID string `yaml:"networkId"`
53+
ExtraLabels map[string]string `yaml:"extraLabels,omitempty"`
5354
}
5455

5556
func readFile(path string) ([]byte, error) {

0 commit comments

Comments
 (0)