Skip to content

Commit

Permalink
Add Annotations Support for Cilium (#747)
Browse files Browse the repository at this point in the history
  • Loading branch information
mateoflorido authored Oct 20, 2024
1 parent fea66ee commit 65bb21d
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/k8s/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.6
require (
dario.cat/mergo v1.0.0
github.com/canonical/go-dqlite v1.22.0
github.com/canonical/k8s-snap-api v1.0.11
github.com/canonical/k8s-snap-api v1.0.12
github.com/canonical/lxd v0.0.0-20240822122218-e7b2a7a83230
github.com/canonical/microcluster/v3 v3.0.0-20240827143335-f7a4d3984970
github.com/go-logr/logr v1.4.2
Expand Down
4 changes: 2 additions & 2 deletions src/k8s/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/canonical/go-dqlite v1.22.0 h1:DuJmfcREl4gkQJyvZzjl2GHFZROhbPyfdjDRQXpkOyw=
github.com/canonical/go-dqlite v1.22.0/go.mod h1:Uvy943N8R4CFUAs59A1NVaziWY9nJ686lScY7ywurfg=
github.com/canonical/k8s-snap-api v1.0.11 h1:nGtwrUQBLiaL3HUXFx2gb4kq6qVpl2yNwMwHVX0dEok=
github.com/canonical/k8s-snap-api v1.0.11/go.mod h1:LDPoIYCeYnfgOFrwVPJ/4edGU264w7BB7g0GsVi36AY=
github.com/canonical/k8s-snap-api v1.0.12 h1:ofS2+JRlPMnpWgHLmnE4QEUqWv9Dgrmsv3hrjI0O4zQ=
github.com/canonical/k8s-snap-api v1.0.12/go.mod h1:LDPoIYCeYnfgOFrwVPJ/4edGU264w7BB7g0GsVi36AY=
github.com/canonical/lxd v0.0.0-20240822122218-e7b2a7a83230 h1:YOqZ+/14OPZ+/TOXpRHIX3KLT0C+wZVpewKIwlGUmW0=
github.com/canonical/lxd v0.0.0-20240822122218-e7b2a7a83230/go.mod h1:YVGI7HStOKsV+cMyXWnJ7RaMPaeWtrkxyIPvGWbgACc=
github.com/canonical/microcluster/v3 v3.0.0-20240827143335-f7a4d3984970 h1:UrnpglbXELlxtufdk6DGDytu2JzyzuS3WTsOwPrkQLI=
Expand Down
50 changes: 50 additions & 0 deletions src/k8s/pkg/k8sd/features/cilium/internal.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
package cilium

import (
"fmt"
"slices"
"strconv"
"strings"

apiv1_annotations "github.com/canonical/k8s-snap-api/api/v1/annotations/cilium"
"github.com/canonical/k8s/pkg/k8sd/types"
)

const (
// minVLANIDValue is the minimum valid 802.1Q VLAN ID value
minVLANIDValue = 0
// maxVLANIDValue is the maximum valid 802.1Q VLAN ID value
maxVLANIDValue = 4094
)

type config struct {
devices string
directRoutingDevice string
vlanBPFBypass []int
}

func validateVLANBPFBypass(vlanList string) ([]int, error) {
vlanList = strings.TrimSpace(vlanList)
// Maintain compatibility with the Cilium chart definition
vlanList = strings.Trim(vlanList, "{}")
vlans := strings.Split(vlanList, ",")

vlanTags := make([]int, 0, len(vlans))
seenTags := make(map[int]struct{})

for _, vlan := range vlans {
vlanID, err := strconv.Atoi(strings.TrimSpace(vlan))
if err != nil {
return []int{}, fmt.Errorf("failed to parse VLAN tag: %w", err)
}
if vlanID < minVLANIDValue || vlanID > maxVLANIDValue {
return []int{}, fmt.Errorf("VLAN tag must be between 0 and %d", maxVLANIDValue)
}

if _, ok := seenTags[vlanID]; ok {
continue
}
seenTags[vlanID] = struct{}{}
vlanTags = append(vlanTags, vlanID)
}

slices.Sort(vlanTags)
return vlanTags, nil
}

func internalConfig(annotations types.Annotations) (config, error) {
Expand All @@ -21,5 +63,13 @@ func internalConfig(annotations types.Annotations) (config, error) {
c.directRoutingDevice = v
}

if v, ok := annotations[apiv1_annotations.AnnotationVLANBPFBypass]; ok {
vlanTags, err := validateVLANBPFBypass(v)
if err != nil {
return config{}, fmt.Errorf("failed to parse VLAN BPF bypass list: %w", err)
}
c.vlanBPFBypass = vlanTags
}

return c, nil
}
104 changes: 98 additions & 6 deletions src/k8s/pkg/k8sd/features/cilium/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestInternalConfig(t *testing.T) {
expectedConfig: config{
devices: "",
directRoutingDevice: "",
vlanBPFBypass: nil,
},
expectError: false,
},
Expand All @@ -28,22 +29,113 @@ func TestInternalConfig(t *testing.T) {
annotations: map[string]string{
apiv1_annotations.AnnotationDevices: "eth+ lxdbr+",
apiv1_annotations.AnnotationDirectRoutingDevice: "eth0",
apiv1_annotations.AnnotationVLANBPFBypass: "1,2,3",
},
expectedConfig: config{
devices: "eth+ lxdbr+",
directRoutingDevice: "eth0",
vlanBPFBypass: []int{1, 2, 3},
},
expectError: false,
},
{
name: "Single valid VLAN",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "1",
},
expectedConfig: config{
vlanBPFBypass: []int{1},
},
expectError: false,
},
{
name: "Multiple valid VLANs",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "1,2,3,4,5",
},
expectedConfig: config{
vlanBPFBypass: []int{1, 2, 3, 4, 5},
},
expectError: false,
},
{
name: "Wildcard VLAN",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "0",
},
expectedConfig: config{
vlanBPFBypass: []int{0},
},
expectError: false,
},
{
name: "Invalid VLAN tag format",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "abc",
},
expectError: true,
},
{
name: "VLAN tag out of range",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "4095",
},
expectError: true,
},
{
name: "VLAN tag negative",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "-1",
},
expectError: true,
},
{
name: "Duplicate VLAN tags",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "1,2,2,3",
},
expectedConfig: config{
vlanBPFBypass: []int{1, 2, 3},
},
expectError: false,
},
{
name: "Mixed spaces and commas",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: " 1, 2,3 ,4 , 5 ",
},
expectedConfig: config{
vlanBPFBypass: []int{1, 2, 3, 4, 5},
},
expectError: false,
},
{
name: "Invalid mixed with valid",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "1,abc,3",
},
expectError: true,
},
{
name: "Nil annotations",
annotations: nil,
expectedConfig: config{},
expectError: false,
},
{
name: "VLAN with curly braces",
annotations: map[string]string{
apiv1_annotations.AnnotationVLANBPFBypass: "{1,2,3}",
},
expectedConfig: config{
vlanBPFBypass: []int{1, 2, 3},
},
expectError: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
annotations := make(map[string]string)
for k, v := range tc.annotations {
annotations[k] = v
}

parsed, err := internalConfig(annotations)
parsed, err := internalConfig(tc.annotations)
if tc.expectError {
g.Expect(err).To(HaveOccurred())
} else {
Expand Down
6 changes: 6 additions & 0 deletions src/k8s/pkg/k8sd/features/cilium/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ func ApplyNetwork(ctx context.Context, snap snap.Snap, apiserver types.APIServer
ciliumNodePortValues["directRoutingDevice"] = config.directRoutingDevice
}

bpfValues := map[string]any{}
if config.vlanBPFBypass != nil {
bpfValues["vlanBypass"] = config.vlanBPFBypass
}

values := map[string]any{
"bpf": bpfValues,
"image": map[string]any{
"repository": ciliumAgentImageRepo,
"tag": CiliumAgentImageTag,
Expand Down

0 comments on commit 65bb21d

Please sign in to comment.