Skip to content

Commit

Permalink
Include upgrade details in diagnostics bundle during ongoing upgrade (#…
Browse files Browse the repository at this point in the history
…3624)

* Remove context and handle cancellation internally instead

* More optimizations

* Add back context

* Adding FSM for upgrades

* Implementing TODO

* WIP

* WIP

* Reorganizing imports

* Running go mod tidy

* Fixing booboos introduced during conflict resolution

* Add nil guard

* Setting logger in test

* Include upgrade details in diagnostics bundle

* Adding CHANGELOG entry

* Add unit test

* Add godoc
  • Loading branch information
ycombinator authored Nov 3, 2023
1 parent a621050 commit d412c3d
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 28 deletions.
32 changes: 32 additions & 0 deletions changelog/fragments/1697588586-upgrade-details-in-diagnostics.yaml
Original file line number Diff line number Diff line change
@@ -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
26 changes: 14 additions & 12 deletions internal/pkg/agent/application/coordinator/coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 {
Expand Down
29 changes: 25 additions & 4 deletions internal/pkg/agent/application/coordinator/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand All @@ -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
Expand All @@ -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
Expand Down
26 changes: 16 additions & 10 deletions internal/pkg/agent/application/upgrade/details/details.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,36 @@ 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
}

// 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 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
// 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 {
Expand All @@ -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()
Expand Down
3 changes: 1 addition & 2 deletions internal/pkg/agent/application/upgrade/step_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down

0 comments on commit d412c3d

Please sign in to comment.