diff --git a/Dockerfile b/Dockerfile index dbbed25..5cb5567 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot AS gardener-extension-provider-metal +LABEL source_repository="https://github.com/ironcore-dev/gardener-extension-provider-metal" WORKDIR / COPY charts /charts COPY --from=builder /workspace/gardener-extension-provider-metal /gardener-extension-provider-metal @@ -39,6 +40,7 @@ USER 65532:65532 ENTRYPOINT ["/gardener-extension-provider-metal"] FROM gcr.io/distroless/static:nonroot AS gardener-extension-admission-metal +LABEL source_repository="https://github.com/ironcore-dev/gardener-extension-provider-metal" WORKDIR / COPY charts /charts COPY --from=builder /workspace/gardener-extension-admission-metal /gardener-extension-admission-metal diff --git a/charts/internal/shoot-system-components/charts/metallb/Chart.yaml b/charts/internal/shoot-system-components/charts/metallb/Chart.yaml new file mode 100644 index 0000000..6d76ece --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: Helm chart for metallb +name: metallb +version: 0.1.0 diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-controller.yaml new file mode 100644 index 0000000..248d87c --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-controller.yaml @@ -0,0 +1,36 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metallb:controller + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +rules: + - apiGroups: [""] + resources: ["services", "namespaces"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] + - apiGroups: [""] + resources: ["services/status"] + verbs: ["update"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] + resourceNames: ["metallb-webhook-configuration"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] + verbs: ["list", "watch"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + resourceNames: ["bfdprofiles.metallb.io","bgpadvertisements.metallb.io", + "bgppeers.metallb.io","ipaddresspools.metallb.io","l2advertisements.metallb.io","communities.metallb.io"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["list", "watch"] diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-speaker.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-speaker.yaml new file mode 100644 index 0000000..688274b --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrole-metallb-speaker.yaml @@ -0,0 +1,21 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metallb:speaker + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +rules: + - apiGroups: [""] + resources: ["services", "endpoints", "nodes", "namespaces"] + verbs: ["get", "list", "watch"] + - apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] + - apiGroups: ["metallb.io"] + resources: ["servicel2statuses","servicel2statuses/status"] + verbs: ["*"] diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-controller.yaml new file mode 100644 index 0000000..1684d9c --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-controller.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metallb:controller + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +subjects: + - kind: ServiceAccount + name: metallb-controller + namespace: metallb-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metallb:controller diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-speaker.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-speaker.yaml new file mode 100644 index 0000000..7475217 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/clusterrolebinding-metallb-speaker.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metallb:speaker + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +subjects: + - kind: ServiceAccount + name: metallb-speaker + namespace: metallb-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metallb:speaker diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/configmap-metallb-excludel2.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/configmap-metallb-excludel2.yaml new file mode 100644 index 0000000..6539d52 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/configmap-metallb-excludel2.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: metallb-excludel2 + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +data: + excludel2.yaml: | + announcedInterfacesToExclude: + - ^docker.* + - ^cbr.* + - ^dummy.* + - ^virbr.* + - ^lxcbr.* + - ^veth.* + - ^lo$ + - ^cali.* + - ^tunl.* + - ^flannel.* + - ^kube-ipvs.* + - ^cni.* + - ^nodelocaldns.* diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/crds.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/crds.yaml new file mode 100644 index 0000000..61f100e --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/crds.yaml @@ -0,0 +1,1205 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: bfdprofiles.metallb.io +spec: + group: metallb.io + names: + kind: BFDProfile + listKind: BFDProfileList + plural: bfdprofiles + singular: bfdprofile + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.passiveMode + name: Passive Mode + type: boolean + - jsonPath: .spec.transmitInterval + name: Transmit Interval + type: integer + - jsonPath: .spec.receiveInterval + name: Receive Interval + type: integer + - jsonPath: .spec.detectMultiplier + name: Multiplier + type: integer + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + BFDProfile represents the settings of the bfd session that can be + optionally associated with a BGP session. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BFDProfileSpec defines the desired state of BFDProfile. + properties: + detectMultiplier: + description: |- + Configures the detection multiplier to determine + packet loss. The remote transmission interval will be multiplied + by this value to determine the connection loss detection timer. + format: int32 + maximum: 255 + minimum: 2 + type: integer + echoInterval: + description: |- + Configures the minimal echo receive transmission + interval that this system is capable of handling in milliseconds. + Defaults to 50ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + echoMode: + description: |- + Enables or disables the echo transmission mode. + This mode is disabled by default, and not supported on multi + hops setups. + type: boolean + minimumTtl: + description: |- + For multi hop sessions only: configure the minimum + expected TTL for an incoming BFD control packet. + format: int32 + maximum: 254 + minimum: 1 + type: integer + passiveMode: + description: |- + Mark session as passive: a passive session will not + attempt to start the connection and will wait for control packets + from peer before it begins replying. + type: boolean + receiveInterval: + description: |- + The minimum interval that this system is capable of + receiving control packets in milliseconds. + Defaults to 300ms. + format: int32 + maximum: 60000 + minimum: 10 + type: integer + transmitInterval: + description: |- + The minimum transmission interval (less jitter) + that this system wants to use to send BFD control packets in + milliseconds. Defaults to 300ms + format: int32 + maximum: 60000 + minimum: 10 + type: integer + type: object + status: + description: BFDProfileStatus defines the observed state of BFDProfile. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: bgpadvertisements.metallb.io +spec: + group: metallb.io + names: + kind: BGPAdvertisement + listKind: BGPAdvertisementList + plural: bgpadvertisements + singular: bgpadvertisement + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.ipAddressPools + name: IPAddressPools + type: string + - jsonPath: .spec.ipAddressPoolSelectors + name: IPAddressPool Selectors + type: string + - jsonPath: .spec.peers + name: Peers + type: string + - jsonPath: .spec.nodeSelectors + name: Node Selectors + priority: 10 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + BGPAdvertisement allows to advertise the IPs coming + from the selected IPAddressPools via BGP, setting the parameters of the + BGP Advertisement. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement. + properties: + aggregationLength: + default: 32 + description: The aggregation-length advertisement option lets you “roll up” the /32s into a larger prefix. Defaults to 32. Works for IPv4 addresses. + format: int32 + minimum: 1 + type: integer + aggregationLengthV6: + default: 128 + description: The aggregation-length advertisement option lets you “roll up” the /128s into a larger prefix. Defaults to 128. Works for IPv6 addresses. + format: int32 + type: integer + communities: + description: |- + The BGP communities to be associated with the announcement. Each item can be a standard community of the + form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the + Community CRD. + items: + type: string + type: array + ipAddressPoolSelectors: + description: |- + A selector for the IPAddressPools which would get advertised via this advertisement. + If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + ipAddressPools: + description: The list of IPAddressPools to advertise via this advertisement, selected by name. + items: + type: string + type: array + localPref: + description: |- + The BGP LOCAL_PREF attribute which is used by BGP best path algorithm, + Path with higher localpref is preferred over one with lower localpref. + format: int32 + type: integer + nodeSelectors: + description: NodeSelectors allows to limit the nodes to announce as next hops for the LoadBalancer IP. When empty, all the nodes having are announced as next hops. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + peers: + description: |- + Peers limits the bgppeer to advertise the ips of the selected pools to. + When empty, the loadbalancer IP is announced to all the BGPPeers configured. + items: + type: string + type: array + type: object + status: + description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: bgppeers.metallb.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGWlRDQ0EwMmdBd0lCQWdJVU5GRW1XcTM3MVpKdGkrMmlSQzk1WmpBV1MxZ3dEUVlKS29aSWh2Y05BUUVMDQpCUUF3UWpFTE1Ba0dBMVVFQmhNQ1dGZ3hGVEFUQmdOVkJBY01ERVJsWm1GMWJIUWdRMmwwZVRFY01Cb0dBMVVFDQpDZ3dUUkdWbVlYVnNkQ0JEYjIxd1lXNTVJRXgwWkRBZUZ3MHlNakEzTVRrd09UTXlNek5hRncweU1qQTRNVGd3DQpPVE15TXpOYU1FSXhDekFKQmdOVkJBWVRBbGhZTVJVd0V3WURWUVFIREF4RVpXWmhkV3gwSUVOcGRIa3hIREFhDQpCZ05WQkFvTUUwUmxabUYxYkhRZ1EyOXRjR0Z1ZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDDQpEd0F3Z2dJS0FvSUNBUUNxVFpxMWZRcC9vYkdlenhES0o3OVB3Ny94azJwellualNzMlkzb1ZYSm5sRmM4YjVlDQpma2ZZQnY2bndscW1keW5PL2phWFBaQmRQSS82aFdOUDBkdVhadEtWU0NCUUpyZzEyOGNXb3F0MGNTN3pLb1VpDQpvcU1tQ0QvRXVBeFFNZjhRZDF2c1gvVllkZ0poVTZBRXJLZEpIaXpFOUJtUkNkTDBGMW1OVW55Rk82UnRtWFZUDQpidkxsTDVYeTc2R0FaQVBLOFB4aVlDa0NtbDdxN0VnTWNiOXlLWldCYmlxQ3VkTXE5TGJLNmdKNzF6YkZnSXV4DQo1L1pXK2JraTB2RlplWk9ZODUxb1psckFUNzJvMDI4NHNTWW9uN0pHZVZkY3NoUnh5R1VpSFpSTzdkaXZVTDVTDQpmM2JmSDFYbWY1ZDQzT0NWTWRuUUV2NWVaOG8zeWVLa3ZrbkZQUGVJMU9BbjdGbDlFRVNNR2dhOGFaSG1URSttDQpsLzlMSmdDYjBnQmtPT0M0WnV4bWh2aERKV1EzWnJCS3pMQlNUZXN0NWlLNVlwcXRWVVk2THRyRW9FelVTK1lsDQpwWndXY2VQWHlHeHM5ZURsR3lNVmQraW15Y3NTU1UvVno2Mmx6MnZCS21NTXBkYldDQWhud0RsRTVqU2dyMjRRDQp0eGNXLys2N3d5KzhuQlI3UXdqVTFITndVRjBzeERWdEwrZ1NHVERnSEVZSlhZelYvT05zMy94TkpoVFNPSkxNDQpoeXNVdyttaGdackdhbUdXcHVIVU1DUitvTWJzMTc1UkcrQjJnUFFHVytPTjJnUTRyOXN2b0ZBNHBBQm8xd1dLDQpRYjRhY3pmeVVscElBOVFoSmFsZEY3S3dPSHVlV3gwRUNrNXg0T2tvVDBvWVp0dzFiR0JjRGtaSmF3SURBUUFCDQpvMU13VVRBZEJnTlZIUTRFRmdRVW90UlNIUm9IWTEyRFZ4R0NCdEhpb1g2ZmVFQXdId1lEVlIwakJCZ3dGb0FVDQpvdFJTSFJvSFkxMkRWeEdDQnRIaW9YNmZlRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCDQpBUXNGQUFPQ0FnRUFSbkpsWWRjMTFHd0VxWnh6RDF2R3BDR2pDN2VWTlQ3aVY1d3IybXlybHdPYi9aUWFEa0xYDQpvVStaOVVXT1VlSXJTdzUydDdmQUpvVVAwSm5iYkMveVIrU1lqUGhvUXNiVHduOTc2ZldBWTduM3FMOXhCd1Y0DQphek41OXNjeUp0dlhMeUtOL2N5ak1ReDRLajBIMFg0bWJ6bzVZNUtzWWtYVU0vOEFPdWZMcEd0S1NGVGgrSEFDDQpab1Q5YnZHS25adnNHd0tYZFF0Wnh0akhaUjVqK3U3ZGtQOTJBT051RFNabS8rWVV4b2tBK09JbzdSR3BwSHNXDQo1ZTdNY0FTVXRtb1FORXd6dVFoVkJaRWQ1OGtKYjUrV0VWbGNzanlXNnRTbzErZ25tTWNqR1BsMWgxR2hVbjV4DQpFY0lWRnBIWXM5YWo1NmpBSjk1MVQvZjhMaWxmTlVnanBLQ0c1bnl0SUt3emxhOHNtdGlPdm1UNEpYbXBwSkI2DQo4bmdHRVluVjUrUTYwWFJ2OEhSSGp1VG9CRHVhaERrVDA2R1JGODU1d09FR2V4bkZpMXZYWUxLVllWb1V2MXRKDQo4dVdUR1pwNllDSVJldlBqbzg5ZytWTlJSaVFYUThJd0dybXE5c0RoVTlqTjA0SjdVL1RvRDFpNHE3VnlsRUc5DQorV1VGNkNLaEdBeTJIaEhwVncyTGFoOS9lUzdZMUZ1YURrWmhPZG1laG1BOCtqdHNZamJadnR5Mm1SWlF0UUZzDQpUU1VUUjREbUR2bVVPRVRmeStpRHdzK2RkWXVNTnJGeVVYV2dkMnpBQU4ydVl1UHFGY2pRcFNPODFzVTJTU3R3DQoxVzAyeUtYOGJEYmZFdjBzbUh3UzliQnFlSGo5NEM1Mjg0YXpsdTBmaUdpTm1OUEM4ckJLRmhBPQ0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + service: + name: metallb-webhook-service + namespace: {{ .Release.Namespace }} + path: /convert + conversionReviewVersions: + - v1beta1 + - v1beta2 + group: metallb.io + names: + kind: BGPPeer + listKind: BGPPeerList + plural: bgppeers + singular: bgppeer + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.peerAddress + name: Address + type: string + - jsonPath: .spec.peerASN + name: ASN + type: string + - jsonPath: .spec.bfdProfile + name: BFD Profile + type: string + - jsonPath: .spec.ebgpMultiHop + name: Multi Hops + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + type: string + ebgpMultiHop: + description: EBGP peer is multi-hops away + type: boolean + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: |- + Only connect to this peer on nodes that match one of these + selectors. + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + minItems: 1 + type: array + required: + - key + - operator + - values + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: array + password: + description: Authentication password for routers enforcing TCP MD5 authenticated sessions + type: string + peerASN: + description: AS number to expect from the remote end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + required: + - myASN + - peerASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.peerAddress + name: Address + type: string + - jsonPath: .spec.peerASN + name: ASN + type: string + - jsonPath: .spec.bfdProfile + name: BFD Profile + type: string + - jsonPath: .spec.ebgpMultiHop + name: Multi Hops + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: BGPPeer is the Schema for the peers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BGPPeerSpec defines the desired state of Peer. + properties: + bfdProfile: + description: The name of the BFD Profile to be used for the BFD session associated to the BGP session. If not set, the BFD session won't be set up. + type: string + connectTime: + description: Requested BGP connect time, controls how long BGP waits between connection attempts to a neighbor. + type: string + x-kubernetes-validations: + - message: connect time should be between 1 seconds to 65535 + rule: duration(self).getSeconds() >= 1 && duration(self).getSeconds() <= 65535 + - message: connect time should contain a whole number of seconds + rule: duration(self).getMilliseconds() % 1000 == 0 + disableMP: + default: false + description: To set if we want to disable MP BGP that will separate IPv4 and IPv6 route exchanges into distinct BGP sessions. + type: boolean + ebgpMultiHop: + description: To set if the BGPPeer is multi-hops away. Needed for FRR mode only. + type: boolean + enableGracefulRestart: + description: |- + EnableGracefulRestart allows BGP peer to continue to forward data packets along + known routes while the routing protocol information is being restored. + This field is immutable because it requires restart of the BGP session + Supported for FRR mode only. + type: boolean + x-kubernetes-validations: + - message: EnableGracefulRestart cannot be changed after creation + rule: self == oldSelf + holdTime: + description: Requested BGP hold time, per RFC4271. + type: string + keepaliveTime: + description: Requested BGP keepalive time, per RFC4271. + type: string + myASN: + description: AS number to use for the local end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + nodeSelectors: + description: |- + Only connect to this peer on nodes that match one of these + selectors. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + password: + description: Authentication password for routers enforcing TCP MD5 authenticated sessions + type: string + passwordSecret: + description: |- + passwordSecret is name of the authentication secret for BGP Peer. + the secret must be of type "kubernetes.io/basic-auth", and created in the + same namespace as the MetalLB deployment. The password is stored in the + secret as the key "password". + properties: + name: + description: name is unique within a namespace to reference a secret resource. + type: string + namespace: + description: namespace defines the space within which the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + peerASN: + description: AS number to expect from the remote end of the session. + format: int32 + maximum: 4294967295 + minimum: 0 + type: integer + peerAddress: + description: Address to dial when establishing the session. + type: string + peerPort: + default: 179 + description: Port to dial when establishing the session. + maximum: 16384 + minimum: 0 + type: integer + routerID: + description: BGP router ID to advertise to the peer + type: string + sourceAddress: + description: Source address to use when establishing the session. + type: string + vrf: + description: |- + To set if we want to peer with the BGPPeer using an interface belonging to + a host vrf + type: string + required: + - myASN + - peerASN + - peerAddress + type: object + status: + description: BGPPeerStatus defines the observed state of Peer. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: communities.metallb.io +spec: + group: metallb.io + names: + kind: Community + listKind: CommunityList + plural: communities + singular: community + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + Community is a collection of aliases for communities. + Users can define named aliases to be used in the BGPPeer CRD. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: CommunitySpec defines the desired state of Community. + properties: + communities: + items: + properties: + name: + description: The name of the alias for the community. + type: string + value: + description: |- + The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 + or a large community of the form large:1234:1234:1234. + type: string + type: object + type: array + type: object + status: + description: CommunityStatus defines the observed state of Community. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: ipaddresspools.metallb.io +spec: + group: metallb.io + names: + kind: IPAddressPool + listKind: IPAddressPoolList + plural: ipaddresspools + singular: ipaddresspool + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.autoAssign + name: Auto Assign + type: boolean + - jsonPath: .spec.avoidBuggyIPs + name: Avoid Buggy IPs + type: boolean + - jsonPath: .spec.addresses + name: Addresses + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + IPAddressPool represents a pool of IP addresses that can be allocated + to LoadBalancer services. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: IPAddressPoolSpec defines the desired state of IPAddressPool. + properties: + addresses: + description: |- + A list of IP address ranges over which MetalLB has authority. + You can list multiple ranges in a single pool, they will all share the + same settings. Each range can be either a CIDR prefix, or an explicit + start-end range of IPs. + items: + type: string + type: array + autoAssign: + default: true + description: |- + AutoAssign flag used to prevent MetallB from automatic allocation + for a pool. + type: boolean + avoidBuggyIPs: + default: false + description: |- + AvoidBuggyIPs prevents addresses ending with .0 and .255 + to be used by a pool. + type: boolean + serviceAllocation: + description: |- + AllocateTo makes ip pool allocation to specific namespace and/or service. + The controller will use the pool with lowest value of priority in case of + multiple matches. A pool with no priority set will be used only if the + pools with priority can't be used. If multiple matching IPAddressPools are + available it will check for the availability of IPs sorting the matching + IPAddressPools by priority, starting from the highest to the lowest. If + multiple IPAddressPools have the same priority, choice will be random. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors list of label selectors to select namespace(s) for ip pool, + an alternative to using namespace list. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + namespaces: + description: Namespaces list of namespace(s) on which ip pool can be attached. + items: + type: string + type: array + priority: + description: Priority priority given for ip pool while ip allocation on a service. + type: integer + serviceSelectors: + description: |- + ServiceSelectors list of label selector to select service(s) for which ip pool + can be used for ip allocation. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + type: object + required: + - addresses + type: object + status: + description: IPAddressPoolStatus defines the observed state of IPAddressPool. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: l2advertisements.metallb.io +spec: + group: metallb.io + names: + kind: L2Advertisement + listKind: L2AdvertisementList + plural: l2advertisements + singular: l2advertisement + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.ipAddressPools + name: IPAddressPools + type: string + - jsonPath: .spec.ipAddressPoolSelectors + name: IPAddressPool Selectors + type: string + - jsonPath: .spec.interfaces + name: Interfaces + type: string + - jsonPath: .spec.nodeSelectors + name: Node Selectors + priority: 10 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + L2Advertisement allows to advertise the LoadBalancer IPs provided + by the selected pools via L2. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: L2AdvertisementSpec defines the desired state of L2Advertisement. + properties: + interfaces: + description: |- + A list of interfaces to announce from. The LB IP will be announced only from these interfaces. + If the field is not set, we advertise from all the interfaces on the host. + items: + type: string + type: array + ipAddressPoolSelectors: + description: |- + A selector for the IPAddressPools which would get advertised via this advertisement. + If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + ipAddressPools: + description: The list of IPAddressPools to advertise via this advertisement, selected by name. + items: + type: string + type: array + nodeSelectors: + description: NodeSelectors allows to limit the nodes to announce as next hops for the LoadBalancer IP. When empty, all the nodes having are announced as next hops. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + type: object + status: + description: L2AdvertisementStatus defines the observed state of L2Advertisement. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: servicel2statuses.metallb.io +spec: + group: metallb.io + names: + kind: ServiceL2Status + listKind: ServiceL2StatusList + plural: servicel2statuses + singular: servicel2status + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.node + name: Allocated Node + type: string + - jsonPath: .status.serviceName + name: Service Name + type: string + - jsonPath: .status.serviceNamespace + name: Service Namespace + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ServiceL2Status reveals the actual traffic status of loadbalancer services in layer2 mode. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ServiceL2StatusSpec defines the desired state of ServiceL2Status. + type: object + status: + description: MetalLBServiceL2Status defines the observed state of ServiceL2Status. + properties: + interfaces: + description: Interfaces indicates the interfaces that receive the directed traffic + items: + description: InterfaceInfo defines interface info of layer2 announcement. + properties: + name: + description: Name the name of network interface card + type: string + type: object + type: array + node: + description: Node indicates the node that receives the directed traffic + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + serviceName: + description: ServiceName indicates the service this status represents + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + serviceNamespace: + description: ServiceNamespace indicates the namespace of the service + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/daemonset-metallb-speaker.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/daemonset-metallb-speaker.yaml new file mode 100644 index 0000000..b464c01 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/daemonset-metallb-speaker.yaml @@ -0,0 +1,103 @@ +{{- if .Values.speaker.enabled }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: metallb-speaker + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: speaker +spec: + updateStrategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: speaker + template: + metadata: + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: speaker + spec: + serviceAccountName: metallb-speaker + terminationGracePeriodSeconds: 0 + hostNetwork: true + volumes: + - name: metallb-excludel2 + configMap: + defaultMode: 256 + name: metallb-excludel2 + - name: metrics + emptyDir: {} + shareProcessNamespace: true + containers: + - name: speaker + image: {{ index .Values.images "metallb-speaker" }} + args: + - --port=7472 + - --log-level=info + env: + - name: METALLB_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: METALLB_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: METALLB_ML_BIND_ADDR + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: METALLB_ML_LABELS + value: "app.kubernetes.io/name=metallb,app.kubernetes.io/component=speaker" + - name: METALLB_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + ports: + - name: monitoring + containerPort: 7472 + livenessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + add: + - NET_RAW + volumeMounts: + - name: metallb-excludel2 + mountPath: /etc/metallb + nodeSelector: + "kubernetes.io/os": linux + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + operator: Exists + - key: node-role.kubernetes.io/control-plane + effect: NoSchedule + operator: Exists +{{- end }} diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/deployment-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/deployment-metallb-controller.yaml new file mode 100644 index 0000000..89f9f76 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/deployment-metallb-controller.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: metallb-controller + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: controller +spec: + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: controller + template: + metadata: + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: controller + spec: + serviceAccountName: metallb-controller + terminationGracePeriodSeconds: 0 + securityContext: + fsGroup: 65534 + runAsNonRoot: true + runAsUser: 65534 + containers: + - name: controller + image: {{ index .Values.images "metallb-controller" }} + args: + - --port=7472 + - --log-level=info + - --tls-min-version=VersionTLS12 + - --webhook-mode=disabled + env: + - name: METALLB_ML_SECRET_NAME + value: metallb-memberlist + - name: METALLB_DEPLOYMENT + value: metallb-controller + ports: + - name: monitoring + containerPort: 7472 + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + livenessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + nodeSelector: + "kubernetes.io/os": linux + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: metallb-webhook-cert diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/ipaddresspool.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/ipaddresspool.yaml new file mode 100644 index 0000000..9c28969 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/ipaddresspool.yaml @@ -0,0 +1,10 @@ +{{- if .Values.ipAddressPool }} +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: default + namespace: metallb-system +spec: + addresses: +{{- toYaml .Values.ipAddressPool | nindent 4 }} +{{- end }} diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/namespace.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/namespace.yaml new file mode 100644 index 0000000..3eea557 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app: metallb + name: metallb-system diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-controller.yaml new file mode 100644 index 0000000..7ebb98e --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-controller.yaml @@ -0,0 +1,41 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: metallb-controller + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + resourceNames: ["metallb-memberlist"] + verbs: ["list"] + - apiGroups: ["apps"] + resources: ["deployments"] + resourceNames: ["metallb-controller"] + verbs: ["get"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] + - apiGroups: ["metallb.io"] + resources: ["ipaddresspools"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["bgppeers"] + verbs: ["get", "list"] + - apiGroups: ["metallb.io"] + resources: ["bgpadvertisements"] + verbs: ["get", "list"] + - apiGroups: ["metallb.io"] + resources: ["l2advertisements"] + verbs: ["get", "list"] + - apiGroups: ["metallb.io"] + resources: ["communities"] + verbs: ["get", "list","watch"] + - apiGroups: ["metallb.io"] + resources: ["bfdprofiles"] + verbs: ["get", "list","watch"] diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-pod-lister.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-pod-lister.yaml new file mode 100644 index 0000000..54eb383 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/role-metallb-pod-lister.yaml @@ -0,0 +1,36 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: metallb-pod-lister + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["list", "get"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["bfdprofiles"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["bgppeers"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["l2advertisements"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["bgpadvertisements"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["ipaddresspools"] + verbs: ["get", "list", "watch"] + - apiGroups: ["metallb.io"] + resources: ["communities"] + verbs: ["get", "list", "watch"] diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-controller.yaml new file mode 100644 index 0000000..a2ed812 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-controller.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: metallb-controller + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: metallb-controller +subjects: + - kind: ServiceAccount + name: metallb-controller diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-pod-lister.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-pod-lister.yaml new file mode 100644 index 0000000..1ab1497 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/rolebinding-metallb-pod-lister.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: metallb-pod-lister + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: metallb-pod-lister +subjects: + - kind: ServiceAccount + name: metallb-speaker diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/secret-metallb-webhook-cert.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/secret-metallb-webhook-cert.yaml new file mode 100644 index 0000000..4889ba4 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/secret-metallb-webhook-cert.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: metallb-webhook-cert + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/service-metallb-webhook-service.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/service-metallb-webhook-service.yaml new file mode 100644 index 0000000..9995403 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/service-metallb-webhook-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: metallb-webhook-service + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: controller diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-controller.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-controller.yaml new file mode 100644 index 0000000..b8c8ea1 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-controller.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: metallb-controller + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: controller diff --git a/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-speaker.yaml b/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-speaker.yaml new file mode 100644 index 0000000..7a13686 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/templates/serviceaccount-metallb-speaker.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: metallb-speaker + namespace: metallb-system + labels: + app.kubernetes.io/name: metallb + app.kubernetes.io/instance: metallb + app.kubernetes.io/component: speaker diff --git a/charts/internal/shoot-system-components/charts/metallb/values.yaml b/charts/internal/shoot-system-components/charts/metallb/values.yaml new file mode 100644 index 0000000..89b6646 --- /dev/null +++ b/charts/internal/shoot-system-components/charts/metallb/values.yaml @@ -0,0 +1,8 @@ +images: + metallb-speaker: image-repository:image-tag + metallb-controller: image-repository:image-tag + +speaker: + enabled: false + +ipAddressPool: [] diff --git a/charts/internal/shoot-system-components/requirements.yaml b/charts/internal/shoot-system-components/requirements.yaml index c4784a4..a79eb49 100644 --- a/charts/internal/shoot-system-components/requirements.yaml +++ b/charts/internal/shoot-system-components/requirements.yaml @@ -3,3 +3,7 @@ dependencies: repository: http://localhost:10191 version: 0.1.0 condition: cloud-controller-manager.enabled +- name: metallb + repository: http://localhost:10191 + version: 0.1.0 + condition: metallb.enabled diff --git a/charts/internal/shoot-system-components/values.yaml b/charts/internal/shoot-system-components/values.yaml index 8bd1e31..ac42ceb 100644 --- a/charts/internal/shoot-system-components/values.yaml +++ b/charts/internal/shoot-system-components/values.yaml @@ -1,2 +1,5 @@ cloud-controller-manager: enabled: true + +metallb: + enabled: false diff --git a/imagevector/images.yaml b/imagevector/images.yaml index 134f36d..85c97f7 100644 --- a/imagevector/images.yaml +++ b/imagevector/images.yaml @@ -26,3 +26,13 @@ images: confidentiality_requirement: 'high' integrity_requirement: 'high' availability_requirement: 'low' + +- name: metallb-speaker + sourceRepository: https://github.com/metallb/metallb + repository: quay.io/metallb/speaker + tag: "v0.14.8" + +- name: metallb-controller + sourceRepository: https://github.com/metallb/metallb + repository: quay.io/metallb/controller + tag: "v0.14.8" diff --git a/pkg/apis/metal/types_controlplane.go b/pkg/apis/metal/types_controlplane.go index 3523b1a..1e9a4bb 100644 --- a/pkg/apis/metal/types_controlplane.go +++ b/pkg/apis/metal/types_controlplane.go @@ -16,6 +16,9 @@ type ControlPlaneConfig struct { // CloudControllerManager contains configuration settings for the cloud-controller-manager. CloudControllerManager *CloudControllerManagerConfig + + // LoadBalancerConfig contains configuration settings for the shoot loadbalancing. + LoadBalancerConfig *LoadBalancerConfig } // CloudControllerManagerConfig contains configuration settings for the cloud-controller-manager. @@ -23,3 +26,12 @@ type CloudControllerManagerConfig struct { // FeatureGates contains information about enabled feature gates. FeatureGates map[string]bool } + +// LoadBalancerConfig contains configuration settings for the shoot loadbalancing. +type LoadBalancerConfig struct { + MetallbConfig *MetallbConfig +} + +type MetallbConfig struct { + IPAddressPool []string +} diff --git a/pkg/apis/metal/v1alpha1/types_controlplane.go b/pkg/apis/metal/v1alpha1/types_controlplane.go index 86b98f8..900a7a7 100644 --- a/pkg/apis/metal/v1alpha1/types_controlplane.go +++ b/pkg/apis/metal/v1alpha1/types_controlplane.go @@ -17,6 +17,10 @@ type ControlPlaneConfig struct { // CloudControllerManager contains configuration settings for the cloud-controller-manager. // +optional CloudControllerManager *CloudControllerManagerConfig `json:"cloudControllerManager,omitempty"` + + // LoadBalancerConfig contains configuration settings for the shoot loadbalancing. + // +optional + LoadBalancerConfig *LoadBalancerConfig `json:"loadBalancerConfig,omitempty"` } // CloudControllerManagerConfig contains configuration settings for the cloud-controller-manager. @@ -25,3 +29,16 @@ type CloudControllerManagerConfig struct { // +optional FeatureGates map[string]bool `json:"featureGates,omitempty"` } + +// LoadBalancerConfig contains configuration settings for the shoot loadbalancing. +type LoadBalancerConfig struct { + // +optional + MetallbConfig *MetallbConfig `json:"metallbConfig,omitempty"` +} + +// LoadBalancerConfig contains configuration settings for metallb. +type MetallbConfig struct { + // IPAddressPool contains IP address pools for metallb. + // +optional + IPAddressPool []string `json:"ipAddressPool,omitempty"` +} diff --git a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go index 35d54be..4cee705 100644 --- a/pkg/apis/metal/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/metal/v1alpha1/zz_generated.conversion.go @@ -73,6 +73,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*LoadBalancerConfig)(nil), (*metal.LoadBalancerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(a.(*LoadBalancerConfig), b.(*metal.LoadBalancerConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*metal.LoadBalancerConfig)(nil), (*LoadBalancerConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(a.(*metal.LoadBalancerConfig), b.(*LoadBalancerConfig), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*MachineImage)(nil), (*metal.MachineImage)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_MachineImage_To_metal_MachineImage(a.(*MachineImage), b.(*metal.MachineImage), scope) }); err != nil { @@ -113,6 +123,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*MetallbConfig)(nil), (*metal.MetallbConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_MetallbConfig_To_metal_MetallbConfig(a.(*MetallbConfig), b.(*metal.MetallbConfig), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*metal.MetallbConfig)(nil), (*MetallbConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(a.(*metal.MetallbConfig), b.(*MetallbConfig), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*RegionConfig)(nil), (*metal.RegionConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_RegionConfig_To_metal_RegionConfig(a.(*RegionConfig), b.(*metal.RegionConfig), scope) }); err != nil { @@ -182,6 +202,7 @@ 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)) return nil } @@ -192,6 +213,7 @@ 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)) return nil } @@ -236,6 +258,26 @@ func Convert_metal_InfrastructureStatus_To_v1alpha1_InfrastructureStatus(in *met return autoConvert_metal_InfrastructureStatus_To_v1alpha1_InfrastructureStatus(in, out, s) } +func autoConvert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(in *LoadBalancerConfig, out *metal.LoadBalancerConfig, s conversion.Scope) error { + out.MetallbConfig = (*metal.MetallbConfig)(unsafe.Pointer(in.MetallbConfig)) + return nil +} + +// Convert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig is an autogenerated conversion function. +func Convert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(in *LoadBalancerConfig, out *metal.LoadBalancerConfig, s conversion.Scope) error { + return autoConvert_v1alpha1_LoadBalancerConfig_To_metal_LoadBalancerConfig(in, out, s) +} + +func autoConvert_metal_LoadBalancerConfig_To_v1alpha1_LoadBalancerConfig(in *metal.LoadBalancerConfig, out *LoadBalancerConfig, s conversion.Scope) error { + out.MetallbConfig = (*MetallbConfig)(unsafe.Pointer(in.MetallbConfig)) + 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 @@ -330,6 +372,26 @@ func Convert_metal_MachineType_To_v1alpha1_MachineType(in *metal.MachineType, ou return autoConvert_metal_MachineType_To_v1alpha1_MachineType(in, out, s) } +func autoConvert_v1alpha1_MetallbConfig_To_metal_MetallbConfig(in *MetallbConfig, out *metal.MetallbConfig, s conversion.Scope) error { + out.IPAddressPool = *(*[]string)(unsafe.Pointer(&in.IPAddressPool)) + return nil +} + +// Convert_v1alpha1_MetallbConfig_To_metal_MetallbConfig is an autogenerated conversion function. +func Convert_v1alpha1_MetallbConfig_To_metal_MetallbConfig(in *MetallbConfig, out *metal.MetallbConfig, s conversion.Scope) error { + return autoConvert_v1alpha1_MetallbConfig_To_metal_MetallbConfig(in, out, s) +} + +func autoConvert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(in *metal.MetallbConfig, out *MetallbConfig, s conversion.Scope) error { + out.IPAddressPool = *(*[]string)(unsafe.Pointer(&in.IPAddressPool)) + return nil +} + +// Convert_metal_MetallbConfig_To_v1alpha1_MetallbConfig is an autogenerated conversion function. +func Convert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(in *metal.MetallbConfig, out *MetallbConfig, s conversion.Scope) error { + return autoConvert_metal_MetallbConfig_To_v1alpha1_MetallbConfig(in, out, s) +} + func autoConvert_v1alpha1_RegionConfig_To_metal_RegionConfig(in *RegionConfig, out *metal.RegionConfig, s conversion.Scope) error { out.Name = in.Name out.Server = in.Server diff --git a/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go index f103d1a..3d2eb20 100644 --- a/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/metal/v1alpha1/zz_generated.deepcopy.go @@ -90,6 +90,11 @@ func (in *ControlPlaneConfig) DeepCopyInto(out *ControlPlaneConfig) { *out = new(CloudControllerManagerConfig) (*in).DeepCopyInto(*out) } + if in.LoadBalancerConfig != nil { + in, out := &in.LoadBalancerConfig, &out.LoadBalancerConfig + *out = new(LoadBalancerConfig) + (*in).DeepCopyInto(*out) + } return } @@ -161,6 +166,27 @@ func (in *InfrastructureStatus) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerConfig) DeepCopyInto(out *LoadBalancerConfig) { + *out = *in + if in.MetallbConfig != nil { + in, out := &in.MetallbConfig, &out.MetallbConfig + *out = new(MetallbConfig) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerConfig. +func (in *LoadBalancerConfig) DeepCopy() *LoadBalancerConfig { + if in == nil { + return nil + } + out := new(LoadBalancerConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineImage) DeepCopyInto(out *MachineImage) { *out = *in @@ -249,6 +275,27 @@ func (in *MachineType) DeepCopy() *MachineType { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetallbConfig) DeepCopyInto(out *MetallbConfig) { + *out = *in + if in.IPAddressPool != nil { + in, out := &in.IPAddressPool, &out.IPAddressPool + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetallbConfig. +func (in *MetallbConfig) DeepCopy() *MetallbConfig { + if in == nil { + return nil + } + out := new(MetallbConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegionConfig) DeepCopyInto(out *RegionConfig) { *out = *in diff --git a/pkg/apis/metal/validation/controlplane.go b/pkg/apis/metal/validation/controlplane.go index f865ebc..3ed1b47 100644 --- a/pkg/apis/metal/validation/controlplane.go +++ b/pkg/apis/metal/validation/controlplane.go @@ -18,6 +18,8 @@ func ValidateControlPlaneConfig(controlPlaneConfig *apismetal.ControlPlaneConfig allErrs = append(allErrs, featurevalidation.ValidateFeatureGates(controlPlaneConfig.CloudControllerManager.FeatureGates, version, fldPath.Child("cloudControllerManager", "featureGates"))...) } + // TODO add validation for IPs + return allErrs } diff --git a/pkg/apis/metal/zz_generated.deepcopy.go b/pkg/apis/metal/zz_generated.deepcopy.go index b7ad386..0706256 100644 --- a/pkg/apis/metal/zz_generated.deepcopy.go +++ b/pkg/apis/metal/zz_generated.deepcopy.go @@ -90,6 +90,11 @@ func (in *ControlPlaneConfig) DeepCopyInto(out *ControlPlaneConfig) { *out = new(CloudControllerManagerConfig) (*in).DeepCopyInto(*out) } + if in.LoadBalancerConfig != nil { + in, out := &in.LoadBalancerConfig, &out.LoadBalancerConfig + *out = new(LoadBalancerConfig) + (*in).DeepCopyInto(*out) + } return } @@ -161,6 +166,27 @@ func (in *InfrastructureStatus) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerConfig) DeepCopyInto(out *LoadBalancerConfig) { + *out = *in + if in.MetallbConfig != nil { + in, out := &in.MetallbConfig, &out.MetallbConfig + *out = new(MetallbConfig) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerConfig. +func (in *LoadBalancerConfig) DeepCopy() *LoadBalancerConfig { + if in == nil { + return nil + } + out := new(LoadBalancerConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineImage) DeepCopyInto(out *MachineImage) { *out = *in @@ -249,6 +275,27 @@ func (in *MachineType) DeepCopy() *MachineType { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetallbConfig) DeepCopyInto(out *MetallbConfig) { + *out = *in + if in.IPAddressPool != nil { + in, out := &in.IPAddressPool, &out.IPAddressPool + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetallbConfig. +func (in *MetallbConfig) DeepCopy() *MetallbConfig { + if in == nil { + return nil + } + out := new(MetallbConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegionConfig) DeepCopyInto(out *RegionConfig) { *out = *in diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index e290e10..ff0dce1 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -4,8 +4,10 @@ package controlplane import ( + "bytes" "context" "fmt" + "net" "path/filepath" "strings" @@ -103,6 +105,7 @@ var ( Name: "shoot-system-components", EmbeddedFS: charts.InternalChart, Path: filepath.Join(charts.InternalChartsPath, "shoot-system-components"), + Images: []string{metal.MetallbControllerImageName, metal.MetallbSpeakerImageName}, SubCharts: []*chart.Chart{ { Name: "cloud-controller-manager", @@ -113,6 +116,29 @@ var ( {Type: &rbacv1.ClusterRoleBinding{}, Name: "metal:cloud-provider"}, }, }, + { + Name: "metallb", + Path: filepath.Join(charts.InternalChartsPath, "metallb"), + Objects: []*chart.Object{ + {Type: &corev1.Namespace{}, Name: "metallb-system"}, + {Type: &rbacv1.ClusterRole{}, Name: "metallb:controller"}, + {Type: &rbacv1.ClusterRole{}, Name: "metallb:speaker"}, + {Type: &rbacv1.ClusterRoleBinding{}, Name: "metallb:controller"}, + {Type: &rbacv1.ClusterRoleBinding{}, Name: "metallb:speaker"}, + {Type: &corev1.ConfigMap{}, Name: "metallb-excludel2"}, + {Type: &appsv1.DaemonSet{}, Name: "metallb-speaker"}, + {Type: &appsv1.Deployment{}, Name: "metallb-controller"}, + {Type: &rbacv1.Role{}, Name: "metallb-controller"}, + {Type: &rbacv1.Role{}, Name: "metallb-pod-lister"}, + {Type: &rbacv1.RoleBinding{}, Name: "metallb-controller"}, + {Type: &rbacv1.RoleBinding{}, Name: "metallb-pod-lister"}, + {Type: &corev1.Secret{}, Name: "metallb-webhook-cert"}, + {Type: &corev1.Service{}, Name: "metallb-webhook-service"}, + {Type: &corev1.ServiceAccount{}, Name: "metallb-controller"}, + {Type: &corev1.ServiceAccount{}, Name: "metallb-speaker"}, + //{Type: &metallbv1beta1.IPAddressPool{}, Name: "default"}, + }, + }, }, } ) @@ -176,7 +202,7 @@ func (vp *valuesProvider) GetControlPlaneChartValues( // GetControlPlaneShootChartValues returns the values for the control plane shoot chart applied by the generic actuator. func (vp *valuesProvider) GetControlPlaneShootChartValues( _ context.Context, - _ *extensionsv1alpha1.ControlPlane, + cp *extensionsv1alpha1.ControlPlane, cluster *extensionscontroller.Cluster, _ secretsmanager.Reader, _ map[string]string, @@ -184,7 +210,13 @@ func (vp *valuesProvider) GetControlPlaneShootChartValues( map[string]interface{}, error, ) { - return vp.getControlPlaneShootChartValues(cluster) + cpConfig := &apismetal.ControlPlaneConfig{} + if cp.Spec.ProviderConfig != nil { + if _, _, err := vp.decoder.Decode(cp.Spec.ProviderConfig.Raw, nil, cpConfig); err != nil { + return nil, fmt.Errorf("could not decode providerConfig of controlplane '%s': %w", kutil.ObjectName(cp), err) + } + } + return vp.getControlPlaneShootChartValues(cluster, cpConfig) } // GetControlPlaneShootCRDsChartValues returns the values for the control plane shoot CRDs chart applied by the generic actuator. @@ -303,13 +335,68 @@ func isOverlayEnabled(networking *gardencorev1beta1.Networking) (bool, error) { } // getControlPlaneShootChartValues collects and returns the control plane shoot chart values. -func (vp *valuesProvider) getControlPlaneShootChartValues(cluster *extensionscontroller.Cluster) (map[string]interface{}, error) { +func (vp *valuesProvider) getControlPlaneShootChartValues(cluster *extensionscontroller.Cluster, cp *apismetal.ControlPlaneConfig) (map[string]interface{}, error) { if cluster.Shoot == nil { return nil, fmt.Errorf("cluster %s does not contain a shoot object", cluster.ObjectMeta.Name) } + metallb, err := getMetallbChartValues(cp) + if err != nil { + return nil, err + } return map[string]interface{}{ metal.CloudControllerManagerName: map[string]interface{}{"enabled": true}, + metal.MetallbName: metallb, + }, nil +} + +// getMetallbChartValues collects and returns the CCM chart values. +func getMetallbChartValues( + cpConfig *apismetal.ControlPlaneConfig, +) (map[string]interface{}, error) { + if cpConfig.LoadBalancerConfig == nil || cpConfig.LoadBalancerConfig.MetallbConfig == nil { + return map[string]interface{}{ + "enabled": false, + }, nil + } + + for _, cidr := range cpConfig.LoadBalancerConfig.MetallbConfig.IPAddressPool { + if err := parseAddressPool(cidr); err != nil { + return nil, fmt.Errorf("invalid CIDR %q in pool: %w", cidr, err) + } + } + + return map[string]interface{}{ + "enabled": true, + "speaker": map[string]interface{}{ + "enabled": false, + }, + "ipAddressPool": cpConfig.LoadBalancerConfig.MetallbConfig.IPAddressPool, }, nil +} +func parseAddressPool(cidr string) error { + if !strings.Contains(cidr, "-") { + _, _, err := net.ParseCIDR(cidr) + if err != nil { + return fmt.Errorf("invalid CIDR %q", cidr) + } + return nil + } + fs := strings.SplitN(cidr, "-", 2) + if len(fs) != 2 { + return fmt.Errorf("invalid IP range %q", cidr) + } + start := net.ParseIP(strings.TrimSpace(fs[0])) + if start == nil { + return fmt.Errorf("invalid IP range %q: invalid start IP %q", cidr, fs[0]) + } + end := net.ParseIP(strings.TrimSpace(fs[1])) + if end == nil { + return fmt.Errorf("invalid IP range %q: invalid end IP %q", cidr, fs[1]) + } + if bytes.Compare(start, end) > 0 { + return fmt.Errorf("invalid IP range %q: start IP %q is after the end IP %q", cidr, start, end) + } + return nil } diff --git a/pkg/controller/controlplane/valuesprovider_test.go b/pkg/controller/controlplane/valuesprovider_test.go index dbf6c8a..9f153e2 100644 --- a/pkg/controller/controlplane/valuesprovider_test.go +++ b/pkg/controller/controlplane/valuesprovider_test.go @@ -210,6 +210,175 @@ var _ = Describe("Valueprovider Reconcile", func() { })) }) }) + + Describe("#GetControlPlaneShootChartValues", func() { + It("should return correct shoot system chart values without metallb", func(ctx SpecContext) { + cp := &extensionsv1alpha1.ControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "control-plane", + Namespace: ns.Name, + }, + Spec: extensionsv1alpha1.ControlPlaneSpec{ + Region: "foo", + SecretRef: corev1.SecretReference{ + Name: "my-infra-creds", + Namespace: ns.Name, + }, + DefaultSpec: extensionsv1alpha1.DefaultSpec{ + Type: metal.Type, + ProviderConfig: &runtime.RawExtension{ + Raw: encode(&apismetal.ControlPlaneConfig{ + CloudControllerManager: &apismetal.CloudControllerManagerConfig{ + FeatureGates: map[string]bool{ + "CustomResourceValidation": true, + }, + }, + }), + }, + }, + }, + } + Expect(k8sClient.Create(ctx, cp)).To(Succeed()) + + providerCloudProfile := &apismetal.CloudProfileConfig{} + providerCloudProfileJson, err := json.Marshal(providerCloudProfile) + Expect(err).NotTo(HaveOccurred()) + networkProviderConfig := &unstructured.Unstructured{Object: map[string]any{ + "kind": "FooNetworkConfig", + "apiVersion": "v1alpha1", + "overlay": map[string]any{ + "enabled": false, + }, + }} + networkProviderConfigData, err := runtime.Encode(unstructured.UnstructuredJSONScheme, networkProviderConfig) + Expect(err).NotTo(HaveOccurred()) + cluster := &controller.Cluster{ + CloudProfile: &gardencorev1beta1.CloudProfile{ + Spec: gardencorev1beta1.CloudProfileSpec{ + ProviderConfig: &runtime.RawExtension{ + Raw: providerCloudProfileJson, + }, + }, + }, + Shoot: &gardencorev1beta1.Shoot{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + Name: "my-shoot", + }, + Spec: gardencorev1beta1.ShootSpec{ + Networking: &gardencorev1beta1.Networking{ + ProviderConfig: &runtime.RawExtension{Raw: networkProviderConfigData}, + Pods: ptr.To[string]("10.0.0.0/16"), + }, + Kubernetes: gardencorev1beta1.Kubernetes{ + Version: "1.26.0", + VerticalPodAutoscaler: &gardencorev1beta1.VerticalPodAutoscaler{ + Enabled: true, + }, + }, + }, + }, + } + + values, err := vp.GetControlPlaneShootChartValues(ctx, cp, cluster, fakeSecretsManager, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(values).To(Equal(map[string]interface{}{ + "cloud-controller-manager": map[string]interface{}{"enabled": true}, + "metallb": map[string]interface{}{ + "enabled": false, + }, + })) + }) + }) + + Describe("#GetControlPlaneShootChartValues", func() { + It("should return correct shoot system chart values with metallb", func(ctx SpecContext) { + cp := &extensionsv1alpha1.ControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "control-plane", + Namespace: ns.Name, + }, + Spec: extensionsv1alpha1.ControlPlaneSpec{ + Region: "foo", + SecretRef: corev1.SecretReference{ + Name: "my-infra-creds", + Namespace: ns.Name, + }, + DefaultSpec: extensionsv1alpha1.DefaultSpec{ + Type: metal.Type, + ProviderConfig: &runtime.RawExtension{ + Raw: encode(&apismetal.ControlPlaneConfig{ + CloudControllerManager: &apismetal.CloudControllerManagerConfig{ + FeatureGates: map[string]bool{ + "CustomResourceValidation": true, + }, + }, + LoadBalancerConfig: &apismetal.LoadBalancerConfig{ + MetallbConfig: &apismetal.MetallbConfig{ + IPAddressPool: []string{"10.10.10.0/24", "10.20.20.10-10.20.20.30"}, + }, + }, + }), + }, + }, + }, + } + Expect(k8sClient.Create(ctx, cp)).To(Succeed()) + + providerCloudProfile := &apismetal.CloudProfileConfig{} + providerCloudProfileJson, err := json.Marshal(providerCloudProfile) + Expect(err).NotTo(HaveOccurred()) + networkProviderConfig := &unstructured.Unstructured{Object: map[string]any{ + "kind": "FooNetworkConfig", + "apiVersion": "v1alpha1", + "overlay": map[string]any{ + "enabled": false, + }, + }} + networkProviderConfigData, err := runtime.Encode(unstructured.UnstructuredJSONScheme, networkProviderConfig) + Expect(err).NotTo(HaveOccurred()) + cluster := &controller.Cluster{ + CloudProfile: &gardencorev1beta1.CloudProfile{ + Spec: gardencorev1beta1.CloudProfileSpec{ + ProviderConfig: &runtime.RawExtension{ + Raw: providerCloudProfileJson, + }, + }, + }, + Shoot: &gardencorev1beta1.Shoot{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + Name: "my-shoot", + }, + Spec: gardencorev1beta1.ShootSpec{ + Networking: &gardencorev1beta1.Networking{ + ProviderConfig: &runtime.RawExtension{Raw: networkProviderConfigData}, + Pods: ptr.To[string]("10.0.0.0/16"), + }, + Kubernetes: gardencorev1beta1.Kubernetes{ + Version: "1.26.0", + VerticalPodAutoscaler: &gardencorev1beta1.VerticalPodAutoscaler{ + Enabled: true, + }, + }, + }, + }, + } + + values, err := vp.GetControlPlaneShootChartValues(ctx, cp, cluster, fakeSecretsManager, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(values).To(Equal(map[string]interface{}{ + "cloud-controller-manager": map[string]interface{}{"enabled": true}, + "metallb": map[string]interface{}{ + "enabled": true, + "speaker": map[string]interface{}{ + "enabled": false, + }, + "ipAddressPool": []string{"10.10.10.0/24", "10.20.20.10-10.20.20.30"}, + }, + })) + }) + }) }) func encode(obj runtime.Object) []byte { diff --git a/pkg/metal/types.go b/pkg/metal/types.go index 42e0dba..0bdfbb3 100644 --- a/pkg/metal/types.go +++ b/pkg/metal/types.go @@ -17,6 +17,10 @@ const ( MachineControllerManagerImageName = "machine-controller-manager" // MachineControllerManagerProviderIroncoreImageName is the name of the MachineController metal image. MachineControllerManagerProviderIroncoreImageName = "machine-controller-manager-provider-metal" + // MetallbSpeakerImageName is the name of the metallb speaker to deploy to the shoot. + MetallbSpeakerImageName = "metallb-speaker" + // MetallbControllerImageName is the name of the metallb controller to deploy to the shoot. + MetallbControllerImageName = "metallb-controller" // UsernameFieldName is the field in a secret where the namespace is stored at. UsernameFieldName = "username" @@ -43,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" + // 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. MachineControllerManagerName = "machine-controller-manager" // MachineControllerManagerVpaName is the name of the VerticalPodAutoscaler of the machine-controller-manager deployment.