Skip to content

Commit 64cb5a0

Browse files
authored
Bundle elastic-agent.app for MacOS, needed to be able to enable the … (#714)
* Bundle elastic-agent.app for MacOS, needed to be able to enable the Full Disk Access * Calm down the linter * Fix pathing for windows unit test
1 parent 6d830e8 commit 64cb5a0

File tree

7 files changed

+280
-114
lines changed

7 files changed

+280
-114
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
APPL????

dev-tools/packaging/packages.yml

Lines changed: 130 additions & 100 deletions
Large diffs are not rendered by default.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleExecutable</key>
6+
<string>elastic-agent</string>
7+
<key>CFBundleIdentifier</key>
8+
<string>co.elastic.elastic-agent</string>
9+
<key>CFBundleInfoDictionaryVersion</key>
10+
<string>6.0</string>
11+
<key>CFBundleName</key>
12+
<string>elastic-agent</string>
13+
<key>CFBundlePackageType</key>
14+
<string>APPL</string>
15+
<key>CFBundleShortVersionString</key>
16+
<string>{{ beat_version }}</string>
17+
<key>CFBundleVersion</key>
18+
<string>{{ beat_version }}</string>
19+
</dict>
20+
</plist>

internal/pkg/agent/application/info/state.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,27 @@ import (
88
"fmt"
99
"os"
1010
"path/filepath"
11+
"runtime"
1112
"strings"
1213

1314
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
1415
"github.com/elastic/elastic-agent/internal/pkg/release"
1516
)
1617

18+
const (
19+
darwin = "darwin"
20+
)
21+
1722
// RunningInstalled returns true when executing Agent is the installed Agent.
1823
//
1924
// This verifies the running executable path based on hard-coded paths
2025
// for each platform type.
2126
func RunningInstalled() bool {
22-
expected := filepath.Join(paths.InstallPath, paths.BinaryName)
27+
expectedPaths := []string{filepath.Join(paths.InstallPath, paths.BinaryName)}
28+
if runtime.GOOS == darwin {
29+
// For the symlink on darwin the execPath is /usr/local/bin/elastic-agent
30+
expectedPaths = append(expectedPaths, paths.ShellWrapperPath)
31+
}
2332
execPath, _ := os.Executable()
2433
execPath, _ = filepath.Abs(execPath)
2534
execName := filepath.Base(execPath)
@@ -28,13 +37,24 @@ func RunningInstalled() bool {
2837
// executable path is being reported as being down inside of data path
2938
// move up to directories to perform the comparison
3039
execDir = filepath.Dir(filepath.Dir(execDir))
40+
if runtime.GOOS == darwin {
41+
execDir = filepath.Dir(filepath.Dir(filepath.Dir(execDir)))
42+
}
3143
execPath = filepath.Join(execDir, execName)
3244
}
33-
return paths.ArePathsEqual(expected, execPath)
45+
for _, expected := range expectedPaths {
46+
if paths.ArePathsEqual(expected, execPath) {
47+
return true
48+
}
49+
}
50+
return false
3451
}
3552

3653
// IsInsideData returns true when the exePath is inside of the current Agents data path.
3754
func IsInsideData(exePath string) bool {
3855
expectedPath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))
56+
if runtime.GOOS == darwin {
57+
expectedPath = filepath.Join(expectedPath, "elastic-agent.app", "Contents", "MacOS")
58+
}
3959
return strings.HasSuffix(exePath, expectedPath)
4060
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package info
6+
7+
import (
8+
"fmt"
9+
"path/filepath"
10+
"runtime"
11+
"testing"
12+
13+
"github.com/elastic/elastic-agent/internal/pkg/release"
14+
"github.com/google/go-cmp/cmp"
15+
)
16+
17+
func TestIsInsideData(t *testing.T) {
18+
19+
validExePath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))
20+
21+
if runtime.GOOS == darwin {
22+
validExePath = filepath.Join(validExePath, "elastic-agent.app", "Contents", "MacOS")
23+
}
24+
25+
tests := []struct {
26+
name string
27+
exePath string
28+
res bool
29+
}{
30+
{
31+
name: "empty",
32+
},
33+
{
34+
name: "invalid",
35+
exePath: "data/elastic-agent",
36+
},
37+
{
38+
name: "valid",
39+
exePath: validExePath,
40+
res: true,
41+
},
42+
}
43+
44+
for _, tc := range tests {
45+
t.Run(tc.name, func(t *testing.T) {
46+
res := IsInsideData(tc.exePath)
47+
diff := cmp.Diff(tc.res, res)
48+
if diff != "" {
49+
t.Error(diff)
50+
}
51+
})
52+
}
53+
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"os"
1111
"path/filepath"
12+
"runtime"
1213
"strings"
1314
"sync"
1415

@@ -21,6 +22,8 @@ const (
2122
// AgentLockFileName is the name of the overall Elastic Agent file lock.
2223
AgentLockFileName = "agent.lock"
2324
tempSubdir = "tmp"
25+
26+
darwin = "darwin"
2427
)
2528

2629
var (
@@ -68,8 +71,10 @@ func SetTop(path string) {
6871
func TempDir() string {
6972
tmpDir := filepath.Join(Data(), tempSubdir)
7073
tmpCreator.Do(func() {
71-
// create tempdir as it probably don't exists
72-
os.MkdirAll(tmpDir, 0750)
74+
// Create tempdir as it probably don't exists.
75+
// The error was not checked here before and the linter is not happy about it.
76+
// Changing this now would lead to the wide change scope that intended at the moment, so just making the linter happy for now.
77+
_ = os.MkdirAll(tmpDir, 0750)
7378
})
7479
return tmpDir
7580
}
@@ -172,10 +177,15 @@ func SetInstall(path string) {
172177
// initialTop returns the initial top-level path for the binary
173178
//
174179
// When nested in top-level/data/elastic-agent-${hash}/ the result is top-level/.
180+
// The agent fexecutable for MacOS is wrappend in the bundle, so the path to the binary is
181+
// top-level/data/elastic-agent-${hash}/elastic-agent.app/Contents/MacOS
175182
func initialTop() string {
176183
exePath := retrieveExecutablePath()
177184
if insideData(exePath) {
178-
return filepath.Dir(filepath.Dir(exePath))
185+
exePath = filepath.Dir(filepath.Dir(exePath))
186+
if runtime.GOOS == darwin {
187+
exePath = filepath.Dir(filepath.Dir(filepath.Dir(exePath)))
188+
}
179189
}
180190
return exePath
181191
}
@@ -196,5 +206,8 @@ func retrieveExecutablePath() string {
196206
// insideData returns true when the exePath is inside of the current Agents data path.
197207
func insideData(exePath string) bool {
198208
expectedPath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))
209+
if runtime.GOOS == darwin {
210+
expectedPath = filepath.Join(expectedPath, "elastic-agent.app", "Contents", "MacOS")
211+
}
199212
return strings.HasSuffix(exePath, expectedPath)
200213
}

internal/pkg/agent/install/install.go

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io/ioutil"
1010
"os"
1111
"path/filepath"
12+
"runtime"
1213

1314
"github.com/otiai10/copy"
1415

@@ -17,6 +18,10 @@ import (
1718
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
1819
)
1920

21+
const (
22+
darwin = "darwin"
23+
)
24+
2025
// Install installs Elastic Agent persistently on the system including creating and starting its service.
2126
func Install(cfgFile string) error {
2227
dir, err := findDirectory()
@@ -53,15 +58,36 @@ func Install(cfgFile string) error {
5358

5459
// place shell wrapper, if present on platform
5560
if paths.ShellWrapperPath != "" {
56-
err = os.MkdirAll(filepath.Dir(paths.ShellWrapperPath), 0755)
57-
if err == nil {
58-
err = ioutil.WriteFile(paths.ShellWrapperPath, []byte(paths.ShellWrapper), 0755)
59-
}
60-
if err != nil {
61-
return errors.New(
62-
err,
63-
fmt.Sprintf("failed to write shell wrapper (%s)", paths.ShellWrapperPath),
64-
errors.M("destination", paths.ShellWrapperPath))
61+
// Install symlink for darwin instead
62+
if runtime.GOOS == darwin {
63+
// Check if previous shell wrapper or symlink exists and remove it so it can be overwritten
64+
if _, err := os.Lstat(paths.ShellWrapperPath); err == nil {
65+
if err := os.Remove(paths.ShellWrapperPath); err != nil {
66+
return errors.New(
67+
err,
68+
fmt.Sprintf("failed to remove (%s)", paths.ShellWrapperPath),
69+
errors.M("destination", paths.ShellWrapperPath))
70+
}
71+
}
72+
err = os.Symlink("/Library/Elastic/Agent/elastic-agent", paths.ShellWrapperPath)
73+
if err != nil {
74+
return errors.New(
75+
err,
76+
fmt.Sprintf("failed to create elastic-agent symlink (%s)", paths.ShellWrapperPath),
77+
errors.M("destination", paths.ShellWrapperPath))
78+
}
79+
} else {
80+
err = os.MkdirAll(filepath.Dir(paths.ShellWrapperPath), 0755)
81+
if err == nil {
82+
//nolint: gosec // this is intended to be an executable shell script, not chaning the permissions for the linter
83+
err = ioutil.WriteFile(paths.ShellWrapperPath, []byte(paths.ShellWrapper), 0755)
84+
}
85+
if err != nil {
86+
return errors.New(
87+
err,
88+
fmt.Sprintf("failed to write shell wrapper (%s)", paths.ShellWrapperPath),
89+
errors.M("destination", paths.ShellWrapperPath))
90+
}
6591
}
6692
}
6793

@@ -151,6 +177,9 @@ func findDirectory() (string, error) {
151177
// executable path is being reported as being down inside of data path
152178
// move up to directories to perform the copy
153179
sourceDir = filepath.Dir(filepath.Dir(sourceDir))
180+
if runtime.GOOS == darwin {
181+
sourceDir = filepath.Dir(filepath.Dir(filepath.Dir(sourceDir)))
182+
}
154183
}
155184
err = verifyDirectory(sourceDir)
156185
if err != nil {

0 commit comments

Comments
 (0)