master
go 96 lines 2.95 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package sd
4
5 import (
6 "encoding/json"
7 "fmt"
8
9 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery/sd/pipeline"
10 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
11
12 "gopkg.in/yaml.v2"
13 )
14
15 // parseDyncfgPayload parses a dyncfg JSON payload into a runtime pipeline.Config.
16 // The dyncfg job name is authoritative and overrides any serialized payload name.
17 func parseDyncfgPayload(payload []byte, discovererType, name string, configDefaults confgroup.Registry, reg Registry, validate bool) (pipeline.Config, error) {
18 if reg == nil {
19 return pipeline.Config{}, fmt.Errorf("discoverer registry is not configured")
20 }
21
22 var cfg pipeline.Config
23 if err := json.Unmarshal(payload, &cfg); err != nil {
24 return pipeline.Config{}, fmt.Errorf("unmarshal %s config: %w", discovererType, err)
25 }
26
27 cfg.Name = name
28 cfg.ConfigDefaults = configDefaults
29
30 // Validate that the config has the expected discoverer type
31 if got := cfg.Discoverer.Type(); got != discovererType {
32 if got == "" {
33 return pipeline.Config{}, fmt.Errorf("no discoverer configured, expected %q", discovererType)
34 }
35 return pipeline.Config{}, fmt.Errorf("config has discoverer type %q, expected %q", got, discovererType)
36 }
37 desc, ok := reg.Get(discovererType)
38 if !ok {
39 return pipeline.Config{}, fmt.Errorf("unknown discoverer type %q", discovererType)
40 }
41 if _, err := desc.ParseJSONConfig(cfg.Discoverer.Config); err != nil {
42 return pipeline.Config{}, fmt.Errorf("invalid %q discoverer config: %w", discovererType, err)
43 }
44
45 if validate {
46 // Perform full semantic validation (name, discoverer, services rules)
47 if err := pipeline.ValidateConfig(cfg); err != nil {
48 return pipeline.Config{}, err
49 }
50 }
51
52 return cfg, nil
53 }
54
55 // pipelineKey returns a unique key for a dyncfg pipeline.
56 // Format: "dyncfg:{discovererType}:{name}"
57 func pipelineKey(discovererType, name string) string {
58 return fmt.Sprintf("dyncfg:%s:%s", discovererType, name)
59 }
60
61 // configToJSON converts stored config JSON to JSON via typed struct.
62 // This ensures consistent field ordering matching the struct definition.
63 func configToJSON(data []byte) ([]byte, error) {
64 var cfg pipeline.Config
65 if err := json.Unmarshal(data, &cfg); err != nil {
66 return nil, fmt.Errorf("unmarshal json: %w", err)
67 }
68
69 bs, err := json.Marshal(cfg)
70 if err != nil {
71 return nil, fmt.Errorf("marshal json: %w", err)
72 }
73
74 return bs, nil
75 }
76
77 // userConfigFromPayload converts a JSON payload to YAML format for user editing.
78 // The returned YAML always uses jobName (or "test") as the top-level pipeline name.
79 func userConfigFromPayload(payload []byte, discovererType, jobName string) ([]byte, error) {
80 var cfg pipeline.Config
81 if err := json.Unmarshal(payload, &cfg); err != nil {
82 return nil, fmt.Errorf("unmarshal json: %w", err)
83 }
84
85 cfg.Name = jobName
86 if cfg.Name == "" {
87 cfg.Name = "test"
88 }
89
90 bs, err := yaml.Marshal(cfg)
91 if err != nil {
92 return nil, fmt.Errorf("marshal yaml: %w", err)
93 }
94
95 return bs, nil
96 }