-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add otelcol-config This commit adds otelcol-config, a tool designed to be used for configuration otelcol-sumo installations. The tool will mainly write values to sumologic.yaml, and link and unlink files in conf.d. Signed-off-by: Eric Chlebek <echlebek@sumologic.com>
- Loading branch information
Showing
22 changed files
with
1,345 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include ../../Makefile.Common |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# otelcol-config | ||
|
||
`otelcol-config` manipulates files in /etc/otelcol-config/conf.d (or in a | ||
user-specified config directory). | ||
|
||
It is used by the install.sh script to configure the collector for first-time | ||
use, and also to adjust the collector configuration after installation. | ||
|
||
Run `otelcol-config --help` for usage information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
"io/fs" | ||
) | ||
|
||
// actionContext provides an abstraction of I/O for all actions | ||
type actionContext struct { | ||
ConfigDir fs.FS | ||
Flags *flagValues | ||
Stdout io.Writer | ||
Stderr io.Writer | ||
WriteConfD func([]byte) error | ||
WriteConfDOverrides func([]byte) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"testing" | ||
) | ||
|
||
func discardWriter([]byte) error { | ||
return nil | ||
} | ||
|
||
type errWriter struct { | ||
} | ||
|
||
func (errWriter) Write([]byte) error { | ||
return errors.New("writer called") | ||
} | ||
|
||
func makeTestActionContext(t testing.TB, | ||
confD fs.FS, | ||
flags []string, | ||
stdout, stderr io.Writer, | ||
writeConfD, writeConfDOverrides func([]byte) error) *actionContext { | ||
|
||
t.Helper() | ||
|
||
flagValues := newFlagValues() | ||
fs := makeFlagSet(flagValues) | ||
if err := fs.Parse(flags); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if stdout == nil { | ||
stdout = io.Discard | ||
} | ||
|
||
if stderr == nil { | ||
stderr = io.Discard | ||
} | ||
|
||
if writeConfD == nil { | ||
writeConfD = discardWriter | ||
} | ||
|
||
if writeConfDOverrides == nil { | ||
writeConfDOverrides = discardWriter | ||
} | ||
|
||
return &actionContext{ | ||
ConfigDir: confD, | ||
Flags: flagValues, | ||
Stdout: stdout, | ||
Stderr: stderr, | ||
WriteConfD: writeConfD, | ||
WriteConfDOverrides: writeConfDOverrides, | ||
} | ||
} | ||
|
||
type testWriter struct { | ||
exp []byte | ||
} | ||
|
||
func (t *testWriter) Write(data []byte) error { | ||
if got, want := data, t.exp; !bytes.Equal(got, want) { | ||
return fmt.Errorf("bad conf.d write: got %q, want %q", got, want) | ||
} | ||
return nil | ||
} | ||
|
||
func newTestWriter(exp []byte) *testWriter { | ||
return &testWriter{ | ||
exp: exp, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
) | ||
|
||
const ( | ||
SumologicDotYaml = "sumologic.yaml" | ||
SumologicRemoteDotYaml = "sumologic-remote.yaml" | ||
ConfDotD = "conf.d" | ||
ConfDotDAvailable = "conf.d-available" | ||
ConfDSettings = "00-otelcol-config-settings.yaml" | ||
ConfDOverrides = "99-otelcol-config-overrides.yaml" | ||
) | ||
|
||
type ConfDir struct { | ||
// Sumologic is the contents of sumologic.yaml. | ||
Sumologic []byte | ||
|
||
// SumologicRemote is the contents of sumologic-remote.yaml. | ||
SumologicRemote []byte | ||
|
||
// ConfD is a mapping of file name to contents of the conf.d directory. | ||
ConfD map[string][]byte | ||
|
||
// ConfDAvailable is a mapping of file name to contents of the | ||
// conf.d-available directory. | ||
ConfDAvailable map[string][]byte | ||
} | ||
|
||
// ReadConfigDir reads the files in /etc/otelcol-sumo according to an expected | ||
// layout. It produces a ConfDir that contains the data read from the files. | ||
func ReadConfigDir(root fs.FS) (conf ConfDir, err error) { | ||
const errMsg = "error reading otelcol-sumo config dir: %s" | ||
|
||
conf.Sumologic, err = fs.ReadFile(root, SumologicDotYaml) | ||
if err != nil { | ||
// sumologic.yaml is not strictly required | ||
if !errors.Is(err, fs.ErrNotExist) { | ||
return conf, fmt.Errorf(errMsg, err) | ||
} | ||
} | ||
|
||
conf.SumologicRemote, err = fs.ReadFile(root, SumologicRemoteDotYaml) | ||
if err != nil { | ||
// sumologic-remote.yaml is not strictly required | ||
if !errors.Is(err, fs.ErrNotExist) { | ||
return conf, fmt.Errorf(errMsg, err) | ||
} | ||
} | ||
|
||
conf.ConfD, err = getDir(root, ConfDotD) | ||
if err != nil { | ||
// conf.d is not strictly required | ||
if !errors.Is(err, fs.ErrNotExist) { | ||
return conf, fmt.Errorf(errMsg, err) | ||
} | ||
} | ||
|
||
conf.ConfDAvailable, err = getDir(root, ConfDotDAvailable) | ||
if err != nil { | ||
// conf.d-available is not strictly required | ||
if !errors.Is(err, fs.ErrNotExist) { | ||
return conf, fmt.Errorf(errMsg, err) | ||
} | ||
} | ||
|
||
return conf, nil | ||
} | ||
|
||
func getDir(root fs.FS, dirName string) (result map[string][]byte, err error) { | ||
result = make(map[string][]byte) | ||
dirFS, err := fs.Sub(root, dirName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
entries, err := fs.ReadDir(dirFS, ".") | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, entry := range entries { | ||
if entry.Type() == fs.ModeDir { | ||
continue | ||
} | ||
contents, err := fs.ReadFile(dirFS, entry.Name()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result[entry.Name()] = contents | ||
} | ||
return result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package main | ||
|
||
import ( | ||
"io/fs" | ||
"testing" | ||
"testing/fstest" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
) | ||
|
||
func TestConfLoader(t *testing.T) { | ||
tests := []struct { | ||
Name string | ||
FS fs.FS | ||
Expected ConfDir | ||
ErrExpected bool | ||
}{ | ||
{ | ||
Name: "load sumologic.yaml", | ||
FS: func() fs.FS { | ||
return fstest.MapFS{ | ||
"sumologic.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
} | ||
}(), | ||
Expected: ConfDir{ | ||
Sumologic: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
}, | ||
{ | ||
Name: "load sumologic-remote.yaml", | ||
FS: func() fs.FS { | ||
return fstest.MapFS{ | ||
"sumologic-remote.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
} | ||
}(), | ||
Expected: ConfDir{ | ||
SumologicRemote: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
}, | ||
{ | ||
Name: "load conf.d", | ||
FS: func() fs.FS { | ||
return fstest.MapFS{ | ||
"conf.d/a.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
"conf.d/b.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
"conf.d/emptydir": &fstest.MapFile{ | ||
Mode: fs.ModeDir, | ||
}, | ||
} | ||
}(), | ||
Expected: ConfDir{ | ||
ConfD: map[string][]byte{ | ||
"a.yaml": []byte(`{"extensions":{"sumologic":{}}}`), | ||
"b.yaml": []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "load conf.d-available", | ||
FS: func() fs.FS { | ||
return fstest.MapFS{ | ||
"conf.d-available/a.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
"conf.d-available/b.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
"conf.d-available/emptydir": &fstest.MapFile{ | ||
Mode: fs.ModeDir, | ||
}, | ||
} | ||
}(), | ||
Expected: ConfDir{ | ||
ConfDAvailable: map[string][]byte{ | ||
"a.yaml": []byte(`{"extensions":{"sumologic":{}}}`), | ||
"b.yaml": []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "load all", | ||
FS: func() fs.FS { | ||
return fstest.MapFS{ | ||
"sumologic.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
"sumologic-remote.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
"conf.d/a.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
"conf.d/b.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
"conf.d/emptydir": &fstest.MapFile{ | ||
Mode: fs.ModeDir, | ||
}, | ||
"conf.d-available/a.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"sumologic":{}}}`), | ||
}, | ||
"conf.d-available/b.yaml": &fstest.MapFile{ | ||
Data: []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
"conf.d-available/emptydir": &fstest.MapFile{ | ||
Mode: fs.ModeDir, | ||
}, | ||
} | ||
}(), | ||
Expected: ConfDir{ | ||
Sumologic: []byte(`{"extensions":{"sumologic":{}}}`), | ||
SumologicRemote: []byte(`{"extensions":{"opamp":{}}}`), | ||
ConfD: map[string][]byte{ | ||
"a.yaml": []byte(`{"extensions":{"sumologic":{}}}`), | ||
"b.yaml": []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
ConfDAvailable: map[string][]byte{ | ||
"a.yaml": []byte(`{"extensions":{"sumologic":{}}}`), | ||
"b.yaml": []byte(`{"extensions":{"opamp":{}}}`), | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.Name, func(t *testing.T) { | ||
conf, err := ReadConfigDir(test.FS) | ||
if err != nil { | ||
if !test.ErrExpected { | ||
t.Fatal(err) | ||
} | ||
} | ||
if got, want := conf, test.Expected; !cmp.Equal(got, want) { | ||
t.Errorf("conf dir not as expected: %s", cmp.Diff(want, got)) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.