From 4fbee983717e716bfbff76539c02286a0595ea44 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Tue, 19 Nov 2024 16:21:45 -0300 Subject: [PATCH 01/14] Fix audit/unenroll calls when agent runs fleet-server --- ...enroll-call-when-running-fleet-server.yaml | 32 +++++++++ internal/pkg/agent/install/uninstall.go | 66 +++++++++++-------- testing/integration/fleetserver_test.go | 4 +- testing/integration/install_test.go | 7 ++ 4 files changed, 79 insertions(+), 30 deletions(-) create mode 100644 changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml diff --git a/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml b/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml new file mode 100644 index 00000000000..d2ece1df50b --- /dev/null +++ b/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.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: bug-fix + +# Change summary; a 80ish characters long description of the change. +summary: Fix audit/unenroll call when running fleet-server + +# 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: Fix the call to the audit/unenroll endpoint that occurs on uninstall when the fleet-server is running locally. + +# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc. +component: fleet-server + +# 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/owner/repo/1234 + +# 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/elastic/elastic-agent/issues/5752 diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index ea9cfca4ca9..2eb16df4936 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -59,6 +59,43 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log return fmt.Errorf("uninstall must be run from outside the installed path '%s'", topPath) } + // check if the agent was installed using --unprivileged by checking the file vault for the agent secret (needed on darwin to correctly load the vault) + unprivileged, err := checkForUnprivilegedVault(ctx) + if err != nil { + return fmt.Errorf("error checking for unprivileged vault: %w", err) + } + + // will only notify fleet of the uninstall command if it can gather config and agentinfo, and is not a stand-alone install + localFleet := false + notifyFleet := false + var ai *info.AgentInfo + c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) + if err != nil { + pt.Describe(fmt.Sprintf("unable to read agent config to determine if notifying Fleet is needed: %v", err)) + } + cfg, err := configuration.NewFromConfig(c) + if err != nil { + pt.Describe(fmt.Sprintf("notify Fleet: unable to transform *config.Config to *configuration.Configuration: %v", err)) + } + + if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { + ai, err = info.NewAgentInfo(ctx, false) + if err != nil { + pt.Describe(fmt.Sprintf("unable to read agent info, Fleet will not be notified of uninstall: %v", err)) + } else { + notifyFleet = true + } + if cfg.Fleet != nil && cfg.Fleet.Server != nil { + localFleet = true + } + } + + // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 + // Once the root-cause is identified then this can be re-enabled on Windows. + // Notify fleet-server while it is still running if it's running locally + if notifyFleet && localFleet && runtime.GOOS != "windows" { + notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it + } // ensure service is stopped status, err := EnsureStoppedService(topPath, pt) if err != nil { @@ -71,12 +108,6 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log return fmt.Errorf("failed trying to kill any running watcher: %w", err) } - // check if the agent was installed using --unprivileged by checking the file vault for the agent secret (needed on darwin to correctly load the vault) - unprivileged, err := checkForUnprivilegedVault(ctx) - if err != nil { - return fmt.Errorf("error checking for unprivileged vault: %w", err) - } - // Uninstall components first if err := uninstallComponents(ctx, cfgFile, uninstallToken, log, pt, unprivileged); err != nil { // If service status was running it was stopped to uninstall the components. @@ -111,27 +142,6 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log } } - // will only notify fleet of the uninstall command if it can gather config and agentinfo, and is not a stand-alone install - notifyFleet := false - var ai *info.AgentInfo - c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) - if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent config to determine if notifying Fleet is needed: %v", err)) - } - cfg, err := configuration.NewFromConfig(c) - if err != nil { - pt.Describe(fmt.Sprintf("notify Fleet: unable to transform *config.Config to *configuration.Configuration: %v", err)) - } - - if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { - ai, err = info.NewAgentInfo(ctx, false) - if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent info, Fleet will not be notified of uninstall: %v", err)) - } else { - notifyFleet = true - } - } - // remove existing directory pt.Describe("Removing install directory") err = RemovePath(topPath) @@ -146,7 +156,7 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 // Once the root-cause is identified then this can be re-enabled on Windows. - if notifyFleet && runtime.GOOS != "windows" { + if notifyFleet && !localFleet && runtime.GOOS != "windows" { notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it } diff --git a/testing/integration/fleetserver_test.go b/testing/integration/fleetserver_test.go index 51d4561b403..33332cf10f6 100644 --- a/testing/integration/fleetserver_test.go +++ b/testing/integration/fleetserver_test.go @@ -47,8 +47,6 @@ func TestInstallFleetServerBootstrap(t *testing.T) { Local: false, }) - t.Skip("Skip until the first 8.16.0-SNAPSHOT is available") - ctx, cancel := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) defer cancel() @@ -165,4 +163,6 @@ func TestInstallFleetServerBootstrap(t *testing.T) { require.Error(t, err, "uninstall should have failed") require.Containsf(t, string(out), "uninstall must be run from outside the installed path", "expected error string not found in: %s err: %s", out, err) } + + t.Run("Test audit/unenroll", testUninstallAuditUnenroll(ctx, fixture, info)) } diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index cd94c013688..0cff58421ef 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -338,6 +338,13 @@ func TestInstallUninstallAudit(t *testing.T) { return waitForAgentAndFleetHealthy(ctx, t, fixture) }, time.Minute, time.Second, "agent never became healthy or connected to Fleet") + t.Run("run uninstall", testUninstallAuditUnenroll(ctx, fixture, info)) +} + +func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, info *define.Info) func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skip Windows as it has been disabled because of https://github.com/elastic/elastic-agent/issues/5952") + } agentID, err := getAgentID(ctx, fixture) require.NoError(t, err, "error getting the agent inspect output") require.NotEmpty(t, agentID, "agent ID empty") From 8b036d75dbd3e16d07e47cce691dbab3cd9ddb7d Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Wed, 20 Nov 2024 10:16:49 -0300 Subject: [PATCH 02/14] Fix integration test --- testing/integration/install_test.go | 54 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index 0cff58421ef..c7214787446 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -342,35 +342,37 @@ func TestInstallUninstallAudit(t *testing.T) { } func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, info *define.Info) func(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("Skip Windows as it has been disabled because of https://github.com/elastic/elastic-agent/issues/5952") - } - agentID, err := getAgentID(ctx, fixture) - require.NoError(t, err, "error getting the agent inspect output") - require.NotEmpty(t, agentID, "agent ID empty") + return func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skip Windows as it has been disabled because of https://github.com/elastic/elastic-agent/issues/5952") + } + agentID, err := getAgentID(ctx, fixture) + require.NoError(t, err, "error getting the agent inspect output") + require.NotEmpty(t, agentID, "agent ID empty") - out, err = fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) - if err != nil { - t.Logf("uninstall output: %s", out) - require.NoError(t, err) - } + out, err := fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) + if err != nil { + t.Logf("uninstall output: %s", out) + require.NoError(t, err) + } - // TODO: replace direct query to ES index with API call to Fleet - // Blocked on https://github.com/elastic/kibana/issues/194884 - response, err := info.ESClient.Get(".fleet-agents", agentID, info.ESClient.Get.WithContext(ctx)) - require.NoError(t, err) - defer response.Body.Close() - p, err := io.ReadAll(response.Body) - require.NoError(t, err) - require.Equalf(t, http.StatusOK, response.StatusCode, "ES status code expected 200, body: %s", p) - var res struct { - Source struct { - AuditUnenrolledReason string `json:"audit_unenrolled_reason"` - } `json:"_source"` + // TODO: replace direct query to ES index with API call to Fleet + // Blocked on https://github.com/elastic/kibana/issues/194884 + response, err := info.ESClient.Get(".fleet-agents", agentID, info.ESClient.Get.WithContext(ctx)) + require.NoError(t, err) + defer response.Body.Close() + p, err := io.ReadAll(response.Body) + require.NoError(t, err) + require.Equalf(t, http.StatusOK, response.StatusCode, "ES status code expected 200, body: %s", p) + var res struct { + Source struct { + AuditUnenrolledReason string `json:"audit_unenrolled_reason"` + } `json:"_source"` + } + err = json.Unmarshal(p, &res) + require.NoError(t, err) + require.Equal(t, "uninstall", res.Source.AuditUnenrolledReason) } - err = json.Unmarshal(p, &res) - require.NoError(t, err) - require.Equal(t, "uninstall", res.Source.AuditUnenrolledReason) } // TestRepeatedInstallUninstall will install then uninstall the agent From 1bcf7f7810fea9efba714d5d289e2de042f91b6a Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Wed, 20 Nov 2024 13:52:26 -0300 Subject: [PATCH 03/14] Increase output for failed test --- testing/integration/install_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index c7214787446..147811b1a73 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -371,7 +371,7 @@ func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, } err = json.Unmarshal(p, &res) require.NoError(t, err) - require.Equal(t, "uninstall", res.Source.AuditUnenrolledReason) + require.Equalf(t, "uninstall", res.Source.AuditUnenrolledReason, "Agent doc: %s\nUninstall output: %s", p, out) } } From 9950ed53f477a28ccd2135dbe461592bff99df0a Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Wed, 20 Nov 2024 17:17:43 -0300 Subject: [PATCH 04/14] move notification attempt --- internal/pkg/agent/install/uninstall.go | 13 +++++++------ testing/integration/install_test.go | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 2eb16df4936..3ca6cc652e0 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -90,12 +90,6 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log } } - // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 - // Once the root-cause is identified then this can be re-enabled on Windows. - // Notify fleet-server while it is still running if it's running locally - if notifyFleet && localFleet && runtime.GOOS != "windows" { - notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it - } // ensure service is stopped status, err := EnsureStoppedService(topPath, pt) if err != nil { @@ -103,6 +97,13 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log return err } + // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 + // Once the root-cause is identified then this can be re-enabled on Windows. + // Notify fleet-server while it is still running if it's running locally + if notifyFleet && localFleet && runtime.GOOS != "windows" { + notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it + } + // kill any running watcher if err := killWatcher(pt); err != nil { return fmt.Errorf("failed trying to kill any running watcher: %w", err) diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index 147811b1a73..64812a980de 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -351,8 +351,8 @@ func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, require.NotEmpty(t, agentID, "agent ID empty") out, err := fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) + t.Logf("uninstall output: %s", out) if err != nil { - t.Logf("uninstall output: %s", out) require.NoError(t, err) } @@ -371,7 +371,7 @@ func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, } err = json.Unmarshal(p, &res) require.NoError(t, err) - require.Equalf(t, "uninstall", res.Source.AuditUnenrolledReason, "Agent doc: %s\nUninstall output: %s", p, out) + require.Equal(t, "uninstall", res.Source.AuditUnenrolledReason) } } From 1b0f34c4c6ae954b72d98eba8a070a80c57f7284 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Wed, 20 Nov 2024 17:17:43 -0300 Subject: [PATCH 05/14] move notification attempt --- testing/integration/install_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index 64812a980de..1bd0d081d70 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -351,8 +351,8 @@ func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, require.NotEmpty(t, agentID, "agent ID empty") out, err := fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) - t.Logf("uninstall output: %s", out) if err != nil { + t.Logf("uninstall output: %s", out) require.NoError(t, err) } @@ -371,7 +371,7 @@ func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, } err = json.Unmarshal(p, &res) require.NoError(t, err) - require.Equal(t, "uninstall", res.Source.AuditUnenrolledReason) + require.Equalf(t, "uninstall", res.Source.AuditUnenrolledReason, "uninstall output: %s", out) } } From c349511aacefc7d6f8fbba317597afe141fb60fc Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Thu, 21 Nov 2024 16:52:07 -0300 Subject: [PATCH 06/14] audit/uninstall will call localhost with certonly tls --- internal/pkg/agent/install/uninstall.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 3ca6cc652e0..d533be5e436 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -20,6 +20,7 @@ import ( "github.com/schollz/progressbar/v3" "github.com/elastic/elastic-agent-libs/logp" + "github.com/elastic/elastic-agent-libs/transport/tlscommon" "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/agent/application/secret" @@ -90,20 +91,24 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log } } - // ensure service is stopped - status, err := EnsureStoppedService(topPath, pt) - if err != nil { - // context for the error already provided in the EnsureStoppedService function - return err - } - // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 // Once the root-cause is identified then this can be re-enabled on Windows. // Notify fleet-server while it is still running if it's running locally if notifyFleet && localFleet && runtime.GOOS != "windows" { + if cfg.Fleet.Client.Transport != nil { + cfg.Fleet.Client.Transport.TLS.VerificationMode = tlscommon.VerifyCertificate + } + cfg.Fleet.Client.Hosts = []string{cfg.Fleet.Client.Host} notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it } + // ensure service is stopped + status, err := EnsureStoppedService(topPath, pt) + if err != nil { + // context for the error already provided in the EnsureStoppedService function + return err + } + // kill any running watcher if err := killWatcher(pt); err != nil { return fmt.Errorf("failed trying to kill any running watcher: %w", err) From 9da083e726be6a11431532827b651590b01f8c03 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Fri, 22 Nov 2024 08:43:11 -0300 Subject: [PATCH 07/14] Fix linter --- internal/pkg/agent/install/uninstall.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index d533be5e436..005d601908c 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -95,7 +95,7 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Once the root-cause is identified then this can be re-enabled on Windows. // Notify fleet-server while it is still running if it's running locally if notifyFleet && localFleet && runtime.GOOS != "windows" { - if cfg.Fleet.Client.Transport != nil { + if cfg.Fleet.Client.Transport.TLS != nil { cfg.Fleet.Client.Transport.TLS.VerificationMode = tlscommon.VerifyCertificate } cfg.Fleet.Client.Hosts = []string{cfg.Fleet.Client.Host} From 3710448d88874ebe64aa99229263fe0c59e36183 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Fri, 29 Nov 2024 10:38:52 -0800 Subject: [PATCH 08/14] remove tls setting --- internal/pkg/agent/install/uninstall.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 005d601908c..94675a892ef 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -20,7 +20,6 @@ import ( "github.com/schollz/progressbar/v3" "github.com/elastic/elastic-agent-libs/logp" - "github.com/elastic/elastic-agent-libs/transport/tlscommon" "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/agent/application/secret" @@ -95,9 +94,6 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Once the root-cause is identified then this can be re-enabled on Windows. // Notify fleet-server while it is still running if it's running locally if notifyFleet && localFleet && runtime.GOOS != "windows" { - if cfg.Fleet.Client.Transport.TLS != nil { - cfg.Fleet.Client.Transport.TLS.VerificationMode = tlscommon.VerifyCertificate - } cfg.Fleet.Client.Hosts = []string{cfg.Fleet.Client.Host} notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it } From 7d68d68fb0727846de5d2bc2df0d1e5ca3c5f93e Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Fri, 29 Nov 2024 12:02:05 -0800 Subject: [PATCH 09/14] Add PR link to changelog --- ...43830-Fix-audit-unenroll-call-when-running-fleet-server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml b/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml index d2ece1df50b..5cd1cb4fc73 100644 --- a/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml +++ b/changelog/fragments/1732043830-Fix-audit-unenroll-call-when-running-fleet-server.yaml @@ -25,7 +25,7 @@ component: fleet-server # 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/owner/repo/1234 +pr: https://github.com/elastic/elastic-agent/pull/6085 # 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. From e5cff30ee2de7728858f25607a0bdd20c3c79d97 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Mon, 2 Dec 2024 13:00:17 -0800 Subject: [PATCH 10/14] stop checking if we need to notify in an error is encountered --- internal/pkg/agent/install/uninstall.go | 40 ++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 94675a892ef..668b9fbeab6 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -69,26 +69,32 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log localFleet := false notifyFleet := false var ai *info.AgentInfo - c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) - if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent config to determine if notifying Fleet is needed: %v", err)) - } - cfg, err := configuration.NewFromConfig(c) - if err != nil { - pt.Describe(fmt.Sprintf("notify Fleet: unable to transform *config.Config to *configuration.Configuration: %v", err)) - } - - if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { - ai, err = info.NewAgentInfo(ctx, false) + var cfg *configuration.Configuration + func() { // check if we need to notify in a func to allow us to return early if a (non-fatal) error is encountered. + c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent info, Fleet will not be notified of uninstall: %v", err)) - } else { - notifyFleet = true + pt.Describe(fmt.Sprintf("unable to read agent config to determine if notifying Fleet is needed: %v", err)) + return } - if cfg.Fleet != nil && cfg.Fleet.Server != nil { - localFleet = true + cfg, err = configuration.NewFromConfig(c) + if err != nil { + pt.Describe(fmt.Sprintf("notify Fleet: unable to transform *config.Config to *configuration.Configuration: %v", err)) + return } - } + + if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { + ai, err = info.NewAgentInfo(ctx, false) + if err != nil { + pt.Describe(fmt.Sprintf("unable to read agent info, Fleet will not be notified of uninstall: %v", err)) + return + } else { + notifyFleet = true + } + if cfg.Fleet != nil && cfg.Fleet.Server != nil { + localFleet = true + } + } + }() // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 // Once the root-cause is identified then this can be re-enabled on Windows. From 8804ab8e674ce2c6a3186a3143f1323e2f69fdbd Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Mon, 9 Dec 2024 11:25:54 -0800 Subject: [PATCH 11/14] Shorten non-fatal error messages to prevent output issues in terminals --- internal/pkg/agent/install/uninstall.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 668b9fbeab6..b1cab177ae5 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -73,19 +73,19 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log func() { // check if we need to notify in a func to allow us to return early if a (non-fatal) error is encountered. c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent config to determine if notifying Fleet is needed: %v", err)) + pt.Describe("notify Fleet failed: unable to read config") return } cfg, err = configuration.NewFromConfig(c) if err != nil { - pt.Describe(fmt.Sprintf("notify Fleet: unable to transform *config.Config to *configuration.Configuration: %v", err)) + pt.Describe("notify Fleet failed: error transforming config") return } if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { ai, err = info.NewAgentInfo(ctx, false) if err != nil { - pt.Describe(fmt.Sprintf("unable to read agent info, Fleet will not be notified of uninstall: %v", err)) + pt.Describe("notify Fleet failed: unable to read agent info") return } else { notifyFleet = true From 62bb765c0d1d83f719965dc8d75279193872cab3 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Mon, 9 Dec 2024 15:42:18 -0800 Subject: [PATCH 12/14] Don't load agentinfo, use ID value from config instead --- internal/pkg/agent/install/uninstall.go | 26 +++++++++++--------- internal/pkg/agent/install/uninstall_test.go | 7 +++--- internal/pkg/fleetapi/ack_cmd.go | 6 ++--- internal/pkg/fleetapi/audit_unenroll_cmd.go | 4 +-- internal/pkg/fleetapi/checkin_cmd.go | 6 ++--- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index b1cab177ae5..990fb6da7d9 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -20,7 +20,6 @@ import ( "github.com/schollz/progressbar/v3" "github.com/elastic/elastic-agent-libs/logp" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/agent/application/secret" "github.com/elastic/elastic-agent/internal/pkg/agent/configuration" @@ -48,6 +47,13 @@ var ( fleetAuditWaitMax = time.Second * 10 ) +// agentInfo is a custom type that implements the fleetapi.AgentInfo interface +type agentInfo string + +func (a *agentInfo) AgentID() string { + return string(*a) +} + // Uninstall uninstalls persistently Elastic Agent on the system. func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log *logp.Logger, pt *progressbar.ProgressBar) error { cwd, err := os.Getwd() @@ -68,9 +74,10 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // will only notify fleet of the uninstall command if it can gather config and agentinfo, and is not a stand-alone install localFleet := false notifyFleet := false - var ai *info.AgentInfo + var agentID agentInfo var cfg *configuration.Configuration func() { // check if we need to notify in a func to allow us to return early if a (non-fatal) error is encountered. + // read local config c, err := operations.LoadFullAgentConfig(ctx, log, cfgFile, false, unprivileged) if err != nil { pt.Describe("notify Fleet failed: unable to read config") @@ -83,13 +90,8 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log } if cfg != nil && !configuration.IsStandalone(cfg.Fleet) { - ai, err = info.NewAgentInfo(ctx, false) - if err != nil { - pt.Describe("notify Fleet failed: unable to read agent info") - return - } else { - notifyFleet = true - } + agentID = agentInfo(cfg.Settings.ID) + notifyFleet = true if cfg.Fleet != nil && cfg.Fleet.Server != nil { localFleet = true } @@ -101,7 +103,7 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Notify fleet-server while it is still running if it's running locally if notifyFleet && localFleet && runtime.GOOS != "windows" { cfg.Fleet.Client.Hosts = []string{cfg.Fleet.Client.Host} - notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it + notifyFleetAuditUninstall(ctx, log, pt, cfg, &agentID) //nolint:errcheck // ignore the error as we can't act on it } // ensure service is stopped @@ -165,7 +167,7 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Skip on Windows because of https://github.com/elastic/elastic-agent/issues/5952 // Once the root-cause is identified then this can be re-enabled on Windows. if notifyFleet && !localFleet && runtime.GOOS != "windows" { - notifyFleetAuditUninstall(ctx, log, pt, cfg, ai) //nolint:errcheck // ignore the error as we can't act on it + notifyFleetAuditUninstall(ctx, log, pt, cfg, &agentID) //nolint:errcheck // ignore the error as we can't act on it } return nil @@ -174,7 +176,7 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // notifyFleetAuditUninstall will attempt to notify fleet-server of the agent's uninstall. // // There are retries for the attempt after a 10s wait, but it is a best-effort approach. -func notifyFleetAuditUninstall(ctx context.Context, log *logp.Logger, pt *progressbar.ProgressBar, cfg *configuration.Configuration, ai *info.AgentInfo) error { +func notifyFleetAuditUninstall(ctx context.Context, log *logp.Logger, pt *progressbar.ProgressBar, cfg *configuration.Configuration, ai fleetapi.AgentInfo) error { ctx, cancel := context.WithCancel(ctx) defer cancel() pt.Describe("Attempting to notify Fleet of uninstall") diff --git a/internal/pkg/agent/install/uninstall_test.go b/internal/pkg/agent/install/uninstall_test.go index c07c4e3ade6..19b5afdee95 100644 --- a/internal/pkg/agent/install/uninstall_test.go +++ b/internal/pkg/agent/install/uninstall_test.go @@ -22,7 +22,6 @@ import ( "go.uber.org/zap" "github.com/elastic/elastic-agent-libs/logp" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/info" "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/agent/application/secret" "github.com/elastic/elastic-agent/internal/pkg/agent/configuration" @@ -178,7 +177,7 @@ func TestNotifyFleetAuditUnenroll(t *testing.T) { log, _ := logp.NewInMemory("test", zap.NewDevelopmentEncoderConfig()) pt := progressbar.NewOptions(-1, progressbar.OptionSetWriter(io.Discard)) - ai := &info.AgentInfo{} + var agentID agentInfo = "testID" for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -194,7 +193,7 @@ func TestNotifyFleetAuditUnenroll(t *testing.T) { }, }, } - err := notifyFleetAuditUninstall(context.Background(), log, pt, cfg, ai) + err := notifyFleetAuditUninstall(context.Background(), log, pt, cfg, &agentID) if tc.err == nil { assert.NoError(t, err) } else { @@ -222,7 +221,7 @@ func TestNotifyFleetAuditUnenroll(t *testing.T) { }, }, } - err := notifyFleetAuditUninstall(context.Background(), log, pt, cfg, ai) + err := notifyFleetAuditUninstall(context.Background(), log, pt, cfg, &agentID) assert.EqualError(t, err, "notify Fleet: failed") }) diff --git a/internal/pkg/fleetapi/ack_cmd.go b/internal/pkg/fleetapi/ack_cmd.go index 72abcfd6373..1feb8e8ee30 100644 --- a/internal/pkg/fleetapi/ack_cmd.go +++ b/internal/pkg/fleetapi/ack_cmd.go @@ -23,7 +23,7 @@ const ackPath = "/api/fleet/agents/%s/acks" type AckEvent struct { EventType string `json:"type"` // 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION' SubType string `json:"subtype"` // 'RUNNING','STARTING','IN_PROGRESS','CONFIG','FAILED','STOPPING','STOPPED','DATA_DUMP','ACKNOWLEDGED','UNKNOWN'; - Timestamp string `json:"timestamp"` // : '2019-01-05T14:32:03.36764-05:00', + Timestamp string `json:"timestamp"` // : '2019-01-05T14:32:03.36764-05:00' ActionID string `json:"action_id"` // : '48cebde1-c906-4893-b89f-595d943b72a2', AgentID string `json:"agent_id"` // : 'agent1', Message string `json:"message,omitempty"` // : 'hello2', @@ -84,11 +84,11 @@ func (e *AckResponse) Validate() error { // AckCmd is a fleet API command. type AckCmd struct { client client.Sender - info agentInfo + info AgentInfo } // NewAckCmd creates a new api command. -func NewAckCmd(info agentInfo, client client.Sender) *AckCmd { +func NewAckCmd(info AgentInfo, client client.Sender) *AckCmd { return &AckCmd{ client: client, info: info, diff --git a/internal/pkg/fleetapi/audit_unenroll_cmd.go b/internal/pkg/fleetapi/audit_unenroll_cmd.go index 841dc13b3af..fd1378a37d5 100644 --- a/internal/pkg/fleetapi/audit_unenroll_cmd.go +++ b/internal/pkg/fleetapi/audit_unenroll_cmd.go @@ -58,10 +58,10 @@ func (e *AuditUnenrollRequest) Validate() error { type AuditUnenrollCmd struct { client client.Sender - info agentInfo + info AgentInfo } -func NewAuditUnenrollCmd(info agentInfo, client client.Sender) *AuditUnenrollCmd { +func NewAuditUnenrollCmd(info AgentInfo, client client.Sender) *AuditUnenrollCmd { return &AuditUnenrollCmd{ client: client, info: info, diff --git a/internal/pkg/fleetapi/checkin_cmd.go b/internal/pkg/fleetapi/checkin_cmd.go index 6b420ed8ade..16ce9afe671 100644 --- a/internal/pkg/fleetapi/checkin_cmd.go +++ b/internal/pkg/fleetapi/checkin_cmd.go @@ -85,15 +85,15 @@ func (e *CheckinResponse) Validate() error { // CheckinCmd is a fleet API command. type CheckinCmd struct { client client.Sender - info agentInfo + info AgentInfo } -type agentInfo interface { +type AgentInfo interface { AgentID() string } // NewCheckinCmd creates a new api command. -func NewCheckinCmd(info agentInfo, client client.Sender) *CheckinCmd { +func NewCheckinCmd(info AgentInfo, client client.Sender) *CheckinCmd { return &CheckinCmd{ client: client, info: info, From 933d6c859e0f4f5e99d74fdf5f2480f2a5a83b29 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Mon, 9 Dec 2024 16:35:37 -0800 Subject: [PATCH 13/14] Add unprivilged tests to audit unenroll integration tests --- testing/integration/fleetserver_test.go | 176 +++++++++++++++--------- testing/integration/install_test.go | 76 +++++++--- 2 files changed, 168 insertions(+), 84 deletions(-) diff --git a/testing/integration/fleetserver_test.go b/testing/integration/fleetserver_test.go index 33332cf10f6..2121abd76fa 100644 --- a/testing/integration/fleetserver_test.go +++ b/testing/integration/fleetserver_test.go @@ -50,12 +50,6 @@ func TestInstallFleetServerBootstrap(t *testing.T) { ctx, cancel := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) defer cancel() - // Get path to Elastic Agent executable - fixture, err := define.NewFixtureFromLocalBuild(t, define.Version(), atesting.WithAdditionalArgs([]string{"-E", "output.elasticsearch.allow_older_versions=true"})) - require.NoError(t, err) - err = fixture.Prepare(ctx) - require.NoError(t, err) - t.Log("Ensure base path is clean") var defaultBasePath string switch runtime.GOOS { @@ -68,7 +62,7 @@ func TestInstallFleetServerBootstrap(t *testing.T) { } topPath := filepath.Join(defaultBasePath, "Elastic", "Agent") - err = os.RemoveAll(topPath) + err := os.RemoveAll(topPath) require.NoError(t, err, "failed to remove %q. The test requires this path not to exist.") t.Log("Create fleet-server policy...") @@ -103,66 +97,124 @@ func TestInstallFleetServerBootstrap(t *testing.T) { t.Logf("fleet-server will enroll with es host: %q", esHost) - // Run `elastic-agent install` with fleet-server bootstrap options. - // We use `--force` to prevent interactive execution. - opts := &atesting.InstallOpts{ - Force: true, - Privileged: true, - FleetBootstrapOpts: atesting.FleetBootstrapOpts{ - ESHost: esHost, - ServiceToken: serviceToken, - Policy: policy.ID, - Port: 8220, - }, - } - out, err := fixture.Install(ctx, opts) - if err != nil { - t.Logf("Install output: %s", out) - require.NoError(t, err, "unable to install elastic-agent with fleet-server bootstrap options") - } + t.Run("privileged", func(t *testing.T) { + // Get path to Elastic Agent executable + fixture, err := define.NewFixtureFromLocalBuild(t, define.Version(), atesting.WithAdditionalArgs([]string{"-E", "output.elasticsearch.allow_older_versions=true"})) + require.NoError(t, err) + err = fixture.Prepare(ctx) + require.NoError(t, err) + + // Run `elastic-agent install` with fleet-server bootstrap options. + // We use `--force` to prevent interactive execution. + opts := &atesting.InstallOpts{ + Force: true, + Privileged: true, + FleetBootstrapOpts: atesting.FleetBootstrapOpts{ + ESHost: esHost, + ServiceToken: serviceToken, + Policy: policy.ID, + Port: 8220, + }, + } + out, err := fixture.Install(ctx, opts) + if err != nil { + t.Logf("Install output: %s", out) + require.NoError(t, err, "unable to install elastic-agent with fleet-server bootstrap options") + } - // checkInstallSuccess(t, fixture, topPath, true) // FIXME fails to build if this is uncommented, but the method is part of install_test.go - t.Run("check agent package version", testAgentPackageVersion(ctx, fixture, true)) + // checkInstallSuccess(t, fixture, topPath, true) // FIXME fails to build if this is uncommented, but the method is part of install_test.go + t.Run("check agent package version", testAgentPackageVersion(ctx, fixture, true)) + t.Run("check fleet-server api", testFleetServerInternalAPI()) + + // Make sure uninstall from within the topPath fails on Windows + if runtime.GOOS == "windows" { + cwd, err := os.Getwd() + require.NoErrorf(t, err, "GetWd failed: %s", err) + err = os.Chdir(topPath) + require.NoErrorf(t, err, "Chdir to topPath failed: %s", err) + t.Cleanup(func() { + _ = os.Chdir(cwd) + }) + out, err = fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) + require.Error(t, err, "uninstall should have failed") + require.Containsf(t, string(out), "uninstall must be run from outside the installed path", "expected error string not found in: %s err: %s", out, err) + } - // elastic-agent will self sign a cert to use with fleet-server if one is not passed - // in order to interact with the API we need to ignore the cert. - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } - fleetOK := false - for i := 0; i < 10; i++ { - t.Log("Checking fleet-server status") - resp, err := client.Get("https://localhost:8220/api/status") + t.Run("Test audit/unenroll", testUninstallAuditUnenroll(ctx, fixture, info)) + }) + t.Run("unprivileged", func(t *testing.T) { + // Get path to Elastic Agent executable + fixture, err := define.NewFixtureFromLocalBuild(t, define.Version(), atesting.WithAdditionalArgs([]string{"-E", "output.elasticsearch.allow_older_versions=true"})) + require.NoError(t, err) + err = fixture.Prepare(ctx) + require.NoError(t, err) + + // Run `elastic-agent install` with fleet-server bootstrap options. + // We use `--force` to prevent interactive execution. + opts := &atesting.InstallOpts{ + Force: true, + Privileged: false, + FleetBootstrapOpts: atesting.FleetBootstrapOpts{ + ESHost: esHost, + ServiceToken: serviceToken, + Policy: policy.ID, + Port: 8220, + }, + } + out, err := fixture.Install(ctx, opts) if err != nil { - t.Logf("fleet-server status check returned error: %v, retry in 10s...", err) + t.Logf("Install output: %s", out) + require.NoError(t, err, "unable to install elastic-agent with fleet-server bootstrap options") + } + + // checkInstallSuccess(t, fixture, topPath, true) // FIXME fails to build if this is uncommented, but the method is part of install_test.go + t.Run("check agent package version", testAgentPackageVersion(ctx, fixture, true)) + t.Run("check fleet-server api", testFleetServerInternalAPI()) + + // Make sure uninstall from within the topPath fails on Windows + if runtime.GOOS == "windows" { + cwd, err := os.Getwd() + require.NoErrorf(t, err, "GetWd failed: %s", err) + err = os.Chdir(topPath) + require.NoErrorf(t, err, "Chdir to topPath failed: %s", err) + t.Cleanup(func() { + _ = os.Chdir(cwd) + }) + out, err = fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) + require.Error(t, err, "uninstall should have failed") + require.Containsf(t, string(out), "uninstall must be run from outside the installed path", "expected error string not found in: %s err: %s", out, err) + } + + t.Run("Test audit/unenroll", testUninstallAuditUnenroll(ctx, fixture, info)) + }) +} + +func testFleetServerInternalAPI() func(t *testing.T) { + return func(t *testing.T) { + // elastic-agent will self sign a cert to use with fleet-server if one is not passed + // in order to interact with the API we need to ignore the cert. + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + fleetOK := false + for i := 0; i < 10; i++ { + t.Log("Checking fleet-server status") + resp, err := client.Get("https://localhost:8220/api/status") + if err != nil { + t.Logf("fleet-server status check returned error: %v, retry in 10s...", err) + time.Sleep(10 * time.Second) + continue + } + if resp.StatusCode == http.StatusOK { + fleetOK = true + break + } + t.Logf("fleet-server status check returned incorrect status: %d, retry in 10s", resp.StatusCode) time.Sleep(10 * time.Second) continue } - if resp.StatusCode == http.StatusOK { - fleetOK = true - break - } - t.Logf("fleet-server status check returned incorrect status: %d, retry in 10s", resp.StatusCode) - time.Sleep(10 * time.Second) - continue + require.True(t, fleetOK, "expected fleet-server /api/status to return 200") } - require.True(t, fleetOK, "expected fleet-server /api/status to return 200") - - // Make sure uninstall from within the topPath fails on Windows - if runtime.GOOS == "windows" { - cwd, err := os.Getwd() - require.NoErrorf(t, err, "GetWd failed: %s", err) - err = os.Chdir(topPath) - require.NoErrorf(t, err, "Chdir to topPath failed: %s", err) - t.Cleanup(func() { - _ = os.Chdir(cwd) - }) - out, err = fixture.Uninstall(ctx, &atesting.UninstallOpts{Force: true}) - require.Error(t, err, "uninstall should have failed") - require.Containsf(t, string(out), "uninstall must be run from outside the installed path", "expected error string not found in: %s err: %s", out, err) - } - - t.Run("Test audit/unenroll", testUninstallAuditUnenroll(ctx, fixture, info)) } diff --git a/testing/integration/install_test.go b/testing/integration/install_test.go index 106ce2b6bd3..59eb8966d6c 100644 --- a/testing/integration/install_test.go +++ b/testing/integration/install_test.go @@ -361,9 +361,6 @@ func TestInstallUninstallAudit(t *testing.T) { ctx, cancel := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) defer cancel() - fixture, err := define.NewFixtureFromLocalBuild(t, define.Version()) - require.NoError(t, err) - policyResp, enrollmentTokenResp := createPolicyAndEnrollmentToken(ctx, t, info.KibanaClient, createBasicPolicy()) t.Logf("Created policy %+v", policyResp.AgentPolicy) @@ -371,28 +368,63 @@ func TestInstallUninstallAudit(t *testing.T) { fleetServerURL, err := fleettools.DefaultURL(ctx, info.KibanaClient) require.NoError(t, err, "failed getting Fleet Server URL") - err = fixture.Prepare(ctx) - require.NoError(t, err) - // Run `elastic-agent install`. We use `--force` to prevent interactive - // execution. - opts := &atesting.InstallOpts{ - Force: true, - EnrollOpts: atesting.EnrollOpts{ - URL: fleetServerURL, - EnrollmentToken: enrollmentTokenResp.APIKey, - }, - } - out, err := fixture.Install(ctx, opts) - if err != nil { - t.Logf("install output: %s", out) + t.Run("privileged", func(t *testing.T) { + fixture, err := define.NewFixtureFromLocalBuild(t, define.Version()) + require.NoError(t, err) + + err = fixture.Prepare(ctx) + require.NoError(t, err) + // Run `elastic-agent install`. We use `--force` to prevent interactive + // execution. + opts := &atesting.InstallOpts{ + Force: true, + Privileged: true, + EnrollOpts: atesting.EnrollOpts{ + URL: fleetServerURL, + EnrollmentToken: enrollmentTokenResp.APIKey, + }, + } + out, err := fixture.Install(ctx, opts) + if err != nil { + t.Logf("install output: %s", out) + require.NoError(t, err) + } + + require.Eventuallyf(t, func() bool { + return waitForAgentAndFleetHealthy(ctx, t, fixture) + }, time.Minute, time.Second, "agent never became healthy or connected to Fleet") + + t.Run("run uninstall", testUninstallAuditUnenroll(ctx, fixture, info)) + }) + + t.Run("unprivileged", func(t *testing.T) { + fixture, err := define.NewFixtureFromLocalBuild(t, define.Version()) require.NoError(t, err) - } - require.Eventuallyf(t, func() bool { - return waitForAgentAndFleetHealthy(ctx, t, fixture) - }, time.Minute, time.Second, "agent never became healthy or connected to Fleet") + err = fixture.Prepare(ctx) + require.NoError(t, err) + // Run `elastic-agent install`. We use `--force` to prevent interactive + // execution. + opts := &atesting.InstallOpts{ + Force: true, + Privileged: false, + EnrollOpts: atesting.EnrollOpts{ + URL: fleetServerURL, + EnrollmentToken: enrollmentTokenResp.APIKey, + }, + } + out, err := fixture.Install(ctx, opts) + if err != nil { + t.Logf("install output: %s", out) + require.NoError(t, err) + } - t.Run("run uninstall", testUninstallAuditUnenroll(ctx, fixture, info)) + require.Eventuallyf(t, func() bool { + return waitForAgentAndFleetHealthy(ctx, t, fixture) + }, time.Minute, time.Second, "agent never became healthy or connected to Fleet") + + t.Run("run uninstall", testUninstallAuditUnenroll(ctx, fixture, info)) + }) } func testUninstallAuditUnenroll(ctx context.Context, fixture *atesting.Fixture, info *define.Info) func(t *testing.T) { From 5913866686582cd3120d642c5efd90ced51c0c79 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Wed, 11 Dec 2024 15:12:27 -0800 Subject: [PATCH 14/14] Add comments for setting hosts --- internal/pkg/agent/install/uninstall.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/pkg/agent/install/uninstall.go b/internal/pkg/agent/install/uninstall.go index 990fb6da7d9..a89bfbde176 100644 --- a/internal/pkg/agent/install/uninstall.go +++ b/internal/pkg/agent/install/uninstall.go @@ -102,6 +102,10 @@ func Uninstall(ctx context.Context, cfgFile, topPath, uninstallToken string, log // Once the root-cause is identified then this can be re-enabled on Windows. // Notify fleet-server while it is still running if it's running locally if notifyFleet && localFleet && runtime.GOOS != "windows" { + // host is set in the agent/cmd/enroll_cmd.go by createFleetServerBootstrapConfig + // hosts is set in agent/application/actions/handlers/handler_action_policy_change.go by updateFleetConfig + // agents running the fleet-server integration should communicate over the internal API (defaults to localhost:8221) + // This may need to be fixed with https://github.com/elastic/elastic-agent/issues/4771 cfg.Fleet.Client.Hosts = []string{cfg.Fleet.Client.Host} notifyFleetAuditUninstall(ctx, log, pt, cfg, &agentID) //nolint:errcheck // ignore the error as we can't act on it }