netdata/netdata

View on GitHub
src/go/collectors/go.d.plugin/agent/confgroup/config.go

Summary

Maintainability
A
2 hrs
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package confgroup

import (
    "fmt"
    "net/url"
    "regexp"
    "strings"

    "github.com/netdata/netdata/go/go.d.plugin/agent/hostinfo"
    "github.com/netdata/netdata/go/go.d.plugin/agent/module"

    "github.com/ilyam8/hashstructure"
    "gopkg.in/yaml.v2"
)

const (
    keyName        = "name"
    keyModule      = "module"
    keyUpdateEvery = "update_every"
    keyDetectRetry = "autodetection_retry"
    keyPriority    = "priority"
    keyLabels      = "labels"
    keyVnode       = "vnode"

    ikeySource     = "__source__"
    ikeySourceType = "__source_type__"
    ikeyProvider   = "__provider__"
)

const (
    TypeStock      = "stock"
    TypeUser       = "user"
    TypeDiscovered = "discovered"
    TypeDyncfg     = "dyncfg"
)

type Config map[string]any

func (c Config) HashIncludeMap(_ string, k, _ any) (bool, error) {
    s := k.(string)
    return !(strings.HasPrefix(s, "__") || strings.HasSuffix(s, "__")), nil
}

func (c Config) Set(key string, value any) Config { c[key] = value; return c }
func (c Config) Get(key string) any               { return c[key] }

func (c Config) Name() string            { v, _ := c.Get(keyName).(string); return v }
func (c Config) Module() string          { v, _ := c.Get(keyModule).(string); return v }
func (c Config) FullName() string        { return fullName(c.Name(), c.Module()) }
func (c Config) UpdateEvery() int        { v, _ := c.Get(keyUpdateEvery).(int); return v }
func (c Config) AutoDetectionRetry() int { v, _ := c.Get(keyDetectRetry).(int); return v }
func (c Config) Priority() int           { v, _ := c.Get(keyPriority).(int); return v }
func (c Config) Labels() map[any]any     { v, _ := c.Get(keyLabels).(map[any]any); return v }
func (c Config) Hash() uint64            { return calcHash(c) }
func (c Config) Vnode() string           { v, _ := c.Get(keyVnode).(string); return v }

func (c Config) SetName(v string) Config   { return c.Set(keyName, v) }
func (c Config) SetModule(v string) Config { return c.Set(keyModule, v) }

func (c Config) UID() string {
    return fmt.Sprintf("%s_%s_%s_%s_%d", c.SourceType(), c.Provider(), c.Source(), c.FullName(), c.Hash())
}

func (c Config) Source() string                { v, _ := c.Get(ikeySource).(string); return v }
func (c Config) SourceType() string            { v, _ := c.Get(ikeySourceType).(string); return v }
func (c Config) Provider() string              { v, _ := c.Get(ikeyProvider).(string); return v }
func (c Config) SetSource(v string) Config     { return c.Set(ikeySource, v) }
func (c Config) SetSourceType(v string) Config { return c.Set(ikeySourceType, v) }
func (c Config) SetProvider(v string) Config   { return c.Set(ikeyProvider, v) }

func (c Config) SourceTypePriority() int {
    switch c.SourceType() {
    default:
        return 0
    case TypeStock:
        return 2
    case TypeDiscovered:
        return 4
    case TypeUser:
        return 8
    case TypeDyncfg:
        return 16
    }
}

func (c Config) Clone() (Config, error) {
    type plain Config
    bytes, err := yaml.Marshal((plain)(c))
    if err != nil {
        return nil, err
    }
    var newConfig Config
    if err := yaml.Unmarshal(bytes, &newConfig); err != nil {
        return nil, err
    }
    return newConfig, nil
}

func (c Config) ApplyDefaults(def Default) {
    if c.UpdateEvery() <= 0 {
        v := firstPositive(def.UpdateEvery, module.UpdateEvery)
        c.Set("update_every", v)
    }
    if c.AutoDetectionRetry() <= 0 {
        v := firstPositive(def.AutoDetectionRetry, module.AutoDetectionRetry)
        c.Set("autodetection_retry", v)
    }
    if c.Priority() <= 0 {
        v := firstPositive(def.Priority, module.Priority)
        c.Set("priority", v)
    }
    if c.UpdateEvery() < def.MinUpdateEvery && def.MinUpdateEvery > 0 {
        c.Set("update_every", def.MinUpdateEvery)
    }
    if c.Name() == "" {
        c.Set("name", c.Module())
    } else {
        c.Set("name", cleanName(jobNameResolveHostname(c.Name())))
    }

    if v, ok := c.Get("url").(string); ok {
        c.Set("url", urlResolveHostname(v))
    }
}

var reInvalidCharacters = regexp.MustCompile(`\s+|\.+`)

func cleanName(name string) string {
    return reInvalidCharacters.ReplaceAllString(name, "_")
}

func fullName(name, module string) string {
    if name == module {
        return name
    }
    return module + "_" + name
}

func calcHash(obj any) uint64 {
    hash, _ := hashstructure.Hash(obj, nil)
    return hash
}

func firstPositive(value int, others ...int) int {
    if value > 0 || len(others) == 0 {
        return value
    }
    return firstPositive(others[0], others[1:]...)
}

func urlResolveHostname(rawURL string) string {
    if hostinfo.Hostname == "" || !strings.Contains(rawURL, "hostname") {
        return rawURL
    }

    u, err := url.Parse(rawURL)
    if err != nil || (u.Hostname() != "hostname" && !strings.Contains(u.Hostname(), "hostname.")) {
        return rawURL
    }

    u.Host = strings.Replace(u.Host, "hostname", hostinfo.Hostname, 1)

    return u.String()
}

func jobNameResolveHostname(name string) string {
    if hostinfo.Hostname == "" || !strings.Contains(name, "hostname") {
        return name
    }

    if name != "hostname" && !strings.HasPrefix(name, "hostname.") && !strings.HasPrefix(name, "hostname_") {
        return name
    }

    return strings.Replace(name, "hostname", hostinfo.Hostname, 1)
}