Skip to content

Commit e2b1889

Browse files
committed
chore: move ACL related functions to a file
1 parent 6bb1c07 commit e2b1889

2 files changed

Lines changed: 147 additions & 126 deletions

File tree

scaleway/acl.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"slices"
8+
"strings"
9+
10+
scwlb "github.com/scaleway/scaleway-sdk-go/api/lb/v1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
v1 "k8s.io/api/core/v1"
13+
"k8s.io/klog/v2"
14+
)
15+
16+
const MaxEntriesPerACL = 60
17+
18+
func (l *loadbalancers) reconcileACLs(ctx context.Context, loadbalancer *scwlb.LB, frontend *scwlb.Frontend, service *v1.Service, nodes []*v1.Node) error {
19+
// List ACLs for the frontend
20+
aclName := makeACLPrefix(frontend)
21+
aclsResp, err := l.api.ListACLs(&scwlb.ZonedAPIListACLsRequest{
22+
Zone: loadbalancer.Zone,
23+
FrontendID: frontend.ID,
24+
Name: &aclName,
25+
}, scw.WithAllPages(), scw.WithContext(ctx))
26+
if err != nil {
27+
return fmt.Errorf("failed to list ACLs for frontend: %s port: %d loadbalancer: %s err: %v", frontend.ID, frontend.InboundPort, loadbalancer.ID, err)
28+
}
29+
30+
svcAcls := makeACLSpecs(service, nodes, frontend)
31+
if !aclsEquals(aclsResp.ACLs, svcAcls) {
32+
klog.Infof("set ACLs for frontend: %s port: %d loadbalancer: %s", frontend.ID, frontend.InboundPort, loadbalancer.ID)
33+
if _, err := l.api.SetACLs(&scwlb.ZonedAPISetACLsRequest{
34+
Zone: loadbalancer.Zone,
35+
FrontendID: frontend.ID,
36+
ACLs: svcAcls,
37+
}, scw.WithContext(ctx)); err != nil {
38+
return fmt.Errorf("failed setting ACLs for frontend: %s port: %d loadbalancer: %s err: %v", frontend.ID, frontend.InboundPort, loadbalancer.ID, err)
39+
}
40+
}
41+
42+
return nil
43+
}
44+
45+
// makeACLPrefix returns the ACL prefix for rules
46+
func makeACLPrefix(frontend *scwlb.Frontend) string {
47+
if frontend == nil {
48+
return "lb-source-range"
49+
}
50+
return fmt.Sprintf("%s-lb-source-range", frontend.ID)
51+
}
52+
53+
// makeACLSpecs converts a service frontend definition to acl specifications
54+
func makeACLSpecs(service *v1.Service, nodes []*v1.Node, frontend *scwlb.Frontend) []*scwlb.ACLSpec {
55+
if len(service.Spec.LoadBalancerSourceRanges) == 0 {
56+
return []*scwlb.ACLSpec{}
57+
}
58+
59+
sourceRanges := make([]string, 0, len(service.Spec.LoadBalancerSourceRanges))
60+
for _, sourceRange := range service.Spec.LoadBalancerSourceRanges {
61+
if _, _, err := net.ParseCIDR(sourceRange); err != nil {
62+
klog.Warningf("ignoring invalid CIDR %s in LoadBalancerSourceRanges for service %s/%s: %v", sourceRange, service.Namespace, service.Name, err)
63+
continue
64+
}
65+
66+
if strings.Contains(sourceRange, ":") {
67+
sourceRange = strings.TrimSuffix(sourceRange, "/128")
68+
} else {
69+
sourceRange = strings.TrimSuffix(sourceRange, "/32")
70+
}
71+
72+
sourceRanges = append(sourceRanges, sourceRange)
73+
}
74+
75+
aclPrefix := makeACLPrefix(frontend)
76+
whitelist := extractNodesInternalIps(nodes)
77+
whitelist = append(whitelist, extractNodesExternalIps(nodes)...)
78+
whitelist = append(whitelist, sourceRanges...)
79+
80+
slices.Sort(whitelist)
81+
82+
subnetsChunks := chunkArray(whitelist, MaxEntriesPerACL)
83+
acls := make([]*scwlb.ACLSpec, len(subnetsChunks)+1)
84+
85+
for idx, subnets := range subnetsChunks {
86+
acls[idx] = &scwlb.ACLSpec{
87+
Name: fmt.Sprintf("%s-%d", aclPrefix, idx),
88+
Action: &scwlb.ACLAction{
89+
Type: scwlb.ACLActionTypeAllow,
90+
},
91+
Index: int32(idx),
92+
Match: &scwlb.ACLMatch{
93+
IPSubnet: scw.StringSlicePtr(subnets),
94+
},
95+
}
96+
}
97+
98+
acls[len(acls)-1] = &scwlb.ACLSpec{
99+
Name: fmt.Sprintf("%s-end", aclPrefix),
100+
Action: &scwlb.ACLAction{
101+
Type: scwlb.ACLActionTypeDeny,
102+
},
103+
Index: int32(len(acls) - 1),
104+
Match: &scwlb.ACLMatch{
105+
IPSubnet: scw.StringSlicePtr([]string{"0.0.0.0/0", "::/0"}),
106+
},
107+
}
108+
109+
return acls
110+
}
111+
112+
// aclsEquals returns true if both acl lists are equal
113+
func aclsEquals(got []*scwlb.ACL, want []*scwlb.ACLSpec) bool {
114+
if len(got) != len(want) {
115+
return false
116+
}
117+
118+
slices.SortStableFunc(got, func(a, b *scwlb.ACL) int { return int(a.Index - b.Index) })
119+
slices.SortStableFunc(want, func(a, b *scwlb.ACLSpec) int { return int(a.Index - b.Index) })
120+
for idx := range want {
121+
if want[idx].Name != got[idx].Name {
122+
return false
123+
}
124+
if want[idx].Index != got[idx].Index {
125+
return false
126+
}
127+
if (want[idx].Action == nil) != (got[idx].Action == nil) {
128+
return false
129+
}
130+
if want[idx].Action != nil && want[idx].Action.Type != got[idx].Action.Type {
131+
return false
132+
}
133+
if (want[idx].Match == nil) != (got[idx].Match == nil) {
134+
return false
135+
}
136+
if want[idx].Match != nil && !stringPtrArrayEqual(want[idx].Match.IPSubnet, got[idx].Match.IPSubnet) {
137+
return false
138+
}
139+
if want[idx].Match != nil && want[idx].Match.Invert != got[idx].Match.Invert {
140+
return false
141+
}
142+
}
143+
144+
return true
145+
}

scaleway/loadbalancers.go

Lines changed: 2 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"errors"
2222
"fmt"
2323
"maps"
24-
"net"
2524
"os"
2625
"reflect"
2726
"slices"
@@ -40,8 +39,6 @@ import (
4039
"github.com/scaleway/scaleway-sdk-go/scw"
4140
)
4241

43-
const MaxEntriesPerACL = 60
44-
4542
type loadbalancers struct {
4643
api LoadBalancerAPI
4744
ipam IPAMAPI
@@ -741,27 +738,8 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
741738
frontend = f
742739
}
743740

744-
// List ACLs for the frontend
745-
aclName := makeACLPrefix(frontend)
746-
aclsResp, err := l.api.ListACLs(&scwlb.ZonedAPIListACLsRequest{
747-
Zone: loadbalancer.Zone,
748-
FrontendID: frontend.ID,
749-
Name: &aclName,
750-
}, scw.WithAllPages(), scw.WithContext(ctx))
751-
if err != nil {
752-
return fmt.Errorf("failed to list ACLs for frontend: %s port: %d loadbalancer: %s err: %v", frontend.ID, frontend.InboundPort, loadbalancer.ID, err)
753-
}
754-
755-
svcAcls := makeACLSpecs(service, nodes, frontend)
756-
if !aclsEquals(aclsResp.ACLs, svcAcls) {
757-
klog.Infof("replace ACLs from frontend: %s port: %d loadbalancer: %s", frontend.ID, frontend.InboundPort, loadbalancer.ID)
758-
if _, err := l.api.SetACLs(&scwlb.ZonedAPISetACLsRequest{
759-
Zone: loadbalancer.Zone,
760-
FrontendID: frontend.ID,
761-
ACLs: svcAcls,
762-
}, scw.WithContext(ctx)); err != nil {
763-
return fmt.Errorf("failed replacing ACLs for frontend: %s port: %d loadbalancer: %s err: %v", frontend.ID, frontend.InboundPort, loadbalancer.ID, err)
764-
}
741+
if err := l.reconcileACLs(ctx, loadbalancer, frontend, service, nodes); err != nil {
742+
return err
765743
}
766744
}
767745

@@ -1679,41 +1657,6 @@ func compareBackends(got []*scwlb.Backend, want map[int32]*scwlb.Backend, filter
16791657
}
16801658
}
16811659

1682-
// aclsEquals returns true if both acl lists are equal
1683-
func aclsEquals(got []*scwlb.ACL, want []*scwlb.ACLSpec) bool {
1684-
if len(got) != len(want) {
1685-
return false
1686-
}
1687-
1688-
slices.SortStableFunc(got, func(a, b *scwlb.ACL) int { return int(a.Index - b.Index) })
1689-
slices.SortStableFunc(want, func(a, b *scwlb.ACLSpec) int { return int(a.Index - b.Index) })
1690-
for idx := range want {
1691-
if want[idx].Name != got[idx].Name {
1692-
return false
1693-
}
1694-
if want[idx].Index != got[idx].Index {
1695-
return false
1696-
}
1697-
if (want[idx].Action == nil) != (got[idx].Action == nil) {
1698-
return false
1699-
}
1700-
if want[idx].Action != nil && want[idx].Action.Type != got[idx].Action.Type {
1701-
return false
1702-
}
1703-
if (want[idx].Match == nil) != (got[idx].Match == nil) {
1704-
return false
1705-
}
1706-
if want[idx].Match != nil && !stringPtrArrayEqual(want[idx].Match.IPSubnet, got[idx].Match.IPSubnet) {
1707-
return false
1708-
}
1709-
if want[idx].Match != nil && want[idx].Match.Invert != got[idx].Match.Invert {
1710-
return false
1711-
}
1712-
}
1713-
1714-
return true
1715-
}
1716-
17171660
// createBackend creates a backend on the load balancer
17181661
func (l *loadbalancers) createBackend(ctx context.Context, loadbalancer *scwlb.LB, backend *scwlb.Backend) (*scwlb.Backend, error) {
17191662
b, err := l.api.CreateBackend(&scwlb.ZonedAPICreateBackendRequest{
@@ -1932,73 +1875,6 @@ func chunkArray(array []string, maxChunkSize int) [][]string {
19321875
return result
19331876
}
19341877

1935-
// makeACLPrefix returns the ACL prefix for rules
1936-
func makeACLPrefix(frontend *scwlb.Frontend) string {
1937-
if frontend == nil {
1938-
return "lb-source-range"
1939-
}
1940-
return fmt.Sprintf("%s-lb-source-range", frontend.ID)
1941-
}
1942-
1943-
// makeACLSpecs converts a service frontend definition to acl specifications
1944-
func makeACLSpecs(service *v1.Service, nodes []*v1.Node, frontend *scwlb.Frontend) []*scwlb.ACLSpec {
1945-
if len(service.Spec.LoadBalancerSourceRanges) == 0 {
1946-
return []*scwlb.ACLSpec{}
1947-
}
1948-
1949-
sourceRanges := make([]string, 0, len(service.Spec.LoadBalancerSourceRanges))
1950-
for _, sourceRange := range service.Spec.LoadBalancerSourceRanges {
1951-
if _, _, err := net.ParseCIDR(sourceRange); err != nil {
1952-
klog.Warningf("ignoring invalid CIDR %s in LoadBalancerSourceRanges for service %s/%s: %v", sourceRange, service.Namespace, service.Name, err)
1953-
continue
1954-
}
1955-
1956-
if strings.Contains(sourceRange, ":") {
1957-
sourceRange = strings.TrimSuffix(sourceRange, "/128")
1958-
} else {
1959-
sourceRange = strings.TrimSuffix(sourceRange, "/32")
1960-
}
1961-
1962-
sourceRanges = append(sourceRanges, sourceRange)
1963-
}
1964-
1965-
aclPrefix := makeACLPrefix(frontend)
1966-
whitelist := extractNodesInternalIps(nodes)
1967-
whitelist = append(whitelist, extractNodesExternalIps(nodes)...)
1968-
whitelist = append(whitelist, sourceRanges...)
1969-
1970-
slices.Sort(whitelist)
1971-
1972-
subnetsChunks := chunkArray(whitelist, MaxEntriesPerACL)
1973-
acls := make([]*scwlb.ACLSpec, len(subnetsChunks)+1)
1974-
1975-
for idx, subnets := range subnetsChunks {
1976-
acls[idx] = &scwlb.ACLSpec{
1977-
Name: fmt.Sprintf("%s-%d", aclPrefix, idx),
1978-
Action: &scwlb.ACLAction{
1979-
Type: scwlb.ACLActionTypeAllow,
1980-
},
1981-
Index: int32(idx),
1982-
Match: &scwlb.ACLMatch{
1983-
IPSubnet: scw.StringSlicePtr(subnets),
1984-
},
1985-
}
1986-
}
1987-
1988-
acls[len(acls)-1] = &scwlb.ACLSpec{
1989-
Name: fmt.Sprintf("%s-end", aclPrefix),
1990-
Action: &scwlb.ACLAction{
1991-
Type: scwlb.ACLActionTypeDeny,
1992-
},
1993-
Index: int32(len(acls) - 1),
1994-
Match: &scwlb.ACLMatch{
1995-
IPSubnet: scw.StringSlicePtr([]string{"0.0.0.0/0", "::/0"}),
1996-
},
1997-
}
1998-
1999-
return acls
2000-
}
2001-
20021878
func ptrInt32ToString(i *int32) string {
20031879
if i == nil {
20041880
return "<nil>"

0 commit comments

Comments
 (0)