From 2ab803a103b792e305386b4a29a05f321196e09c Mon Sep 17 00:00:00 2001 From: Omer Schwartz Date: Wed, 2 Oct 2024 17:05:24 +0000 Subject: [PATCH] Add pools.yaml generator This commit adds a module that will generate an "up-to-date" pools.yaml file. It will use both Bind9, Mdns and NsRecords configmaps as input to generate that file. --- controllers/designate_controller.go | 85 +++++++++- controllers/designatemdns_controller.go | 19 +-- controllers/designateworker_controller.go | 1 + demo/examples/ns_records_CR_example.yaml | 11 ++ go.mod | 2 +- pkg/designate/const.go | 10 ++ pkg/designate/generate_bind9_pools_yaml.go | 147 ++++++++++++++++++ .../config/pools.yaml.tmpl | 45 ++++++ .../basic/ns-records-configmap/02-assert.yaml | 10 ++ .../basic/ns-records-configmap/02-deploy.yaml | 6 + 10 files changed, 310 insertions(+), 26 deletions(-) create mode 100644 demo/examples/ns_records_CR_example.yaml create mode 100644 pkg/designate/generate_bind9_pools_yaml.go create mode 100644 templates/designatepoolmanager/config/pools.yaml.tmpl create mode 100644 tests/kuttl/tests/basic/ns-records-configmap/02-assert.yaml create mode 100644 tests/kuttl/tests/basic/ns-records-configmap/02-deploy.yaml diff --git a/controllers/designate_controller.go b/controllers/designate_controller.go index 43b8a811..0df6d0d6 100644 --- a/controllers/designate_controller.go +++ b/controllers/designate_controller.go @@ -378,7 +378,7 @@ func (r *DesignateReconciler) reconcileInit( // create hash over all the different input resources to identify if any those changed // and a restart/recreate is required. // - _, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configMapVars) + _, hashChanged, err := r.createHashOfInputHashes(ctx, instance, common.InputHashName, configMapVars, nil) if err != nil { return ctrl.Result{}, err } else if hashChanged { @@ -742,6 +742,12 @@ func (r *DesignateReconciler) reconcileNormal(ctx context.Context, instance *des return ctrl.Result{}, err } + nsRecordsLabels := labels.GetLabels(instance, labels.GetGroupLabel(instance.ObjectMeta.Name), map[string]string{}) + nsRecordsConfigMap, err := r.handleConfigMap(ctx, helper, instance, designate.NsRecordsConfigMap, nsRecordsLabels) + if err != nil { + return ctrl.Result{}, err + } + allocatedIPs := make(map[string]bool) for _, predIP := range bindConfigMap.Data { allocatedIPs[predIP] = true @@ -807,6 +813,49 @@ func (r *DesignateReconciler) reconcileNormal(ctx context.Context, instance *des return ctrl.Result{}, err } + if err != nil { + return ctrl.Result{}, err + } + if len(nsRecordsConfigMap.Data) > 0 { + poolsYamlConfigMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: designate.PoolsYamlsConfigMap, + Namespace: instance.GetNamespace(), + Labels: bindLabels, + }, + Data: make(map[string]string), + } + poolsYaml, err := designate.GeneratePoolsYamlData(bindConfigMap.Data, mdnsConfigMap.Data, nsRecordsConfigMap.Data) + if err != nil { + return ctrl.Result{}, err + } + Log.Info(fmt.Sprintf("pools.yaml content is\n%v", poolsYaml)) + updatedPoolsYaml := make(map[string]string) + updatedPoolsYaml[designate.PoolsYamlsConfigMap] = poolsYaml + + _, err = controllerutil.CreateOrPatch(ctx, helper.GetClient(), poolsYamlConfigMap, func() error { + poolsYamlConfigMap.Labels = util.MergeStringMaps(poolsYamlConfigMap.Labels, bindLabels) + poolsYamlConfigMap.Data = updatedPoolsYaml + return controllerutil.SetControllerReference(instance, poolsYamlConfigMap, helper.GetScheme()) + }) + if err != nil { + Log.Info("Unable to create config map for pools.yaml file") + return ctrl.Result{}, err + } + configMaps := []interface{}{ + poolsYamlConfigMap.Data, + } + + poolsYamlsEnvVars := make(map[string]env.Setter) + _, changed, err := r.createHashOfInputHashes(ctx, instance, designate.PoolsYamlHash, poolsYamlsEnvVars, configMaps) + if err != nil { + return ctrl.Result{}, err + } else if changed { + // launch the pool update job + Log.Info("Creating a pool update job") + } + } + // deploy designate-central designateCentral, op, err := r.centralDeploymentCreateOrUpdate(ctx, instance) if err != nil { @@ -1087,7 +1136,7 @@ func (r *DesignateReconciler) handleConfigMap(ctx context.Context, helper *helpe err := helper.GetClient().Get(ctx, types.NamespacedName{Name: configMapName, Namespace: instance.GetNamespace()}, foundMap) if err != nil { if k8s_errors.IsNotFound(err) { - Log.Info(fmt.Sprintf("Ip map %s doesn't exist, creating.", configMapName)) + Log.Info(fmt.Sprintf("configmap %s doesn't exist, creating.", configMapName)) } else { return nil, err } @@ -1346,23 +1395,45 @@ func (r *DesignateReconciler) generateServiceConfigMaps( func (r *DesignateReconciler) createHashOfInputHashes( ctx context.Context, instance *designatev1beta1.Designate, + hashType string, envVars map[string]env.Setter, + additionalConfigmaps []interface{}, ) (string, bool, error) { Log := r.GetLogger(ctx) var hashMap map[string]string changed := false mergedMapVars := env.MergeEnvs([]corev1.EnvVar{}, envVars) - hash, err := util.ObjectHash(mergedMapVars) + combinedHashes := []string{} + + envHash, err := util.ObjectHash(mergedMapVars) if err != nil { Log.Info("XXX - Error creating hash") - return hash, changed, err + return "", changed, err + } + combinedHashes = append(combinedHashes, envHash) + + for _, configMap := range additionalConfigmaps { + configMapHash, err := util.ObjectHash(configMap) + if err != nil { + Log.Info(fmt.Sprintf("Error creating hash for %v", configMap)) + return "", changed, err + } + combinedHashes = append(combinedHashes, configMapHash) + } + + finalHash, err := util.ObjectHash(combinedHashes) + if err != nil { + Log.Info("Error creating final hash") + return "", changed, err } - if hashMap, changed = util.SetHash(instance.Status.Hash, common.InputHashName, hash); changed { + + if hashMap, changed = util.SetHash(instance.Status.Hash, hashType, finalHash); changed { instance.Status.Hash = hashMap - Log.Info(fmt.Sprintf("Input maps hash %s - %s", common.InputHashName, hash)) + Log.Info(fmt.Sprintf("Input maps hash %s - %s", hashType, finalHash)) } - return hash, changed, nil + + return finalHash, changed, nil } func (r *DesignateReconciler) transportURLCreateOrUpdate( diff --git a/controllers/designatemdns_controller.go b/controllers/designatemdns_controller.go index e1c964c3..aa19dc12 100644 --- a/controllers/designatemdns_controller.go +++ b/controllers/designatemdns_controller.go @@ -43,7 +43,6 @@ import ( designatemdns "github.com/openstack-k8s-operators/designate-operator/pkg/designatemdns" "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - "github.com/openstack-k8s-operators/lib-common/modules/common/configmap" "github.com/openstack-k8s-operators/lib-common/modules/common/daemonset" "github.com/openstack-k8s-operators/lib-common/modules/common/env" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" @@ -496,7 +495,7 @@ func (r *DesignateMdnsReconciler) reconcileNormal(ctx context.Context, instance // create hash over all the different input resources to identify if any those changed // and a restart/recreate is required. // - inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, helper, instance, configMapVars) + inputHash, hashChanged, err := r.createHashOfInputHashes(ctx, instance, configMapVars) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -810,7 +809,6 @@ func (r *DesignateMdnsReconciler) generateServiceConfigMaps( // returns the hash, whether the hash changed (as a bool) and any error func (r *DesignateMdnsReconciler) createHashOfInputHashes( ctx context.Context, - h *helper.Helper, instance *designatev1beta1.DesignateMdns, envVars map[string]env.Setter, ) (string, bool, error) { @@ -819,21 +817,6 @@ func (r *DesignateMdnsReconciler) createHashOfInputHashes( var hashMap map[string]string changed := false - // If MdnsPredIPConfigMap exists, add its hash to status hash - mdnsPredIPCM := &corev1.ConfigMap{} - err := h.GetClient().Get(ctx, types.NamespacedName{ - Name: designate.MdnsPredIPConfigMap, - Namespace: instance.Namespace, - }, mdnsPredIPCM) - if err != nil { - Log.Error(err, "Unable to retrieve Mdns predictable IPs ConfigMap") - return "", false, err - } - mdnsPredIPCMHash, err := configmap.Hash(mdnsPredIPCM) - if err != nil { - return mdnsPredIPCMHash, changed, err - } - mergedMapVars := env.MergeEnvs([]corev1.EnvVar{}, envVars) hash, err := util.ObjectHash(mergedMapVars) if err != nil { diff --git a/controllers/designateworker_controller.go b/controllers/designateworker_controller.go index 320b925d..a886cc41 100644 --- a/controllers/designateworker_controller.go +++ b/controllers/designateworker_controller.go @@ -833,6 +833,7 @@ func (r *DesignateWorkerReconciler) createHashOfInputHashes( if err != nil { return hash, changed, err } + if hashMap, changed = util.SetHash(instance.Status.Hash, common.InputHashName, hash); changed { instance.Status.Hash = hashMap Log.Info(fmt.Sprintf("Input maps hash %s - %s", common.InputHashName, hash)) diff --git a/demo/examples/ns_records_CR_example.yaml b/demo/examples/ns_records_CR_example.yaml new file mode 100644 index 00000000..b5568099 --- /dev/null +++ b/demo/examples/ns_records_CR_example.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: +# Note, if you change this name, please change the name on pkg/designate/const.go file as well + name: designate-ns-records-params +data: + ns_records: | + - host: host1 + priority: 1 + - host: host2 + priority: 2 diff --git a/go.mod b/go.mod index 8cb15954..8dc14e08 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/openstack-k8s-operators/keystone-operator/api v0.4.1-0.20241013092400-3f9337945472 github.com/openstack-k8s-operators/lib-common/modules/common v0.4.1-0.20241014140317-e5c35d28f3af github.com/openstack-k8s-operators/mariadb-operator/api v0.4.1-0.20241015090956-b0954ab72dcd + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.29.9 k8s.io/apimachinery v0.29.9 k8s.io/client-go v0.29.9 @@ -70,7 +71,6 @@ require ( google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.29.9 // indirect k8s.io/component-base v0.29.9 // indirect diff --git a/pkg/designate/const.go b/pkg/designate/const.go index 54b8a08f..e619aa1b 100644 --- a/pkg/designate/const.go +++ b/pkg/designate/const.go @@ -41,5 +41,15 @@ const ( MdnsPredIPConfigMap = "designate-mdns-ip-map" + NsRecordsConfigMap = "designate-ns-records-params" + BindPredIPConfigMap = "designate-bind-ip-map" + + RndcConfDir = "/etc/designate/rndc-keys" + + PoolsYamlsConfigMap = "designate-pools-yaml-config-map" + + PoolsYamlPath = "templates/designatepoolmanager/config/pools.yaml.tmpl" + + PoolsYamlHash = "pools-yaml-hash" ) diff --git a/pkg/designate/generate_bind9_pools_yaml.go b/pkg/designate/generate_bind9_pools_yaml.go new file mode 100644 index 00000000..f1237684 --- /dev/null +++ b/pkg/designate/generate_bind9_pools_yaml.go @@ -0,0 +1,147 @@ +/* +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 designate + +import ( + "bytes" + "fmt" + "gopkg.in/yaml.v2" + "os" + "text/template" +) + +type Pool struct { + Name string + Description string + Attributes map[string]string + NSRecords []NSRecord + Nameservers []Nameserver + Targets []Target + CatalogZone *CatalogZone // it is a pointer because it is optional +} + +type NSRecord struct { + Hostname string + Priority int +} + +type Nameserver struct { + Host string + Port int +} + +type Target struct { + Type string + Description string + Masters []Master + Options Options +} + +type Master struct { + Host string + Port int +} + +type Options struct { + Host string + Port int + RNDCHost string + RNDCPort int + RNDCConfigFile string +} + +type CatalogZone struct { + FQDN string + Refresh string +} + +func GeneratePoolsYamlData(BindMap, MdnsMap, NsRecordsMap map[string]string) (string, error) { + // Create ns_records + nsRecords := []NSRecord{} + for _, data := range NsRecordsMap { + err := yaml.Unmarshal([]byte(data), &nsRecords) + if err != nil { + return "", fmt.Errorf("error unmarshalling yaml: %w", err) + } + } + + // Create targets and nameservers + nameservers := []Nameserver{} + targets := []Target{} + rndcKeyNum := 1 + + for _, bindIP := range BindMap { + nameservers = append(nameservers, Nameserver{ + Host: bindIP, + Port: 53, + }) + + masters := []Master{} + for _, masterHost := range MdnsMap { + masters = append(masters, Master{ + Host: masterHost, + Port: 5354, + }) + } + + target := Target{ + Type: "bind9", + Description: fmt.Sprintf("BIND9 Server %d (%s)", rndcKeyNum, bindIP), + Masters: masters, + Options: Options{ + Host: bindIP, + Port: 53, + RNDCHost: bindIP, + RNDCPort: 953, + RNDCConfigFile: fmt.Sprintf("%s/%s-%d.conf", RndcConfDir, DesignateRndcKey, rndcKeyNum), + }, + } + targets = append(targets, target) + rndcKeyNum++ + } + + // Catalog zone is an optional section + // catalogZone := &CatalogZone{ + // FQDN: "example.org.", + // Refresh: "60", + // } + defaultAttributes := make(map[string]string) + // Create the Pool struct with the dynamic values + pool := Pool{ + Name: "default", + Description: "Default BIND Pool", + Attributes: defaultAttributes, + NSRecords: nsRecords, + Nameservers: nameservers, + Targets: targets, + CatalogZone: nil, // set to catalogZone if this section should be presented + } + + PoolsYaml, err := os.ReadFile(PoolsYamlPath) + if err != nil { + return "", err + } + tmpl, err := template.New("pool").Parse(string(PoolsYaml)) + if err != nil { + return "", err + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, pool) + if err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/templates/designatepoolmanager/config/pools.yaml.tmpl b/templates/designatepoolmanager/config/pools.yaml.tmpl new file mode 100644 index 00000000..e03fe15a --- /dev/null +++ b/templates/designatepoolmanager/config/pools.yaml.tmpl @@ -0,0 +1,45 @@ +--- +- name: {{.Name}} + description: {{.Description}} + attributes: { + {{- range $key, $value := .Attributes }} + "{{ $key }}": "{{ $value }}", + {{- end }} + } + + ns_records: + {{- range .NSRecords }} + - hostname: {{.Hostname}} + priority: {{.Priority}} + {{- end }} + + nameservers: + {{- range .Nameservers }} + - host: {{.Host}} + port: {{.Port}} + {{- end }} + + targets: + {{- range .Targets }} + - type: {{.Type}} + description: {{.Description}} + + masters: + {{- range .Masters }} + - host: {{.Host}} + port: {{.Port}} + {{- end }} + + options: + host: {{.Options.Host}} + port: {{.Options.Port}} + rndc_host: {{.Options.RNDCHost}} + rndc_port: {{.Options.RNDCPort}} + rndc_config_file: {{.Options.RNDCConfigFile}} + {{- end }} + + {{- if .CatalogZone }} + catalog_zone: + catalog_zone_fqdn: {{.CatalogZone.FQDN}} + catalog_zone_refresh: '{{.CatalogZone.Refresh}}' + {{- end }} diff --git a/tests/kuttl/tests/basic/ns-records-configmap/02-assert.yaml b/tests/kuttl/tests/basic/ns-records-configmap/02-assert.yaml new file mode 100644 index 00000000..181cb976 --- /dev/null +++ b/tests/kuttl/tests/basic/ns-records-configmap/02-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: designate-ns-records-params +data: + ns_records: | + - host: host1 + priority: 1 + - host: host2 + priority: 2 diff --git a/tests/kuttl/tests/basic/ns-records-configmap/02-deploy.yaml b/tests/kuttl/tests/basic/ns-records-configmap/02-deploy.yaml new file mode 100644 index 00000000..6eb6d480 --- /dev/null +++ b/tests/kuttl/tests/basic/ns-records-configmap/02-deploy.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + cp ../../../../demo/examples/ns_records_CR_example.yaml deploy + oc kustomize deploy | oc apply -n $NAMESPACE -f -