From c46a379a9ca9770dc707ca563e625c9e41c671d2 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Tue, 2 Jul 2024 12:44:53 -0400 Subject: [PATCH] Write `container-paths.yml` into the `STATE_PATH` (#4995) * Don't write container paths. * Clean up. * Add changelog. * Store container-paths.yml in STATE_PATH. * Update 1719266012-Don't-fail-when-unable-to-write-container-paths.yaml * fix issue with empty value * Fix test. --- ...-when-unable-to-write-container-paths.yaml | 32 +++++++++++++++++++ internal/pkg/agent/cmd/container.go | 13 +++++--- testing/integration/container_cmd_test.go | 25 ++++++++++++--- 3 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 changelog/fragments/1719266012-Don't-fail-when-unable-to-write-container-paths.yaml diff --git a/changelog/fragments/1719266012-Don't-fail-when-unable-to-write-container-paths.yaml b/changelog/fragments/1719266012-Don't-fail-when-unable-to-write-container-paths.yaml new file mode 100644 index 00000000000..74518c41703 --- /dev/null +++ b/changelog/fragments/1719266012-Don't-fail-when-unable-to-write-container-paths.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: Allow Elastic Agent container to work with a read-only filesystem + +# 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: + +# 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/4995 + +# 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 diff --git a/internal/pkg/agent/cmd/container.go b/internal/pkg/agent/cmd/container.go index db454cf5fa9..0e4b375314f 100644 --- a/internal/pkg/agent/cmd/container.go +++ b/internal/pkg/agent/cmd/container.go @@ -804,7 +804,6 @@ func setPaths(statePath, configPath, logsPath, socketPath string, writePaths boo } originalInstall := paths.Install() - originalTop := paths.Top() paths.SetTop(topPath) paths.SetConfig(configPath) paths.SetControlSocket(socketPath) @@ -830,7 +829,7 @@ func setPaths(statePath, configPath, logsPath, socketPath string, writePaths boo // persist the paths so other commands in the container will use the correct paths if writePaths { - if err := writeContainerPaths(originalTop, statePath, configPath, logsPath, socketPath); err != nil { + if err := writeContainerPaths(statePath, configPath, logsPath, socketPath); err != nil { return err } } @@ -844,8 +843,8 @@ type containerPaths struct { SocketPath string `config:"socket_path" yaml:"socket_path,omitempty"` } -func writeContainerPaths(original, statePath, configPath, logsPath, socketPath string) error { - pathFile := filepath.Join(original, "container-paths.yml") +func writeContainerPaths(statePath, configPath, logsPath, socketPath string) error { + pathFile := filepath.Join(statePath, "container-paths.yml") fp, err := os.Create(pathFile) if err != nil { return fmt.Errorf("failed creating %s: %w", pathFile, err) @@ -867,7 +866,11 @@ func writeContainerPaths(original, statePath, configPath, logsPath, socketPath s } func tryContainerLoadPaths() error { - pathFile := filepath.Join(paths.Top(), "container-paths.yml") + statePath := envWithDefault("", "STATE_PATH") + if statePath == "" { + statePath = defaultStateDirectory + } + pathFile := filepath.Join(statePath, "container-paths.yml") _, err := os.Stat(pathFile) if os.IsNotExist(err) { // no container-paths.yml file exists, so nothing to do diff --git a/testing/integration/container_cmd_test.go b/testing/integration/container_cmd_test.go index fa20c65e27f..35c9dab37f0 100644 --- a/testing/integration/container_cmd_test.go +++ b/testing/integration/container_cmd_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" "github.com/elastic/elastic-agent-libs/kibana" + "github.com/elastic/elastic-agent/pkg/core/process" atesting "github.com/elastic/elastic-agent/pkg/testing" "github.com/elastic/elastic-agent/pkg/testing/define" "github.com/elastic/elastic-agent/pkg/testing/tools/fleettools" @@ -115,6 +116,11 @@ func TestContainerCMD(t *testing.T) { agentFixture, err := define.NewFixtureFromLocalBuild(t, define.Version()) require.NoError(t, err) + // prepare must be called otherwise `agentFixture.WorkDir()` will be empty + // and it must be set so the `STATE_PATH` below gets a valid path. + err = agentFixture.Prepare(ctx) + require.NoError(t, err) + fleetURL, err := fleettools.DefaultURL(ctx, info.KibanaClient) if err != nil { t.Fatalf("could not get Fleet URL: %s", err) @@ -147,7 +153,7 @@ func TestContainerCMD(t *testing.T) { // the agent logs will be present in the error message // which should help to explain why the agent was not // healthy. - err = agentFixture.IsHealthy(ctx) + err = agentFixture.IsHealthy(ctx, withEnv(env)) return err == nil }, 5*time.Minute, time.Second, @@ -225,7 +231,7 @@ func TestContainerCMDWithAVeryLongStatePath(t *testing.T) { // the agent logs will be present in the error message // which should help to explain why the agent was not // healthy. - err = agentFixture.IsHealthy(ctx) + err = agentFixture.IsHealthy(ctx, withEnv(env)) return err == nil }, 1*time.Minute, time.Second, @@ -245,16 +251,27 @@ func TestContainerCMDWithAVeryLongStatePath(t *testing.T) { if _, err := os.Stat(tc.expectedSocketPath); err != nil { t.Errorf("cannot stat expected socket path ('%s'): %s", tc.expectedSocketPath, err) } + containerPaths := filepath.Join(tc.expectedStatePath, "container-paths.yml") + if _, err := os.Stat(tc.expectedSocketPath); err != nil { + t.Errorf("cannot stat expected container-paths.yml path ('%s'): %s", containerPaths, err) + } if t.Failed() { - containerPaths, err := os.ReadFile(filepath.Join(agentFixture.WorkDir(), "container-paths.yml")) + containerPathsContent, err := os.ReadFile(containerPaths) if err != nil { t.Fatalf("could not read container-paths.yml: %s", err) } t.Log("contents of 'container-paths-yml'") - t.Log(string(containerPaths)) + t.Log(string(containerPathsContent)) } }) } } + +func withEnv(env []string) process.CmdOption { + return func(c *exec.Cmd) error { + c.Env = append(os.Environ(), env...) + return nil + } +}