Skip to content

Commit de1d034

Browse files
authored
Add disable providers by default option (#4166)
* add disable providers by default option * don't replace agent config upon enrollment if unnecessary
1 parent 6f7a116 commit de1d034

14 files changed

+347
-5
lines changed

.github/CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
/deploy/kubernetes @elastic/obs-cloudnative-monitoring
55
/dev-tools/kubernetes @elastic/obs-cloudnative-monitoring
66
/internal/pkg/composable/providers/kubernetes @elastic/obs-cloudnative-monitoring
7+
/internal/pkg/agent/application/configuration_embed_changed_test.go @elastic/cloudbeat

_meta/config/providers.yml.tmpl

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
# and conditionals. Each provider's keys are automatically prefixed with the name
55
# of the provider.
66

7+
# All registered providers are enabled by default.
8+
9+
# Disable all providers by default and only enable explicitly configured providers.
10+
# agent.providers.initial_default: false
11+
712
#providers:
813

914
# Agent provides information about the running agent.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: enhancement
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Add agent.providers.initial_default configuration flag to disable providers by default
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: elastic-agent
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/4166
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
issue: https://github.com/elastic/elastic-agent/issues/4145

elastic-agent.docker.yml

+5
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ agent.logging.to_stderr: true
218218
# and conditionals. Each provider's keys are automatically prefixed with the name
219219
# of the provider.
220220

221+
# All registered providers are enabled by default.
222+
223+
# Disable all providers by default and only enable explicitly configured providers.
224+
# agent.providers.initial_default: false
225+
221226
#providers:
222227

223228
# Agent provides information about the running agent.

elastic-agent.reference.yml

+5
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ agent.logging.to_stderr: true
271271
# and conditionals. Each provider's keys are automatically prefixed with the name
272272
# of the provider.
273273

274+
# All registered providers are enabled by default.
275+
276+
# Disable all providers by default and only enable explicitly configured providers.
277+
# agent.providers.initial_default: false
278+
274279
#providers:
275280

276281
# Agent provides information about the running agent.

elastic-agent.yml

+5
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ agent.logging.to_stderr: true
261261
# and conditionals. Each provider's keys are automatically prefixed with the name
262262
# of the provider.
263263

264+
# All registered providers are enabled by default.
265+
266+
# Disable all providers by default and only enable explicitly configured providers.
267+
# agent.providers.initial_default: false
268+
264269
#providers:
265270

266271
# Agent provides information about the running agent.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package application
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"gopkg.in/yaml.v2"
12+
)
13+
14+
// This test exists to notify the cloudbeat team in case the default agent fleet config is changed.
15+
func TestDefaultAgentFleetConfig(t *testing.T) {
16+
cfg := map[string]interface{}{}
17+
18+
err := yaml.Unmarshal(DefaultAgentFleetConfig, &cfg)
19+
assert.NoError(t, err)
20+
21+
assert.Equal(t, map[string]interface{}{"fleet": map[interface{}]interface{}{"enabled": true}}, cfg)
22+
}

internal/pkg/agent/storage/replace_store.go

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ func (r *ReplaceOnSuccessStore) Save(in io.Reader) error {
5959
return nil
6060
}
6161

62+
if shouldSkipReplace(target, r.replaceWith) {
63+
return nil
64+
}
65+
6266
// Windows is tricky with the characters permitted for the path and filename, so we have
6367
// to remove any colon from the string. We are using nanosec precision here because of automated
6468
// tools.
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package storage
6+
7+
import (
8+
"strings"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"gopkg.in/yaml.v3"
12+
)
13+
14+
// This is a temporary solution to avoid replacing the target file if the content of the replacement is contained in the target file.
15+
// It only works for YAML files, since the only use case is for the default agent fleet config.
16+
// Returns true only if the replacement configuration is already contained in the original.
17+
func shouldSkipReplace(original []byte, replacement []byte) bool {
18+
replacementYaml := map[string]interface{}{}
19+
originalYaml := map[string]interface{}{}
20+
21+
err := yaml.Unmarshal(replacement, &replacementYaml)
22+
if err != nil {
23+
return false
24+
}
25+
26+
err = yaml.Unmarshal(original, &originalYaml)
27+
if err != nil {
28+
return false
29+
}
30+
31+
diff := cmp.Diff(replacementYaml, originalYaml)
32+
if strings.HasPrefix(diff, "-") || strings.Contains(diff, "\n-") {
33+
// Lines starting with - represent values in the replacement that are not present in the original
34+
return false
35+
}
36+
37+
return true
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package storage
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestShouldSkipReplace(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
original []byte
17+
replacement []byte
18+
expected bool
19+
}{
20+
{
21+
name: "original and replacement are the same",
22+
original: []byte("fleet:\n enabled: true\n"),
23+
replacement: []byte("fleet:\n enabled: true\n"),
24+
expected: true,
25+
},
26+
{
27+
name: "original and replacement are different",
28+
original: []byte("fleet:\n enabled: true\n"),
29+
replacement: []byte("fleet:\n enabled: false\n"),
30+
expected: false,
31+
},
32+
{
33+
name: "original is not a valid yaml",
34+
original: []byte("fleet: enabled: true\n"),
35+
expected: false,
36+
},
37+
{
38+
name: "replacement is not a valid yaml",
39+
replacement: []byte("fleet: enabled: true\n"),
40+
expected: false,
41+
},
42+
{
43+
name: "original and replacement are different just in comments and spaces",
44+
original: []byte("#bla bla bla\nfleet:\n enabled: true\n"),
45+
replacement: []byte("fleet: \n enabled: true\n#oh right bla bla bla\n"),
46+
expected: true,
47+
},
48+
{
49+
name: "original contains replacement and more",
50+
original: []byte("#bla bla bla\nfleet:\n enabled: true\nanother: value\nmore:\n stuff: true\n"),
51+
replacement: []byte("fleet:\n enabled: true\n"),
52+
expected: true,
53+
},
54+
{
55+
name: "original contains replacement and more, but in different order",
56+
original: []byte("fleet:\n a_key_that_ruins: sad\n enabled: true\n"),
57+
replacement: []byte("fleet:\n enabled: true\n"),
58+
expected: true,
59+
},
60+
}
61+
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
assert.Equal(t, tt.expected, shouldSkipReplace(tt.original, tt.replacement))
65+
})
66+
}
67+
}

internal/pkg/agent/storage/storage_test.go

+32-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package storage
66

77
import (
88
"bytes"
9+
"errors"
910
"fmt"
1011
"io"
1112
"os"
@@ -14,7 +15,6 @@ import (
1415
"testing"
1516
"time"
1617

17-
"github.com/pkg/errors"
1818
"github.com/stretchr/testify/require"
1919
)
2020

@@ -101,6 +101,33 @@ func TestReplaceOrRollbackStore(t *testing.T) {
101101

102102
})
103103

104+
t.Run("when replace is skipped due to target already containing source content", func(t *testing.T) {
105+
yamlTarget := []byte("fleet:\n enabled: true\nother: value\n")
106+
yamlReplaceWith := []byte("#This comment is left out\nfleet:\n enabled: true\n")
107+
target, err := genFile(yamlTarget)
108+
109+
require.NoError(t, err)
110+
dir := filepath.Dir(target)
111+
defer os.RemoveAll(dir)
112+
113+
requireFilesCount(t, dir, 1)
114+
115+
s := NewReplaceOnSuccessStore(
116+
target,
117+
yamlReplaceWith,
118+
failure,
119+
)
120+
121+
err = s.Save(in)
122+
require.Error(t, err)
123+
124+
writtenContent, err := os.ReadFile(target)
125+
require.NoError(t, err)
126+
127+
require.True(t, bytes.Equal(writtenContent, yamlTarget))
128+
requireFilesCount(t, dir, 1)
129+
})
130+
104131
t.Run("when target file do not exist", func(t *testing.T) {
105132
s := NewReplaceOnSuccessStore(
106133
fmt.Sprintf("%s/%d", os.TempDir(), time.Now().Unix()),
@@ -176,7 +203,10 @@ func genFile(b []byte) (string, error) {
176203
if err != nil {
177204
return "", err
178205
}
179-
f.Write(b)
206+
_, err = f.Write(b)
207+
if err != nil {
208+
return "", err
209+
}
180210
name := f.Name()
181211
if err := f.Close(); err != nil {
182212
return "", err

internal/pkg/composable/config.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ import "github.com/elastic/elastic-agent/internal/pkg/config"
88

99
// Config is config for multiple providers.
1010
type Config struct {
11-
Providers map[string]*config.Config `config:"providers"`
11+
Providers map[string]*config.Config `config:"providers"`
12+
ProvidersInitialDefault *bool `config:"agent.providers.initial_default"`
1213
}

internal/pkg/composable/controller.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,17 @@ func New(log *logger.Logger, c *config.Config, managed bool) (Controller, error)
6161
}
6262
}
6363

64+
// Unless explicitly configured otherwise, All registered providers are enabled by default
65+
providersInitialDefault := true
66+
if providersCfg.ProvidersInitialDefault != nil {
67+
providersInitialDefault = *providersCfg.ProvidersInitialDefault
68+
}
69+
6470
// build all the context providers
6571
contextProviders := map[string]*contextProviderState{}
6672
for name, builder := range Providers.contextProviders {
6773
pCfg, ok := providersCfg.Providers[name]
68-
if ok && !pCfg.Enabled() {
74+
if (ok && !pCfg.Enabled()) || (!ok && !providersInitialDefault) {
6975
// explicitly disabled; skipping
7076
continue
7177
}
@@ -84,7 +90,7 @@ func New(log *logger.Logger, c *config.Config, managed bool) (Controller, error)
8490
dynamicProviders := map[string]*dynamicProviderState{}
8591
for name, builder := range Providers.dynamicProviders {
8692
pCfg, ok := providersCfg.Providers[name]
87-
if ok && !pCfg.Enabled() {
93+
if (ok && !pCfg.Enabled()) || (!ok && !providersInitialDefault) {
8894
// explicitly disabled; skipping
8995
continue
9096
}

0 commit comments

Comments
 (0)