pkg/ndbconfig/config_summary.go (132 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 ndbconfig import ( "fmt" "strconv" "github.com/mysql/ndb-operator/config/debug" v1 "github.com/mysql/ndb-operator/pkg/apis/ndbcontroller/v1" "github.com/mysql/ndb-operator/pkg/constants" "github.com/mysql/ndb-operator/pkg/ndbconfig/configparser" ) // ConfigSummary contains a summary of information extracted from the // configMap data. It is used during creation and updation of various // K8s resources and also to compare any new incoming Ndb spec change. type ConfigSummary struct { // NdbClusterGeneration is the generation of the NdbCluster this config is based on. NdbClusterGeneration int64 // MySQLClusterConfigVersion is the version of the config.ini stored in the config map MySQLClusterConfigVersion int32 // MySQLServerConfigVersion is the version of the my.cnf stored in the config map MySQLServerConfigVersion int32 // NumOfManagementNodes is number of Management Nodes (1 or 2). NumOfManagementNodes int32 // NumOfDataNodes is the number of Data Nodes. NumOfDataNodes int32 // NumOfMySQLServers is the number of MySQL Servers // expected to connect to the MySQL Cluster data nodes. NumOfMySQLServers int32 // NumOfMySQLServerSlots is the total number of [mysqld] sections required by the MySQL Servers. NumOfMySQLServerSlots int32 // NumOfFreeApiSlots is the number of [api] sections declared in the config based on spec.freeApiSlots NumOfFreeApiSlots int32 // RedundancyLevel is the number of replicas of the data stored in MySQL Cluster. RedundancyLevel int32 // defaultNdbdSection has the values extracted from the default ndbd section of the management config. defaultNdbdSection configparser.Section // defaultMgmdSection has the values extracted from the default ndbd section of the management config. defaultMgmdSection configparser.Section // MySQLLoadBalancer indicates if the load balancer service for MySQL servers needs to be enabled MySQLLoadBalancer bool // ManagementLoadBalancer indicates if the load balancer service for management nodes needs to be enabled ManagementLoadBalancer bool // myCnfConfig has the parsed My.cnf config myCnfConfig configparser.ConfigIni // The host of the MySQL root user MySQLRootHost string // TDEPasswordSecretName refers to the name of the secret that stores the password used for Transparent Data Encryption (TDE) TDEPasswordSecretName string // DataNodeInitialRestart indicates if the data nodes need to perform a initial restart DataNodeInitialRestart bool } // parseInt32 parses the given string into an Int32 func parseInt32(strValue string) int32 { value, err := strconv.ParseInt(strValue, 10, 32) if err != nil { debug.Panic(fmt.Sprintf("parseUint32 failed to parse %s : %s", strValue, err.Error())) } return int32(value) } // parseBool parses the given string into an Bool func parseBool(strValue string) bool { value, err := strconv.ParseBool(strValue) if err != nil { debug.Panic(fmt.Sprintf("parseBool failed to parse %s : %s", strValue, err.Error())) } return value } // NewConfigSummary creates a new ConfigSummary with the information extracted from the config map. func NewConfigSummary(configMapData map[string]string) (*ConfigSummary, error) { config, err := configparser.ParseString(configMapData[constants.ConfigIniKey]) if err != nil { // Should never happen as the operator generated the config.ini return nil, debug.InternalError(err) } cs := &ConfigSummary{ NdbClusterGeneration: int64(parseInt32(configMapData[constants.NdbClusterGeneration])), MySQLClusterConfigVersion: parseInt32( config.GetValueFromSection("system", "ConfigGenerationNumber")), NumOfManagementNodes: int32(config.GetNumberOfSections("ndb_mgmd")), NumOfDataNodes: int32(config.GetNumberOfSections("ndbd")), NumOfMySQLServers: parseInt32(configMapData[constants.NumOfMySQLServers]), NumOfMySQLServerSlots: int32(config.GetNumberOfSections("mysqld")), NumOfFreeApiSlots: int32(config.GetNumberOfSections("api")), RedundancyLevel: parseInt32( config.GetValueFromSection("ndbd default", "NoOfReplicas")), MySQLLoadBalancer: parseBool(configMapData[constants.MySQLLoadBalancer]), ManagementLoadBalancer: parseBool(configMapData[constants.ManagementLoadBalancer]), defaultNdbdSection: config.GetSection("ndbd default"), defaultMgmdSection: config.GetSection("mgmd default"), MySQLRootHost: configMapData[constants.MySQLRootHost], TDEPasswordSecretName: configMapData[constants.TDEPasswordSecretName], DataNodeInitialRestart: parseBool(configMapData[constants.DataNodeInitialRestart]), } // Update MySQL Config details if it exists mysqlConfigString := configMapData[constants.MySQLConfigKey] if mysqlConfigString != "" { cs.myCnfConfig, err = configparser.ParseString(mysqlConfigString) if err != nil { // Should never happen as the operator generated the my.cnf return nil, debug.InternalError(err) } cs.MySQLServerConfigVersion = parseInt32( cs.myCnfConfig.GetValueFromSection("header", "ConfigVersion")) } return cs, nil } // MySQLClusterConfigNeedsUpdate checks if the config of the MySQL Cluster needs to be updated. func (cs *ConfigSummary) MySQLClusterConfigNeedsUpdate(nc *v1.NdbCluster) (needsUpdate bool) { newNdbdConfig := nc.Spec.DataNode.Config // Operator sets some default config parameters - take them into account when comparing configs. totalNdbdConfig := len(newNdbdConfig) + numOfOperatorSetConfigs // Add a count for EncryptedFileSystem if TDE is enabled if cs.TDEPasswordSecretName != "" { totalNdbdConfig = totalNdbdConfig + 1 } // Check if the default ndbd section has been updated if totalNdbdConfig != len(cs.defaultNdbdSection) { // A config has been added (or) removed from default ndbd section return true } // Check if all configs exist and their value has not changed // TODO: Compare with actual values from the DataNodes for configKey, configValue := range newNdbdConfig { if value, exists := cs.defaultNdbdSection.GetValue(configKey); !exists || value != configValue.String() { // Either the config doesn't exist or the value has been changed return true } } // Check if the data nodes are being added if cs.NumOfDataNodes < nc.Spec.DataNode.NodeCount { return true } // Check if there is a change in the number of MySQL server // slots or number of free api slots. if cs.NumOfMySQLServerSlots != GetNumOfSectionsRequiredForMySQLServers(nc) { return true } // Check if there is a change in the TDE password if cs.TDEPasswordSecretName != GetTDESecretName(nc) { // If the field is unset in new or old config. Then the EncryptedFileSystem config parameter // needs to be added/deleted from the config.ini if cs.TDEPasswordSecretName == "" || GetTDESecretName(nc) == "" { return true } } // Check if more freeAPISlots slots are being declared by the new spec. // Note that the config will have an extra API slot dedicated for use by the NDB Operator if cs.NumOfFreeApiSlots != nc.Spec.FreeAPISlots+1 { // Number of free slots have been changed. Regenerate config and apply it. return true } // Check if the default mgmd section has been updated if nc.Spec.ManagementNode != nil { newMgmdConfig := nc.Spec.ManagementNode.Config if len(newMgmdConfig) != len(cs.defaultMgmdSection) { // A config has been added (or) removed from default mgmd section return true } // Check if all configs exist and their value has not changed for configKey, configValue := range newMgmdConfig { if value, exists := cs.defaultMgmdSection.GetValue(configKey); !exists || value != configValue.String() { // Either the config doesn't exist or the value has been changed return true } } } // No update required to the MySQL Cluster config. return false } // MySQLCnfNeedsUpdate checks if the my.cnf config stored in the configMap needs to be updated func (cs *ConfigSummary) MySQLCnfNeedsUpdate(nc *v1.NdbCluster) (needsUpdate bool, err error) { myCnf := nc.GetMySQLCnf() if cs == nil { // NdbCluster resource created for the first time. // Need to update my.cnf if it is specified in the spec. return myCnf != "", nil } if myCnf == "" { // myCnf is empty. // Update required if it previously had a value return cs.myCnfConfig != nil, nil } myCnfConfig, err := configparser.ParseString(myCnf) if err != nil { // Cannot happen as it is already validated return false, debug.InternalError(err) } // Compare the configs and return if the config needs an update return !cs.myCnfConfig.IsEqual(myCnfConfig), nil }