diff --git a/clients/pkg/promtail/config/config.go b/clients/pkg/promtail/config/config.go index 615b8e9abaad..0454a8facf49 100644 --- a/clients/pkg/promtail/config/config.go +++ b/clients/pkg/promtail/config/config.go @@ -40,6 +40,28 @@ type Config struct { WAL wal.Config `yaml:"wal"` } +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = Config{} + // We want to set c to the defaults and then overwrite it with the input. + // To make unmarshal fill the plain data struct rather than calling UnmarshalYAML + // again, we have to hide it using a type indirection. + type plain Config + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + // Validate unique names. + jobNames := map[string]struct{}{} + for _, j := range c.ScrapeConfig { + if _, ok := jobNames[j.JobName]; ok { + return fmt.Errorf("found multiple scrape configs with job name %q", j.JobName) + } + jobNames[j.JobName] = struct{}{} + } + return nil +} + // RegisterFlags with prefix registers flags where every name is prefixed by // prefix. If prefix is a non-empty string, prefix should end with a period. func (c *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { diff --git a/clients/pkg/promtail/config/config_test.go b/clients/pkg/promtail/config/config_test.go index 32bab70501e3..a812dd984abc 100644 --- a/clients/pkg/promtail/config/config_test.go +++ b/clients/pkg/promtail/config/config_test.go @@ -47,12 +47,47 @@ clients: name: value ` +const testDuplicateJobsName = ` +clients: + - external_labels: + cluster: dev1 + url: https://1:shh@example.com/loki/api/v1/push + - external_labels: + cluster: prod1 + url: https://1:shh@example.com/loki/api/v1/push +scrape_configs: + - job_name: kubernetes-pods-name + kubernetes_sd_configs: + - role: pod + - job_name: system + static_configs: + - targets: + - localhost + labels: + job: varlogs + - job_name: system + static_configs: + - targets: + - localhost + labels: + job: varlogs2 +limits_config: + readline_rate: 100 + readline_burst: 200 +` + func Test_Load(t *testing.T) { var dst Config err := yaml.Unmarshal([]byte(testFile), &dst) require.Nil(t, err) } +func Test_Load_DuplicateJobsName(t *testing.T) { + var dst Config + err := yaml.Unmarshal([]byte(testDuplicateJobsName), &dst) + require.ErrorContains(t, err, `found multiple scrape configs with job name "system"`) +} + func TestHeadersConfigLoad(t *testing.T) { var dst Config err := yaml.Unmarshal([]byte(headersTestFile), &dst)