func generateOtelConfig()

in otelcollector/prom-config-validator-builder/main.go [94:268]


func generateOtelConfig(promFilePath string, outputFilePath string, otelConfigTemplatePath string) error {
	var otelConfig OtelConfig

	otelConfigFileContents, err := ioutil.ReadFile(otelConfigTemplatePath)
	if err != nil {
		return err
	}
	err = yaml.Unmarshal([]byte(otelConfigFileContents), &otelConfig)
	if err != nil {
		return err
	}

	promConfigFileContents, err := ioutil.ReadFile(promFilePath)
	if err != nil {
		return err
	}

	var prometheusConfig map[string]interface{}
	err = yaml.Unmarshal([]byte(promConfigFileContents), &prometheusConfig)
	if err != nil {
		return err
	}

	scrapeConfigs := prometheusConfig["scrape_configs"]
	if scrapeConfigs != nil {
		var sc = scrapeConfigs.([]interface{})
		for _, scrapeConfig := range sc {
			scrapeConfig := scrapeConfig.(map[interface{}]interface{})
			if scrapeConfig["relabel_configs"] != nil {
				relabelConfigs := scrapeConfig["relabel_configs"].([]interface{})
				for _, relabelConfig := range relabelConfigs {
					relabelConfig := relabelConfig.(map[interface{}]interface{})
					//replace $ with $$ for regex field
					if relabelConfig["regex"] != nil {
						// Adding this check here since regex can be boolean and the conversion will fail
						if _, isString := relabelConfig["regex"].(string); isString {
							regexString := relabelConfig["regex"].(string)
							modifiedRegexString := strings.ReplaceAll(regexString, "$$", "$")
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$", "$$")
							// Doing the below since we dont want to substitute $ with $$ for env variables NODE_NAME and NODE_IP.
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$$NODE_NAME", "${env:NODE_NAME}")
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$$NODE_IP", "${env:NODE_IP}")
							relabelConfig["regex"] = modifiedRegexString
						}
					}
					//replace $ with $$ for replacement field
					if relabelConfig["replacement"] != nil {
						replacement := relabelConfig["replacement"].(string)
						modifiedReplacementString := strings.ReplaceAll(replacement, "$$", "$")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$", "$$")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$$NODE_NAME", "${env:NODE_NAME}")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$$NODE_IP", "${env:NODE_IP}")
						relabelConfig["replacement"] = modifiedReplacementString
					}
				}
			}

			if scrapeConfig["metric_relabel_configs"] != nil {
				metricRelabelConfigs := scrapeConfig["metric_relabel_configs"].([]interface{})
				for _, metricRelabelConfig := range metricRelabelConfigs {
					metricRelabelConfig := metricRelabelConfig.(map[interface{}]interface{})
					//replace $ with $$ for regex field
					if metricRelabelConfig["regex"] != nil {
						// Adding this check here since regex can be boolean and the conversion will fail
						if _, isString := metricRelabelConfig["regex"].(string); isString {
							regexString := metricRelabelConfig["regex"].(string)
							modifiedRegexString := strings.ReplaceAll(regexString, "$$", "$")
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$", "$$")
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$$NODE_NAME", "${env:NODE_NAME}")
							modifiedRegexString = strings.ReplaceAll(modifiedRegexString, "$$NODE_IP", "${env:NODE_IP}")
							metricRelabelConfig["regex"] = modifiedRegexString
						}
					}

					//replace $ with $$ for replacement field
					if metricRelabelConfig["replacement"] != nil {
						replacement := metricRelabelConfig["replacement"].(string)
						modifiedReplacementString := strings.ReplaceAll(replacement, "$$", "$")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$", "$$")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$$NODE_NAME", "${env:NODE_NAME}")
						modifiedReplacementString = strings.ReplaceAll(modifiedReplacementString, "$$NODE_IP", "${env:NODE_IP}")
						metricRelabelConfig["replacement"] = modifiedReplacementString
					}
				}
			}

			if scrapeConfig["static_configs"] != nil {
				staticConfigs := scrapeConfig["static_configs"].([]interface{})
				for _, staticConfig := range staticConfigs {
					staticConfig := staticConfig.(map[interface{}]interface{})
					if staticConfig["targets"] != nil {
						targets := staticConfig["targets"].([]interface{})
						for i, target := range targets {
							if _, isString := target.(string); isString {
								targetValue := target.(string)
								modifiedtargetValue := strings.ReplaceAll(targetValue, "$$NODE_NAME", "$NODE_NAME")
								modifiedtargetValue = strings.ReplaceAll(modifiedtargetValue, "$$NODE_IP", "$NODE_IP")
								modifiedtargetValue = strings.ReplaceAll(modifiedtargetValue, "$NODE_NAME", "${env:NODE_NAME}")
								modifiedtargetValue = strings.ReplaceAll(modifiedtargetValue, "$NODE_IP", "${env:NODE_IP}")
								staticConfig["targets"].([]interface{})[i] = modifiedtargetValue
							}
						}
					}
				}
			}
		}
	}

	// Need this here even though it is present in the receiver's config validate method since we only do the $ manipulation for regex and replacement fields
	// in scrape configs sections and the load method which is called before the validate method fails to unmarshal due to single $.
	// Either approach will fail but the receiver's config load wont return the right error message
	unsupportedFeatures := make([]string, 0, 4)

	if prometheusConfig["remote_write"] != nil {
		unsupportedFeatures = append(unsupportedFeatures, "remote_write")
	}
	if prometheusConfig["remote_read"] != nil {
		unsupportedFeatures = append(unsupportedFeatures, "remote_read")
	}
	if prometheusConfig["rule_files"] != nil {
		unsupportedFeatures = append(unsupportedFeatures, "rule_files")
	}
	if prometheusConfig["alerting"] != nil {
		unsupportedFeatures = append(unsupportedFeatures, "alerting")
	}
	if len(unsupportedFeatures) != 0 {
		return fmt.Errorf("unsupported features:\n\t%s", strings.Join(unsupportedFeatures, "\n\t"))
	}

	globalSettingsFromMergedOtelConfig := prometheusConfig["global"]

	if globalSettingsFromMergedOtelConfig != nil {
		globalSettings := globalSettingsFromMergedOtelConfig.(map[interface{}]interface{})
		scrapeInterval := globalSettings["scrape_interval"]
		if (len(globalSettings) > 1) || (len(globalSettings) == 1 && scrapeInterval != "15s") {
			setEnvVarString := fmt.Sprintf("AZMON_GLOBAL_SETTINGS_CONFIGURED=true\n")
			file, err := os.OpenFile("/opt/microsoft/prom_config_validator_env_var", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				log.Println("prom-config-validator::Unable to open file - prom_config_validator_env_var")
			}
			_, err = file.Write([]byte(setEnvVarString))
			if err != nil {
				log.Println("prom-config-validator::Unable to write to the file prom_config_validator_env_var")
			}
			file.Close()
			if err != nil {
				log.Println("prom-config-validator::Unable to close file prom_config_validator_env_var", err)
			} else {
				log.Printf("prom-config-validator::Successfully set env variables for global config in file prom_config_validator_env_var\n")
			}
		}
	}

	otelConfig.Receivers.Prometheus.Config = prometheusConfig

	if os.Getenv("DEBUG_MODE_ENABLED") == "true" {
		otelConfig.Service.Pipelines.Metrics.Exporters = []interface{}{"otlp", "prometheus"}
		if os.Getenv("CCP_METRICS_ENABLED") != "true" {
			otelConfig.Service.Pipelines.MetricsTelemetry.Receivers = []interface{}{"prometheus"}
			otelConfig.Service.Pipelines.MetricsTelemetry.Exporters = []interface{}{"prometheus/telemetry"}
			otelConfig.Service.Pipelines.MetricsTelemetry.Processors = []interface{}{"filter/telemetry"}
		}
	}

	mergedConfig, err := yaml.Marshal(otelConfig)
	if err != nil {
		return err
	}

	if err := ioutil.WriteFile(outputFilePath, mergedConfig, 0644); err != nil {
		return err
	}
	fmt.Printf("prom-config-validator::Successfully generated otel config\n")
	return nil
}