Skip to content

Commit 083dfc4

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 39e836e commit 083dfc4

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-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
@@ -27,6 +27,7 @@ import (
2727
"github.com/elastic/elastic-agent/internal/pkg/agent/application/info"
2828
"github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec"
2929
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade"
30+
upgradeErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
3031
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3132
"github.com/elastic/elastic-agent/internal/pkg/agent/configuration"
3233
"github.com/elastic/elastic-agent/internal/pkg/agent/transpiler"
@@ -659,6 +660,15 @@ func (c *Coordinator) Upgrade(ctx context.Context, version string, sourceURI str
659660
det.SetState(details.StateCompleted)
660661
return c.upgradeMgr.AckAction(ctx, c.fleetAcker, action)
661662
}
663+
664+
c.logger.Errorw("upgrade failed", "error", logp.Error(err))
665+
// If ErrInsufficientDiskSpace is in the error chain, we want to set the
666+
// the error to ErrInsufficientDiskSpace so that the error message is
667+
// more concise and clear.
668+
if errors.Is(err, upgradeErrors.ErrInsufficientDiskSpace) {
669+
err = upgradeErrors.ErrInsufficientDiskSpace
670+
}
671+
662672
det.Fail(err)
663673
return err
664674
}

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

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,27 @@ package coordinator
1313
import (
1414
"context"
1515
"errors"
16+
"fmt"
1617
"net"
18+
<<<<<<< HEAD
1719
"testing"
1820
"time"
1921

22+
=======
23+
"net/http"
24+
"net/http/httptest"
25+
"os"
26+
"path/filepath"
27+
"strings"
28+
"sync"
29+
"testing"
30+
"time"
31+
32+
"github.com/elastic/elastic-agent-client/v7/pkg/proto"
33+
"github.com/elastic/elastic-agent/internal/pkg/fleetapi/acker"
34+
"github.com/elastic/elastic-agent/internal/pkg/testutils"
35+
36+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))
2037
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/status"
2138
"go.opentelemetry.io/collector/component/componentstatus"
2239
"go.opentelemetry.io/collector/confmap"
@@ -28,8 +45,15 @@ import (
2845
"github.com/elastic/elastic-agent-libs/logp"
2946
"github.com/elastic/elastic-agent/internal/pkg/agent/application/info"
3047
"github.com/elastic/elastic-agent/internal/pkg/agent/application/monitoring/reload"
48+
<<<<<<< HEAD
49+
=======
50+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
51+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/reexec"
52+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/secret"
53+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))
3154
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade"
3255
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact"
56+
upgradeErrors "github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download/errors"
3357
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/details"
3458
"github.com/elastic/elastic-agent/internal/pkg/agent/transpiler"
3559
"github.com/elastic/elastic-agent/internal/pkg/config"
@@ -1293,3 +1317,252 @@ func (fs *fakeMonitoringServer) Reset() {
12931317
func (fs *fakeMonitoringServer) Addr() net.Addr {
12941318
return nil
12951319
}
1320+
<<<<<<< HEAD
1321+
=======
1322+
1323+
func TestMergeFleetConfig(t *testing.T) {
1324+
testutils.InitStorage(t)
1325+
1326+
cfg := map[string]interface{}{
1327+
"fleet": map[string]interface{}{
1328+
"enabled": true,
1329+
"kibana": map[string]interface{}{"host": "demo"},
1330+
"access_api_key": "123",
1331+
},
1332+
"agent": map[string]interface{}{
1333+
"grpc": map[string]interface{}{
1334+
"port": uint16(6790),
1335+
},
1336+
},
1337+
}
1338+
1339+
path := paths.AgentConfigFile()
1340+
store, err := storage.NewEncryptedDiskStore(t.Context(), path)
1341+
require.NoError(t, err)
1342+
1343+
rawConfig := config.MustNewConfigFrom(cfg)
1344+
conf, err := mergeFleetConfig(t.Context(), rawConfig, store)
1345+
require.NoError(t, err)
1346+
assert.NotNil(t, conf)
1347+
assert.Equal(t, conf.Fleet.Enabled, cfg["fleet"].(map[string]interface{})["enabled"])
1348+
assert.Equal(t, conf.Fleet.AccessAPIKey, cfg["fleet"].(map[string]interface{})["access_api_key"])
1349+
assert.Equal(t, conf.Settings.GRPC.Port, cfg["agent"].(map[string]interface{})["grpc"].(map[string]interface{})["port"].(uint16))
1350+
}
1351+
1352+
func TestComputeEnrollOptions(t *testing.T) {
1353+
testutils.InitStorage(t)
1354+
tmp := t.TempDir()
1355+
1356+
storePath := filepath.Join(tmp, "fleet.enc")
1357+
cfgPath := filepath.Join(tmp, "elastic-agent.yml")
1358+
1359+
cfg := map[string]interface{}{
1360+
"fleet": map[string]interface{}{
1361+
"enabled": true,
1362+
"access_api_key": "123",
1363+
},
1364+
"agent": map[string]interface{}{
1365+
"grpc": map[string]interface{}{
1366+
"port": uint16(6790),
1367+
},
1368+
},
1369+
}
1370+
1371+
rawAgentConfigData, err := yaml.Marshal(cfg)
1372+
require.NoError(t, err)
1373+
1374+
require.NoError(t, os.WriteFile(cfgPath, rawAgentConfigData, 0644))
1375+
1376+
store, err := storage.NewEncryptedDiskStore(t.Context(), storePath)
1377+
require.NoError(t, err)
1378+
1379+
fleetConfig := `fleet:
1380+
hosts: [localhost:1234]
1381+
ssl:
1382+
ca_sha256: ["sha1", "sha2"]
1383+
verification_mode: none
1384+
proxy_url: http://proxy.example.com:8080
1385+
proxy_disable: false
1386+
proxy_headers:
1387+
Proxy-Authorization: "Bearer token"
1388+
Custom-Header: "custom-value"
1389+
enrollment_token: enrollment-token-123
1390+
force: true
1391+
insecure: true
1392+
agent:
1393+
id: test-agent-id
1394+
`
1395+
require.NoError(t, store.Save(bytes.NewReader([]byte(fleetConfig))))
1396+
1397+
options, err := computeEnrollOptions(t.Context(), cfgPath, storePath)
1398+
require.NoError(t, err)
1399+
1400+
require.NoError(t, err)
1401+
assert.NotNil(t, options)
1402+
1403+
assert.Equal(t, "123", options.EnrollAPIKey, "EnrollAPIKey mismatch")
1404+
assert.Equal(t, "http://localhost:1234", options.URL, "URL mismatch")
1405+
1406+
assert.Equal(t, []string{"sha1", "sha2"}, options.CASha256, "CASha256 mismatch")
1407+
assert.Equal(t, true, options.Insecure, "Insecure mismatch")
1408+
assert.Equal(t, "test-agent-id", options.ID, "ID mismatch")
1409+
assert.Equal(t, "http://proxy.example.com:8080", options.ProxyURL, "ProxyURL mismatch")
1410+
assert.Equal(t, false, options.ProxyDisabled, "ProxyDisabled mismatch")
1411+
expectedProxyHeaders := map[string]string{
1412+
"Proxy-Authorization": "Bearer token",
1413+
"Custom-Header": "custom-value",
1414+
}
1415+
assert.Equal(t, expectedProxyHeaders, options.ProxyHeaders, "ProxyHeaders mismatch")
1416+
}
1417+
1418+
func TestHasEndpoint(t *testing.T) {
1419+
testCases := []struct {
1420+
name string
1421+
state State
1422+
expected bool
1423+
}{
1424+
{
1425+
"endpoint",
1426+
State{
1427+
Components: []runtime.ComponentComponentState{
1428+
{
1429+
Component: component.Component{
1430+
InputType: endpoint,
1431+
},
1432+
},
1433+
},
1434+
},
1435+
true,
1436+
},
1437+
{
1438+
"no endpoint",
1439+
State{
1440+
Components: []runtime.ComponentComponentState{
1441+
{
1442+
Component: component.Component{
1443+
InputType: "not endpoint",
1444+
},
1445+
},
1446+
},
1447+
},
1448+
false,
1449+
},
1450+
1451+
{
1452+
"no component",
1453+
State{
1454+
Components: []runtime.ComponentComponentState{},
1455+
},
1456+
false,
1457+
},
1458+
}
1459+
1460+
for _, tc := range testCases {
1461+
t.Run(tc.name, func(t *testing.T) {
1462+
c := &Coordinator{
1463+
state: tc.state,
1464+
}
1465+
1466+
result := c.HasEndpoint()
1467+
assert.Equal(t, tc.expected, result, "HasEndpoint result mismatch")
1468+
})
1469+
}
1470+
}
1471+
1472+
type mockUpgradeManager struct {
1473+
upgradeErr error
1474+
}
1475+
1476+
func (m *mockUpgradeManager) Upgradeable() bool {
1477+
return true
1478+
}
1479+
1480+
func (m *mockUpgradeManager) Reload(cfg *config.Config) error {
1481+
return nil
1482+
}
1483+
1484+
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) {
1485+
return nil, m.upgradeErr
1486+
}
1487+
1488+
func (m *mockUpgradeManager) Ack(ctx context.Context, acker acker.Acker) error {
1489+
return nil
1490+
}
1491+
1492+
func (m *mockUpgradeManager) AckAction(ctx context.Context, acker acker.Acker, action fleetapi.Action) error {
1493+
return nil
1494+
}
1495+
1496+
func (m *mockUpgradeManager) MarkerWatcher() upgrade.MarkerWatcher {
1497+
return nil
1498+
}
1499+
1500+
func TestCoordinator_Upgrade_InsufficientDiskSpaceError(t *testing.T) {
1501+
log, _ := loggertest.New("coordinator-insufficient-disk-space-test")
1502+
1503+
mockUpgradeManager := &mockUpgradeManager{
1504+
upgradeErr: fmt.Errorf("wrapped: %w", upgradeErrors.ErrInsufficientDiskSpace),
1505+
}
1506+
1507+
initialState := State{
1508+
CoordinatorState: agentclient.Healthy,
1509+
CoordinatorMessage: "Running",
1510+
}
1511+
1512+
coord := &Coordinator{
1513+
state: initialState,
1514+
logger: log,
1515+
upgradeMgr: mockUpgradeManager,
1516+
stateBroadcaster: broadcaster.New(initialState, 64, 32),
1517+
overrideStateChan: make(chan *coordinatorOverrideState),
1518+
upgradeDetailsChan: make(chan *details.Details),
1519+
}
1520+
1521+
wg := sync.WaitGroup{}
1522+
wg.Add(2)
1523+
1524+
overrideStates := []agentclient.State{}
1525+
go func() {
1526+
state1 := <-coord.overrideStateChan
1527+
overrideStates = append(overrideStates, state1.state)
1528+
1529+
state2 := <-coord.overrideStateChan
1530+
if state2 != nil {
1531+
overrideStates = append(overrideStates, state2.state)
1532+
}
1533+
1534+
wg.Done()
1535+
}()
1536+
1537+
upgradeDetails := []*details.Details{}
1538+
go func() {
1539+
upgradeDetails = append(upgradeDetails, <-coord.upgradeDetailsChan)
1540+
upgradeDetails = append(upgradeDetails, <-coord.upgradeDetailsChan)
1541+
wg.Done()
1542+
}()
1543+
1544+
err := coord.Upgrade(t.Context(), "", "", nil)
1545+
require.Error(t, err)
1546+
require.Equal(t, err, upgradeErrors.ErrInsufficientDiskSpace)
1547+
1548+
wg.Wait()
1549+
1550+
require.Equal(t, []agentclient.State{agentclient.Upgrading}, overrideStates)
1551+
1552+
require.Equal(t, []*details.Details{
1553+
{
1554+
TargetVersion: "",
1555+
State: details.StateRequested,
1556+
ActionID: "",
1557+
},
1558+
{
1559+
TargetVersion: "",
1560+
State: details.StateFailed,
1561+
Metadata: details.Metadata{
1562+
FailedState: details.StateRequested,
1563+
ErrorMsg: upgradeErrors.ErrInsufficientDiskSpace.Error(),
1564+
},
1565+
},
1566+
}, upgradeDetails)
1567+
}
1568+
>>>>>>> a7a76f6e1 (Enhancement/5235 use disk space error to set upgrade detail in coordinator (#9392))

0 commit comments

Comments
 (0)