diff --git a/charts/internal/shoot-system-components/charts/calico-bgp/Chart.yaml b/charts/internal/shoot-system-components/charts/calico-bgp/Chart.yaml new file mode 100644 index 0000000..3724431 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/calico-bgp/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: Helm chart for Calico BGP +name: calico-bgp +version: 0.1.0 diff --git a/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgpconfiguration.yaml b/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgpconfiguration.yaml new file mode 100644 index 0000000..4031696 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgpconfiguration.yaml @@ -0,0 +1,30 @@ +{{- if .Values.bgp.enabled }} +apiVersion: crd.projectcalico.org/v1 +kind: BGPConfiguration +metadata: + name: default +spec: + logSeverityScreen: {{ .Values.bgp.logSeverityScreen }} + nodeToNodeMeshEnabled: {{ .Values.bgp.nodeToNodeMeshEnabled }} + asNumber: {{ required ".Values.bgp.asNumber is required" .Values.bgp.asNumber }} + {{ if .Values.bgp.serviceLoadBalancerIPs }} + serviceLoadBalancerIPs: + {{- range $cidr := .Values.bgp.serviceLoadBalancerIPs }} + - cidr: {{ $cidr }} + {{- end }} + {{- end }} + {{ if .Values.bgp.serviceExternalIPs }} + serviceExternalIPs: + {{- range $cidr := .Values.bgp.serviceExternalIPs }} + - cidr: {{ $cidr }} + {{- end }} + {{- end }} + {{ if .Values.bgp.serviceClusterIPs }} + serviceClusterIPs: + {{- range $cidr := .Values.bgp.serviceClusterIPs }} + - cidr: {{ $cidr }} + {{- end }} + {{- end }} + listenPort: {{ .Values.bgp.listenPort }} + bindMode: {{ .Values.bgp.bindMode }} +{{- end }} diff --git a/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgppeer.yaml b/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgppeer.yaml new file mode 100644 index 0000000..6e7f82e --- /dev/null +++ b/charts/internal/shoot-system-components/charts/calico-bgp/templates/bgppeer.yaml @@ -0,0 +1,16 @@ +{{- if .Values.bgp.enabled }} +{{- if .Values.bgp.peers }} +{{- range $peer := .Values.bgp.peers }} +apiVersion: crd.projectcalico.org/v1 +kind: BGPPeer +metadata: + name: bgppeer-{{ $peer.peerIP }}-{{ $peer.asNumber }} +spec: + asNumber: ${{ required ".peer.asNumber is required" peer.asNumber }} + {{- if $peer.nodeSelector }} + nodeSelector: {{ $peer.nodeSelector }} + {{- end }} + peerIP: ${{ required ".peer.peerIP is required" peer.peerIP }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/internal/shoot-system-components/charts/calico-bgp/values.yaml b/charts/internal/shoot-system-components/charts/calico-bgp/values.yaml new file mode 100644 index 0000000..4325ab0 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/calico-bgp/values.yaml @@ -0,0 +1,8 @@ +bgp: + enabled: false + logSeverityScreen: Info + nodeToNodeMeshEnabled: false + listenPort: 179 + bindMode: NodeIP + asNumber: + peers: [] diff --git a/charts/internal/shoot-system-components/values.yaml b/charts/internal/shoot-system-components/values.yaml index ac42ceb..74c8ddf 100644 --- a/charts/internal/shoot-system-components/values.yaml +++ b/charts/internal/shoot-system-components/values.yaml @@ -3,3 +3,6 @@ cloud-controller-manager: metallb: enabled: false + +calico-bgp: + enabled: false diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md index 8355b88..e5523c4 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -180,6 +180,142 @@ string +

BgpPeer +

+

+(Appears on: +CalicoBgpConfig) +

+

+

BgpPeer contains configuration for BGPPeer resource.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+peerIP
+ +string + +
+(Optional) +

PeerIP contains IP address of BGP peer followed by an optional port number to peer with.

+
+asNumber
+ +int + +
+(Optional) +

ASNumber contains the AS number of the BGP peer.

+
+nodeSelector
+ +string + +
+(Optional) +

NodeSelector is a key-value pair to select nodes that should have this peering.

+
+

CalicoBgpConfig +

+

+(Appears on: +LoadBalancerConfig) +

+

+

CalicoBgpConfig contains BGP configuration settings for calico.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+asNumber
+ +int + +
+(Optional) +

ASNumber is the default AS number used by a node.

+
+serviceLoadBalancerIPs
+ +[]string + +
+(Optional) +

ServiceLoadBalancerIPs are the CIDR blocks for Kubernetes Service LoadBalancer IPs.

+
+serviceExternalIPs
+ +[]string + +
+(Optional) +

ServiceExternalIPs are the CIDR blocks for Kubernetes Service External IPs.

+
+serviceClusterIPs
+ +[]string + +
+(Optional) +

ServiceClusterIPs are the CIDR blocks from which service cluster IPs are allocated.

+
+bgpPeer
+ + +[]BgpPeer + + +
+(Optional) +

BGPPeer contains configuration for BGPPeer resource.

+

CloudControllerManagerConfig

@@ -257,6 +393,20 @@ MetallbConfig

MetallbConfig contains configuration settings for metallb.

+ + +calicoBgpConfig
+ + +CalicoBgpConfig + + + + +(Optional) +

CalicoBgpConfig contains configuration settings for calico.

+ +

MachineImage diff --git a/pkg/apis/metal/types_controlplane.go b/pkg/apis/metal/types_controlplane.go index 66d8f83..bb21ed0 100644 --- a/pkg/apis/metal/types_controlplane.go +++ b/pkg/apis/metal/types_controlplane.go @@ -31,6 +31,9 @@ type CloudControllerManagerConfig struct { type LoadBalancerConfig struct { // MetallbConfig contains configuration settings for metallb. MetallbConfig *MetallbConfig + + // CalicoBgpConfig contains configuration settings for calico. + CalicoBgpConfig *CalicoBgpConfig } // MetallbConfig contains configuration settings for metallb. @@ -44,3 +47,33 @@ type MetallbConfig struct { // EnableL2Advertisement enables L2 advertisement. EnableL2Advertisement bool } + +// CalicoBgpConfig contains BGP configuration settings for calico. +type CalicoBgpConfig struct { + // ASNumber is the default AS number used by a node. + ASNumber int + + // ServiceLoadBalancerIPs are the CIDR blocks for Kubernetes Service LoadBalancer IPs. + ServiceLoadBalancerIPs []string + + // ServiceExternalIPs are the CIDR blocks for Kubernetes Service External IPs. + ServiceExternalIPs []string + + // ServiceClusterIPs are the CIDR blocks from which service cluster IPs are allocated. + ServiceClusterIPs []string + + // BGPPeer contains configuration for BGPPeer resource. + BgpPeer []BgpPeer +} + +// BgpPeer contains configuration for BGPPeer resource. +type BgpPeer struct { + // PeerIP contains IP address of BGP peer followed by an optional port number to peer with. + PeerIP string + + // ASNumber contains the AS number of the BGP peer. + ASNumber int + + // NodeSelector is a key-value pair to select nodes that should have this peering. + NodeSelector string +} diff --git a/pkg/apis/metal/v1alpha1/types_controlplane.go b/pkg/apis/metal/v1alpha1/types_controlplane.go index f7d9c94..627d500 100644 --- a/pkg/apis/metal/v1alpha1/types_controlplane.go +++ b/pkg/apis/metal/v1alpha1/types_controlplane.go @@ -35,6 +35,10 @@ type LoadBalancerConfig struct { // MetallbConfig contains configuration settings for metallb. // +optional MetallbConfig *MetallbConfig `json:"metallbConfig,omitempty"` + + // CalicoBgpConfig contains configuration settings for calico. + // +optional + CalicoBgpConfig *CalicoBgpConfig `json:"calicoBgpConfig,omitempty"` } // MetallbConfig contains configuration settings for metallb. @@ -51,3 +55,41 @@ type MetallbConfig struct { // +optional EnableL2Advertisement bool `json:"enableL2Advertisement,omitempty"` } + +// CalicoBgpConfig contains BGP configuration settings for calico. +type CalicoBgpConfig struct { + // ASNumber is the default AS number used by a node. + // +optional + ASNumber int `json:"asNumber,omitempty"` + + // ServiceLoadBalancerIPs are the CIDR blocks for Kubernetes Service LoadBalancer IPs. + // +optional + ServiceLoadBalancerIPs []string `json:"serviceLoadBalancerIPs,omitempty"` + + // ServiceExternalIPs are the CIDR blocks for Kubernetes Service External IPs. + // +optional + ServiceExternalIPs []string `json:"serviceExternalIPs,omitempty"` + + // ServiceClusterIPs are the CIDR blocks from which service cluster IPs are allocated. + // +optional + ServiceClusterIPs []string `json:"serviceClusterIPs,omitempty"` + + // BGPPeer contains configuration for BGPPeer resource. + // +optional + BgpPeer []BgpPeer `json:"bgpPeer,omitempty"` +} + +// BgpPeer contains configuration for BGPPeer resource. +type BgpPeer struct { + // PeerIP contains IP address of BGP peer followed by an optional port number to peer with. + // +optional + PeerIP string `json:"peerIP,omitempty"` + + // ASNumber contains the AS number of the BGP peer. + // +optional + ASNumber int `json:"asNumber,omitempty"` + + // NodeSelector is a key-value pair to select nodes that should have this peering. + // +optional + NodeSelector string `json:"nodeSelector,omitempty"` +} diff --git a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go index a3ea91c..2e06f62 100644 --- a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go @@ -202,7 +202,15 @@ func Convert_metal_CloudProfileConfig_To_v1alpha1_CloudProfileConfig(in *metal.C func autoConvert_v1alpha1_ControlPlaneConfig_To_metal_ControlPlaneConfig(in *ControlPlaneConfig, out *metal.ControlPlaneConfig, s conversion.Scope) error { out.CloudControllerManager = (*metal.CloudControllerManagerConfig)(unsafe.Pointer(in.CloudControllerManager)) - out.LoadBalancerConfig = (*metal.LoadBalancerConfig)(unsafe.Pointer(in.LoadBalancerConfig)) + if in.LoadBalancerConfig != nil { + in, out := &in.LoadBalancerConfig, &out.LoadBalancerConfig + *out = new(metal.LoadBalancerConfig) + if err := Convert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(*in, *out, s); err != nil { + return err + } + } else { + out.LoadBalancerConfig = nil + } return nil } @@ -213,7 +221,15 @@ func Convert_v1alpha1_ControlPlaneConfig_To_metal_ControlPlaneConfig(in *Control func autoConvert_metal_ControlPlaneConfig_To_v1alpha1_ControlPlaneConfig(in *metal.ControlPlaneConfig, out *ControlPlaneConfig, s conversion.Scope) error { out.CloudControllerManager = (*CloudControllerManagerConfig)(unsafe.Pointer(in.CloudControllerManager)) - out.LoadBalancerConfig = (*LoadBalancerConfig)(unsafe.Pointer(in.LoadBalancerConfig)) + if in.LoadBalancerConfig != nil { + in, out := &in.LoadBalancerConfig, &out.LoadBalancerConfig + *out = new(LoadBalancerConfig) + if err := Convert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(*in, *out, s); err != nil { + return err + } + } else { + out.LoadBalancerConfig = nil + } return nil } @@ -270,14 +286,10 @@ func Convert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(in *LoadBal func autoConvert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(in *metal.LoadBalancerConfig, out *LoadBalancerConfig, s conversion.Scope) error { out.MetallbConfig = (*MetallbConfig)(unsafe.Pointer(in.MetallbConfig)) + // WARNING: in.CalicoBgpConfig requires manual conversion: does not exist in peer-type return nil } -// Convert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig is an autogenerated conversion function. -func Convert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(in *metal.LoadBalancerConfig, out *LoadBalancerConfig, s conversion.Scope) error { - return autoConvert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(in, out, s) -} - func autoConvert_v1alpha1_MachineImage_To_metal_MachineImage(in *MachineImage, out *metal.MachineImage, s conversion.Scope) error { out.Name = in.Name out.Version = in.Version diff --git a/pkg/apis/metal/zz_generated.deepcopy.go b/pkg/apis/metal/zz_generated.deepcopy.go index 0706256..6a7ddc1 100644 --- a/pkg/apis/metal/zz_generated.deepcopy.go +++ b/pkg/apis/metal/zz_generated.deepcopy.go @@ -12,6 +12,58 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BgpPeer) DeepCopyInto(out *BgpPeer) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BgpPeer. +func (in *BgpPeer) DeepCopy() *BgpPeer { + if in == nil { + return nil + } + out := new(BgpPeer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CalicoBgpConfig) DeepCopyInto(out *CalicoBgpConfig) { + *out = *in + if in.ServiceLoadBalancerIPs != nil { + in, out := &in.ServiceLoadBalancerIPs, &out.ServiceLoadBalancerIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ServiceExternalIPs != nil { + in, out := &in.ServiceExternalIPs, &out.ServiceExternalIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ServiceClusterIPs != nil { + in, out := &in.ServiceClusterIPs, &out.ServiceClusterIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.BgpPeer != nil { + in, out := &in.BgpPeer, &out.BgpPeer + *out = make([]BgpPeer, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CalicoBgpConfig. +func (in *CalicoBgpConfig) DeepCopy() *CalicoBgpConfig { + if in == nil { + return nil + } + out := new(CalicoBgpConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CloudControllerManagerConfig) DeepCopyInto(out *CloudControllerManagerConfig) { *out = *in @@ -174,6 +226,11 @@ func (in *LoadBalancerConfig) DeepCopyInto(out *LoadBalancerConfig) { *out = new(MetallbConfig) (*in).DeepCopyInto(*out) } + if in.CalicoBgpConfig != nil { + in, out := &in.CalicoBgpConfig, &out.CalicoBgpConfig + *out = new(CalicoBgpConfig) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index a1d27bb..16d62ad 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -342,13 +342,20 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(cluster *extensionscon if err != nil { return nil, err } + + calicoBgp, err := getCalicoBgpChartValues(cp) + if err != nil { + return nil, err + } + return map[string]any{ metal.CloudControllerManagerName: map[string]any{"enabled": true}, metal.MetallbName: metallb, + metal.CalicoBgpName: calicoBgp, }, nil } -// getMetallbChartValues collects and returns the CCM chart values. +// getMetallbChartValues collects and returns the MetalLB chart values. func getMetallbChartValues( cpConfig *apismetal.ControlPlaneConfig, ) (map[string]any, error) { @@ -376,6 +383,66 @@ func getMetallbChartValues( }, nil } +// getCalicoBgpChartValues collects and returns the Calico BGP chart values. +func getCalicoBgpChartValues( + cpConfig *apismetal.ControlPlaneConfig, +) (map[string]any, error) { + if cpConfig.LoadBalancerConfig == nil || cpConfig.LoadBalancerConfig.CalicoBgpConfig == nil { + return map[string]any{ + "enabled": false, + }, nil + } + + var serviceLbIPs, serviceExtIPs, serviceClusterIPs []string + var peers []map[string]any + if cpConfig.LoadBalancerConfig.CalicoBgpConfig != nil { + if cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceLoadBalancerIPs != nil { + for _, cidr := range cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceLoadBalancerIPs { + if err := parseAddressPool(cidr); err != nil { + return nil, fmt.Errorf("invalid CIDR %q in pool: %w", cidr, err) + } + serviceLbIPs = append(serviceLbIPs, fmt.Sprintf("cidr: %s", cidr)) + } + } + + if cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceExternalIPs != nil { + for _, cidr := range cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceExternalIPs { + if err := parseAddressPool(cidr); err != nil { + return nil, fmt.Errorf("invalid CIDR %q in pool: %w", cidr, err) + } + serviceExtIPs = append(serviceExtIPs, fmt.Sprintf("cidr: %s", cidr)) + } + } + + if cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceClusterIPs != nil { + for _, cidr := range cpConfig.LoadBalancerConfig.CalicoBgpConfig.ServiceClusterIPs { + if err := parseAddressPool(cidr); err != nil { + return nil, fmt.Errorf("invalid CIDR %q in pool: %w", cidr, err) + } + serviceClusterIPs = append(serviceClusterIPs, fmt.Sprintf("cidr: %s", cidr)) + } + } + + if cpConfig.LoadBalancerConfig.CalicoBgpConfig.BgpPeer != nil { + for _, peer := range cpConfig.LoadBalancerConfig.CalicoBgpConfig.BgpPeer { + peers = append(peers, map[string]any{ + "peerIP": peer.PeerIP, + "asNumber": peer.ASNumber, + "nodeSelector": peer.NodeSelector, + }) + } + } + } + return map[string]any{ + "enabled": true, + "asNumber": cpConfig.LoadBalancerConfig.CalicoBgpConfig.ASNumber, + "serviceLoadBalancerIPs": serviceLbIPs, + "serviceExternalIPs": serviceExtIPs, + "serviceClusterIPs": serviceClusterIPs, + "peers": peers, + }, nil +} + func parseAddressPool(cidr string) error { if !strings.Contains(cidr, "-") { _, _, err := net.ParseCIDR(cidr) diff --git a/pkg/metal/types.go b/pkg/metal/types.go index 0bdfbb3..44c3c69 100644 --- a/pkg/metal/types.go +++ b/pkg/metal/types.go @@ -47,6 +47,8 @@ const ( CloudProviderConfigName = "cloud-provider-config" // CloudControllerManagerName is a constant for the name of the CloudController deployed by the worker controller. CloudControllerManagerName = "cloud-controller-manager" + // CalicoBgpName is a constant for the name of the Calico BGP deployed by the worker controller. + CalicoBgpName = "calico-bgp" // MetallbName is a constant for the name of the MetalLB deployed by the worker controller. MetallbName = "metallb" // MachineControllerManagerName is a constant for the name of the machine-controller-manager.