From 7c719b9ce2b3fc86798ca36f098d5386d9a1568c Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Mon, 11 Nov 2024 11:44:26 +0100 Subject: [PATCH] [wip] Signed-off-by: Martin Schuppert --- PROJECT | 13 + ...twork.openstack.org_bgpconfigurations.yaml | 155 +++++ apis/go.mod | 18 +- apis/go.sum | 40 +- .../network/v1beta1/bgpconfiguration_types.go | 78 +++ apis/network/v1beta1/zz_generated.deepcopy.go | 119 ++++ ...twork.openstack.org_bgpconfigurations.yaml | 155 +++++ config/crd/kustomization.yaml | 3 + ...njection_in_network_bgpconfigurations.yaml | 7 + .../webhook_in_network_bgpconfigurations.yaml | 16 + .../network_bgpconfiguration_editor_role.yaml | 31 + .../network_bgpconfiguration_viewer_role.yaml | 27 + config/rbac/role.yaml | 41 ++ config/samples/kustomization.yaml | 1 + .../network_v1beta1_bgpconfiguration.yaml | 12 + .../network/bgpconfiguration_controller.go | 579 ++++++++++++++++++ go.mod | 24 +- go.sum | 46 +- main.go | 11 + 19 files changed, 1318 insertions(+), 58 deletions(-) create mode 100644 apis/bases/network.openstack.org_bgpconfigurations.yaml create mode 100644 apis/network/v1beta1/bgpconfiguration_types.go create mode 100644 config/crd/bases/network.openstack.org_bgpconfigurations.yaml create mode 100644 config/crd/patches/cainjection_in_network_bgpconfigurations.yaml create mode 100644 config/crd/patches/webhook_in_network_bgpconfigurations.yaml create mode 100644 config/rbac/network_bgpconfiguration_editor_role.yaml create mode 100644 config/rbac/network_bgpconfiguration_viewer_role.yaml create mode 100644 config/samples/network_v1beta1_bgpconfiguration.yaml create mode 100644 controllers/network/bgpconfiguration_controller.go diff --git a/PROJECT b/PROJECT index 4a77a99c..e0687e7d 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: openstack.org layout: - go.kubebuilder.io/v3 @@ -116,4 +120,13 @@ resources: defaulting: true validation: true webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: openstack.org + group: network + kind: BGPConfiguration + path: github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1 + version: v1beta1 version: "3" diff --git a/apis/bases/network.openstack.org_bgpconfigurations.yaml b/apis/bases/network.openstack.org_bgpconfigurations.yaml new file mode 100644 index 00000000..31037ae8 --- /dev/null +++ b/apis/bases/network.openstack.org_bgpconfigurations.yaml @@ -0,0 +1,155 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: bgpconfigurations.network.openstack.org +spec: + group: network.openstack.org + names: + kind: BGPConfiguration + listKind: BGPConfigurationList + plural: bgpconfigurations + singular: bgpconfiguration + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: BGPConfiguration is the Schema for the bgpconfigurations 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: BGPConfigurationSpec defines the desired state of BGPConfiguration + properties: + frrConfigurationNamespace: + default: metallb-system + description: FRRConfigurationNamespace - namespace where to create + the FRRConfiguration. Defaults to metallb-system. + type: string + frrNodeConfigurationSelector: + description: 'FRRNodeConfigurationSelector - per default the FRRConfiguration + per node within the FRRConfigurationNamespace gets queried using + the FRRConfiguration.spec.NodeSelector `kubernetes.io/hostname: + worker-0`. In case a more specific' + items: + description: FRRNodeConfigurationSelectorType - + properties: + frrConfigurationNamespace: + description: NodeName - name of the node object as seen by + running the `oc get nodes` command + type: string + nodeSelector: + description: NodeSelector to identify the correct FRRConfiguration + from spec.nodeSelector + 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 + required: + - key + - operator + type: object + type: array + 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: object + type: array + type: object + status: + description: BGPConfigurationStatus defines the observed state of BGPConfiguration + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: Severity provides a classification of Reason code, + so the current situation is immediately understandable and + could act accordingly. It is meant for situations where Status=False + and it should be indicated if it is just informational, warning + (next reconciliation might fix it) or an error (e.g. DB create + issue and no actions to automatically resolve the issue can/should + be done). For conditions where Status=Unknown or Status=True + the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/go.mod b/apis/go.mod index 8f5096cd..5123fa80 100644 --- a/apis/go.mod +++ b/apis/go.mod @@ -19,14 +19,14 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/swag v0.22.9 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -47,14 +47,14 @@ require ( github.com/openshift/api v3.9.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.51.1 // indirect + github.com/prometheus/procfs v0.13.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect @@ -69,7 +69,7 @@ require ( k8s.io/apiextensions-apiserver v0.29.10 // indirect k8s.io/component-base v0.29.10 // indirect k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/apis/go.sum b/apis/go.sum index bb76d09c..23031e4a 100644 --- a/apis/go.sum +++ b/apis/go.sum @@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= -github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -18,12 +18,12 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -84,20 +84,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= +github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -192,8 +192,8 @@ k8s.io/component-base v0.29.10 h1:YQrQ/bpzGPGqIPEPaBzxjH0/1DJOI+yZPZNbbz7ZCBY= k8s.io/component-base v0.29.10/go.mod h1:IbwsBob2DnYiAONsSHIuYenchqcDycbHSLHrXshuLgM= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 h1:qVoMaQV5t62UUvHe16Q3eb2c5HPzLHYzsi0Tu/xLndo= +k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.17.6 h1:12IXsozEsIXWAMRpgRlYS1jjAHQXHtWEOMdULh3DbEw= diff --git a/apis/network/v1beta1/bgpconfiguration_types.go b/apis/network/v1beta1/bgpconfiguration_types.go new file mode 100644 index 00000000..21183dd8 --- /dev/null +++ b/apis/network/v1beta1/bgpconfiguration_types.go @@ -0,0 +1,78 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FRRNodeConfigurationSelectorType - +type FRRNodeConfigurationSelectorType struct { + // +kubebuilder:validation:Optional + // NodeName - name of the node object as seen by running the `oc get nodes` command + NodeName string `json:"frrConfigurationNamespace,omitempty"` + + // +kubebuilder:validation:Optional + // NodeSelector to identify the correct FRRConfiguration from spec.nodeSelector + NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"` +} + +// BGPConfigurationSpec defines the desired state of BGPConfiguration +type BGPConfigurationSpec struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default="metallb-system" + // FRRConfigurationNamespace - namespace where to create the FRRConfiguration. Defaults to metallb-system. + FRRConfigurationNamespace string `json:"frrConfigurationNamespace,omitempty"` + + // +kubebuilder:validation:Optional + // FRRNodeConfigurationSelector - per default the FRRConfiguration per node within the FRRConfigurationNamespace + // gets queried using the FRRConfiguration.spec.NodeSelector `kubernetes.io/hostname: worker-0`. In case a more + // specific + FRRNodeConfigurationSelector []FRRNodeConfigurationSelectorType `json:"frrNodeConfigurationSelector,omitempty"` +} + +// BGPConfigurationStatus defines the observed state of BGPConfiguration +type BGPConfigurationStatus struct { + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// BGPConfiguration is the Schema for the bgpconfigurations API +type BGPConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BGPConfigurationSpec `json:"spec,omitempty"` + Status BGPConfigurationStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// BGPConfigurationList contains a list of BGPConfiguration +type BGPConfigurationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BGPConfiguration `json:"items"` +} + +func init() { + SchemeBuilder.Register(&BGPConfiguration{}, &BGPConfigurationList{}) +} diff --git a/apis/network/v1beta1/zz_generated.deepcopy.go b/apis/network/v1beta1/zz_generated.deepcopy.go index 1b53b355..acb8f56a 100644 --- a/apis/network/v1beta1/zz_generated.deepcopy.go +++ b/apis/network/v1beta1/zz_generated.deepcopy.go @@ -42,6 +42,109 @@ func (in *AllocationRange) DeepCopy() *AllocationRange { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPConfiguration) DeepCopyInto(out *BGPConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPConfiguration. +func (in *BGPConfiguration) DeepCopy() *BGPConfiguration { + if in == nil { + return nil + } + out := new(BGPConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BGPConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPConfigurationList) DeepCopyInto(out *BGPConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BGPConfiguration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPConfigurationList. +func (in *BGPConfigurationList) DeepCopy() *BGPConfigurationList { + if in == nil { + return nil + } + out := new(BGPConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BGPConfigurationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPConfigurationSpec) DeepCopyInto(out *BGPConfigurationSpec) { + *out = *in + if in.FRRNodeConfigurationSelector != nil { + in, out := &in.FRRNodeConfigurationSelector, &out.FRRNodeConfigurationSelector + *out = make([]FRRNodeConfigurationSelectorType, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPConfigurationSpec. +func (in *BGPConfigurationSpec) DeepCopy() *BGPConfigurationSpec { + if in == nil { + return nil + } + out := new(BGPConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPConfigurationStatus) DeepCopyInto(out *BGPConfigurationStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPConfigurationStatus. +func (in *BGPConfigurationStatus) DeepCopy() *BGPConfigurationStatus { + if in == nil { + return nil + } + out := new(BGPConfigurationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DNSData) DeepCopyInto(out *DNSData) { *out = *in @@ -369,6 +472,22 @@ func (in *DNSMasqStatus) DeepCopy() *DNSMasqStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRNodeConfigurationSelectorType) DeepCopyInto(out *FRRNodeConfigurationSelectorType) { + *out = *in + in.NodeSelector.DeepCopyInto(&out.NodeSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRNodeConfigurationSelectorType. +func (in *FRRNodeConfigurationSelectorType) DeepCopy() *FRRNodeConfigurationSelectorType { + if in == nil { + return nil + } + out := new(FRRNodeConfigurationSelectorType) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPAddress) DeepCopyInto(out *IPAddress) { *out = *in diff --git a/config/crd/bases/network.openstack.org_bgpconfigurations.yaml b/config/crd/bases/network.openstack.org_bgpconfigurations.yaml new file mode 100644 index 00000000..31037ae8 --- /dev/null +++ b/config/crd/bases/network.openstack.org_bgpconfigurations.yaml @@ -0,0 +1,155 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: bgpconfigurations.network.openstack.org +spec: + group: network.openstack.org + names: + kind: BGPConfiguration + listKind: BGPConfigurationList + plural: bgpconfigurations + singular: bgpconfiguration + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: BGPConfiguration is the Schema for the bgpconfigurations 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: BGPConfigurationSpec defines the desired state of BGPConfiguration + properties: + frrConfigurationNamespace: + default: metallb-system + description: FRRConfigurationNamespace - namespace where to create + the FRRConfiguration. Defaults to metallb-system. + type: string + frrNodeConfigurationSelector: + description: 'FRRNodeConfigurationSelector - per default the FRRConfiguration + per node within the FRRConfigurationNamespace gets queried using + the FRRConfiguration.spec.NodeSelector `kubernetes.io/hostname: + worker-0`. In case a more specific' + items: + description: FRRNodeConfigurationSelectorType - + properties: + frrConfigurationNamespace: + description: NodeName - name of the node object as seen by + running the `oc get nodes` command + type: string + nodeSelector: + description: NodeSelector to identify the correct FRRConfiguration + from spec.nodeSelector + 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 + required: + - key + - operator + type: object + type: array + 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: object + type: array + type: object + status: + description: BGPConfigurationStatus defines the observed state of BGPConfiguration + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. This should be when the underlying condition changed. + If that is not known, then using the time when the API field + changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: Severity provides a classification of Reason code, + so the current situation is immediately understandable and + could act accordingly. It is meant for situations where Status=False + and it should be indicated if it is just informational, warning + (next reconciliation might fix it) or an error (e.g. DB create + issue and no actions to automatically resolve the issue can/should + be done). For conditions where Status=Unknown or Status=True + the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 7a56abf6..5719edfd 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -11,6 +11,7 @@ resources: - bases/network.openstack.org_netconfigs.yaml - bases/network.openstack.org_ipsets.yaml - bases/network.openstack.org_reservations.yaml +- bases/network.openstack.org_bgpconfigurations.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -24,6 +25,7 @@ patchesStrategicMerge: #- patches/webhook_in_netconfigs.yaml #- patches/webhook_in_reservations.yaml #- patches/webhook_in_ipsets.yaml +#- patches/webhook_in_bgpconfigurations.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -36,6 +38,7 @@ patchesStrategicMerge: #- patches/cainjection_in_netconfigs.yaml #- patches/cainjection_in_reservations.yaml #- patches/cainjection_in_ipsets.yaml +#- patches/cainjection_in_bgpconfigurations.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_network_bgpconfigurations.yaml b/config/crd/patches/cainjection_in_network_bgpconfigurations.yaml new file mode 100644 index 00000000..c10bd3ba --- /dev/null +++ b/config/crd/patches/cainjection_in_network_bgpconfigurations.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: bgpconfigurations.network.openstack.org diff --git a/config/crd/patches/webhook_in_network_bgpconfigurations.yaml b/config/crd/patches/webhook_in_network_bgpconfigurations.yaml new file mode 100644 index 00000000..ce55faa0 --- /dev/null +++ b/config/crd/patches/webhook_in_network_bgpconfigurations.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: bgpconfigurations.network.openstack.org +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/network_bgpconfiguration_editor_role.yaml b/config/rbac/network_bgpconfiguration_editor_role.yaml new file mode 100644 index 00000000..aebb897a --- /dev/null +++ b/config/rbac/network_bgpconfiguration_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit bgpconfigurations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: bgpconfiguration-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: infra-operator + app.kubernetes.io/part-of: infra-operator + app.kubernetes.io/managed-by: kustomize + name: bgpconfiguration-editor-role +rules: +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations/status + verbs: + - get diff --git a/config/rbac/network_bgpconfiguration_viewer_role.yaml b/config/rbac/network_bgpconfiguration_viewer_role.yaml new file mode 100644 index 00000000..7f0fc43f --- /dev/null +++ b/config/rbac/network_bgpconfiguration_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view bgpconfigurations. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: bgpconfiguration-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: infra-operator + app.kubernetes.io/part-of: infra-operator + app.kubernetes.io/managed-by: kustomize + name: bgpconfiguration-viewer-role +rules: +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations + verbs: + - get + - list + - watch +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 4a52c079..54068152 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -71,6 +71,9 @@ rules: verbs: - get - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -95,6 +98,18 @@ rules: - patch - update - watch +- apiGroups: + - frrk8s.metallb.io + resources: + - frrconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - instanceha.openstack.org resources: @@ -149,6 +164,32 @@ rules: - get - patch - update +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations/finalizers + verbs: + - update +- apiGroups: + - network.openstack.org + resources: + - bgpconfigurations/status + verbs: + - get + - patch + - update - apiGroups: - network.openstack.org resources: diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 7f7b6ecc..0415831f 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -9,4 +9,5 @@ resources: - network_v1beta1_netconfig.yaml - network_v1beta1_ipset.yaml - network_v1beta1_reservation.yaml +- network_v1beta1_bgpconfiguration.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/network_v1beta1_bgpconfiguration.yaml b/config/samples/network_v1beta1_bgpconfiguration.yaml new file mode 100644 index 00000000..0e6c6bed --- /dev/null +++ b/config/samples/network_v1beta1_bgpconfiguration.yaml @@ -0,0 +1,12 @@ +apiVersion: network.openstack.org/v1beta1 +kind: BGPConfiguration +metadata: + labels: + app.kubernetes.io/name: bgpconfiguration + app.kubernetes.io/instance: bgpconfiguration-sample + app.kubernetes.io/part-of: infra-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: infra-operator + name: bgpconfiguration-sample +spec: + # TODO(user): Add fields here diff --git a/controllers/network/bgpconfiguration_controller.go b/controllers/network/bgpconfiguration_controller.go new file mode 100644 index 00000000..43a2900f --- /dev/null +++ b/controllers/network/bgpconfiguration_controller.go @@ -0,0 +1,579 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package network + +import ( + "context" + "fmt" + "strings" + + "golang.org/x/exp/slices" + "k8s.io/apimachinery/pkg/api/equality" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/go-logr/logr" + k8s_networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + frrk8sv1 "github.com/metallb/frr-k8s/api/v1beta1" + networkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + labels "github.com/openstack-k8s-operators/lib-common/modules/common/labels" + nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" + util "github.com/openstack-k8s-operators/lib-common/modules/common/util" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// BGPConfigurationReconciler reconciles a BGPConfiguration object +type BGPConfigurationReconciler struct { + client.Client + Kclient kubernetes.Interface + Scheme *runtime.Scheme +} + +type podDetail struct { + name string + namespace string + node string + networkStatus []k8s_networkv1.NetworkStatus +} + +// GetLogger returns a logger object with a prefix of "controller.name" and additional controller context fields +func (r *BGPConfigurationReconciler) GetLogger(ctx context.Context) logr.Logger { + return log.FromContext(ctx).WithName("Controllers").WithName("BGPConfiguration") +} + +//+kubebuilder:rbac:groups=network.openstack.org,resources=bgpconfigurations,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=network.openstack.org,resources=bgpconfigurations/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=network.openstack.org,resources=bgpconfigurations/finalizers,verbs=update +//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch +//+kubebuilder:rbac:groups=frrk8s.metallb.io,resources=frrconfigurations,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the BGPConfiguration object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile +func (r *BGPConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, _err error) { + Log := r.GetLogger(ctx) + + // Fetch the BGPConfiguration instance + instance := &networkv1.BGPConfiguration{} + err := r.Client.Get(ctx, req.NamespacedName, instance) + if err != nil { + if k8s_errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. + // For additional cleanup logic use finalizers. Return and don't requeue. + return ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + return ctrl.Result{}, err + } + + helper, err := helper.NewHelper( + instance, + r.Client, + r.Kclient, + r.Scheme, + Log, + ) + if err != nil { + return ctrl.Result{}, err + } + + // initialize status if Conditions is nil, but do not reset if it already + // exists + isNewInstance := instance.Status.Conditions == nil + if isNewInstance { + instance.Status.Conditions = condition.Conditions{} + } + + // Save a copy of the condtions so that we can restore the LastTransitionTime + // when a condition's state doesn't change. + savedConditions := instance.Status.Conditions.DeepCopy() + + // Always patch the instance status when exiting this function so we can + // persist any changes. + defer func() { + condition.RestoreLastTransitionTimes( + &instance.Status.Conditions, savedConditions) + if instance.Status.Conditions.IsUnknown(condition.ReadyCondition) { + instance.Status.Conditions.Set( + instance.Status.Conditions.Mirror(condition.ReadyCondition)) + } + err := helper.PatchInstance(ctx, instance) + if err != nil { + _err = err + return + } + }() + + // initialize status + cl := condition.CreateList( + condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage), + condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage), + ) + + instance.Status.Conditions.Init(&cl) + + // If we're not deleting this and the service object doesn't have our finalizer, add it. + if instance.DeletionTimestamp.IsZero() && controllerutil.AddFinalizer(instance, helper.GetFinalizer()) || isNewInstance { + return ctrl.Result{}, nil + } + + // Handle service delete + if !instance.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, instance, helper) + } + + // Handle non-deleted clusters + return r.reconcileNormal(ctx, instance, helper) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *BGPConfigurationReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + Log := r.GetLogger(ctx) + podFN := handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request { + result := []reconcile.Request{} + + // For each Pod create / update event get the list of all + // BGPConfiguration to trigger reconcile for the one in the same namespace + bgpConfigurationList := &networkv1.BGPConfigurationList{} + + listOpts := []client.ListOption{ + client.InNamespace(o.GetNamespace()), + } + if err := r.Client.List(ctx, bgpConfigurationList, listOpts...); err != nil { + Log.Error(err, "Unable to retrieve BGPConfigurationList") + return nil + } + + // For each BGPConfiguration instance create a reconcile request + for _, i := range bgpConfigurationList.Items { + name := client.ObjectKey{ + Namespace: o.GetNamespace(), + Name: i.Name, + } + result = append(result, reconcile.Request{NamespacedName: name}) + } + if len(result) > 0 { + Log.Info("Reconcile request for:", "result", result) + + return result + } + return nil + }) + + // 'UpdateFunc' and 'CreateFunc' used to judge if a event about the object is + // what we want. If that is true, the event will be processed by the reconciler. + p := predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Skip if the NAD + // * annotation key is missing + // * of the oldConfig and newConfig is the same + oldConfigured := true + var oldConfig string + var ok bool + if oldConfig, ok = e.ObjectOld.GetAnnotations()[k8s_networkv1.NetworkAttachmentAnnot]; !ok || len(oldConfig) == 0 { + oldConfigured = false + } + newConfigured := true + if newConfig, ok := e.ObjectNew.GetAnnotations()[k8s_networkv1.NetworkAttachmentAnnot]; !ok || oldConfig == newConfig { + newConfigured = false + return false + } + if !oldConfigured && !newConfigured { + return false + } + + return e.ObjectOld.GetResourceVersion() != e.ObjectNew.GetResourceVersion() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Skip if the NAD + // * annotation key is missing + // * there is no additional network configured + if val, ok := e.Object.GetAnnotations()[k8s_networkv1.NetworkAttachmentAnnot]; !ok || len(val) == 0 { + return false + } + + return true + }, + CreateFunc: func(e event.CreateEvent) bool { + // Skip if the NAD + // * annotation key is missing + // * there is no additional network configured + if val, ok := e.Object.GetAnnotations()[k8s_networkv1.NetworkAttachmentAnnot]; !ok || len(val) == 0 { + return false + } + + return true + }, + } + return ctrl.NewControllerManagedBy(mgr). + For(&networkv1.BGPConfiguration{}). + Owns(&frrk8sv1.FRRConfiguration{}). + // Watch pods with networkv1.FRRLabelSelectorKey label in the same namespace + Watches(&corev1.Pod{}, + podFN, + builder.WithPredicates(p)). + /* + // TODO Watch FRRConfigurations we do not own, or embad them into the crd?!? + Watches(&frrk8sv1.FRRConfiguration{}, + podFN, + builder.WithPredicates(p)). + */ + Complete(r) +} + +func (r *BGPConfigurationReconciler) reconcileDelete(ctx context.Context, instance *networkv1.BGPConfiguration, helper *helper.Helper) (ctrl.Result, error) { + Log := r.GetLogger(ctx) + Log.Info("Reconciling Service delete") + + // TODO cleany FRRConfiguration in metallb-system namespace + + // Service is deleted so remove the finalizer. + controllerutil.RemoveFinalizer(instance, helper.GetFinalizer()) + Log.Info("Reconciled Service delete successfully") + + return ctrl.Result{}, nil +} + +func (r *BGPConfigurationReconciler) reconcileNormal(ctx context.Context, instance *networkv1.BGPConfiguration, helper *helper.Helper) (ctrl.Result, error) { + Log := r.GetLogger(ctx) + Log.Info("Reconciling Service") + + // Get a list of pods which are in the same namespace as the ctlplane + // to verify if a FRRConfiguration needs to be created for. + podList := &corev1.PodList{} + + listOpts := []client.ListOption{ + client.InNamespace(instance.Namespace), + } + if err := r.Client.List(ctx, podList, listOpts...); err != nil { + return ctrl.Result{}, fmt.Errorf("Unable to retrieve PodList %w", err) + } + + // get podDetail all pods which have additional interfaces configured + podNetworkDetailList, err := getPodNetworkDetails(podList) + if err != nil { + return ctrl.Result{}, err + } + + // get all nodes we run pods on + nodes := []string{} + for _, p := range podNetworkDetailList { + if !util.StringInSlice(p.node, nodes) { + nodes = append(nodes, p.node) + } + } + + // get all frr configs for those nodes + frrNodeConfigs := map[string]frrk8sv1.FRRConfiguration{} + for _, nodeName := range nodes { + frrConfigList := &frrk8sv1.FRRConfigurationList{} + listOpts := []client.ListOption{ + client.InNamespace(instance.Spec.FRRConfigurationNamespace), + //client.MatchingLabels(map[string]string{ + // corev1.LabelHostname: nodeName, + //}), + } + if err := r.Client.List(ctx, frrConfigList, listOpts...); err != nil { + return ctrl.Result{}, fmt.Errorf("Unable to retrieve FRRConfigurationList %w", err) + } + + var nodeSelector metav1.LabelSelector + + // validate if a nodeSelector is configured for the nodeName + f := func(c networkv1.FRRNodeConfigurationSelectorType) bool { + return c.NodeName == nodeName + } + idx := slices.IndexFunc(instance.Spec.FRRNodeConfigurationSelector, f) + if idx >= 0 { + nodeSelector = instance.Spec.FRRNodeConfigurationSelector[idx].NodeSelector + } else { + // if non configured via the spec, use + // nodeSelector: + // matchLabels: + // kubernetes.io/hostname: worker-0 + nodeSelector.MatchLabels = map[string]string{ + corev1.LabelHostname: nodeName, + } + } + + var frrCfg *frrk8sv1.FRRConfiguration + for _, cfg := range frrConfigList.Items { + if equality.Semantic.DeepEqual(cfg.Spec.NodeSelector, nodeSelector) { + frrCfg = cfg.DeepCopy() + } + } + + if frrCfg != nil { + frrNodeConfigs[nodeName] = *frrCfg + } else { + // error, we have not found the frrConfig for the node + return ctrl.Result{}, fmt.Errorf("no FRRConfiguration found for node %s using nodeSelector %v", nodeName, nodeSelector) + } + } + + // create FRRConfigurations for the podNetworkDetailList + for _, podNetworkDetail := range podNetworkDetailList { + // TODO: optimize do it per node + + err = r.createOrPatchFRRConfiguration( + ctx, + instance, + &podNetworkDetail, + frrNodeConfigs, + labels.GetLabels(instance, + labels.GetGroupLabel(strings.ToLower(instance.GroupVersionKind().Kind)), + map[string]string{}), + ) + if err != nil { + return ctrl.Result{}, err + } + } + + Log.Info("Reconciled Service successfully") + return ctrl.Result{}, nil +} + +// getPodNetworkDetails - returns the podDetails for a list of pods +// where the pod has the multus k8s_networkv1.NetworkAttachmentAnnot annotation +// and its value is not '[]' +func getPodNetworkDetails(pods *corev1.PodList) ([]podDetail, error) { + detailList := []podDetail{} + if pods != nil { + for _, pod := range pods.Items { + if val, ok := pod.Annotations[k8s_networkv1.NetworkAttachmentAnnot]; ok && val != "[]" { + detail := podDetail{ + name: pod.Name, + namespace: pod.Namespace, + node: pod.Spec.NodeName, + } + + netsStatus, err := nad.GetNetworkStatusFromAnnotation(pod.Annotations) + if err != nil { + return detailList, err + } + detail.networkStatus = netsStatus + + detailList = append(detailList, detail) + } + } + } + + return detailList, nil +} + +// createOrPatchFRRConfiguration - +func (r *BGPConfigurationReconciler) createOrPatchFRRConfiguration( + ctx context.Context, + instance *networkv1.BGPConfiguration, + podDtl *podDetail, + nodeFRRCfgs map[string]frrk8sv1.FRRConfiguration, + frrLabels map[string]string, +) error { + Log := r.GetLogger(ctx) + + podPrefixes := getFRRPodPrefixes(podDtl.networkStatus) + + nodeFRRCfg := nodeFRRCfgs[podDtl.node] + + frrConfig := &frrk8sv1.FRRConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: instance.Namespace + "-" + podDtl.name, + Namespace: instance.Spec.FRRConfigurationNamespace, + }, + } + + frrConfigSpec := &frrk8sv1.FRRConfigurationSpec{} + var routers []frrk8sv1.Router + for _, r := range nodeFRRCfg.Spec.BGP.Routers { + routers = append(routers, frrk8sv1.Router{ + ASN: r.ASN, + Neighbors: getFRRNeighbors(r.Neighbors, podPrefixes), + Prefixes: podPrefixes, + }) + } + frrConfigSpec.BGP.Routers = routers + frrConfigSpec.NodeSelector = nodeFRRCfg.Spec.NodeSelector + + // create or update the FRRConfiguration + op, err := controllerutil.CreateOrPatch(ctx, r.Client, frrConfig, func() error { + frrConfig.Labels = util.MergeMaps(frrConfig.Labels, frrLabels) + frrConfigSpec.DeepCopyInto(&frrConfig.Spec) + + return nil + }) + if err != nil { + return fmt.Errorf("error create/updating service FRRConfiguration: %w", err) + } + + if op != controllerutil.OperationResultNone { + Log.Info("operation:", "FRRConfiguration name", frrConfig.Name, "Operation", string(op)) + } + + return nil +} + +func getFRRPodPrefixes(networkStatus []k8s_networkv1.NetworkStatus) []string { + podPrefixes := []string{} + for _, podNetStat := range networkStatus { + if podNetStat.Name == "ovn-kubernetes" { + continue + } + + for _, ip := range podNetStat.IPs { + podPrefixes = append(podPrefixes, ip+"/32") + } + } + + return podPrefixes +} + +func getFRRNeighbors(nodeNeighbors []frrk8sv1.Neighbor, podPrefixes []string) []frrk8sv1.Neighbor { + podNeighbors := []frrk8sv1.Neighbor{} + + for _, n := range nodeNeighbors { + podNeighbors = append(podNeighbors, + frrk8sv1.Neighbor{ + ASN: n.ASN, + Address: n.Address, + ToAdvertise: frrk8sv1.Advertise{ + Allowed: frrk8sv1.AllowedOutPrefixes{ + Mode: n.ToAdvertise.Allowed.Mode, + Prefixes: podPrefixes, + }, + }, + }, + ) + } + + return podNeighbors +} + +/* +// cleanupFRRConfiguration - +func (r *BGPConfigurationReconciler) cleanupFRRConfiguration( + ctx context.Context, + instance *networkv1.BGPConfiguration, + podDtl *podDetail, +) error { + Log := r.GetLogger(ctx) + + frrConfig := &frrk8sv1.FRRConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: podDtl.name, + Namespace: instance.Spec.FRRConfigurationNamespace, + Labels: map[string]string{ + networkv1.OpenStackNamespaceSelectorKey: podDtl.namespace, // to be used to select all frrconfigs for a specific openstack deployment + }, + }, + } + + // create or update the FRRConfiguration + op, err := controllerutil.CreateOrPatch(ctx, r.Client, frrConfig, func() error { + + frrConfig.Spec.BGP = frrk8sv1.BGPConfig{ + // TODO - get details for node + Routers: []frrk8sv1.Router{}, + } + + frrConfig.Spec.NodeSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + corev1.LabelHostname: podDtl.node, + }, + } + + return nil + }) + if err != nil { + return fmt.Errorf("error create/updating service FRRConfiguration: %w", err) + } + + if op != controllerutil.OperationResultNone { + Log.Info("operation:", "FRRConfiguration name", frrConfig.Name, "Operation", string(op)) + } + + return nil +} +*/ + +/* +// getFRRConfigurations - gel all FRRConfigurations within a namespace +// matching the labelSelector. +func getFRRConfigurations( + ctx context.Context, + clnt client.Client, + frrConfigurationNamespace string, + labelSelector map[string]string, +) (*frrk8sv1.FRRConfigurationList, error) { + frrConfigList := &frrk8sv1.FRRConfigurationList{} + + listOpts := []client.ListOption{ + client.InNamespace(frrConfigurationNamespace), + client.MatchingLabels(labelSelector), + } + // TODO is this still valid and we have to use the kclient? + // use kclient to not use a cached client to be able to list services in namespace which are not cached + // otherwise we hit "Error listing services for labels: map[ ... ] - unable to get: default because of unknown namespace for the cache" + if err := clnt.List(ctx, frrConfigList, listOpts...); err != nil { + return frrConfigList, fmt.Errorf("Unable to retrieve FRRConfigurationList: %w", err) + } + + return frrConfigList, nil +} +*/ + +/* +// DeleteSecretsWithLabel - Delete all secrets in namespace of the obj matching label selector +func (r *BGPConfigurationReconciler) DeleteSecretsWithLabel( + ctx context.Context, + h *helper.Helper, + obj client.Object, + labelSelectorMap map[string]string, +) error { + err := h.GetClient().DeleteAllOf( + ctx, + &corev1.Secret{}, + client.InNamespace(obj.GetNamespace()), + client.MatchingLabels(labelSelectorMap), + ) + if err != nil && !k8s_errors.IsNotFound(err) { + err = fmt.Errorf("Error DeleteAllOf Secret: %w", err) + return err + } + + return nil +} +*/ diff --git a/go.mod b/go.mod index 6a34c993..1ccccc0c 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.5 + github.com/metallb/frr-k8s v0.0.0-00010101000000-000000000000 github.com/onsi/ginkgo/v2 v2.20.1 github.com/onsi/gomega v1.34.1 github.com/openstack-k8s-operators/infra-operator/apis v0.3.0 @@ -25,13 +26,15 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/swag v0.22.9 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -50,14 +53,14 @@ require ( github.com/openshift/api v3.9.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.51.1 // indirect + github.com/prometheus/procfs v0.13.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect @@ -72,7 +75,7 @@ require ( k8s.io/apiextensions-apiserver v0.29.10 // indirect k8s.io/component-base v0.29.10 // indirect k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect @@ -86,3 +89,6 @@ replace github.com/openshift/api => github.com/openshift/api v0.0.0-202408300231 // custom RabbitmqClusterSpecCore for OpenStackControlplane (v2.6.0_patches_tag) replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20241017142550-a3524acedd49 //allow-merging + +// pin frr-k8s to v0.0.11 because later versions have a req for golang 1.22 +replace github.com/metallb/frr-k8s => github.com/metallb/frr-k8s v0.0.11 diff --git a/go.sum b/go.sum index d1025df4..544659ba 100644 --- a/go.sum +++ b/go.sum @@ -6,24 +6,28 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= -github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -63,6 +67,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/metallb/frr-k8s v0.0.11 h1:Y0SkDHg3BBHIcxOV/H5S8AzklYsyEraC6i1MMO8qoKc= +github.com/metallb/frr-k8s v0.0.11/go.mod h1:Yeqy4z+77hlwto8sAg/gozP8Flu0lWZIua96YAhnkYI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -88,20 +94,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= +github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -130,8 +136,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -198,8 +204,8 @@ k8s.io/component-base v0.29.10 h1:YQrQ/bpzGPGqIPEPaBzxjH0/1DJOI+yZPZNbbz7ZCBY= k8s.io/component-base v0.29.10/go.mod h1:IbwsBob2DnYiAONsSHIuYenchqcDycbHSLHrXshuLgM= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 h1:qVoMaQV5t62UUvHe16Q3eb2c5HPzLHYzsi0Tu/xLndo= +k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.17.6 h1:12IXsozEsIXWAMRpgRlYS1jjAHQXHtWEOMdULh3DbEw= diff --git a/main.go b/main.go index ed42e9bd..cc877a7b 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ import ( "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client/config" + frrk8sv1 "github.com/metallb/frr-k8s/api/v1beta1" instancehav1 "github.com/openstack-k8s-operators/infra-operator/apis/instanceha/v1beta1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" networkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" @@ -68,6 +69,7 @@ func init() { utilruntime.Must(instancehav1.AddToScheme(scheme)) utilruntime.Must(redisv1.AddToScheme(scheme)) utilruntime.Must(networkv1.AddToScheme(scheme)) + utilruntime.Must(frrk8sv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -205,6 +207,15 @@ func main() { os.Exit(1) } + if err = (&networkcontrollers.BGPConfigurationReconciler{ + Client: mgr.GetClient(), + Kclient: kclient, + Scheme: mgr.GetScheme(), + }).SetupWithManager(context.Background(), mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "BGPConfiguration") + os.Exit(1) + } + // Acquire environmental defaults and initialize operator defaults with them memcachedv1.SetupDefaults() redisv1.SetupDefaults()