From 7bc8fc8d836890f9c09cf9f030ca2e2d3dcf4254 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 12 Oct 2023 05:45:17 -0700 Subject: [PATCH 01/16] Remove context and handle cancellation internally instead --- .../artifact/download/http/progress_reporter.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go index 491646b3ab5..21aca351dc4 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go @@ -66,7 +66,7 @@ func (dp *downloadProgressReporter) Report(ctx context.Context) { defer t.Stop() for { select { - case <-ctx.Done(): + case <-dp.done: return case <-dp.done: return @@ -107,6 +107,8 @@ func (dp *downloadProgressReporter) ReportComplete() { for _, obs := range dp.progressObservers { obs.ReportCompleted(dp.sourceURI, timePast, bytesPerSecond) } + + dp.close() } // ReportFailed reports the failure of a download to registered observers. Callers MUST call @@ -132,4 +134,10 @@ func (dp *downloadProgressReporter) ReportFailed(err error) { for _, obs := range dp.progressObservers { obs.ReportFailed(dp.sourceURI, timePast, downloaded, dp.length, percentComplete, bytesPerSecond, err) } + + dp.close() +} + +func (dp *downloadProgressReporter) close() { + dp.done <- struct{}{} } From b6bd1ead95e30aea49850f6c4ca9eaf5697bd8d0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 12 Oct 2023 05:47:51 -0700 Subject: [PATCH 02/16] More optimizations --- .../upgrade/artifact/download/http/progress_reporter.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go index 21aca351dc4..3c9561937ac 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go @@ -107,8 +107,6 @@ func (dp *downloadProgressReporter) ReportComplete() { for _, obs := range dp.progressObservers { obs.ReportCompleted(dp.sourceURI, timePast, bytesPerSecond) } - - dp.close() } // ReportFailed reports the failure of a download to registered observers. Callers MUST call @@ -134,10 +132,4 @@ func (dp *downloadProgressReporter) ReportFailed(err error) { for _, obs := range dp.progressObservers { obs.ReportFailed(dp.sourceURI, timePast, downloaded, dp.length, percentComplete, bytesPerSecond, err) } - - dp.close() -} - -func (dp *downloadProgressReporter) close() { - dp.done <- struct{}{} } From 28ce34f4682112cb8a068774714ecf0b3ba41c87 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 12 Oct 2023 09:45:53 -0700 Subject: [PATCH 03/16] Add back context --- .../upgrade/artifact/download/http/progress_reporter.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go index 3c9561937ac..2df81be6018 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go @@ -66,6 +66,8 @@ func (dp *downloadProgressReporter) Report(ctx context.Context) { defer t.Stop() for { select { + case <-ctx.Done(): + return case <-dp.done: return case <-dp.done: From 0ec900a990dd0159ec86ab8b476cde57c6dcd1f7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Oct 2023 17:55:11 -0700 Subject: [PATCH 04/16] Adding FSM for upgrades --- go.mod | 1 + go.sum | 2 + internal/pkg/agent/application/upgrade/fsm.go | 108 ++++++++++++++++++ .../pkg/agent/application/upgrade/fsm_test.go | 42 +++++++ 4 files changed, 153 insertions(+) create mode 100644 internal/pkg/agent/application/upgrade/fsm.go create mode 100644 internal/pkg/agent/application/upgrade/fsm_test.go diff --git a/go.mod b/go.mod index 847bf6d35c5..fa00c749819 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7 + github.com/looplab/fsm v1.0.1 github.com/magefile/mage v1.15.0 github.com/mitchellh/gox v1.0.1 github.com/mitchellh/hashstructure v1.1.0 diff --git a/go.sum b/go.sum index 708ac86b4fc..c6d2633bfea 100644 --- a/go.sum +++ b/go.sum @@ -1269,6 +1269,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= +github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= +github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= diff --git a/internal/pkg/agent/application/upgrade/fsm.go b/internal/pkg/agent/application/upgrade/fsm.go new file mode 100644 index 00000000000..4d8289baa70 --- /dev/null +++ b/internal/pkg/agent/application/upgrade/fsm.go @@ -0,0 +1,108 @@ +package upgrade + +import ( + "context" + "fmt" + + "github.com/elastic/elastic-agent/pkg/core/logger" + "github.com/looplab/fsm" +) + +const ( + StateRequested = "UPG_REQUESTED" + StateScheduled = "UPG_SCHEDULED" + StateDownloading = "UPG_DOWNLOADING" + StateExtracting = "UPG_EXTRACTING" + StateReplacing = "UPG_REPLACING" + StateRestarting = "UPG_RESTARTING" + StateWatching = "UPG_WATCHING" + StateRollback = "UPG_ROLLBACK" + StateCompleted = "UPG_COMPLETED" + StateFailed = "UPG_FAILED" + + ActionSucceeded = "succeeded" + ActionFailed = "failed" + ActionScheduling = "scheduling" + ActionRollingBack = "rolling_back" +) + +type FSM struct { + m *fsm.FSM +} + +type StateTransitionCallback func(oldState, newState, action string) + +// NewFSM models the following finite state machine for Elastic Agent upgrades: +// +// UPG_REQUESTED +// ├─ scheduling ─> UPG_SCHEDULED +// └─ succeeded ─> UPG_DOWNLOADING +// UPG_SCHEDULED +// ├─ succeeded ─> UPG_DOWNLOADING +// └─ failed ─> UPG_FAILED +// UPG_DOWNLOADING +// ├─ succeeded ─> UPG_EXTRACTING +// └─ failed ─> UPG_FAILED +// UPG_EXTRACTING +// ├─ succeeded ─> UPG_REPLACING +// └─ failed ─> UPG_FAILED +// UPG_REPLACING +// ├─ succeeded ─> UPG_RESTARTING +// └─ failed ─> UPG_FAILED +// UPG_RESTARTING +// ├─ succeeded ─> UPG_WATCHING +// └─ failed ─> UPG_FAILED +// UPG_WATCHING +// ├─ succeeded ─> UPG_COMPLETED +// ├─ rolling_back ─> UPG_ROLLBACK +// └─ failed ─> UPG_FAILED +// UPG_ROLLBACK +// ├─ succeeded ─> UPG_COMPLETED +// └─ failed ─> UPG_FAILED +// +// It accepts an onAction callback that will be called any time a transition from one +// state to the next is performed. +func NewFSM(log *logger.Logger, onTransition StateTransitionCallback) *FSM { + return &FSM{ + m: fsm.NewFSM( + StateRequested, + fsm.Events{ + {Name: ActionScheduling, Src: []string{StateRequested}, Dst: StateScheduled}, + {Name: ActionSucceeded, Src: []string{StateRequested, StateScheduled}, Dst: StateDownloading}, + {Name: ActionSucceeded, Src: []string{StateDownloading}, Dst: StateExtracting}, + {Name: ActionSucceeded, Src: []string{StateExtracting}, Dst: StateReplacing}, + {Name: ActionSucceeded, Src: []string{StateReplacing}, Dst: StateRestarting}, + {Name: ActionSucceeded, Src: []string{StateRestarting}, Dst: StateWatching}, + {Name: ActionSucceeded, Src: []string{StateWatching}, Dst: StateCompleted}, + {Name: ActionRollingBack, Src: []string{StateWatching}, Dst: StateRollback}, + {Name: ActionSucceeded, Src: []string{StateRollback}, Dst: StateCompleted}, + {Name: ActionFailed, Src: []string{StateScheduled, StateDownloading, StateExtracting, StateReplacing, StateRestarting, StateWatching, StateRollback}, Dst: StateFailed}, + }, + fsm.Callbacks{ + "enter_state": func(_ context.Context, e *fsm.Event) { + // Log state entry + log.Infow( + "transitioning upgrade state machine", + "from", e.Src, "to", e.Dst, "action", e.Event, + ) + + // Call onTransition + onTransition(e.Src, e.Dst, e.Event) + }, + }, + ), + } +} + +func (f *FSM) Transition(ctx context.Context, action string, args ...interface{}) error { + current := f.Current() + if err := f.m.Event(ctx, action, args...); err != nil { + return fmt.Errorf("unable to transition from [%s] with action [%s]: %w", current, action, err) + } + + return nil +} + +func (f *FSM) Current() string { + return f.m.Current() +} diff --git a/internal/pkg/agent/application/upgrade/fsm_test.go b/internal/pkg/agent/application/upgrade/fsm_test.go new file mode 100644 index 00000000000..08860faa502 --- /dev/null +++ b/internal/pkg/agent/application/upgrade/fsm_test.go @@ -0,0 +1,42 @@ +package upgrade + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-agent/pkg/core/logger" +) + +func TestFSM(t *testing.T) { + l, obs := logger.NewTesting("upgrade_fsm") + cb := func(oldState, newState, action string) { + // TODO + } + + fsm := NewFSM(l, cb) + + // Test that valid transition succeeds + err := fsm.Transition(context.Background(), ActionSucceeded) + require.NoError(t, err) + require.Equal(t, StateDownloading, fsm.Current()) + require.Equal(t, 1, obs.Len()) + + log := obs.TakeAll()[0] + require.Equal(t, "transitioning upgrade state machine", log.Message) + require.Equal(t, "from", log.Context[0].Key) + require.Equal(t, StateRequested, log.Context[0].String) + require.Equal(t, "to", log.Context[1].Key) + require.Equal(t, StateDownloading, log.Context[1].String) + + // Test that invalid transition returns error + err = fsm.Transition(context.Background(), ActionRollingBack) + require.Error(t, err) + require.EqualError(t, err, fmt.Sprintf( + "unable to transition from [%[1]s] with action [%[2]s]: event %[2]s inappropriate in current state %[1]s", + StateDownloading, ActionRollingBack, + )) + require.Equal(t, 0, obs.Len()) +} From 5682966e9e0ec240780c155bb7be1cec9189c0a8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 2 Oct 2023 18:49:34 -0700 Subject: [PATCH 05/16] Implementing TODO --- internal/pkg/agent/application/upgrade/fsm.go | 7 +- .../pkg/agent/application/upgrade/fsm_test.go | 69 ++++++++++++++----- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/internal/pkg/agent/application/upgrade/fsm.go b/internal/pkg/agent/application/upgrade/fsm.go index 4d8289baa70..a5469cc893d 100644 --- a/internal/pkg/agent/application/upgrade/fsm.go +++ b/internal/pkg/agent/application/upgrade/fsm.go @@ -1,11 +1,16 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + package upgrade import ( "context" "fmt" - "github.com/elastic/elastic-agent/pkg/core/logger" "github.com/looplab/fsm" + + "github.com/elastic/elastic-agent/pkg/core/logger" ) const ( diff --git a/internal/pkg/agent/application/upgrade/fsm_test.go b/internal/pkg/agent/application/upgrade/fsm_test.go index 08860faa502..b116b022fb6 100644 --- a/internal/pkg/agent/application/upgrade/fsm_test.go +++ b/internal/pkg/agent/application/upgrade/fsm_test.go @@ -1,42 +1,79 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + package upgrade import ( "context" "fmt" + "sync/atomic" "testing" "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest/observer" "github.com/elastic/elastic-agent/pkg/core/logger" ) func TestFSM(t *testing.T) { l, obs := logger.NewTesting("upgrade_fsm") + + var cbCallCounter atomic.Int64 cb := func(oldState, newState, action string) { - // TODO + cbCallCounter.Add(1) } fsm := NewFSM(l, cb) + require.Equal(t, int64(0), cbCallCounter.Load()) + + // Test that valid transitions succeed + t.Run("valid_transition", func(t *testing.T) { + // UPG_REQUESTED ─── scheduling ───> UPG_SCHEDULED + err := fsm.Transition(context.Background(), ActionScheduling) + verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateRequested, StateScheduled, 1) + + // UPG_SCHEDULED ─── succeeded ───> UPG_DOWNLOADING + err = fsm.Transition(context.Background(), ActionSucceeded) + verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateScheduled, StateDownloading, 2) + + // UPG_DOWNLOADING ─── failed ───> UPG_FAILED + err = fsm.Transition(context.Background(), ActionFailed) + verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateDownloading, StateFailed, 3) + }) + + // Test that an invalid transition returns error + t.Run("invalid_transition", func(t *testing.T) { + // UPG_FAILED ─── rolling_back ───> error + err := fsm.Transition(context.Background(), ActionRollingBack) + require.Error(t, err) + require.EqualError(t, err, fmt.Sprintf( + "unable to transition from [%[1]s] with action [%[2]s]: event %[2]s inappropriate in current state %[1]s", + StateFailed, ActionRollingBack, + )) + require.Equal(t, int64(3), cbCallCounter.Load()) + require.Equal(t, 0, obs.Len()) + }) +} + +func verifyValidTransition( + t *testing.T, + upgradeFSM *FSM, obs *observer.ObservedLogs, + actualErr error, actualCBCallCounter int64, + expectedStartState, expectedEndState string, + expectedCBCallCounter int64, +) { + t.Helper() - // Test that valid transition succeeds - err := fsm.Transition(context.Background(), ActionSucceeded) - require.NoError(t, err) - require.Equal(t, StateDownloading, fsm.Current()) + require.NoError(t, actualErr) + require.Equal(t, expectedEndState, upgradeFSM.Current()) require.Equal(t, 1, obs.Len()) + require.Equal(t, expectedCBCallCounter, actualCBCallCounter) log := obs.TakeAll()[0] require.Equal(t, "transitioning upgrade state machine", log.Message) require.Equal(t, "from", log.Context[0].Key) - require.Equal(t, StateRequested, log.Context[0].String) + require.Equal(t, expectedStartState, log.Context[0].String) require.Equal(t, "to", log.Context[1].Key) - require.Equal(t, StateDownloading, log.Context[1].String) - - // Test that invalid transition returns error - err = fsm.Transition(context.Background(), ActionRollingBack) - require.Error(t, err) - require.EqualError(t, err, fmt.Sprintf( - "unable to transition from [%[1]s] with action [%[2]s]: event %[2]s inappropriate in current state %[1]s", - StateDownloading, ActionRollingBack, - )) - require.Equal(t, 0, obs.Len()) + require.Equal(t, expectedEndState, log.Context[1].String) } From ba3e4f7b226d8c2171a7fc4507ed66c34cb0e52a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Oct 2023 05:41:18 -0700 Subject: [PATCH 06/16] WIP --- internal/pkg/agent/application/upgrade/fsm.go | 113 ------------------ .../pkg/agent/application/upgrade/fsm_test.go | 79 ------------ .../pkg/agent/application/upgrade/state.go | 24 ++++ .../application/upgrade/step_download.go | 3 +- 4 files changed, 25 insertions(+), 194 deletions(-) delete mode 100644 internal/pkg/agent/application/upgrade/fsm.go delete mode 100644 internal/pkg/agent/application/upgrade/fsm_test.go create mode 100644 internal/pkg/agent/application/upgrade/state.go diff --git a/internal/pkg/agent/application/upgrade/fsm.go b/internal/pkg/agent/application/upgrade/fsm.go deleted file mode 100644 index a5469cc893d..00000000000 --- a/internal/pkg/agent/application/upgrade/fsm.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package upgrade - -import ( - "context" - "fmt" - - "github.com/looplab/fsm" - - "github.com/elastic/elastic-agent/pkg/core/logger" -) - -const ( - StateRequested = "UPG_REQUESTED" - StateScheduled = "UPG_SCHEDULED" - StateDownloading = "UPG_DOWNLOADING" - StateExtracting = "UPG_EXTRACTING" - StateReplacing = "UPG_REPLACING" - StateRestarting = "UPG_RESTARTING" - StateWatching = "UPG_WATCHING" - StateRollback = "UPG_ROLLBACK" - StateCompleted = "UPG_COMPLETED" - StateFailed = "UPG_FAILED" - - ActionSucceeded = "succeeded" - ActionFailed = "failed" - ActionScheduling = "scheduling" - ActionRollingBack = "rolling_back" -) - -type FSM struct { - m *fsm.FSM -} - -type StateTransitionCallback func(oldState, newState, action string) - -// NewFSM models the following finite state machine for Elastic Agent upgrades: -// -// UPG_REQUESTED -// ├─ scheduling ─> UPG_SCHEDULED -// └─ succeeded ─> UPG_DOWNLOADING -// UPG_SCHEDULED -// ├─ succeeded ─> UPG_DOWNLOADING -// └─ failed ─> UPG_FAILED -// UPG_DOWNLOADING -// ├─ succeeded ─> UPG_EXTRACTING -// └─ failed ─> UPG_FAILED -// UPG_EXTRACTING -// ├─ succeeded ─> UPG_REPLACING -// └─ failed ─> UPG_FAILED -// UPG_REPLACING -// ├─ succeeded ─> UPG_RESTARTING -// └─ failed ─> UPG_FAILED -// UPG_RESTARTING -// ├─ succeeded ─> UPG_WATCHING -// └─ failed ─> UPG_FAILED -// UPG_WATCHING -// ├─ succeeded ─> UPG_COMPLETED -// ├─ rolling_back ─> UPG_ROLLBACK -// └─ failed ─> UPG_FAILED -// UPG_ROLLBACK -// ├─ succeeded ─> UPG_COMPLETED -// └─ failed ─> UPG_FAILED -// -// It accepts an onAction callback that will be called any time a transition from one -// state to the next is performed. -func NewFSM(log *logger.Logger, onTransition StateTransitionCallback) *FSM { - return &FSM{ - m: fsm.NewFSM( - StateRequested, - fsm.Events{ - {Name: ActionScheduling, Src: []string{StateRequested}, Dst: StateScheduled}, - {Name: ActionSucceeded, Src: []string{StateRequested, StateScheduled}, Dst: StateDownloading}, - {Name: ActionSucceeded, Src: []string{StateDownloading}, Dst: StateExtracting}, - {Name: ActionSucceeded, Src: []string{StateExtracting}, Dst: StateReplacing}, - {Name: ActionSucceeded, Src: []string{StateReplacing}, Dst: StateRestarting}, - {Name: ActionSucceeded, Src: []string{StateRestarting}, Dst: StateWatching}, - {Name: ActionSucceeded, Src: []string{StateWatching}, Dst: StateCompleted}, - {Name: ActionRollingBack, Src: []string{StateWatching}, Dst: StateRollback}, - {Name: ActionSucceeded, Src: []string{StateRollback}, Dst: StateCompleted}, - {Name: ActionFailed, Src: []string{StateScheduled, StateDownloading, StateExtracting, StateReplacing, StateRestarting, StateWatching, StateRollback}, Dst: StateFailed}, - }, - fsm.Callbacks{ - "enter_state": func(_ context.Context, e *fsm.Event) { - // Log state entry - log.Infow( - "transitioning upgrade state machine", - "from", e.Src, "to", e.Dst, "action", e.Event, - ) - - // Call onTransition - onTransition(e.Src, e.Dst, e.Event) - }, - }, - ), - } -} - -func (f *FSM) Transition(ctx context.Context, action string, args ...interface{}) error { - current := f.Current() - if err := f.m.Event(ctx, action, args...); err != nil { - return fmt.Errorf("unable to transition from [%s] with action [%s]: %w", current, action, err) - } - - return nil -} - -func (f *FSM) Current() string { - return f.m.Current() -} diff --git a/internal/pkg/agent/application/upgrade/fsm_test.go b/internal/pkg/agent/application/upgrade/fsm_test.go deleted file mode 100644 index b116b022fb6..00000000000 --- a/internal/pkg/agent/application/upgrade/fsm_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package upgrade - -import ( - "context" - "fmt" - "sync/atomic" - "testing" - - "github.com/stretchr/testify/require" - "go.uber.org/zap/zaptest/observer" - - "github.com/elastic/elastic-agent/pkg/core/logger" -) - -func TestFSM(t *testing.T) { - l, obs := logger.NewTesting("upgrade_fsm") - - var cbCallCounter atomic.Int64 - cb := func(oldState, newState, action string) { - cbCallCounter.Add(1) - } - - fsm := NewFSM(l, cb) - require.Equal(t, int64(0), cbCallCounter.Load()) - - // Test that valid transitions succeed - t.Run("valid_transition", func(t *testing.T) { - // UPG_REQUESTED ─── scheduling ───> UPG_SCHEDULED - err := fsm.Transition(context.Background(), ActionScheduling) - verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateRequested, StateScheduled, 1) - - // UPG_SCHEDULED ─── succeeded ───> UPG_DOWNLOADING - err = fsm.Transition(context.Background(), ActionSucceeded) - verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateScheduled, StateDownloading, 2) - - // UPG_DOWNLOADING ─── failed ───> UPG_FAILED - err = fsm.Transition(context.Background(), ActionFailed) - verifyValidTransition(t, fsm, obs, err, cbCallCounter.Load(), StateDownloading, StateFailed, 3) - }) - - // Test that an invalid transition returns error - t.Run("invalid_transition", func(t *testing.T) { - // UPG_FAILED ─── rolling_back ───> error - err := fsm.Transition(context.Background(), ActionRollingBack) - require.Error(t, err) - require.EqualError(t, err, fmt.Sprintf( - "unable to transition from [%[1]s] with action [%[2]s]: event %[2]s inappropriate in current state %[1]s", - StateFailed, ActionRollingBack, - )) - require.Equal(t, int64(3), cbCallCounter.Load()) - require.Equal(t, 0, obs.Len()) - }) -} - -func verifyValidTransition( - t *testing.T, - upgradeFSM *FSM, obs *observer.ObservedLogs, - actualErr error, actualCBCallCounter int64, - expectedStartState, expectedEndState string, - expectedCBCallCounter int64, -) { - t.Helper() - - require.NoError(t, actualErr) - require.Equal(t, expectedEndState, upgradeFSM.Current()) - require.Equal(t, 1, obs.Len()) - require.Equal(t, expectedCBCallCounter, actualCBCallCounter) - - log := obs.TakeAll()[0] - require.Equal(t, "transitioning upgrade state machine", log.Message) - require.Equal(t, "from", log.Context[0].Key) - require.Equal(t, expectedStartState, log.Context[0].String) - require.Equal(t, "to", log.Context[1].Key) - require.Equal(t, expectedEndState, log.Context[1].String) -} diff --git a/internal/pkg/agent/application/upgrade/state.go b/internal/pkg/agent/application/upgrade/state.go new file mode 100644 index 00000000000..f21c4f799da --- /dev/null +++ b/internal/pkg/agent/application/upgrade/state.go @@ -0,0 +1,24 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package upgrade + +type State string + +const ( + StateRequested State = "UPG_REQUESTED" + StateScheduled State = "UPG_SCHEDULED" + StateDownloading State = "UPG_DOWNLOADING" + StateExtracting State = "UPG_EXTRACTING" + StateReplacing State = "UPG_REPLACING" + StateRestarting State = "UPG_RESTARTING" + StateWatching State = "UPG_WATCHING" + StateRollback State = "UPG_ROLLBACK" + StateCompleted State = "UPG_COMPLETED" + StateFailed State = "UPG_FAILED" +) + +func (s State) String() string { + return string(s) +} diff --git a/internal/pkg/agent/application/upgrade/step_download.go b/internal/pkg/agent/application/upgrade/step_download.go index d86a43a5a3b..121a8e9cc26 100644 --- a/internal/pkg/agent/application/upgrade/step_download.go +++ b/internal/pkg/agent/application/upgrade/step_download.go @@ -12,8 +12,6 @@ import ( "strings" "time" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" - "github.com/cenkalti/backoff/v4" "go.elastic.co/apm" @@ -26,6 +24,7 @@ import ( "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/http" "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/localremote" "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/snapshot" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" "github.com/elastic/elastic-agent/internal/pkg/agent/errors" "github.com/elastic/elastic-agent/internal/pkg/release" "github.com/elastic/elastic-agent/pkg/core/logger" From ff0a03804227f8aa95e4b64a22bb3500f5ebef41 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Oct 2023 15:54:52 -0700 Subject: [PATCH 07/16] WIP --- .../handlers/handler_action_upgrade_test.go | 2 ++ .../application/coordinator/coordinator.go | 3 ++- .../pkg/agent/application/upgrade/state.go | 24 ------------------- 3 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 internal/pkg/agent/application/upgrade/state.go diff --git a/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go b/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go index b917b00b900..2dfc74519a0 100644 --- a/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go +++ b/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" + "github.com/stretchr/testify/require" "github.com/elastic/elastic-agent/internal/pkg/agent/application/coordinator" diff --git a/internal/pkg/agent/application/coordinator/coordinator.go b/internal/pkg/agent/application/coordinator/coordinator.go index 778232b91d8..9d691a1c66b 100644 --- a/internal/pkg/agent/application/coordinator/coordinator.go +++ b/internal/pkg/agent/application/coordinator/coordinator.go @@ -10,6 +10,8 @@ import ( "fmt" "time" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" + "github.com/hashicorp/go-multierror" "go.elastic.co/apm" @@ -20,7 +22,6 @@ import ( "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" "github.com/elastic/elastic-agent/internal/pkg/agent/configuration" "github.com/elastic/elastic-agent/internal/pkg/agent/transpiler" "github.com/elastic/elastic-agent/internal/pkg/capabilities" diff --git a/internal/pkg/agent/application/upgrade/state.go b/internal/pkg/agent/application/upgrade/state.go deleted file mode 100644 index f21c4f799da..00000000000 --- a/internal/pkg/agent/application/upgrade/state.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -// or more contributor license agreements. Licensed under the Elastic License; -// you may not use this file except in compliance with the Elastic License. - -package upgrade - -type State string - -const ( - StateRequested State = "UPG_REQUESTED" - StateScheduled State = "UPG_SCHEDULED" - StateDownloading State = "UPG_DOWNLOADING" - StateExtracting State = "UPG_EXTRACTING" - StateReplacing State = "UPG_REPLACING" - StateRestarting State = "UPG_RESTARTING" - StateWatching State = "UPG_WATCHING" - StateRollback State = "UPG_ROLLBACK" - StateCompleted State = "UPG_COMPLETED" - StateFailed State = "UPG_FAILED" -) - -func (s State) String() string { - return string(s) -} From 1730cd91445c40cd7f783fb5aee6d181fff72b8d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Oct 2023 15:56:53 -0700 Subject: [PATCH 08/16] Reorganizing imports --- .../actions/handlers/handler_action_upgrade_test.go | 2 -- internal/pkg/agent/application/coordinator/coordinator.go | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go b/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go index 2dfc74519a0..b917b00b900 100644 --- a/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go +++ b/internal/pkg/agent/application/actions/handlers/handler_action_upgrade_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" - "github.com/stretchr/testify/require" "github.com/elastic/elastic-agent/internal/pkg/agent/application/coordinator" diff --git a/internal/pkg/agent/application/coordinator/coordinator.go b/internal/pkg/agent/application/coordinator/coordinator.go index 9d691a1c66b..778232b91d8 100644 --- a/internal/pkg/agent/application/coordinator/coordinator.go +++ b/internal/pkg/agent/application/coordinator/coordinator.go @@ -10,8 +10,6 @@ import ( "fmt" "time" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" - "github.com/hashicorp/go-multierror" "go.elastic.co/apm" @@ -22,6 +20,7 @@ import ( "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" "github.com/elastic/elastic-agent/internal/pkg/agent/configuration" "github.com/elastic/elastic-agent/internal/pkg/agent/transpiler" "github.com/elastic/elastic-agent/internal/pkg/capabilities" From 3cd9f84d7064922e7bca81f702f991ddc80cf401 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Oct 2023 15:59:48 -0700 Subject: [PATCH 09/16] Running go mod tidy --- go.mod | 1 - go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index fa00c749819..847bf6d35c5 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,6 @@ require ( github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 github.com/josephspurrier/goversioninfo v0.0.0-20190209210621-63e6d1acd3dd github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7 - github.com/looplab/fsm v1.0.1 github.com/magefile/mage v1.15.0 github.com/mitchellh/gox v1.0.1 github.com/mitchellh/hashstructure v1.1.0 diff --git a/go.sum b/go.sum index c6d2633bfea..708ac86b4fc 100644 --- a/go.sum +++ b/go.sum @@ -1269,8 +1269,6 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= -github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= -github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= From b1f368f4765f62411a9acf1c428139333044b8c2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 13 Oct 2023 05:48:47 -0700 Subject: [PATCH 10/16] Fixing booboos introduced during conflict resolution --- .../upgrade/artifact/download/http/progress_reporter.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go index 2df81be6018..491646b3ab5 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/http/progress_reporter.go @@ -70,8 +70,6 @@ func (dp *downloadProgressReporter) Report(ctx context.Context) { return case <-dp.done: return - case <-dp.done: - return case <-t.C: now := time.Now() timePast := now.Sub(started) From f98765d491f5da6e3737f94dd33cccde0491c29f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Oct 2023 14:03:58 -0700 Subject: [PATCH 11/16] Add nil guard --- internal/pkg/agent/application/coordinator/coordinator.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/pkg/agent/application/coordinator/coordinator.go b/internal/pkg/agent/application/coordinator/coordinator.go index 778232b91d8..d4338f9cca6 100644 --- a/internal/pkg/agent/application/coordinator/coordinator.go +++ b/internal/pkg/agent/application/coordinator/coordinator.go @@ -490,6 +490,9 @@ func (c *Coordinator) Upgrade(ctx context.Context, version string, sourceURI str } func (c *Coordinator) logUpgradeDetails(details *details.Details) { + if details == nil { + return + } c.logger.Infow("updated upgrade details", "upgrade_details", details) } From 2f864f079b252ae8d8692e7a99e5693ef57d978b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Oct 2023 14:21:42 -0700 Subject: [PATCH 12/16] Setting logger in test --- internal/pkg/agent/application/coordinator/coordinator.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/pkg/agent/application/coordinator/coordinator.go b/internal/pkg/agent/application/coordinator/coordinator.go index d4338f9cca6..778232b91d8 100644 --- a/internal/pkg/agent/application/coordinator/coordinator.go +++ b/internal/pkg/agent/application/coordinator/coordinator.go @@ -490,9 +490,6 @@ func (c *Coordinator) Upgrade(ctx context.Context, version string, sourceURI str } func (c *Coordinator) logUpgradeDetails(details *details.Details) { - if details == nil { - return - } c.logger.Infow("updated upgrade details", "upgrade_details", details) } From f7dbb2d0cca9168c248088539ab1b86dc15a58b1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Oct 2023 17:19:51 -0700 Subject: [PATCH 13/16] Include upgrade details in diagnostics bundle --- .../application/coordinator/coordinator.go | 26 ++++++++++--------- .../application/upgrade/details/details.go | 18 ++++++------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/internal/pkg/agent/application/coordinator/coordinator.go b/internal/pkg/agent/application/coordinator/coordinator.go index 778232b91d8..4604313fb04 100644 --- a/internal/pkg/agent/application/coordinator/coordinator.go +++ b/internal/pkg/agent/application/coordinator/coordinator.go @@ -791,12 +791,13 @@ func (c *Coordinator) DiagnosticHooks() diagnostics.Hooks { State runtime.ComponentState `yaml:"state"` } type StateHookOutput struct { - State agentclient.State `yaml:"state"` - Message string `yaml:"message"` - FleetState agentclient.State `yaml:"fleet_state"` - FleetMessage string `yaml:"fleet_message"` - LogLevel logp.Level `yaml:"log_level"` - Components []StateComponentOutput `yaml:"components"` + State agentclient.State `yaml:"state"` + Message string `yaml:"message"` + FleetState agentclient.State `yaml:"fleet_state"` + FleetMessage string `yaml:"fleet_message"` + LogLevel logp.Level `yaml:"log_level"` + Components []StateComponentOutput `yaml:"components"` + UpgradeDetails *details.Details `yaml:"upgrade_details,omitempty"` } s := c.State() @@ -809,12 +810,13 @@ func (c *Coordinator) DiagnosticHooks() diagnostics.Hooks { } } output := StateHookOutput{ - State: s.State, - Message: s.Message, - FleetState: s.FleetState, - FleetMessage: s.FleetMessage, - LogLevel: s.LogLevel, - Components: compStates, + State: s.State, + Message: s.Message, + FleetState: s.FleetState, + FleetMessage: s.FleetMessage, + LogLevel: s.LogLevel, + Components: compStates, + UpgradeDetails: s.UpgradeDetails, } o, err := yaml.Marshal(output) if err != nil { diff --git a/internal/pkg/agent/application/upgrade/details/details.go b/internal/pkg/agent/application/upgrade/details/details.go index 028990aafcd..5dd9ce1d7a3 100644 --- a/internal/pkg/agent/application/upgrade/details/details.go +++ b/internal/pkg/agent/application/upgrade/details/details.go @@ -24,10 +24,10 @@ type Observer func(details *Details) // Details consists of details regarding an ongoing upgrade. type Details struct { - TargetVersion string `json:"target_version"` - State State `json:"state"` - ActionID string `json:"action_id,omitempty"` - Metadata Metadata `json:"metadata"` + TargetVersion string `json:"target_version" yaml:"target_version"` + State State `json:"state" yaml:"state"` + ActionID string `json:"action_id,omitempty" yaml:"action_id,omitempty"` + Metadata Metadata `json:"metadata" yaml:"metadata"` observers []Observer mu sync.Mutex @@ -35,19 +35,19 @@ type Details struct { // Metadata consists of metadata relating to a specific upgrade state type Metadata struct { - ScheduledAt time.Time `json:"scheduled_at,omitempty"` - DownloadPercent float64 `json:"download_percent,omitempty"` - DownloadRate downloadRate `json:"download_rate,omitempty"` + ScheduledAt time.Time `json:"scheduled_at,omitempty" yaml:"scheduled_at,omitempty"` + DownloadPercent float64 `json:"download_percent,omitempty" yaml:"download_percent,omitempty"` + DownloadRate downloadRate `json:"download_rate,omitempty" yaml:"download_rate,omitempty"` // FailedState is the state an upgrade was in if/when it failed. Use the // Fail() method of UpgradeDetails to correctly record details when // an upgrade fails. - FailedState State `json:"failed_state,omitempty"` + FailedState State `json:"failed_state,omitempty" yaml:"failed_state,omitempty"` // ErrorMsg is any error message encountered if/when an upgrade fails. Use // the Fail() method of UpgradeDetails to correctly record details when // an upgrade fails. - ErrorMsg string `json:"error_msg,omitempty"` + ErrorMsg string `json:"error_msg,omitempty" yaml:"error_msg,omitempty"` } func NewDetails(targetVersion string, initialState State, actionID string) *Details { From cba774df3ef85aca794cc34fc73ac195c7712718 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 17 Oct 2023 17:23:56 -0700 Subject: [PATCH 14/16] Adding CHANGELOG entry --- ...588586-upgrade-details-in-diagnostics.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 changelog/fragments/1697588586-upgrade-details-in-diagnostics.yaml diff --git a/changelog/fragments/1697588586-upgrade-details-in-diagnostics.yaml b/changelog/fragments/1697588586-upgrade-details-in-diagnostics.yaml new file mode 100644 index 00000000000..ed415252d53 --- /dev/null +++ b/changelog/fragments/1697588586-upgrade-details-in-diagnostics.yaml @@ -0,0 +1,32 @@ +# Kind can be one of: +# - breaking-change: a change to previously-documented behavior +# - deprecation: functionality that is being removed in a later release +# - bug-fix: fixes a problem in a previous version +# - enhancement: extends functionality but does not break or fix existing behavior +# - feature: new functionality +# - known-issue: problems that we are aware of in a given version +# - security: impacts on the security of a product or a user’s deployment. +# - upgrade: important information for someone upgrading from a prior version +# - other: does not fit into any of the other categories +kind: enhancement + +# Change summary; a 80ish characters long description of the change. +summary: Include upgrade details in diagnostics bundle. + +# Long description; in case the summary is not enough to describe the change +# this field accommodate a description without length limits. +# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment. +#description: + +# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc. +component: elastic-agent + +# PR URL; optional; the PR number that added the changeset. +# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added. +# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number. +# Please provide it if you are adding a fragment for a different PR. +pr: https://github.com/elastic/elastic-agent/pull/3624 + +# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of). +# If not present is automatically filled by the tooling with the issue linked to the PR number. +#issue: https://github.com/owner/repo/1234 From f93d5e24092fc95b307d7150da4ae32923b00984 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Nov 2023 11:42:52 +0530 Subject: [PATCH 15/16] Add unit test --- .../coordinator/diagnostics_test.go | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/internal/pkg/agent/application/coordinator/diagnostics_test.go b/internal/pkg/agent/application/coordinator/diagnostics_test.go index c6f2dc4fc97..9fec8e1844b 100644 --- a/internal/pkg/agent/application/coordinator/diagnostics_test.go +++ b/internal/pkg/agent/application/coordinator/diagnostics_test.go @@ -7,16 +7,18 @@ package coordinator import ( "context" "errors" + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" - "github.com/elastic/elastic-agent-client/v7/pkg/proto" - "github.com/elastic/elastic-agent-client/v7/pkg/client" + "github.com/elastic/elastic-agent-client/v7/pkg/proto" + "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details" "github.com/elastic/elastic-agent/internal/pkg/agent/configuration" "github.com/elastic/elastic-agent/internal/pkg/agent/transpiler" monitoringCfg "github.com/elastic/elastic-agent/internal/pkg/core/monitoring/config" @@ -413,6 +415,7 @@ func TestDiagnosticState(t *testing.T) { // Create a coordinator with a test state and verify that the state // diagnostic reports it + now := time.Now().UTC() state := State{ State: agentclient.Starting, Message: "starting up", @@ -432,9 +435,19 @@ func TestDiagnosticState(t *testing.T) { }, }, }, + UpgradeDetails: &details.Details{ + TargetVersion: "8.12.0", + State: "UPG_DOWNLOADING", + ActionID: "foobar", + Metadata: details.Metadata{ + DownloadPercent: 0.17469, + ScheduledAt: now, + DownloadRate: 123.56, + }, + }, } - expected := ` + expected := fmt.Sprintf(` state: 0 message: "starting up" fleet_state: 1 @@ -451,7 +464,15 @@ components: version_info: name: "version name" version: "version value" -` +upgrade_details: + target_version: 8.12.0 + state: UPG_DOWNLOADING + action_id: foobar + metadata: + download_percent: 0.17469 + scheduled_at: %s + download_rate: 123.56 +`, now.Format(time.RFC3339Nano)) coord := &Coordinator{ // This test needs a broadcaster since the components-actual diagnostic From a8aed84a70aef2551185ec65f0d7ad07c413da63 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Nov 2023 11:42:57 +0530 Subject: [PATCH 16/16] Add godoc --- .../agent/application/upgrade/details/details.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/internal/pkg/agent/application/upgrade/details/details.go b/internal/pkg/agent/application/upgrade/details/details.go index 5dd9ce1d7a3..a4cb9954d4b 100644 --- a/internal/pkg/agent/application/upgrade/details/details.go +++ b/internal/pkg/agent/application/upgrade/details/details.go @@ -35,9 +35,15 @@ type Details struct { // Metadata consists of metadata relating to a specific upgrade state type Metadata struct { - ScheduledAt time.Time `json:"scheduled_at,omitempty" yaml:"scheduled_at,omitempty"` - DownloadPercent float64 `json:"download_percent,omitempty" yaml:"download_percent,omitempty"` - DownloadRate downloadRate `json:"download_rate,omitempty" yaml:"download_rate,omitempty"` + ScheduledAt time.Time `json:"scheduled_at,omitempty" yaml:"scheduled_at,omitempty"` + + // DownloadPercent is the percentage of the artifact that has been + // downloaded. Minimum value is 0 and maximum value is 1. + DownloadPercent float64 `json:"download_percent,omitempty" yaml:"download_percent,omitempty"` + + // DownloadRate is the rate, in bytes per second, at which the download + // is progressing. + DownloadRate downloadRate `json:"download_rate,omitempty" yaml:"download_rate,omitempty"` // FailedState is the state an upgrade was in if/when it failed. Use the // Fail() method of UpgradeDetails to correctly record details when @@ -71,7 +77,7 @@ func (d *Details) SetState(s State) { } // SetDownloadProgress is a convenience method to set the download percent -// when the upgrade is in UPG_DOWNLOADING state. +// and download rate when the upgrade is in UPG_DOWNLOADING state. func (d *Details) SetDownloadProgress(percent, rateBytesPerSecond float64) { d.mu.Lock() defer d.mu.Unlock()