173 lines
4.7 KiB
Go
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
|
|
}
|