diff --git a/asconfig/as_setconfig.go b/asconfig/as_setconfig.go index 3f2b1b2..dab2c5c 100644 --- a/asconfig/as_setconfig.go +++ b/asconfig/as_setconfig.go @@ -23,8 +23,8 @@ const ( // convertValueToString converts the value of a config to a string. // only string type can be used to populate set-config commands with values. -func convertValueToString(v1 map[Operation]interface{}) (map[Operation][]string, error) { - valueMap := make(map[Operation][]string) +func convertValueToString(v1 map[OpType]interface{}) (map[OpType][]string, error) { + valueMap := make(map[OpType][]string) for k, v := range v1 { values := make([]string, 0) @@ -55,7 +55,7 @@ func convertValueToString(v1 map[Operation]interface{}) (map[Operation][]string, } // createSetConfigServiceCmdList creates set-config commands for service context. -func createSetConfigServiceCmdList(tokens []string, operationValueMap map[Operation][]string) []string { +func createSetConfigServiceCmdList(tokens []string, operationValueMap map[OpType][]string) []string { val := operationValueMap[Update] cmdList := make([]string, 0, len(val)) cmd := cmdSetConfigService @@ -75,7 +75,7 @@ func createSetConfigServiceCmdList(tokens []string, operationValueMap map[Operat } // createSetConfigNetworkCmdList creates set-config commands for network context. -func createSetConfigNetworkCmdList(tokens []string, operationValueMap map[Operation][]string) []string { +func createSetConfigNetworkCmdList(tokens []string, operationValueMap map[OpType][]string) []string { val := operationValueMap[Update] cmdList := make([]string, 0, len(val)) cmd := cmdSetConfigNetwork @@ -95,7 +95,7 @@ func createSetConfigNetworkCmdList(tokens []string, operationValueMap map[Operat } // createSetConfigSecurityCmdList creates set-config commands for security context. -func createSetConfigSecurityCmdList(tokens []string, operationValueMap map[Operation][]string) []string { +func createSetConfigSecurityCmdList(tokens []string, operationValueMap map[OpType][]string) []string { cmdList := make([]string, 0, len(operationValueMap)) cmd := cmdSetConfigSecurity @@ -179,7 +179,7 @@ func createSetConfigSecurityCmdList(tokens []string, operationValueMap map[Opera } // createSetConfigNamespaceCmdList creates set-config commands for namespace context. -func createSetConfigNamespaceCmdList(tokens []string, operationValueMap map[Operation][]string) []string { +func createSetConfigNamespaceCmdList(tokens []string, operationValueMap map[OpType][]string) []string { val := operationValueMap[Update] cmdList := make([]string, 0, len(val)) cmd := cmdSetConfigNamespace @@ -218,7 +218,7 @@ func createSetConfigNamespaceCmdList(tokens []string, operationValueMap map[Oper } // createLogSetCmdList creates log-set commands for logging context. -func createLogSetCmdList(tokens []string, operationValueMap map[Operation][]string, +func createLogSetCmdList(tokens []string, operationValueMap map[OpType][]string, conn deployment.ASConnInterface, aerospikePolicy *aero.ClientPolicy) ([]string, error) { val := operationValueMap[Update] cmdList := make([]string, 0, len(val)) @@ -251,7 +251,7 @@ func createLogSetCmdList(tokens []string, operationValueMap map[Operation][]stri } // createSetConfigXDRCmdList creates set-config commands for XDR context. -func createSetConfigXDRCmdList(tokens []string, operationValueMap map[Operation][]string) []string { +func createSetConfigXDRCmdList(tokens []string, operationValueMap map[OpType][]string) []string { cmdList := make([]string, 0, len(operationValueMap)) cmd := cmdSetConfigXDR prevToken := "" @@ -445,3 +445,98 @@ func rearrangeConfigMap(log logr.Logger, configMap DynamicConfigMap) []string { return finalList } + +func ValidConfigOperations() []OpType { + return []OpType{Add, Update, Remove} +} + +func (o OpType) Validate() error { + switch o { + case Add, Update, Remove: + return nil + } + + return fmt.Errorf("invalid operation type: %s, Valid operations: %v", o, ValidConfigOperations()) +} + +type ConfigOperation struct { + Operation OpType `json:"op"` + Context string `json:"context"` + Config string `json:"config"` + Value string `json:"value,omitempty"` +} + +func (p ConfigOperation) Validate() error { + return p.Operation.Validate() +} + +func CreateConfigSetCmdsUsingPatch( + configMap map[string]interface{}, conn *deployment.ASConn, aerospikePolicy *aero.ClientPolicy, version string, +) ([]string, error) { + conf, err := NewMapAsConfig(conn.Log, configMap) + if err != nil { + return nil, err + } + + flatConf := conf.GetFlatMap() + asConfChange := make(DynamicConfigMap) + + for k, v := range *flatConf { + if strings.HasSuffix(k, sep+KeyName) || strings.HasSuffix(k, sep+keyIndex) { + // skip namespace, dc, etc names + continue + } + + valueMap := make(map[OpType]interface{}) + valueMap[Update] = v + asConfChange[k] = valueMap + } + + isDynamic, err := IsAllDynamicConfig(conn.Log, asConfChange, version) + if err != nil { + return nil, err + } + + if !isDynamic { + return nil, fmt.Errorf("static field has been changed, cannot change config dynamically") + } + + return CreateSetConfigCmdList(conn.Log, asConfChange, conn, aerospikePolicy) +} + +func CreateConfigSetCmdsUsingOperation( + confOp ConfigOperation, conn *deployment.ASConn, aerospikePolicy *aero.ClientPolicy, version string, +) ([]string, error) { + if err := confOp.Validate(); err != nil { + return nil, err + } + // Context: security.log, Config:report-data-op, Value:test set + // Map: security.log.report-data-op:map[remove:"test set"] + path := confOp.Context + sep + confOp.Config + value := confOp.Value + + if confOp.Config == KeyName { + if confOp.Operation == Update { + return nil, fmt.Errorf("cannot update name field") + } + // Context: namespaces, Config: name, value: ns1 + // Map: namespaces.{testMem}.name:map[remove:testMem] + path = confOp.Context + sep + string(SectionNameStartChar) + value + string(SectionNameEndChar) + sep + KeyName + } + + asConfChange := make(DynamicConfigMap) + valueMap := make(map[OpType]interface{}) + valueMap[confOp.Operation] = value + asConfChange[path] = valueMap + + isDynamic, err := IsAllDynamicConfig(conn.Log, asConfChange, version) + if err != nil { + return nil, err + } + + if !isDynamic { + return nil, fmt.Errorf("static field has been changed, cannot change config dynamically") + } + + return CreateSetConfigCmdList(conn.Log, asConfChange, conn, aerospikePolicy) +} diff --git a/asconfig/constants.go b/asconfig/constants.go index 80462b4..3dd2cb3 100644 --- a/asconfig/constants.go +++ b/asconfig/constants.go @@ -1,6 +1,6 @@ package asconfig -type Operation string +type OpType string // All the aerospike config related keys const ( @@ -33,8 +33,8 @@ const ( equal = "=" colon = ":" - // Enum values for Operation - Add Operation = "add" - Remove Operation = "remove" - Update Operation = "update" + // Enum values for OpType + Add OpType = "add" + Remove OpType = "remove" + Update OpType = "update" ) diff --git a/asconfig/schema.go b/asconfig/schema.go index 76d0231..0d5418c 100644 --- a/asconfig/schema.go +++ b/asconfig/schema.go @@ -195,8 +195,8 @@ func IsAllDynamicConfig(log logr.Logger, configMap DynamicConfigMap, version str // isFieldDynamic returns true if the given field is dynamically configured. func isFieldDynamic(log logr.Logger, dynamic sets.Set[string], conf string, - valueMap map[Operation]interface{}) bool { - tokens := SplitKey(log, conf, ".") + valueMap map[OpType]interface{}) bool { + tokens := SplitKey(log, conf, sep) baseKey := tokens[len(tokens)-1] context := tokens[0] diff --git a/asconfig/utils.go b/asconfig/utils.go index c2911f2..3b46f26 100644 --- a/asconfig/utils.go +++ b/asconfig/utils.go @@ -546,7 +546,7 @@ func handleMissingSection(log logr.Logger, key string, desired, current Conf, d // Whole section which has "name" as key is not present in current // If token is under "{}", then it is a named section if _, okay := current[nameKeyPath]; ReCurlyBraces.MatchString(token) && !okay { - operationValueMap := make(map[Operation]interface{}) + operationValueMap := make(map[OpType]interface{}) if desiredToActual { if _, updated := d[key]; !updated { @@ -582,7 +582,7 @@ func handlePartialMissingSection(desiredKey, ver string, current Conf, d Dynamic continue } - operationValueMap := make(map[Operation]interface{}) + operationValueMap := make(map[OpType]interface{}) // If removed subsection is of type slice, then there is no default values to be set. // eg. current = security.log.report-data-op: []string{test} // desired = security: {} @@ -607,7 +607,7 @@ func handlePartialMissingSection(desiredKey, ver string, current Conf, d Dynamic } func handleSliceFields(key string, desired Conf, d DynamicConfigMap, desiredToActual bool) { - operationValueMap := make(map[Operation]interface{}) + operationValueMap := make(map[OpType]interface{}) if reflect.ValueOf(desired[key]).Kind() == reflect.Slice { if desiredToActual { @@ -623,7 +623,7 @@ func handleSliceFields(key string, desired Conf, d DynamicConfigMap, desiredToAc } func handleValueDiff(key string, desiredValue, currentValue interface{}, d DynamicConfigMap) { - operationValueMap := make(map[Operation]interface{}) + operationValueMap := make(map[OpType]interface{}) if reflect.ValueOf(desiredValue).Kind() == reflect.Slice { currentSet := sets.NewSet[string]() @@ -755,7 +755,7 @@ func ConfDiff( return nil, err } - valueMap := make(map[Operation]interface{}) + valueMap := make(map[OpType]interface{}) valueMap[Update] = getDefaultValue(defaultMap, removedConfigKey) diffs[removedConfigKey] = valueMap } @@ -1478,7 +1478,7 @@ var ReCurlyBraces = regexp.MustCompile(`^\{.*\}$`) // DynamicConfigMap is a map of config flatten keys and their operations and values // for eg: "xdr.dcs.{DC3}.node-address-ports": {Remove: []string{"1.1.2.1 3000"}} -type DynamicConfigMap map[string]map[Operation]interface{} +type DynamicConfigMap map[string]map[OpType]interface{} // SplitKey splits key by using sep // it ignores sep inside sectionNameStartChar and sectionNameEndChar