Skip to content

Commit 56afcbd

Browse files
Kaan Yaltimergify[bot]
authored andcommitted
Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392)
* enhancement(5235): added insufficinet disk space error handling in the coordinator * enhancement(5235): added coordinator tests for insufficient disk space error enhancement(5235): updated error in test enhancement(5235): fix coordinator test * enhancement(5235): added changelog fragment (cherry picked from commit a7a76f6) # Conflicts: # internal/pkg/agent/application/coordinator/coordinator_unit_test.go
1 parent bda6a98 commit 56afcbd

File tree

3 files changed

+312
-0
lines changed

3 files changed

+312
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: enhancement
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: when there is a disk space error during an upgrade, agent responds with clean insufficient disk space error message
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: "elastic-agent"
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/9392
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
issue: https://github.com/elastic/elastic-agent/issues/5235

internal/pkg/agent/application/coordinator/coordinator.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/elastic/elastic-agent/internal/pkg/agent/application/info"
3030
"github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec"
3131
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade"
32+
upgradeErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
3233
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3334
"github.com/elastic/elastic-agent/internal/pkg/agent/configuration"
3435
"github.com/elastic/elastic-agent/internal/pkg/agent/transpiler"
@@ -671,6 +672,15 @@ func (c *Coordinator) Upgrade(ctx context.Context, version string, sourceURI str
671672
det.SetState(details.StateCompleted)
672673
return c.upgradeMgr.AckAction(ctx, c.fleetAcker, action)
673674
}
675+
676+
c.logger.Errorw("upgrade failed", "error", logp.Error(err))
677+
// If ErrInsufficientDiskSpace is in the error chain, we want to set the
678+
// the error to ErrInsufficientDiskSpace so that the error message is
679+
// more concise and clear.
680+
if errors.Is(err, upgradeErrors.ErrInsufficientDiskSpace) {
681+
err = upgradeErrors.ErrInsufficientDiskSpace
682+
}
683+
674684
det.Fail(err)
675685
return err
676686
}

internal/pkg/agent/application/coordinator/coordinator_unit_test.go

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,25 @@ import (
1515
"errors"
1616
"fmt"
1717
"net"
18+
<<<<<<< HEAD
19+
=======
20+
"net/http"
21+
"net/http/httptest"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
"sync"
26+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))
1827
"testing"
1928
"time"
2029

2130
"github.com/elastic/elastic-agent-client/v7/pkg/proto"
31+
<<<<<<< HEAD
2232
"github.com/elastic/elastic-agent/internal/pkg/otel/translate"
33+
=======
34+
"github.com/elastic/elastic-agent/internal/pkg/fleetapi/acker"
35+
"github.com/elastic/elastic-agent/internal/pkg/testutils"
36+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))
2337

2438
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/status"
2539
"go.opentelemetry.io/collector/component/componentstatus"
@@ -32,8 +46,15 @@ import (
3246
"github.com/elastic/elastic-agent-libs/logp"
3347
"github.com/elastic/elastic-agent/internal/pkg/agent/application/info"
3448
"github.com/elastic/elastic-agent/internal/pkg/agent/application/monitoring/reload"
49+
<<<<<<< HEAD
50+
=======
51+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
52+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec"
53+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/secret"
54+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))
3555
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade"
3656
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact"
57+
upgradeErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
3758
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3859
"github.com/elastic/elastic-agent/internal/pkg/agent/transpiler"
3960
"github.com/elastic/elastic-agent/internal/pkg/config"
@@ -1650,3 +1671,252 @@ func (fs *fakeMonitoringServer) Reset() {
16501671
func (fs *fakeMonitoringServer) Addr() net.Addr {
16511672
return nil
16521673
}
1674+
<<<<<<< HEAD
1675+
=======
1676+
1677+
func TestMergeFleetConfig(t *testing.T) {
1678+
testutils.InitStorage(t)
1679+
1680+
cfg := map[string]interface{}{
1681+
"fleet": map[string]interface{}{
1682+
"enabled": true,
1683+
"kibana": map[string]interface{}{"host": "demo"},
1684+
"access_api_key": "123",
1685+
},
1686+
"agent": map[string]interface{}{
1687+
"grpc": map[string]interface{}{
1688+
"port": uint16(6790),
1689+
},
1690+
},
1691+
}
1692+
1693+
path := paths.AgentConfigFile()
1694+
store, err := storage.NewEncryptedDiskStore(t.Context(), path)
1695+
require.NoError(t, err)
1696+
1697+
rawConfig := config.MustNewConfigFrom(cfg)
1698+
conf, err := mergeFleetConfig(t.Context(), rawConfig, store)
1699+
require.NoError(t, err)
1700+
assert.NotNil(t, conf)
1701+
assert.Equal(t, conf.Fleet.Enabled, cfg["fleet"].(map[string]interface{})["enabled"])
1702+
assert.Equal(t, conf.Fleet.AccessAPIKey, cfg["fleet"].(map[string]interface{})["access_api_key"])
1703+
assert.Equal(t, conf.Settings.GRPC.Port, cfg["agent"].(map[string]interface{})["grpc"].(map[string]interface{})["port"].(uint16))
1704+
}
1705+
1706+
func TestComputeEnrollOptions(t *testing.T) {
1707+
testutils.InitStorage(t)
1708+
tmp := t.TempDir()
1709+
1710+
storePath := filepath.Join(tmp, "fleet.enc")
1711+
cfgPath := filepath.Join(tmp, "elastic-agent.yml")
1712+
1713+
cfg := map[string]interface{}{
1714+
"fleet": map[string]interface{}{
1715+
"enabled": true,
1716+
"access_api_key": "123",
1717+
},
1718+
"agent": map[string]interface{}{
1719+
"grpc": map[string]interface{}{
1720+
"port": uint16(6790),
1721+
},
1722+
},
1723+
}
1724+
1725+
rawAgentConfigData, err := yaml.Marshal(cfg)
1726+
require.NoError(t, err)
1727+
1728+
require.NoError(t, os.WriteFile(cfgPath, rawAgentConfigData, 0644))
1729+
1730+
store, err := storage.NewEncryptedDiskStore(t.Context(), storePath)
1731+
require.NoError(t, err)
1732+
1733+
fleetConfig := `fleet:
1734+
hosts: [localhost:1234]
1735+
ssl:
1736+
ca_sha256: ["sha1", "sha2"]
1737+
verification_mode: none
1738+
proxy_url: http://proxy.example.com:8080
1739+
proxy_disable: false
1740+
proxy_headers:
1741+
Proxy-Authorization: "Bearer token"
1742+
Custom-Header: "custom-value"
1743+
enrollment_token: enrollment-token-123
1744+
force: true
1745+
insecure: true
1746+
agent:
1747+
id: test-agent-id
1748+
`
1749+
require.NoError(t, store.Save(bytes.NewReader([]byte(fleetConfig))))
1750+
1751+
options, err := computeEnrollOptions(t.Context(), cfgPath, storePath)
1752+
require.NoError(t, err)
1753+
1754+
require.NoError(t, err)
1755+
assert.NotNil(t, options)
1756+
1757+
assert.Equal(t, "123", options.EnrollAPIKey, "EnrollAPIKey mismatch")
1758+
assert.Equal(t, "http://localhost:1234", options.URL, "URL mismatch")
1759+
1760+
assert.Equal(t, []string{"sha1", "sha2"}, options.CASha256, "CASha256 mismatch")
1761+
assert.Equal(t, true, options.Insecure, "Insecure mismatch")
1762+
assert.Equal(t, "test-agent-id", options.ID, "ID mismatch")
1763+
assert.Equal(t, "http://proxy.example.com:8080", options.ProxyURL, "ProxyURL mismatch")
1764+
assert.Equal(t, false, options.ProxyDisabled, "ProxyDisabled mismatch")
1765+
expectedProxyHeaders := map[string]string{
1766+
"Proxy-Authorization": "Bearer token",
1767+
"Custom-Header": "custom-value",
1768+
}
1769+
assert.Equal(t, expectedProxyHeaders, options.ProxyHeaders, "ProxyHeaders mismatch")
1770+
}
1771+
1772+
func TestHasEndpoint(t *testing.T) {
1773+
testCases := []struct {
1774+
name string
1775+
state State
1776+
expected bool
1777+
}{
1778+
{
1779+
"endpoint",
1780+
State{
1781+
Components: []runtime.ComponentComponentState{
1782+
{
1783+
Component: component.Component{
1784+
InputType: endpoint,
1785+
},
1786+
},
1787+
},
1788+
},
1789+
true,
1790+
},
1791+
{
1792+
"no endpoint",
1793+
State{
1794+
Components: []runtime.ComponentComponentState{
1795+
{
1796+
Component: component.Component{
1797+
InputType: "not endpoint",
1798+
},
1799+
},
1800+
},
1801+
},
1802+
false,
1803+
},
1804+
1805+
{
1806+
"no component",
1807+
State{
1808+
Components: []runtime.ComponentComponentState{},
1809+
},
1810+
false,
1811+
},
1812+
}
1813+
1814+
for _, tc := range testCases {
1815+
t.Run(tc.name, func(t *testing.T) {
1816+
c := &Coordinator{
1817+
state: tc.state,
1818+
}
1819+
1820+
result := c.HasEndpoint()
1821+
assert.Equal(t, tc.expected, result, "HasEndpoint result mismatch")
1822+
})
1823+
}
1824+
}
1825+
1826+
type mockUpgradeManager struct {
1827+
upgradeErr error
1828+
}
1829+
1830+
func (m *mockUpgradeManager) Upgradeable() bool {
1831+
return true
1832+
}
1833+
1834+
func (m *mockUpgradeManager) Reload(cfg *config.Config) error {
1835+
return nil
1836+
}
1837+
1838+
func (m *mockUpgradeManager) Upgrade(ctx context.Context, version string, rollback bool, sourceURI string, action *fleetapi.ActionUpgrade, details *details.Details, skipVerifyOverride bool, skipDefaultPgp bool, pgpBytes ...string) (_ reexec.ShutdownCallbackFn, err error) {
1839+
return nil, m.upgradeErr
1840+
}
1841+
1842+
func (m *mockUpgradeManager) Ack(ctx context.Context, acker acker.Acker) error {
1843+
return nil
1844+
}
1845+
1846+
func (m *mockUpgradeManager) AckAction(ctx context.Context, acker acker.Acker, action fleetapi.Action) error {
1847+
return nil
1848+
}
1849+
1850+
func (m *mockUpgradeManager) MarkerWatcher() upgrade.MarkerWatcher {
1851+
return nil
1852+
}
1853+
1854+
func TestCoordinator_Upgrade_InsufficientDiskSpaceError(t *testing.T) {
1855+
log, _ := loggertest.New("coordinator-insufficient-disk-space-test")
1856+
1857+
mockUpgradeManager := &mockUpgradeManager{
1858+
upgradeErr: fmt.Errorf("wrapped: %w", upgradeErrors.ErrInsufficientDiskSpace),
1859+
}
1860+
1861+
initialState := State{
1862+
CoordinatorState: agentclient.Healthy,
1863+
CoordinatorMessage: "Running",
1864+
}
1865+
1866+
coord := &Coordinator{
1867+
state: initialState,
1868+
logger: log,
1869+
upgradeMgr: mockUpgradeManager,
1870+
stateBroadcaster: broadcaster.New(initialState, 64, 32),
1871+
overrideStateChan: make(chan *coordinatorOverrideState),
1872+
upgradeDetailsChan: make(chan *details.Details),
1873+
}
1874+
1875+
wg := sync.WaitGroup{}
1876+
wg.Add(2)
1877+
1878+
overrideStates := []agentclient.State{}
1879+
go func() {
1880+
state1 := <-coord.overrideStateChan
1881+
overrideStates = append(overrideStates, state1.state)
1882+
1883+
state2 := <-coord.overrideStateChan
1884+
if state2 != nil {
1885+
overrideStates = append(overrideStates, state2.state)
1886+
}
1887+
1888+
wg.Done()
1889+
}()
1890+
1891+
upgradeDetails := []*details.Details{}
1892+
go func() {
1893+
upgradeDetails = append(upgradeDetails, <-coord.upgradeDetailsChan)
1894+
upgradeDetails = append(upgradeDetails, <-coord.upgradeDetailsChan)
1895+
wg.Done()
1896+
}()
1897+
1898+
err := coord.Upgrade(t.Context(), "", "", nil)
1899+
require.Error(t, err)
1900+
require.Equal(t, err, upgradeErrors.ErrInsufficientDiskSpace)
1901+
1902+
wg.Wait()
1903+
1904+
require.Equal(t, []agentclient.State{agentclient.Upgrading}, overrideStates)
1905+
1906+
require.Equal(t, []*details.Details{
1907+
{
1908+
TargetVersion: "",
1909+
State: details.StateRequested,
1910+
ActionID: "",
1911+
},
1912+
{
1913+
TargetVersion: "",
1914+
State: details.StateFailed,
1915+
Metadata: details.Metadata{
1916+
FailedState: details.StateRequested,
1917+
ErrorMsg: upgradeErrors.ErrInsufficientDiskSpace.Error(),
1918+
},
1919+
},
1920+
}, upgradeDetails)
1921+
}
1922+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))

0 commit comments

Comments
 (0)