gocommon/tracing/config.go
Matthieu 'JP' DERASSE 45c43b44a0
All checks were successful
continuous-integration/drone/push Build is passing
feat(go): Upgrade to go 1.21
2023-08-11 19:16:12 +00:00

173 lines
4.7 KiB
Go

package tracing
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/juju/errors"
"gopkg.in/yaml.v3"
"git.dev.m-and-m.ovh/mderasse/gocommon/convert"
"git.dev.m-and-m.ovh/mderasse/gocommon/tracing/exporter/jaeger"
"git.dev.m-and-m.ovh/mderasse/gocommon/tracing/exporter/zipkin"
)
// envPrefix is the prefix that will be used for any environnement variable used to configure the tracing system.
const envPrefix = "TRACING_"
// CONFIG_FILE is the default file that will be searched to apply configuration from the filesystem.
const defaultConfigFile = "conf/tracing.yaml"
// SECRET_NAME is the default name of the secret that will be searched in vault.
const defaultSecretName = "tracing"
// ConfigStruct represent the configuration of our tracing system.
type ConfigStruct struct {
Attributes map[string]string `yaml:"attributes"`
Enabled bool `yaml:"enabled"`
Exporter ExporterName `yaml:"exporter"`
ServiceName string `yaml:"service_name"`
ServiceVersion string `yaml:"service_version"`
ServiceInstanceID string `yaml:"service_instance_id"`
JaegerConfig *jaeger.ConfigStruct `yaml:"jaeger_config"`
ZipkinConfig *zipkin.ConfigStruct `yaml:"zipkin_config"`
}
func newDefaultConfig() *ConfigStruct {
return &ConfigStruct{
Enabled: false,
}
}
func loadConfigFromVault(secret string) (*ConfigStruct, error) {
_ = secret
return nil, os.ErrNotExist
}
// loadConfigFromFile will read the given file and return a config struct.
func loadConfigFromFile(path string) (*ConfigStruct, error) {
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
return nil, errors.Trace(err)
}
//nolint:gosec // we did compute the file path
f, err := os.ReadFile(path)
if err != nil {
return nil, errors.Trace(err)
}
var config ConfigStruct
if err := yaml.Unmarshal(f, &config); err != nil {
return nil, errors.Trace(err)
}
return &config, nil
}
func loadConfig() (*ConfigStruct, error) {
c, err := loadConfigFromVault(defaultSecretName)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err
} else if err == nil {
return c, nil
}
c, err = loadConfigFromFile(defaultConfigFile)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err
} else if err == nil {
return c, nil
}
return newDefaultConfig(), nil
}
// applyEnv will retrieve info from environment variable and overide those got by config / vault.
// validity of the value is not checked here and will be check in a IsValid method.
func (c *ConfigStruct) applyEnv() error {
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "ENABLED")); v != "" {
b, err := strconv.ParseBool(v)
if err != nil {
return errors.NewNotValid(err, fmt.Sprintf("Invalid %s%s environment variable. Should be a boolean", envPrefix, "ENABLED"))
}
c.Enabled = b
}
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "SERVICE_NAME")); v != "" {
c.ServiceName = v
}
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "SERVICE_VERSION")); v != "" {
c.ServiceVersion = v
}
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "SERVICE_INSTANCE_ID")); v != "" {
c.ServiceInstanceID = v
}
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "EXPORTER")); v != "" {
c.Exporter = ExporterName(v)
}
// Attributes
if v := os.Getenv(fmt.Sprintf("%s%s", envPrefix, "ATTRIBUTES")); v != "" {
if c.Attributes == nil {
c.Attributes = make(map[string]string)
}
attributeParts := strings.Split(v, ",")
for _, ap := range attributeParts {
attributeKV := strings.SplitN(ap, ":", 2)
if len(attributeKV) != 2 {
return errors.NotValidf(fmt.Sprintf("Invalid attribute %s in environment variable. Should be a key1:value1,key2:value2 format", ap))
}
c.Attributes[attributeKV[0]] = attributeKV[1]
}
}
return nil
}
// IsValid will check a config struct.
func (c *ConfigStruct) IsValid() error {
if c.Enabled {
if c.ServiceName == "" {
return errors.NotValidf("ServiceName is empty")
}
if !c.Exporter.IsValid() {
return errors.NotValidf("Invalid Exporter %s. Allowed values: %s", c.Exporter.String(), strings.Join(convert.StringerSliceToStringSlice(GetListExporterName()), ", "))
}
switch c.Exporter {
case ExporterName_JAEGER:
if c.JaegerConfig == nil {
return errors.NotValidf("jaeger configuration is empty")
}
err := c.JaegerConfig.IsValid()
if err != nil {
return errors.Trace(err)
}
case ExporterName_ZIPKIN:
if c.ZipkinConfig == nil {
return errors.NotValidf("zipkin configuration is empty")
}
err := c.ZipkinConfig.IsValid()
if err != nil {
return errors.Trace(err)
}
default:
return errors.NotValidf("Exporter not handled")
}
}
return nil
}