Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow control server to pin version for osqueryd and launcher #1629

Merged
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
Loading