pkg/resources/configmap.go (115 lines of code) (raw):
// Copyright (c) 2020, 2023, Oracle and/or its affiliates.
//
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
package resources
import (
"embed"
"fmt"
v1 "github.com/mysql/ndb-operator/pkg/apis/ndbcontroller/v1"
"github.com/mysql/ndb-operator/pkg/constants"
"github.com/mysql/ndb-operator/pkg/ndbconfig"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
klog "k8s.io/klog/v2"
)
// Embed the helper scripts in the ndb operator binary
//
//go:embed statefulset/scripts
var scriptsFS embed.FS
// updateManagementConfig updates the Data map with a new config.ini
// if there is any change to the MySQL Cluster configuration.
func updateManagementConfig(
ndb *v1.NdbCluster, data map[string]string, oldConfigSummary *ndbconfig.ConfigSummary) error {
// Update management if required
if oldConfigSummary == nil || oldConfigSummary.MySQLClusterConfigNeedsUpdate(ndb) {
// get the updated config string
if oldConfigSummary != nil {
klog.Infof("MySQL Cluster config for NdbCluster resource %q needs to be updated", ndb.Name)
}
configString, err := ndbconfig.GetConfigString(ndb, oldConfigSummary)
if err != nil {
klog.Errorf("Failed to get the config string : %v", err)
return err
}
// add/update that to the data map
data[constants.ConfigIniKey] = configString
}
if oldConfigSummary != nil && oldConfigSummary.TDEPasswordSecretName != ndb.Spec.TDESecretName {
// TDE password is changed, All data nodes need to perform initial node restart to adopt
// to this change
data[constants.DataNodeInitialRestart] = "true"
}
// add/update the API slot information
data[constants.NumOfMySQLServers] = fmt.Sprintf("%d", ndb.GetMySQLServerNodeCount())
// add/update service type info for management nodes
data[constants.ManagementLoadBalancer] = fmt.Sprintf("%v",
ndb.Spec.ManagementNode != nil && ndb.Spec.ManagementNode.EnableLoadBalancer)
// add/update the TDE password secret name
data[constants.TDEPasswordSecretName] = ndb.Spec.TDESecretName
return nil
}
// updateMySQLConfig updates the my.cnf key in the configMap if required
func updateMySQLConfig(
nc *v1.NdbCluster, data map[string]string, oldConfigSummary *ndbconfig.ConfigSummary) error {
if needsUpdate, err := oldConfigSummary.MySQLCnfNeedsUpdate(nc); err != nil {
klog.Errorf("Failed to check if the my.cnf needs to be updated : %s", err)
return err
} else if needsUpdate {
// Update the my.cnf key in configmap
if data[constants.MySQLConfigKey], err = ndbconfig.GetMySQLConfigString(nc, oldConfigSummary); err != nil {
klog.Errorf("Failed to get the my.cnf config string : %s", err)
return err
}
}
// Add/update service type info and root host for MySQL servers
if nc.Spec.MysqlNode != nil {
data[constants.MySQLRootHost] = nc.Spec.MysqlNode.RootHost
data[constants.MySQLLoadBalancer] = fmt.Sprintf("%v", nc.Spec.MysqlNode.EnableLoadBalancer)
} else {
data[constants.MySQLRootHost] = ""
data[constants.MySQLLoadBalancer] = "false"
}
return nil
}
// updateHelperScripts updates the data map with the helper
// scripts used for the MySQL Server initialisation & health
// probes and Data node health probe.
func updateHelperScripts(data map[string]string) error {
for fileName, desc := range map[string]string{
constants.MysqldInitScript: "MySQL Server init",
constants.MysqldHealthCheckScript: "MySQL Server Healthcheck",
constants.DataNodeStartupProbeScript: "Data Node Startup Probe",
constants.MgmdStartupProbeScript: "Mgmd Startup Probe",
} {
fileBytes, err := scriptsFS.ReadFile("statefulset/scripts/" + fileName)
if err != nil {
klog.Errorf("Failed to read %s script at %q : %v",
desc, fileName, err)
return err
}
// Use the script file name as the key in configmap.
data[fileName] = string(fileBytes)
}
return nil
}
// GetUpdatedConfigMap creates and returns a new config map with updated data
func GetUpdatedConfigMap(
ndb *v1.NdbCluster, cm *corev1.ConfigMap, oldConfigSummary *ndbconfig.ConfigSummary) *corev1.ConfigMap {
// create a deep copy of the original ConfigMap
updatedCm := cm.DeepCopy()
// If the generations are the same, the patch config map is only called to remove the
// initial flag. When data node pods are restarted with the --initial flag, they lose all
// data in their data directory and start fresh by rebuilding data from other nodes.
// Therefore, using --initial in the data node container command is not appropriate. Once
// the need for initial restart is over, the operator removes the --initial flag from the
// data node container command. This ensures that when a pod goes down for any reason,
// restarting the data node pod takes less time as the required data is already present
// in the data node's directory.
if oldConfigSummary != nil && oldConfigSummary.NdbClusterGeneration == ndb.Generation {
updatedCm.Data[constants.DataNodeInitialRestart] = "false"
return updatedCm
}
// Update the config.ini
if err := updateManagementConfig(ndb, updatedCm.Data, oldConfigSummary); err != nil {
klog.Errorf("Failed to update the config map : %v", err)
return nil
}
// Update the MySQL custom config
if err := updateMySQLConfig(ndb, updatedCm.Data, oldConfigSummary); err != nil {
klog.Errorf("Failed to update the config map : %v", err)
return nil
}
// Update the generation the config map is based on
updatedCm.Data[constants.NdbClusterGeneration] = fmt.Sprintf("%d", ndb.Generation)
return updatedCm
}
// CreateConfigMap creates a config map object with the
// information available in the ndb object
func CreateConfigMap(ndb *v1.NdbCluster) *corev1.ConfigMap {
/*
kind: ConfigMap
apiVersion: v1
metadata:
name: config-ini
namespace: default
data:
config.ini: |
[DB DEFAULT]
....
*/
// Labels for the configmap
cmLabels := ndb.GetCompleteLabels(map[string]string{
constants.ClusterResourceTypeLabel: "ndb-configmap",
})
// Data for the config map
data := make(map[string]string)
// Add the config ini value
if updateManagementConfig(ndb, data, nil) != nil {
return nil
}
// Add the MySQL custom config
if updateMySQLConfig(ndb, data, nil) != nil {
return nil
}
// Add the helper scripts
if updateHelperScripts(data) != nil {
return nil
}
// Add the data node initial restart value
data[constants.DataNodeInitialRestart] = "false"
// Update the generation the config map is based on
data[constants.NdbClusterGeneration] = fmt.Sprintf("%d", ndb.Generation)
return &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: ndb.GetConfigMapName(),
Namespace: ndb.Namespace,
Labels: cmLabels,
OwnerReferences: ndb.GetOwnerReferences(),
},
Data: data,
}
}