Skip to content

Commit

Permalink
Allow control server to pin version for osqueryd and launcher (kolide…
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMahany authored Feb 29, 2024
1 parent 3a88d8d commit a3198f4
Show file tree
Hide file tree
Showing 19 changed files with 883 additions and 155 deletions.
33 changes: 33 additions & 0 deletions ee/agent/flags/flag_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/kolide/launcher/ee/agent/flags/keys"
"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/ee/tuf"
"github.com/kolide/launcher/pkg/autoupdate"
"github.com/kolide/launcher/pkg/launcher"
"golang.org/x/exp/maps"
Expand Down Expand Up @@ -486,6 +487,38 @@ func (fc *FlagController) UpdateDirectory() string {
).get(fc.getControlServerValue(keys.UpdateDirectory))
}

func (fc *FlagController) SetPinnedLauncherVersion(version string) error {
return fc.setControlServerValue(keys.PinnedLauncherVersion, []byte(version))
}
func (fc *FlagController) PinnedLauncherVersion() string {
fc.overrideMutex.RLock()
defer fc.overrideMutex.RUnlock()

return NewStringFlagValue(
WithOverrideString(fc.overrides[keys.PinnedLauncherVersion]),
WithDefaultString(""),
WithSanitizer(func(version string) string {
return tuf.SanitizePinnedVersion("launcher", version)
}),
).get(fc.getControlServerValue(keys.PinnedLauncherVersion))
}

func (fc *FlagController) SetPinnedOsquerydVersion(version string) error {
return fc.setControlServerValue(keys.PinnedOsquerydVersion, []byte(version))
}
func (fc *FlagController) PinnedOsquerydVersion() string {
fc.overrideMutex.RLock()
defer fc.overrideMutex.RUnlock()

return NewStringFlagValue(
WithOverrideString(fc.overrides[keys.PinnedOsquerydVersion]),
WithDefaultString(""),
WithSanitizer(func(version string) string {
return tuf.SanitizePinnedVersion("osqueryd", version)
}),
).get(fc.getControlServerValue(keys.PinnedOsquerydVersion))
}

func (fc *FlagController) SetExportTraces(enabled bool) error {
return fc.setControlServerValue(keys.ExportTraces, boolToBytes(enabled))
}
Expand Down
2 changes: 2 additions & 0 deletions ee/agent/flags/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const (
UpdateChannel FlagKey = "update_channel"
AutoupdateInitialDelay FlagKey = "autoupdater_initial_delay"
UpdateDirectory FlagKey = "update_directory"
PinnedLauncherVersion FlagKey = "pinned_launcher_version"
PinnedOsquerydVersion FlagKey = "pinned_osqueryd_version"
ExportTraces FlagKey = "export_traces"
TraceSamplingRate FlagKey = "trace_sampling_rate"
TraceBatchTimeout FlagKey = "trace_batch_timeout"
Expand Down
2 changes: 1 addition & 1 deletion ee/agent/knapsack/knapsack.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (k *knapsack) getKVStore(storeType storage.Store) types.KVStore {
}

func (k *knapsack) LatestOsquerydPath(ctx context.Context) string {
latestBin, err := tuf.CheckOutLatest(ctx, "osqueryd", k.RootDirectory(), k.UpdateDirectory(), k.UpdateChannel(), k.Slogger())
latestBin, err := tuf.CheckOutLatest(ctx, "osqueryd", k.RootDirectory(), k.UpdateDirectory(), k.PinnedOsquerydVersion(), k.UpdateChannel(), k.Slogger())
if err != nil {
return autoupdate.FindNewest(ctx, k.OsquerydPath())
}
Expand Down
10 changes: 6 additions & 4 deletions ee/agent/startupsettings/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ func OpenWriter(ctx context.Context, knapsack types.Knapsack) (*startupSettingsW
kvStore: store,
knapsack: knapsack,
storedFlags: map[keys.FlagKey]func() string{
keys.UpdateChannel: func() string { return knapsack.UpdateChannel() },
keys.UpdateChannel: func() string { return knapsack.UpdateChannel() },
keys.PinnedLauncherVersion: func() string { return knapsack.PinnedLauncherVersion() },
keys.PinnedOsquerydVersion: func() string { return knapsack.PinnedOsquerydVersion() },
},
}

// Attempt to ensure flags are up-to-date
if err := s.setFlags(ctx); err != nil {
if err := s.setFlags(); err != nil {
s.knapsack.Slogger().Log(ctx, slog.LevelWarn, "could not set flags", "err", err)
}

Expand All @@ -54,7 +56,7 @@ func OpenWriter(ctx context.Context, knapsack types.Knapsack) (*startupSettingsW
}

// setFlags updates the flags with their values from the agent flag data store.
func (s *startupSettingsWriter) setFlags(ctx context.Context) error {
func (s *startupSettingsWriter) setFlags() error {
updatedFlags := make(map[string]string)
for flag, getter := range s.storedFlags {
updatedFlags[flag.String()] = getter()
Expand All @@ -71,7 +73,7 @@ func (s *startupSettingsWriter) setFlags(ctx context.Context) error {
// that the startup database is registered for has a new value, the startup database
// stores that updated value.
func (s *startupSettingsWriter) FlagsChanged(flagKeys ...keys.FlagKey) {
if err := s.setFlags(context.Background()); err != nil {
if err := s.setFlags(); err != nil {
s.knapsack.Slogger().Log(context.Background(), slog.LevelError,
"could not set flags after change",
"err", err,
Expand Down
54 changes: 54 additions & 0 deletions ee/agent/startupsettings/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ func TestOpenWriter_NewDatabase(t *testing.T) {
k := typesmocks.NewKnapsack(t)
k.On("RootDirectory").Return(testRootDir)
k.On("RegisterChangeObserver", mock.Anything, keys.UpdateChannel)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedLauncherVersion)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedOsquerydVersion)
updateChannelVal := "stable"
k.On("UpdateChannel").Return(updateChannelVal)
k.On("PinnedLauncherVersion").Return("")
k.On("PinnedOsquerydVersion").Return("5.11.0")

// Set up storage db, which should create the database and set all flags
s, err := OpenWriter(context.TODO(), k)
Expand All @@ -44,22 +48,38 @@ func TestOpenWriter_DatabaseAlreadyExists(t *testing.T) {
store, err := agentsqlite.OpenRW(context.TODO(), testRootDir, agentsqlite.StartupSettingsStore)
require.NoError(t, err, "getting connection to test db")
require.NoError(t, store.Set([]byte(keys.UpdateChannel.String()), []byte("some_old_value")), "setting key")
require.NoError(t, store.Set([]byte(keys.PinnedLauncherVersion.String()), []byte("")), "setting key")
require.NoError(t, store.Set([]byte(keys.PinnedOsquerydVersion.String()), []byte("")), "setting key")

// Confirm flags were set
v1, err := store.Get([]byte(keys.UpdateChannel.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, "some_old_value", string(v1), "incorrect flag value")

v2, err := store.Get([]byte(keys.PinnedLauncherVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, "", string(v2), "incorrect flag value")

v3, err := store.Get([]byte(keys.PinnedOsquerydVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, "", string(v3), "incorrect flag value")

require.NoError(t, store.Close(), "closing setup connection")

// Set up dependencies
k := typesmocks.NewKnapsack(t)
k.On("RootDirectory").Return(testRootDir)
k.On("RegisterChangeObserver", mock.Anything, keys.UpdateChannel)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedLauncherVersion)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedOsquerydVersion)

// Set up flag
updateChannelVal := "alpha"
pinnedLauncherVersion := "1.6.0"
pinnedOsquerydVersion := "5.11.0"
k.On("UpdateChannel").Return(updateChannelVal)
k.On("PinnedLauncherVersion").Return(pinnedLauncherVersion)
k.On("PinnedOsquerydVersion").Return(pinnedOsquerydVersion)

// Set up storage db, which should create the database and set all flags
s, err := OpenWriter(context.TODO(), k)
Expand All @@ -70,6 +90,14 @@ func TestOpenWriter_DatabaseAlreadyExists(t *testing.T) {
require.NoError(t, err, "getting startup value")
require.Equal(t, updateChannelVal, string(v1), "incorrect flag value")

v2, err = s.kvStore.Get([]byte(keys.PinnedLauncherVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, pinnedLauncherVersion, string(v2), "incorrect flag value")

v3, err = s.kvStore.Get([]byte(keys.PinnedOsquerydVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, pinnedOsquerydVersion, string(v3), "incorrect flag value")

require.NoError(t, s.Close(), "closing startup db")
}

Expand All @@ -81,8 +109,14 @@ func TestFlagsChanged(t *testing.T) {
k := typesmocks.NewKnapsack(t)
k.On("RootDirectory").Return(testRootDir)
k.On("RegisterChangeObserver", mock.Anything, keys.UpdateChannel)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedLauncherVersion)
k.On("RegisterChangeObserver", mock.Anything, keys.PinnedOsquerydVersion)
updateChannelVal := "beta"
k.On("UpdateChannel").Return(updateChannelVal).Once()
pinnedLauncherVersion := "1.2.3"
k.On("PinnedLauncherVersion").Return(pinnedLauncherVersion).Once()
pinnedOsquerydVersion := "5.3.2"
k.On("PinnedOsquerydVersion").Return(pinnedOsquerydVersion).Once()

// Set up storage db, which should create the database and set all flags
s, err := OpenWriter(context.TODO(), k)
Expand All @@ -93,16 +127,36 @@ func TestFlagsChanged(t *testing.T) {
require.NoError(t, err, "getting startup value")
require.Equal(t, updateChannelVal, string(v1), "incorrect flag value")

v2, err := s.kvStore.Get([]byte(keys.PinnedLauncherVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, pinnedLauncherVersion, string(v2), "incorrect flag value")

v3, err := s.kvStore.Get([]byte(keys.PinnedOsquerydVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, pinnedOsquerydVersion, string(v3), "incorrect flag value")

// Now, prepare for flag changes
newFlagValue := "alpha"
k.On("UpdateChannel").Return(newFlagValue).Once()
newPinnedLauncherVersion := ""
k.On("PinnedLauncherVersion").Return(newPinnedLauncherVersion).Once()
newPinnedOsquerydVersion := "5.4.3"
k.On("PinnedOsquerydVersion").Return(newPinnedOsquerydVersion).Once()

// Call FlagsChanged and expect that all flag values are updated
s.FlagsChanged(keys.UpdateChannel)
v1, err = s.kvStore.Get([]byte(keys.UpdateChannel.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, newFlagValue, string(v1), "incorrect flag value")

v2, err = s.kvStore.Get([]byte(keys.PinnedLauncherVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, newPinnedLauncherVersion, string(v2), "incorrect flag value")

v3, err = s.kvStore.Get([]byte(keys.PinnedOsquerydVersion.String()))
require.NoError(t, err, "getting startup value")
require.Equal(t, newPinnedOsquerydVersion, string(v3), "incorrect flag value")

require.NoError(t, s.Close(), "closing startup db")
}

Expand Down
8 changes: 8 additions & 0 deletions ee/agent/types/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ type Flags interface {
SetUpdateDirectory(directory string) error
UpdateDirectory() string

// PinnedLauncherVersion is the launcher version to lock the autoupdater to, rather than autoupdating via the update channel.
SetPinnedLauncherVersion(version string) error
PinnedLauncherVersion() string

// PinnedOsquerydVersion is the osqueryd version to lock the autoupdater to, rather than autoupdating via the update channel.
SetPinnedOsquerydVersion(version string) error
PinnedOsquerydVersion() string

// ExportTraces enables exporting our traces
SetExportTraces(enabled bool) error
SetExportTracesOverride(value bool, duration time.Duration)
Expand Down
56 changes: 56 additions & 0 deletions ee/agent/types/mocks/flags.go

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

56 changes: 56 additions & 0 deletions ee/agent/types/mocks/knapsack.go

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

Loading

0 comments on commit a3198f4

Please sign in to comment.