github.com/Axway/agent-sdk@v1.1.101/pkg/cmd/properties/properties.go (about) 1 package properties 2 3 import ( 4 "encoding/json" 5 goflag "flag" 6 "fmt" 7 "os" 8 "regexp" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/Axway/agent-sdk/pkg/util" 14 "github.com/Axway/agent-sdk/pkg/util/errors" 15 "github.com/Axway/agent-sdk/pkg/util/log" 16 "github.com/spf13/cobra" 17 flag "github.com/spf13/pflag" 18 "github.com/spf13/viper" 19 ) 20 21 // ErrInvalidSecretReference - Error for parsing properties with secret reference 22 var ErrInvalidSecretReference = errors.Newf(1411, "invalid secret reference - %s, please check the value for %s config") 23 24 // QA EnvVars 25 const qaEnforceDurationLowerLimit = "QA_ENFORCE_DURATION_LOWER_LIMIT" 26 27 const ( 28 lowerLimitName = "%s-lowerLimit" 29 upperLimitName = "%s-upperLimit" 30 qaVarNameFormat = "qa.%s" 31 ) 32 33 // SecretPropertyResolver - interface for resolving property values with secret references 34 type SecretPropertyResolver interface { 35 ResolveSecret(secretRef string) (string, error) 36 } 37 38 // Properties - Root Command Properties interface for all configs to use for adding and parsing values 39 type Properties interface { 40 // Methods for adding yaml properties and command flag 41 AddStringProperty(name string, defaultVal string, description string) 42 AddStringPersistentFlag(name string, defaultVal string, description string) 43 AddStringFlag(name string, description string) 44 AddDurationProperty(name string, defaultVal time.Duration, description string, options ...DurationOpt) 45 AddIntProperty(name string, defaultVal int, description string, options ...IntOpt) 46 AddBoolProperty(name string, defaultVal bool, description string) 47 AddBoolFlag(name, description string) 48 AddStringSliceProperty(name string, defaultVal []string, description string) 49 AddObjectSliceProperty(name string, objectPropertyNames []string) 50 51 // Methods to get the configured properties 52 StringPropertyValue(name string) string 53 StringFlagValue(name string) (bool, string) 54 DurationPropertyValue(name string) time.Duration 55 IntPropertyValue(name string) int 56 BoolPropertyValue(name string) bool 57 BoolPropertyValueOrTrue(name string) bool // Use this method when the default value, no config given, is true 58 BoolFlagValue(name string) bool 59 StringSlicePropertyValue(name string) []string 60 ObjectSlicePropertyValue(name string) []map[string]interface{} 61 62 // Methods to set a property 63 SetStringFlagValue(name string, value string) 64 65 // Log Properties 66 MaskValues(name string) 67 DebugLogProperties() 68 SetAliasKeyPrefix(aliasKeyPrefix string) 69 } 70 71 type durationOpts struct { 72 lower time.Duration 73 upper time.Duration 74 qaOverride bool 75 } 76 77 // DurationOpt are duration range options passed into AddDurationProperty 78 type DurationOpt func(prop *durationOpts) 79 80 // WithLowerLimit - lower limit of the duration range 81 func WithLowerLimit(lower time.Duration) DurationOpt { 82 return func(d *durationOpts) { 83 d.lower = lower 84 } 85 } 86 87 // WithUpperLimit - upper limit of the duration range 88 func WithUpperLimit(upper time.Duration) DurationOpt { 89 return func(d *durationOpts) { 90 d.upper = upper 91 } 92 } 93 94 // WithQAOverride - set to true to allow this setting to be overwritten with a qa env var 95 func WithQAOverride() DurationOpt { 96 return func(d *durationOpts) { 97 d.qaOverride = true 98 } 99 } 100 101 type intOpts struct { 102 lower int 103 upper int 104 } 105 106 // DurationOpt are duration range options passed into AddDurationProperty 107 type IntOpt func(prop *intOpts) 108 109 // WithLowerLimitInt - lower limit of the int range 110 func WithLowerLimitInt(lower int) IntOpt { 111 return func(d *intOpts) { 112 d.lower = lower 113 } 114 } 115 116 // WithUpperLimitInt - upper limit of the int range 117 func WithUpperLimitInt(upper int) IntOpt { 118 return func(d *intOpts) { 119 d.upper = upper 120 } 121 } 122 123 var aliasKeyPrefix string 124 125 type properties struct { 126 Properties 127 rootCmd *cobra.Command 128 envIntfArrayPropValues map[string][]map[string]interface{} 129 envIntfArrayPropertyKeys map[string]map[string]bool 130 secretResolver SecretPropertyResolver 131 flattenedProperties map[string]string 132 } 133 134 var expansionRegEx *regexp.Regexp 135 136 func init() { 137 expansionRegEx = regexp.MustCompile(`\$\{(\w+):(.*)\}|\$\{(\w+)\}`) 138 } 139 140 // NewProperties - Creates a new Properties struct 141 func NewProperties(rootCmd *cobra.Command) Properties { 142 cmdprops := &properties{ 143 rootCmd: rootCmd, 144 envIntfArrayPropertyKeys: make(map[string]map[string]bool), 145 flattenedProperties: make(map[string]string), 146 } 147 148 return cmdprops 149 } 150 151 // NewPropertiesWithSecretResolver - Creates a new Properties struct with secret resolver for string property/flag 152 func NewPropertiesWithSecretResolver(rootCmd *cobra.Command, secretResolver SecretPropertyResolver) Properties { 153 cmdprops := &properties{ 154 rootCmd: rootCmd, 155 envIntfArrayPropertyKeys: make(map[string]map[string]bool), 156 flattenedProperties: make(map[string]string), 157 secretResolver: secretResolver, 158 } 159 160 return cmdprops 161 } 162 163 // SetAliasKeyPrefix - 164 func SetAliasKeyPrefix(keyPrefix string) { 165 aliasKeyPrefix = keyPrefix 166 } 167 168 // GetAliasKeyPrefix - 169 func GetAliasKeyPrefix() string { 170 return aliasKeyPrefix 171 } 172 173 func (p *properties) bindOrPanic(key string, flg *flag.Flag) { 174 if err := viper.BindPFlag(key, flg); err != nil { 175 panic(err) 176 } 177 if aliasKeyPrefix != "" { 178 if err := viper.BindPFlag(aliasKeyPrefix+"."+key, flg); err != nil { 179 panic(err) 180 } 181 } 182 } 183 184 func (p *properties) AddObjectSliceProperty(envPrefix string, intfPropertyNames []string) { 185 envPrefix = strings.ReplaceAll(envPrefix, ".", "_") 186 envPrefix = strings.ToUpper(envPrefix) 187 if !strings.HasSuffix(envPrefix, "_") { 188 envPrefix += "_" 189 } 190 iPropNames := make(map[string]bool) 191 for _, propName := range intfPropertyNames { 192 iPropNames[propName] = true 193 } 194 195 p.envIntfArrayPropertyKeys[envPrefix] = iPropNames 196 } 197 198 func (p *properties) AddStringProperty(name string, defaultVal string, description string) { 199 if p.rootCmd != nil { 200 flagName := p.nameToFlagName(name) 201 p.rootCmd.Flags().String(flagName, defaultVal, description) 202 p.bindOrPanic(name, p.rootCmd.Flags().Lookup(flagName)) 203 p.rootCmd.Flags().MarkHidden(flagName) 204 } 205 } 206 207 func (p *properties) AddStringPersistentFlag(flagName string, defaultVal string, description string) { 208 if p.rootCmd != nil { 209 flg := goflag.CommandLine.Lookup(flagName) 210 if flg == nil { 211 goflag.CommandLine.String(flagName, "", description) 212 flg = goflag.CommandLine.Lookup(flagName) 213 } 214 215 p.rootCmd.PersistentFlags().AddGoFlag(flg) 216 } 217 } 218 219 func (p *properties) AddStringFlag(flagName string, description string) { 220 if p.rootCmd != nil { 221 p.rootCmd.Flags().String(flagName, "", description) 222 } 223 } 224 225 func (p *properties) SetStringFlagValue(flagName string, value string) { 226 if p.rootCmd != nil { 227 p.rootCmd.Flags().Set(flagName, value) 228 } 229 } 230 231 func (p *properties) AddStringSliceProperty(name string, defaultVal []string, description string) { 232 if p.rootCmd != nil { 233 flagName := p.nameToFlagName(name) 234 p.rootCmd.Flags().StringSlice(flagName, defaultVal, description) 235 p.bindOrPanic(name, p.rootCmd.Flags().Lookup(flagName)) 236 p.rootCmd.Flags().MarkHidden(flagName) 237 } 238 } 239 240 func (p *properties) AddDurationProperty(name string, defaultVal time.Duration, description string, options ...DurationOpt) { 241 if p.rootCmd != nil { 242 flagName := p.nameToFlagName(name) 243 244 opts := &durationOpts{ 245 lower: time.Second * 30, 246 } 247 248 // validate if WithLowerLimit and WithUpperLimit were called 249 for _, option := range options { 250 option(opts) 251 } 252 253 p.configureUpperAndLowerLimits(defaultVal, opts, flagName) 254 255 if opts.qaOverride { 256 qaName := fmt.Sprintf(qaVarNameFormat, name) 257 qaFlagName := p.nameToFlagName(qaName) 258 p.rootCmd.Flags().Duration(qaFlagName, -1*time.Second, "") 259 p.bindOrPanic(qaName, p.rootCmd.Flags().Lookup(qaFlagName)) 260 p.rootCmd.Flags().MarkHidden(qaFlagName) 261 } 262 263 p.rootCmd.Flags().Duration(flagName, defaultVal, description) 264 p.bindOrPanic(name, p.rootCmd.Flags().Lookup(flagName)) 265 p.rootCmd.Flags().MarkHidden(flagName) 266 } 267 } 268 269 func (p *properties) configureUpperAndLowerLimitsInt(defaultVal int, limits *intOpts, flagName string) { 270 lowerLimitFlag := fmt.Sprintf(lowerLimitName, flagName) 271 upperLimitFlag := fmt.Sprintf(upperLimitName, flagName) 272 273 // set lower limit 274 if limits.lower > -1 { 275 if defaultVal < limits.lower { 276 panic(fmt.Errorf("default value (%v) can not be smaller than lower limit (%v) for %s", defaultVal, limits.lower, flagName)) 277 } 278 p.rootCmd.Flags().Int(lowerLimitFlag, limits.lower, fmt.Sprintf("lower limit flag for configuration %s", flagName)) 279 p.rootCmd.Flags().MarkHidden(lowerLimitFlag) 280 } 281 282 // set upper limit if greater than zero 283 if limits.upper > -1 { 284 p.rootCmd.Flags().Int(upperLimitFlag, limits.upper, fmt.Sprintf("upper limit flag for configuration %s", flagName)) 285 p.rootCmd.Flags().MarkHidden(upperLimitFlag) 286 // check for valid upper and lower limits 287 if limits.upper < limits.lower { 288 panic(fmt.Errorf("upper limit (%v) can not be smaller than lower limit (%v) for %s", limits.upper, limits.lower, flagName)) 289 } 290 if defaultVal > limits.upper { 291 panic(fmt.Errorf("default value (%v) can not be larger than upper limit (%v) for %s", defaultVal, limits.upper, flagName)) 292 } 293 } 294 } 295 296 func (p *properties) configureUpperAndLowerLimits(defaultVal time.Duration, limits *durationOpts, flagName string) { 297 lowerLimitFlag := fmt.Sprintf(lowerLimitName, flagName) 298 upperLimitFlag := fmt.Sprintf(upperLimitName, flagName) 299 300 // set lower limit 301 if defaultVal < limits.lower { 302 panic(fmt.Errorf("default value (%s) can not be smaller than lower limit (%s) for %s", defaultVal, limits.lower, flagName)) 303 } 304 p.rootCmd.Flags().Duration(lowerLimitFlag, limits.lower, fmt.Sprintf("lower limit flag for configuration %s", flagName)) 305 p.rootCmd.Flags().MarkHidden(lowerLimitFlag) 306 307 // set upper limit if greater than zero 308 if limits.upper > 0 { 309 p.rootCmd.Flags().Duration(upperLimitFlag, limits.upper, fmt.Sprintf("upper limit flag for configuration %s", flagName)) 310 p.rootCmd.Flags().MarkHidden(upperLimitFlag) 311 // check for valid upper and lower limits 312 if limits.upper < limits.lower { 313 panic(fmt.Errorf("upper limit (%v) can not be smaller than lower limit (%v) for %s", limits.upper, limits.lower, flagName)) 314 } 315 if defaultVal > limits.upper { 316 panic(fmt.Errorf("default value (%v) can not be larger than upper limit (%v) for %s", defaultVal, limits.upper, flagName)) 317 } 318 } 319 } 320 321 func (p *properties) AddIntProperty(name string, defaultVal int, description string, options ...IntOpt) { 322 if p.rootCmd != nil { 323 flagName := p.nameToFlagName(name) 324 325 opts := &intOpts{ 326 lower: -1, 327 upper: -1, 328 } 329 330 // validate if WithLowerLimit and WithUpperLimit were called 331 for _, option := range options { 332 option(opts) 333 } 334 335 p.configureUpperAndLowerLimitsInt(defaultVal, opts, flagName) 336 337 p.rootCmd.Flags().Int(flagName, defaultVal, description) 338 p.bindOrPanic(name, p.rootCmd.Flags().Lookup(flagName)) 339 p.rootCmd.Flags().MarkHidden(flagName) 340 } 341 } 342 343 func (p *properties) AddBoolProperty(name string, defaultVal bool, description string) { 344 if p.rootCmd != nil { 345 flagName := p.nameToFlagName(name) 346 p.rootCmd.Flags().Bool(flagName, defaultVal, description) 347 p.bindOrPanic(name, p.rootCmd.Flags().Lookup(flagName)) 348 p.rootCmd.Flags().MarkHidden(flagName) 349 } 350 } 351 352 func (p *properties) AddBoolFlag(flagName string, description string) { 353 if p.rootCmd != nil { 354 p.rootCmd.Flags().Bool(flagName, false, description) 355 } 356 } 357 358 func (p *properties) StringSlicePropertyValue(name string) []string { 359 val := viper.Get(name) 360 361 // special check to differentiate between yaml and commandline parsing. For commandline, must 362 // turn it into an array ourselves 363 switch val := val.(type) { 364 case string: 365 p.addPropertyToFlatMap(name, val) 366 return p.convertStringToSlice(fmt.Sprintf("%v", viper.Get(name))) 367 default: 368 return viper.GetStringSlice(name) 369 } 370 } 371 372 func (p *properties) convertStringToSlice(value string) []string { 373 slc := strings.Split(value, ",") 374 for i := range slc { 375 slc[i] = strings.TrimSpace(slc[i]) 376 } 377 return slc 378 } 379 380 func (p *properties) parseStringValueForKey(key string) string { 381 s := strings.TrimSpace(viper.GetString(key)) 382 if strings.Index(s, "$") == 0 { 383 matches := expansionRegEx.FindAllSubmatch([]byte(s), -1) 384 if len(matches) > 0 { 385 expSlice := matches[0] 386 if len(expSlice) > 2 { 387 s = p.parseSlice(s, expSlice) 388 } 389 } 390 } 391 return s 392 } 393 394 func (p *properties) parseSlice(s string, expSlice [][]byte) string { 395 rtnS := s 396 envVar := string(expSlice[1]) 397 defaultVal := "" 398 if envVar == "" { 399 if len(expSlice) >= 4 { 400 envVar = strings.Trim(string(expSlice[3]), "\"") 401 } 402 } else { 403 if len(expSlice) >= 3 { 404 defaultVal = strings.Trim(string(expSlice[2]), "\"") 405 } 406 } 407 408 if envVar != "" { 409 rtnS = os.Getenv(envVar) 410 if rtnS == "" && defaultVal != "" { 411 rtnS = defaultVal 412 } 413 } 414 415 return rtnS 416 } 417 418 func (p *properties) parseStringValue(key string) string { 419 var s string 420 if aliasKeyPrefix != "" { 421 s = p.parseStringValueForKey(aliasKeyPrefix + "." + key) 422 } 423 // If no alias or no value parsed for alias key 424 if s == "" { 425 s = p.parseStringValueForKey(key) 426 } 427 return s 428 } 429 430 func (p *properties) resolveSecretReference(cfgName, cfgValue string) string { 431 if p.secretResolver != nil { 432 secretValue, err := p.secretResolver.ResolveSecret(cfgValue) 433 if err != nil { 434 // only log the error and return empty string, 435 // validation on config triggers the agent to return error to root command 436 log.Error(ErrInvalidSecretReference.FormatError(err.Error(), cfgName)) 437 cfgValue = "" 438 } 439 if secretValue != "" { 440 cfgValue = secretValue 441 } 442 } 443 return cfgValue 444 } 445 446 func (p *properties) StringPropertyValue(name string) string { 447 s := p.parseStringValue(name) 448 s = p.resolveSecretReference(name, s) 449 p.addPropertyToFlatMap(name, s) 450 return s 451 } 452 453 func (p *properties) StringFlagValue(name string) (bool, string) { 454 flag := p.rootCmd.Flag(name) 455 if flag == nil || flag.Value.String() == "" { 456 return false, "" 457 } 458 fv := flag.Value.String() 459 fv = p.resolveSecretReference(name, fv) 460 p.addPropertyToFlatMap(name, fv) 461 return true, fv 462 } 463 464 func (p *properties) DurationPropertyValue(name string) time.Duration { 465 s := p.parseStringValue(name) 466 d, _ := time.ParseDuration(s) 467 468 // check if the duration has a qa equivalent that should be used 469 if qaVal := p.getQADuration(name); qaVal > 0 { 470 return qaVal 471 } 472 473 if !isDurationLowerLimitEnforced() { 474 return d 475 } 476 477 flagName := p.nameToFlagName(name) 478 flag := p.rootCmd.Flag(flagName) 479 lowerLimit, upperLimit := p.getDurationLimits(flagName) 480 defaultVal, _ := time.ParseDuration(flag.DefValue) 481 482 if lowerLimit > 0 && d < lowerLimit { 483 d = defaultVal 484 log.Warnf("Configuration %s has been set to the default value of %s. Please update this value greater than the lower limit of %s", name, d, lowerLimit) 485 } else if upperLimit > 0 && d > upperLimit { 486 d = defaultVal 487 log.Warnf("Configuration %s has been set to the default value of %s. Please update this value lower than the upper limit of %s", name, d, upperLimit) 488 } 489 490 p.addPropertyToFlatMap(name, s) 491 return d 492 } 493 494 // getQADuration - returns the qa variables duration 495 func (p *properties) getQADuration(name string) time.Duration { 496 qaName := fmt.Sprintf(qaVarNameFormat, name) 497 qaVal := -1 * time.Second 498 if s := p.parseStringValue(qaName); s != "" { 499 qaVal, _ = time.ParseDuration(s) 500 } 501 502 return qaVal 503 } 504 505 // getDurationLimits - returns the duration limits, negative number returned for unset 506 func (p *properties) getDurationLimits(flagName string) (time.Duration, time.Duration) { 507 lowerLimitFlag := p.rootCmd.Flag(fmt.Sprintf(lowerLimitName, flagName)) 508 upperLimitFlag := p.rootCmd.Flag(fmt.Sprintf(upperLimitName, flagName)) 509 510 lowerLimit := -1 * time.Second 511 upperLimit := -1 * time.Second 512 if lowerLimitFlag != nil { 513 lowerLimit, _ = time.ParseDuration(lowerLimitFlag.Value.String()) 514 } 515 if upperLimitFlag != nil { 516 upperLimit, _ = time.ParseDuration(upperLimitFlag.Value.String()) 517 } 518 519 return lowerLimit, upperLimit 520 } 521 522 func (p *properties) IntPropertyValue(name string) int { 523 s := p.parseStringValue(name) 524 i, _ := strconv.Atoi(s) 525 526 p.addPropertyToFlatMap(name, s) 527 return i 528 } 529 530 func (p *properties) BoolPropertyValue(name string) bool { 531 return p.boolPropertyValue(name, false) 532 } 533 534 func (p *properties) BoolPropertyValueOrTrue(name string) bool { 535 return p.boolPropertyValue(name, true) 536 } 537 538 func (p *properties) boolPropertyValue(name string, defVal bool) bool { 539 s := p.parseStringValue(name) 540 if s == "" { 541 return defVal 542 } 543 b, _ := strconv.ParseBool(s) 544 545 p.addPropertyToFlatMap(name, s) 546 return b 547 } 548 549 func (p *properties) BoolFlagValue(name string) bool { 550 flag := p.rootCmd.Flag(name) 551 if flag == nil { 552 return false 553 } 554 if flag.Value.String() == "true" { 555 return true 556 } 557 return false 558 } 559 560 func (p *properties) nameToFlagName(name string) (flagName string) { 561 parts := strings.Split(name, ".") 562 flagName = parts[0] 563 for _, part := range parts[1:] { 564 flagName += strings.Title(part) 565 } 566 return 567 } 568 569 // String array containing any sensitive data that needs to be masked with "*" (asterisks) 570 // Add any sensitive data here using flattened key format 571 var maskValues = make([]string, 0) 572 573 func (p *properties) addPropertyToFlatMap(key, value string) { 574 for _, maskValue := range maskValues { 575 match, _ := regexp.MatchString("\\b"+strings.TrimSpace(maskValue)+"\\b", key) 576 if match { 577 value = util.MaskValue(value) 578 } 579 } 580 p.flattenedProperties[key] = value 581 } 582 583 func (p *properties) MaskValues(maskedKeys string) { 584 maskValues = strings.Split(maskedKeys, ",") 585 } 586 587 func (p *properties) DebugLogProperties() { 588 data, _ := json.MarshalIndent(p.flattenedProperties, "", " ") 589 log.Debugf("%s\n", data) 590 } 591 592 // enforce the lower limit by default 593 func isDurationLowerLimitEnforced() bool { 594 if val := os.Getenv(qaEnforceDurationLowerLimit); val != "" { 595 ret, err := strconv.ParseBool(val) 596 if err != nil { 597 log.Errorf("Invalid value %s for env variable %s", val, qaEnforceDurationLowerLimit) 598 return true 599 } 600 return ret 601 } 602 return true 603 } 604 605 // ObjectSlicePropertyValue 606 func (p *properties) ObjectSlicePropertyValue(name string) []map[string]interface{} { 607 p.readEnv() 608 name = strings.ReplaceAll(name, ".", "_") 609 name = strings.ToUpper(name) 610 if !strings.HasSuffix(name, "_") { 611 name += "_" 612 } 613 return p.envIntfArrayPropValues[name] 614 } 615 616 func (p *properties) readEnv() { 617 if p.envIntfArrayPropValues != nil { 618 return 619 } 620 621 p.envIntfArrayPropValues = make(map[string][]map[string]interface{}) 622 envVarsMap := p.parseEnvPropertiesFlatMap() 623 624 for prefix, eVals := range envVarsMap { 625 propValues, ok := p.envIntfArrayPropValues[prefix] 626 if !ok { 627 propValues = make([]map[string]interface{}, 0) 628 } 629 630 for _, val := range eVals { 631 v := p.convertFlatMap(val) 632 propValues = append(propValues, v) 633 } 634 p.envIntfArrayPropValues[prefix] = propValues 635 } 636 } 637 638 func (p *properties) convertFlatMap(flatMap map[string]string) map[string]interface{} { 639 m := make(map[string]interface{}) 640 for key, val := range flatMap { 641 ok := strings.Contains(key, ".") 642 if !ok { 643 m[key] = val 644 } else { 645 k := strings.Split(key, ".") 646 p.setChildMapProperty(m, k, val) 647 } 648 } 649 return m 650 } 651 652 func (p *properties) setChildMapProperty(parentMap map[string]interface{}, childKeys []string, val string) { 653 cm, ok := parentMap[childKeys[0]] 654 if !ok { 655 cm = make(map[string]interface{}) 656 } 657 658 childMap, ok := cm.(map[string]interface{}) 659 if ok { 660 if len(childKeys) > 2 { 661 p.setChildMapProperty(childMap, childKeys[1:], val) 662 } else { 663 childMap[childKeys[1]] = val 664 } 665 parentMap[childKeys[0]] = cm 666 } 667 668 } 669 670 func (p *properties) parseEnvPropertiesFlatMap() map[string]map[string]map[string]string { 671 envVarsMap := make(map[string]map[string]map[string]string) 672 for _, element := range os.Environ() { 673 variable := strings.SplitN(element, "=", 2) 674 name := variable[0] 675 val := variable[1] 676 for prefix, iPropNames := range p.envIntfArrayPropertyKeys { 677 p.fillEnvVarsMap(name, val, prefix, iPropNames, envVarsMap) 678 } 679 } 680 return envVarsMap 681 } 682 683 func (p *properties) fillEnvVarsMap(name string, val string, prefix string, iPropNames map[string]bool, envVarsMap map[string]map[string]map[string]string) { 684 if strings.HasPrefix(name, prefix) { 685 n := strings.ReplaceAll(name, prefix, "") 686 elements := strings.Split(name, "_") 687 lastSuffix := elements[len(elements)-1] 688 _, ok := envVarsMap[prefix] 689 690 if !ok { 691 envVarsMap[prefix] = make(map[string]map[string]string) 692 } 693 694 m, ok := envVarsMap[prefix][lastSuffix] 695 if !ok { 696 m = make(map[string]string) 697 } 698 for pName := range iPropNames { 699 propName := strings.ReplaceAll(pName, ".", "_") 700 propName = strings.ToUpper(propName) 701 if strings.HasPrefix(n, propName) { 702 m[pName] = val 703 envVarsMap[prefix][lastSuffix] = m 704 } 705 } 706 } 707 }