Skip to content

Commit

Permalink
otelcol-config: add launchd support
Browse files Browse the repository at this point in the history
Signed-off-by: Justin Kolberg <[email protected]>
  • Loading branch information
amdprophet committed Oct 22, 2024
1 parent fb94e90 commit 17a2d06
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 17 deletions.
3 changes: 3 additions & 0 deletions pkg/tools/otelcol-config/action_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
// for carrying values across API boundaries. It is for abstracting I/O.
type actionContext struct {
ConfigDir fs.FS
LaunchdDir fs.FS
Flags *flagValues
Stdout io.Writer
Stderr io.Writer
Expand All @@ -24,4 +25,6 @@ type actionContext struct {
UnlinkEphemeral func() error
SystemdEnabled bool
WriteInstallationTokenEnv func([]byte) (int, error)
LaunchdEnabled bool
WriteLaunchdConfig func([]byte) (int, error)
}
2 changes: 1 addition & 1 deletion pkg/tools/otelcol-config/action_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type testWriter struct {

func (t *testWriter) Write(data []byte) (int, error) {
if got, want := data, t.exp; !bytes.Equal(got, want) {
return 0, fmt.Errorf("bad conf.d write: got %q, want %q", got, want)
return 0, fmt.Errorf("bad write: got %q, want %q", got, want)
}
return len(data), nil
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/tools/otelcol-config/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
flagEnableRemoteControl = "enable-remote-control"
flagDisableRemoteControl = "disable-remote-control"
flagConfigDir = "config"
flagLaunchdDir = "launchd"
flagWriteKV = "write-kv"
flagReadKV = "read-kv"
flagOverride = "override"
Expand All @@ -37,6 +38,7 @@ const (
disableRemoteControlUsage = "disables remote control, uses local configuration only"
setOpAmpEndpointUsage = "sets the opamp endpoint (eg: wss://example.com)"
configUsage = "path to sumologic configuration directory"
launchdUsage = "path to LaunchDaemons directory (macOS only)"
writeKVUsage = `write key-value in conf.d with yq expression (eg: --write-kv '.extensions.sumologic.foo = "bar"')`
getKVUsage = "read key-value from conf.d with yq path (eg: --read-kv .extensions.sumologic.foo)"
overrideUsage = "for write-kv, override all other user settings"
Expand All @@ -56,6 +58,7 @@ type flagValues struct {
SetOpAmpEndpoint string
Help bool
ConfigDir string
LaunchdDir string
WriteKV []string
ReadKV []string
Override bool
Expand All @@ -82,6 +85,7 @@ func makeFlagSet(fv *flagValues) *pflag.FlagSet {
flags.BoolVar(&fv.DisableRemoteControl, flagDisableRemoteControl, false, disableRemoteControlUsage)
flags.StringVarP(&fv.SetOpAmpEndpoint, flagSetOpAmpEndpoint, "e", "", setOpAmpEndpointUsage)
flags.StringVarP(&fv.ConfigDir, flagConfigDir, "c", "/etc/otelcol-sumo", configUsage)
flags.StringVarP(&fv.LaunchdDir, flagLaunchdDir, "l", "/etc/otelcol-sumo", launchdUsage)
flags.StringArrayVar(&fv.WriteKV, flagWriteKV, nil, writeKVUsage)
flags.StringArrayVar(&fv.ReadKV, flagReadKV, nil, getKVUsage)
flags.BoolVar(&fv.Override, flagOverride, false, overrideUsage)
Expand Down
2 changes: 2 additions & 0 deletions pkg/tools/otelcol-config/flag_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type action func(context *actionContext) error
var flagActions = map[string]action{
flagHelp: helpAction,
flagConfigDir: nullAction,
flagLaunchdDir: nullAction,
flagAddTag: AddTagAction,
flagDeleteTag: DeleteTagAction,
flagSetInstallationToken: SetInstallationTokenAction,
Expand All @@ -41,6 +42,7 @@ func nullAction(*actionContext) error {
var actionOrder = []string{
flagHelp,
flagConfigDir,
flagLaunchdDir,
flagEnableRemoteControl,
flagDisableRemoteControl,
flagAddTag,
Expand Down
3 changes: 2 additions & 1 deletion pkg/tools/otelcol-config/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/SumoLogic/sumologic-otel-collector/pkg/tools/otelcol-config

go 1.21.3
go 1.21

require (
github.com/google/go-cmp v0.6.0
Expand All @@ -9,6 +9,7 @@ require (
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
)

require (
Expand Down
7 changes: 6 additions & 1 deletion pkg/tools/otelcol-config/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
Expand Down Expand Up @@ -80,6 +81,10 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
24 changes: 24 additions & 0 deletions pkg/tools/otelcol-config/installation_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ import (

"github.com/mikefarah/yq/v4/pkg/yqlib"
"gopkg.in/yaml.v3"
"howett.net/plist"
)

func SetInstallationTokenAction(ctx *actionContext) error {
if ctx.SystemdEnabled {
return setInstallationTokenSystemd(ctx)
}

if ctx.LaunchdEnabled {
return setInstallationTokenLaunchd(ctx)
}

conf, err := ReadConfigDir(ctx.ConfigDir)
if err != nil {
return err
Expand Down Expand Up @@ -78,3 +83,22 @@ func setInstallationTokenSystemd(ctx *actionContext) error {
}
return nil
}

func setInstallationTokenLaunchd(ctx *actionContext) error {
conf, err := ReadLaunchdConfig(ctx.LaunchdDir)
if err != nil {
return err
}

conf.Root.EnvironmentVariables["SUMOLOGIC_INSTALLATION_TOKEN"] = ctx.Flags.InstallationToken

newBytes, err := plist.Marshal(&conf.Root, conf.Format)
if err != nil {
return fmt.Errorf("couldn't marshal launchd config: %s", err)
}

if _, err := ctx.WriteLaunchdConfig(newBytes); err != nil {
return fmt.Errorf("couldn't write launchd config: %s", err)
}
return nil
}
65 changes: 60 additions & 5 deletions pkg/tools/otelcol-config/installation_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ func TestSetInstallationTokenAction(t *testing.T) {
Name string
Flags []string
Conf fs.FS
Launchd fs.FS
SystemdEnabled bool
LaunchdEnabled bool
ExpWrite []byte
ExpErr bool
}{
Expand Down Expand Up @@ -72,6 +74,44 @@ func TestSetInstallationTokenAction(t *testing.T) {
ExpWrite: []byte("SUMOLOGIC_INSTALLATION_TOKEN=abcdef\n"),
SystemdEnabled: true,
},
{
Name: "launchd",
Flags: []string{"--set-installation-token", "abcdef"},
Launchd: fstest.MapFS{
launchdConfigPlist: &fstest.MapFile{
Data: []byte(defaultLaunchdConfigXML),
},
},
ExpWrite: []byte(xmlPreamble +
`<plist version="1.0">` +
`<dict>` +
`<key>EnvironmentVariables</key>` +
`<dict>` +
`<key>SUMOLOGIC_INSTALLATION_TOKEN</key>` +
`<string>abcdef</string>` +
`</dict>` +
`<key>GroupName</key>` +
`<string>_otelcol-sumo</string>` +
`<key>KeepAlive</key>` +
`<true/>` +
`<key>Label</key>` +
`<string>otelcol-sumo</string>` +
`<key>ProgramArguments</key>` +
`<array>` +
`<string>/usr/share/otelcol-sumo.sh</string>` +
`</array>` +
`<key>RunAtLoad</key>` +
`<true/>` +
`<key>StandardErrorPath</key>` +
`<string>/var/log/otelcol-sumo/otelcol-sumo.log</string>` +
`<key>StandardOutPath</key>` +
`<string>/var/log/otelcol-sumo/otelcol-sumo.log</string>` +
`<key>UserName</key>` +
`<string>_otelcol-sumo</string>` +
`</dict>` +
`</plist>`),
LaunchdEnabled: true,
},
}

for _, test := range tests {
Expand All @@ -80,21 +120,33 @@ func TestSetInstallationTokenAction(t *testing.T) {
errWriter := errWriter{}.Write

flagValues := newFlagValues()
fs := makeFlagSet(flagValues)
flagSet := makeFlagSet(flagValues)

if err := fs.Parse(test.Flags); err != nil {
if err := flagSet.Parse(test.Flags); err != nil {
t.Fatal(err)
}

var settingsWriter, overridesWriter, sumologicRemoteWriter, tokenEnvWriter func([]byte) (int, error)
var (
settingsWriter,
overridesWriter,
sumologicRemoteWriter,
tokenEnvWriter,
launchdWriter func([]byte) (int, error)
)

tokenEnvWriter = errWriter
if test.SystemdEnabled {
switch {
case test.SystemdEnabled:
tokenEnvWriter = writer
settingsWriter = errWriter
sumologicRemoteWriter = errWriter
overridesWriter = errWriter
} else {
case test.LaunchdEnabled:
launchdWriter = writer
settingsWriter = errWriter
sumologicRemoteWriter = errWriter
overridesWriter = errWriter
default:
if flagValues.Override {
settingsWriter = errWriter
sumologicRemoteWriter = errWriter
Expand All @@ -112,14 +164,17 @@ func TestSetInstallationTokenAction(t *testing.T) {

ctx := &actionContext{
ConfigDir: test.Conf,
LaunchdDir: test.Launchd,
Flags: flagValues,
Stdout: io.Discard,
Stderr: io.Discard,
WriteConfD: settingsWriter,
WriteConfDOverrides: overridesWriter,
WriteSumologicRemote: sumologicRemoteWriter,
WriteInstallationTokenEnv: tokenEnvWriter,
WriteLaunchdConfig: launchdWriter,
SystemdEnabled: test.SystemdEnabled,
LaunchdEnabled: test.LaunchdEnabled,
}

err := SetInstallationTokenAction(ctx)
Expand Down
54 changes: 54 additions & 0 deletions pkg/tools/otelcol-config/launchd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"fmt"
"io/fs"

"howett.net/plist"
)

const (
launchdConfigPlist = "com.sumologic.otelcol-sumo.plist"
)

type launchdConfig struct {
Format int
Root launchdRoot
}

type launchdRoot struct {
Label string `plist:"Label"`
ProgramArguments []string `plist:"ProgramArguments"`
EnvironmentVariables map[string]string `plist:"EnvironmentVariables"`
UserName string `plist:"UserName"`
GroupName string `plist:"GroupName"`
RunAtLoad bool `plist:"RunAtLoad"`
KeepAlive bool `plist:"KeepAlive"`
StandardOutPath string `plist:"StandardOutPath"`
StandardErrorPath string `plist:"StandardErrorPath"`
}

// ReadLaunchdConfig reads and unmarshals the launchd config from
// /Library/LaunchDaemons/com.sumologic.otelcol-sumo.plist. It produces a
// launchdConfig that contains the data read from the launchd config.
func ReadLaunchdConfig(root fs.FS) (launchdConfig, error) {
const errMsg = ""

conf := launchdConfig{}

bytes, err := fs.ReadFile(root, launchdConfigPlist)
if err != nil {
return conf, fmt.Errorf("error reading launchd config: %s", err)
}

confRoot := launchdRoot{}
format, err := plist.Unmarshal(bytes, &confRoot)
if err != nil {
return conf, fmt.Errorf("error unmarshaling launchd config: %s", err)
}

conf.Format = format
conf.Root = confRoot

return conf, nil
}
40 changes: 40 additions & 0 deletions pkg/tools/otelcol-config/launchd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

const xmlPreamble = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
`

var defaultLaunchdConfigXML = xmlPreamble +
`<plist version="1.0">` +
`<dict>` +
`<key>Label</key>` +
`<string>otelcol-sumo</string>` +
`<key>ProgramArguments</key>` +
`<array>` +
`<string>/usr/share/otelcol-sumo.sh</string>` +
`</array>` +
`<key>EnvironmentVariables</key>` +
`<dict>` +
`<key>SUMOLOGIC_INSTALLATION_TOKEN</key>` +
`<string></string>` +
`</dict>` +
`<!-- Service user -->` +
`<key>UserName</key>` +
`<string>_otelcol-sumo</string>` +
`<!-- Service group -->` +
`<key>GroupName</key>` +
`<string>_otelcol-sumo</string>` +
`<!-- Run the service immediately after it is loaded -->` +
`<key>RunAtLoad</key>` +
`<true/>` +
`<!-- Restart the process if it exits -->` +
`<key>KeepAlive</key>` +
`<true/>` +
`<!-- Redirect stdout to a log file -->` +
`<key>StandardOutPath</key>` +
`<string>/var/log/otelcol-sumo/otelcol-sumo.log</string>` +
`<!-- Redirect stderr to a log file -->` +
`<key>StandardErrorPath</key>` +
`<string>/var/log/otelcol-sumo/otelcol-sumo.log</string>` +
`</dict>` +
`</plist>`
Loading

0 comments on commit 17a2d06

Please sign in to comment.