Skip to content

Commit

Permalink
pkg/integrations: support *_configs field for integrations (#1130)
Browse files Browse the repository at this point in the history
Creates the basic code to unmarshal integrations from a YAML field
called <integration name>_configs, which is a slice of that integration.

Note that this is NOT wired up to the integrations manager yet, and
trying to run the agent with more than one integration of the same type
will likely cause problems.
  • Loading branch information
rfratto committed Nov 29, 2021
1 parent 5017bcc commit 69ba2dd
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 20 deletions.
1 change: 1 addition & 0 deletions cmd/agent/service.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

package main
Expand Down
1 change: 1 addition & 0 deletions cmd/agent/service_windows.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build windows
// +build windows

package main
Expand Down
57 changes: 47 additions & 10 deletions pkg/integrations/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ use_hostname_label: true
`
var (
cfg ManagerConfig
listenPort int = 12345
listenHost string = "127.0.0.1"
listenPort = 12345
listenHost = "127.0.0.1"
)
require.NoError(t, yaml.Unmarshal([]byte(cfgText), &cfg))

Expand All @@ -57,18 +57,55 @@ use_hostname_label: true
func TestConfig_Remarshal(t *testing.T) {
RegisterIntegration(&testIntegrationA{})
cfgText := `
scrape_integrations: true
replace_instance_label: true
integration_restart_backoff: 5s
use_hostname_label: true
test:
text: Hello, world!
truth: true
`
expect := `
scrape_integrations: true
replace_instance_label: true
integration_restart_backoff: 5s
use_hostname_label: true
test_configs:
- text: Hello, world!
truth: true
`

var (
cfg ManagerConfig
listenPort = 12345
listenHost = "127.0.0.1"
)
require.NoError(t, yaml.Unmarshal([]byte(cfgText), &cfg))

// Listen port must be set before applying defaults. Normally applied by the
// config package.
cfg.ListenPort = listenPort
cfg.ListenHost = listenHost

outBytes, err := yaml.Marshal(cfg)
require.NoError(t, err, "Failed creating integration")
fmt.Println(string(outBytes))
require.YAMLEq(t, expect, string(outBytes))
}

// Test that marshaling works for *_configs integrations.
func TestConfig_Remarshal_ConfigsSlice(t *testing.T) {
setRegisteredIntegrations([]Config{&testIntegrationA{}})
cfgText := `
scrape_integrations: true
replace_instance_label: true
integration_restart_backoff: 5s
use_hostname_label: true
test_configs:
- text: Hello, world!
truth: true
- text: Hello again!
truth: true`
var (
cfg ManagerConfig
listenPort int = 12345
listenHost string = "127.0.0.1"
listenPort = 12345
listenHost = "127.0.0.1"
)
require.NoError(t, yaml.Unmarshal([]byte(cfgText), &cfg))

Expand All @@ -90,8 +127,8 @@ agent: {}

var (
cfg ManagerConfig
listenPort int = 12345
listenHost string = "127.0.0.1"
listenPort = 12345
listenHost = "127.0.0.1"
)
require.NoError(t, yaml.Unmarshal([]byte(cfgText), &cfg))

Expand Down
1 change: 1 addition & 0 deletions pkg/integrations/node_exporter/node_exporter_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !race
// +build !race

package node_exporter //nolint:golint
Expand Down
1 change: 1 addition & 0 deletions pkg/integrations/process_exporter/process-exporter.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !linux
// +build !linux

// Package process_exporter embeds https://github.com/ncabatoff/process-exporter
Expand Down
45 changes: 36 additions & 9 deletions pkg/integrations/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ func RegisterIntegration(cfg Config) {
configFieldNames[reflect.TypeOf(cfg)] = cfg.Name()
}

// setRegisteredIntegrations clears the set of registered integrations and
// overrides it with cc. Used by tests.
func setRegisteredIntegrations(cc []Config) {
registeredIntegrations = registeredIntegrations[:0]
configFieldNames = make(map[reflect.Type]string)

for _, c := range cc {
RegisterIntegration(c)
}
}

// RegisteredIntegrations all Configs that were passed to RegisterIntegration.
// Each call will generate a new set of pointers.
func RegisteredIntegrations() []Config {
Expand Down Expand Up @@ -89,12 +100,14 @@ func MarshalYAML(v interface{}) (interface{}, error) {
}

for _, c := range configs {
// TODO(rfratto): check to see if integration is a singleton only

fieldName, ok := configFieldNames[reflect.TypeOf(c)]
if !ok {
return nil, fmt.Errorf("integrations: cannot marshal unregistered Config type: %T", c)
}
field := cfgVal.FieldByName("XXX_Config_" + fieldName)
field.Set(reflect.ValueOf(c))
field := cfgVal.FieldByName("XXX_Configs_" + fieldName)
field.Set(reflect.Append(field, reflect.ValueOf(c)))
}

return cfgPointer.Interface(), nil
Expand Down Expand Up @@ -165,16 +178,25 @@ func unmarshalIntegrationsWithList(integrations []Config, out interface{}, unmar
outVal.Field(i).Set(cfgVal.Field(i))
}

// Iterate through the remainder of our fields, which should all be of
// type Config.
// Iterate through the remainder of our fields, which should all be
// either a Config or a slice of types that implement Config.
for i := outVal.NumField(); i < cfgVal.NumField(); i++ {
field := cfgVal.Field(i)

if field.IsNil() {
continue
}
val := cfgVal.Field(i).Interface().(Config)
*configs = append(*configs, val)

switch field.Kind() {
case reflect.Slice:
for i := 0; i < field.Len(); i++ {
val := field.Index(i).Interface().(Config)
*configs = append(*configs, val)
}
default:
val := field.Interface().(Config)
*configs = append(*configs, val)
}
}

return nil
Expand All @@ -197,14 +219,19 @@ func getConfigTypeForIntegrations(integrations []Config, out reflect.Type) refle
})
}
}

for _, cfg := range integrations {
// Use a prefix that's unlikely to collide with anything else.
fieldName := "XXX_Config_" + cfg.Name()
// Fields use a prefix that's unlikely to collide with anything else.
fields = append(fields, reflect.StructField{
Name: fieldName,
Name: "XXX_Config_" + cfg.Name(),
Tag: reflect.StructTag(fmt.Sprintf(`yaml:"%s,omitempty"`, cfg.Name())),
Type: reflect.TypeOf(cfg),
})
fields = append(fields, reflect.StructField{
Name: "XXX_Configs_" + cfg.Name(),
Tag: reflect.StructTag(fmt.Sprintf(`yaml:"%s_configs,omitempty"`, cfg.Name())),
Type: reflect.SliceOf(reflect.TypeOf(cfg)),
})
}
return reflect.StructOf(fields)
}
Expand Down
24 changes: 24 additions & 0 deletions pkg/integrations/register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@ test:
require.Equal(t, expect, fullCfg)
}

func TestIntegrationRegistration_Multiple(t *testing.T) {
var cfgToParse = `
name: John Doe
duration: 500ms
test_configs:
- text: Hello, world!
- text: Hello again!`

var fullCfg testFullConfig
err := yaml.UnmarshalStrict([]byte(cfgToParse), &fullCfg)
require.NoError(t, err)

expect := testFullConfig{
Name: "John Doe",
Duration: 500 * time.Millisecond,
Default: 12345,
Configs: []Config{
&testIntegrationA{Text: "Hello, world!", Truth: true},
&testIntegrationA{Text: "Hello again!", Truth: true},
},
}
require.Equal(t, expect, fullCfg)
}

type testIntegrationA struct {
Text string `yaml:"text"`
Truth bool `yaml:"truth"`
Expand Down
1 change: 1 addition & 0 deletions pkg/integrations/windows_exporter/windows_exporter.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

package windows_exporter //nolint:golint
Expand Down
3 changes: 2 additions & 1 deletion pkg/logs/logs_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//+build !race
//go:build !race
// +build !race

package logs

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/operator/kubelet_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// These tests depend on test assets from controller-runtime which don't work on Windows.

//go:build !windows && has_network
// +build !windows,has_network

package operator
Expand Down
1 change: 1 addition & 0 deletions pkg/operator/selector_eventhandler_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// These tests depend on test assets from controller-runtime which don't work on Windows.

//go:build !windows && has_network
// +build !windows,has_network

package operator
Expand Down

0 comments on commit 69ba2dd

Please sign in to comment.