Skip to content

Commit b4d1389

Browse files
aleksmausmergify[bot]
authored andcommitted
Fix Elastic Agent non-fleet broken upgrade between 8.3.x releases (#701)
* Fix Elastic Agent non-fleet broken upgrade between 8.3.x releases * Migrates vault directory on linux and windows to the top directory of the agent, so it can be shared without needing the upgrade handler call, like for example with side-by-side install/upgrade from .rpm/.deb * Extended vault to allow read-only open, useful when the vault at particular location needs to be only read not created. * Correct the typo in the log messages * Update lint flagged function comment with 'unused', was flagged with 'deadcode' on the previous run * Address code review feedback * Add missing import for linux utz * Change vault path from Top() to Config(), this a better location, next to fleet.enc based on the install/upgrade testing with .rpm/.deb installs * Fix the missing state migration for .rpm/.deb upgrade. The post install script now performs the migration and creates the symlink after that. * Fix typo in the postinstall script * Update the vault migration code, add the agent configuration match check with the agent secret (cherry picked from commit 8ef98f1)
1 parent fcee44f commit b4d1389

File tree

15 files changed

+772
-77
lines changed

15 files changed

+772
-77
lines changed

dev-tools/packaging/packages.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@ shared:
2020
# Deb/RPM spec for community beats.
2121
- &deb_rpm_agent_spec
2222
<<: *common
23-
post_install_script: '{{ elastic_beats_dir }}/dev-tools/packaging/files/linux/systemd-daemon-reload.sh'
23+
post_install_script: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/linux/postinstall.sh.tmpl'
2424
files:
25-
/usr/share/{{.BeatName}}/bin/{{.BeatName}}{{.BinaryExt}}:
26-
source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}}
27-
mode: 0755
2825
/usr/share/{{.BeatName}}/LICENSE.txt:
2926
source: '{{ repo.RootDir }}/LICENSE.txt'
3027
mode: 0644
@@ -1083,11 +1080,6 @@ specs:
10831080
spec:
10841081
<<: *deb_rpm_agent_spec
10851082
<<: *elastic_license_for_deb_rpm
1086-
files:
1087-
/usr/share/{{.BeatName}}/bin/{{.BeatName}}{{.BinaryExt}}:
1088-
source: /var/lib/{{.BeatName}}/data/{{.BeatName}}-{{ commit_short }}/{{.BeatName}}{{.BinaryExt}}
1089-
symlink: true
1090-
mode: 0755
10911083

10921084
- os: linux
10931085
arch: amd64
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
symlink="/usr/share/elastic-agent/bin/elastic-agent"
6+
old_agent_dir="$( dirname "$(readlink -f -- "$symlink")" )"
7+
8+
commit_hash="{{ commit_short }}"
9+
10+
yml_path="$old_agent_dir/state.yml"
11+
enc_path="$old_agent_dir/state.enc"
12+
13+
new_agent_dir="$( dirname "$old_agent_dir")/elastic-agent-$commit_hash"
14+
15+
if ! [[ "$old_agent_dir" -ef "$new_agent_dir" ]]; then
16+
echo "migrate state from $old_agent_dir to $new_agent_dir"
17+
18+
if test -f "$yml_path"; then
19+
echo "found "$yml_path", copy to "$new_agent_dir"."
20+
cp "$yml_path" "$new_agent_dir"
21+
fi
22+
23+
if test -f "$enc_path"; then
24+
echo "found "$enc_path", copy to "$new_agent_dir"."
25+
cp "$enc_path" "$new_agent_dir"
26+
fi
27+
28+
if test -f "$symlink"; then
29+
echo "found symlink $symlink, unlink"
30+
unlink "$symlink"
31+
fi
32+
33+
echo "create symlink "$symlink" to "$new_agent_dir/elastic-agent""
34+
ln -s "$new_agent_dir/elastic-agent" "$symlink"
35+
fi
36+
37+
systemctl daemon-reload 2> /dev/null
38+
exit 0

internal/pkg/agent/application/paths/paths_linux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ const defaultAgentVaultPath = "vault"
1414

1515
// AgentVaultPath is the directory that contains all the files for the value
1616
func AgentVaultPath() string {
17-
return filepath.Join(Home(), defaultAgentVaultPath)
17+
return filepath.Join(Config(), defaultAgentVaultPath)
1818
}

internal/pkg/agent/application/paths/paths_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@ func ArePathsEqual(expected, actual string) bool {
4242

4343
// AgentVaultPath is the directory that contains all the files for the value
4444
func AgentVaultPath() string {
45-
return filepath.Join(Home(), defaultAgentVaultPath)
45+
return filepath.Join(Config(), defaultAgentVaultPath)
4646
}

internal/pkg/agent/application/secret/secret.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package secret
66

77
import (
88
"encoding/json"
9+
"fmt"
910
"runtime"
1011
"sync"
1112
"time"
@@ -51,7 +52,7 @@ func Create(key string, opts ...OptionFunc) error {
5152
options := applyOptions(opts...)
5253
v, err := vault.New(options.vaultPath)
5354
if err != nil {
54-
return err
55+
return fmt.Errorf("could not create new vault: %w", err)
5556
}
5657
defer v.Close()
5758

@@ -79,23 +80,25 @@ func Create(key string, opts ...OptionFunc) error {
7980
CreatedOn: time.Now().UTC(),
8081
}
8182

82-
b, err := json.Marshal(secret)
83-
if err != nil {
84-
return err
85-
}
86-
87-
return v.Set(key, b)
83+
return set(v, key, secret)
8884
}
8985

9086
// GetAgentSecret read the agent secret from the vault
9187
func GetAgentSecret(opts ...OptionFunc) (secret Secret, err error) {
9288
return Get(agentSecretKey, opts...)
9389
}
9490

91+
// SetAgentSecret saves the agent secret from the vault
92+
// This is needed for migration from 8.3.0-8.3.2 to higher versions
93+
func SetAgentSecret(secret Secret, opts ...OptionFunc) error {
94+
return Set(agentSecretKey, secret, opts...)
95+
}
96+
9597
// Get reads the secret key from the vault
9698
func Get(key string, opts ...OptionFunc) (secret Secret, err error) {
9799
options := applyOptions(opts...)
98-
v, err := vault.New(options.vaultPath)
100+
// open vault readonly, will not create the vault directory or the seed it was not created before
101+
v, err := vault.New(options.vaultPath, vault.WithReadonly(true))
99102
if err != nil {
100103
return secret, err
101104
}
@@ -110,12 +113,32 @@ func Get(key string, opts ...OptionFunc) (secret Secret, err error) {
110113
return secret, err
111114
}
112115

116+
// Set saves the secret key to the vault
117+
func Set(key string, secret Secret, opts ...OptionFunc) error {
118+
options := applyOptions(opts...)
119+
v, err := vault.New(options.vaultPath)
120+
if err != nil {
121+
return fmt.Errorf("could not create new vault: %w", err)
122+
}
123+
defer v.Close()
124+
return set(v, key, secret)
125+
}
126+
127+
func set(v *vault.Vault, key string, secret Secret) error {
128+
b, err := json.Marshal(secret)
129+
if err != nil {
130+
return fmt.Errorf("could not marshal secret: %w", err)
131+
}
132+
133+
return v.Set(key, b)
134+
}
135+
113136
// Remove removes the secret key from the vault
114137
func Remove(key string, opts ...OptionFunc) error {
115138
options := applyOptions(opts...)
116139
v, err := vault.New(options.vaultPath)
117140
if err != nil {
118-
return err
141+
return fmt.Errorf("could not create new vault: %w", err)
119142
}
120143
defer v.Close()
121144

internal/pkg/agent/application/upgrade/upgrade.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"io/ioutil"
1111
"os"
1212
"path/filepath"
13-
"runtime"
1413
"strings"
1514

1615
"github.com/otiai10/copy"
@@ -33,7 +32,6 @@ const (
3332
agentName = "elastic-agent"
3433
hashLen = 6
3534
agentCommitFile = ".elastic-agent.active.commit"
36-
darwin = "darwin"
3735
)
3836

3937
var (
@@ -161,11 +159,6 @@ func (u *Upgrader) Upgrade(ctx context.Context, a Action, reexecNow bool) (_ ree
161159
return nil, nil
162160
}
163161

164-
// Copy vault directory for linux/windows only
165-
if err := copyVault(newHash); err != nil {
166-
return nil, errors.New(err, "failed to copy vault")
167-
}
168-
169162
if err := copyActionStore(newHash); err != nil {
170163
return nil, errors.New(err, "failed to copy action store")
171164
}
@@ -300,36 +293,6 @@ func copyActionStore(newHash string) error {
300293
return nil
301294
}
302295

303-
func getVaultPath(newHash string) string {
304-
vaultPath := paths.AgentVaultPath()
305-
if runtime.GOOS == darwin {
306-
return vaultPath
307-
}
308-
newHome := filepath.Join(filepath.Dir(paths.Home()), fmt.Sprintf("%s-%s", agentName, newHash))
309-
return filepath.Join(newHome, filepath.Base(vaultPath))
310-
}
311-
312-
// Copies the vault files for windows and linux
313-
func copyVault(newHash string) error {
314-
// No vault files to copy on darwin
315-
if runtime.GOOS == darwin {
316-
return nil
317-
}
318-
319-
vaultPath := paths.AgentVaultPath()
320-
newVaultPath := getVaultPath(newHash)
321-
322-
err := copyDir(vaultPath, newVaultPath)
323-
if err != nil {
324-
if os.IsNotExist(err) {
325-
return nil
326-
}
327-
return err
328-
}
329-
330-
return nil
331-
}
332-
333296
// shutdownCallback returns a callback function to be executing during shutdown once all processes are closed.
334297
// this goes through runtime directory of agent and copies all the state files created by processes to new versioned
335298
// home directory with updated process name to match new version.

internal/pkg/agent/cmd/run.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/elastic/elastic-agent/internal/pkg/agent/configuration"
3838
"github.com/elastic/elastic-agent/internal/pkg/agent/control/server"
3939
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
40+
"github.com/elastic/elastic-agent/internal/pkg/agent/migration"
4041
"github.com/elastic/elastic-agent/internal/pkg/agent/storage"
4142
"github.com/elastic/elastic-agent/internal/pkg/cli"
4243
"github.com/elastic/elastic-agent/internal/pkg/config"
@@ -121,6 +122,22 @@ func run(override cfgOverrider) error {
121122
createAgentID = false
122123
}
123124

125+
// This is specific for the agent upgrade from 8.3.0 - 8.3.2 to 8.x and above on Linux and Windows platforms.
126+
// Addresses the issue: https://github.com/elastic/elastic-agent/issues/682
127+
// The vault directory was located in the hash versioned "Home" directory of the agent.
128+
// This moves the vault directory two levels up into the "Config" directory next to fleet.enc file
129+
// in order to be able to "upgrade" the agent from deb/rpm that is not invoking the upgrade handle and
130+
// doesn't perform the migration of the state or vault.
131+
// If the agent secret doesn't exist, then search for the newest agent secret in the agent data directories
132+
// and migrate it into the new vault location.
133+
err = migration.MigrateAgentSecret(logger)
134+
logger.Debug("migration of agent secret completed, err: %v", err)
135+
if err != nil {
136+
err = errors.New(err, "failed to perfrom the agent secret migration")
137+
logger.Error(err)
138+
return err
139+
}
140+
124141
// Ensure we have the agent secret created.
125142
// The secret is not created here if it exists already from the previous enrollment.
126143
// This is needed for compatibility with agent running in standalone mode,

0 commit comments

Comments
 (0)