Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundle elastic-agent.app for MacOS, needed to be able to enable the … #714

Merged
merged 3 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-tools/packaging/files/darwin/PkgInfo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
APPL????
230 changes: 130 additions & 100 deletions dev-tools/packaging/packages.yml

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions dev-tools/packaging/templates/darwin/Info.plist.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>elastic-agent</string>
<key>CFBundleIdentifier</key>
<string>co.elastic.elastic-agent</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>elastic-agent</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{{ beat_version }}</string>
<key>CFBundleVersion</key>
<string>{{ beat_version }}</string>
</dict>
</plist>
24 changes: 22 additions & 2 deletions internal/pkg/agent/application/info/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
"github.com/elastic/elastic-agent/internal/pkg/release"
)

const (
darwin = "darwin"
)

// RunningInstalled returns true when executing Agent is the installed Agent.
//
// This verifies the running executable path based on hard-coded paths
// for each platform type.
func RunningInstalled() bool {
expected := filepath.Join(paths.InstallPath, paths.BinaryName)
expectedPaths := []string{filepath.Join(paths.InstallPath, paths.BinaryName)}
if runtime.GOOS == darwin {
// For the symlink on darwin the execPath is /usr/local/bin/elastic-agent
expectedPaths = append(expectedPaths, paths.ShellWrapperPath)
}
execPath, _ := os.Executable()
execPath, _ = filepath.Abs(execPath)
execName := filepath.Base(execPath)
Expand All @@ -28,13 +37,24 @@ func RunningInstalled() bool {
// executable path is being reported as being down inside of data path
// move up to directories to perform the comparison
execDir = filepath.Dir(filepath.Dir(execDir))
if runtime.GOOS == darwin {
execDir = filepath.Dir(filepath.Dir(filepath.Dir(execDir)))
}
execPath = filepath.Join(execDir, execName)
}
return paths.ArePathsEqual(expected, execPath)
for _, expected := range expectedPaths {
if paths.ArePathsEqual(expected, execPath) {
return true
}
}
return false
}

// IsInsideData returns true when the exePath is inside of the current Agents data path.
func IsInsideData(exePath string) bool {
expectedPath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))
if runtime.GOOS == darwin {
expectedPath = filepath.Join(expectedPath, "elastic-agent.app", "Contents", "MacOS")
}
return strings.HasSuffix(exePath, expectedPath)
}
53 changes: 53 additions & 0 deletions internal/pkg/agent/application/info/state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package info

import (
"fmt"
"path/filepath"
"runtime"
"testing"

"github.com/elastic/elastic-agent/internal/pkg/release"
"github.com/google/go-cmp/cmp"
)

func TestIsInsideData(t *testing.T) {

validExePath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))

if runtime.GOOS == darwin {
validExePath = filepath.Join(validExePath, "elastic-agent.app", "Contents", "MacOS")
}

tests := []struct {
name string
exePath string
res bool
}{
{
name: "empty",
},
{
name: "invalid",
exePath: "data/elastic-agent",
},
{
name: "valid",
exePath: validExePath,
res: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
res := IsInsideData(tc.exePath)
diff := cmp.Diff(tc.res, res)
if diff != "" {
t.Error(diff)
}
})
}
}
19 changes: 16 additions & 3 deletions internal/pkg/agent/application/paths/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"

Expand All @@ -21,6 +22,8 @@ const (
// AgentLockFileName is the name of the overall Elastic Agent file lock.
AgentLockFileName = "agent.lock"
tempSubdir = "tmp"

darwin = "darwin"
)

var (
Expand Down Expand Up @@ -68,8 +71,10 @@ func SetTop(path string) {
func TempDir() string {
tmpDir := filepath.Join(Data(), tempSubdir)
tmpCreator.Do(func() {
// create tempdir as it probably don't exists
os.MkdirAll(tmpDir, 0750)
// Create tempdir as it probably don't exists.
// The error was not checked here before and the linter is not happy about it.
// Changing this now would lead to the wide change scope that intended at the moment, so just making the linter happy for now.
_ = os.MkdirAll(tmpDir, 0750)
})
return tmpDir
}
Expand Down Expand Up @@ -172,10 +177,15 @@ func SetInstall(path string) {
// initialTop returns the initial top-level path for the binary
//
// When nested in top-level/data/elastic-agent-${hash}/ the result is top-level/.
// The agent fexecutable for MacOS is wrappend in the bundle, so the path to the binary is
// top-level/data/elastic-agent-${hash}/elastic-agent.app/Contents/MacOS
func initialTop() string {
exePath := retrieveExecutablePath()
if insideData(exePath) {
return filepath.Dir(filepath.Dir(exePath))
exePath = filepath.Dir(filepath.Dir(exePath))
if runtime.GOOS == darwin {
exePath = filepath.Dir(filepath.Dir(filepath.Dir(exePath)))
}
}
return exePath
}
Expand All @@ -196,5 +206,8 @@ func retrieveExecutablePath() string {
// insideData returns true when the exePath is inside of the current Agents data path.
func insideData(exePath string) bool {
expectedPath := filepath.Join("data", fmt.Sprintf("elastic-agent-%s", release.ShortCommit()))
if runtime.GOOS == darwin {
expectedPath = filepath.Join(expectedPath, "elastic-agent.app", "Contents", "MacOS")
}
return strings.HasSuffix(exePath, expectedPath)
}
47 changes: 38 additions & 9 deletions internal/pkg/agent/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"

"github.com/otiai10/copy"

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

const (
darwin = "darwin"
)

// Install installs Elastic Agent persistently on the system including creating and starting its service.
func Install(cfgFile string) error {
dir, err := findDirectory()
Expand Down Expand Up @@ -53,15 +58,36 @@ func Install(cfgFile string) error {

// place shell wrapper, if present on platform
if paths.ShellWrapperPath != "" {
err = os.MkdirAll(filepath.Dir(paths.ShellWrapperPath), 0755)
if err == nil {
err = ioutil.WriteFile(paths.ShellWrapperPath, []byte(paths.ShellWrapper), 0755)
}
if err != nil {
return errors.New(
err,
fmt.Sprintf("failed to write shell wrapper (%s)", paths.ShellWrapperPath),
errors.M("destination", paths.ShellWrapperPath))
// Install symlink for darwin instead
if runtime.GOOS == darwin {
// Check if previous shell wrapper or symlink exists and remove it so it can be overwritten
if _, err := os.Lstat(paths.ShellWrapperPath); err == nil {
if err := os.Remove(paths.ShellWrapperPath); err != nil {
return errors.New(
err,
fmt.Sprintf("failed to remove (%s)", paths.ShellWrapperPath),
errors.M("destination", paths.ShellWrapperPath))
}
}
err = os.Symlink("/Library/Elastic/Agent/elastic-agent", paths.ShellWrapperPath)
if err != nil {
return errors.New(
err,
fmt.Sprintf("failed to create elastic-agent symlink (%s)", paths.ShellWrapperPath),
errors.M("destination", paths.ShellWrapperPath))
}
} else {
err = os.MkdirAll(filepath.Dir(paths.ShellWrapperPath), 0755)
if err == nil {
//nolint: gosec // this is intended to be an executable shell script, not chaning the permissions for the linter
err = ioutil.WriteFile(paths.ShellWrapperPath, []byte(paths.ShellWrapper), 0755)
}
if err != nil {
return errors.New(
err,
fmt.Sprintf("failed to write shell wrapper (%s)", paths.ShellWrapperPath),
errors.M("destination", paths.ShellWrapperPath))
}
}
}

Expand Down Expand Up @@ -151,6 +177,9 @@ func findDirectory() (string, error) {
// executable path is being reported as being down inside of data path
// move up to directories to perform the copy
sourceDir = filepath.Dir(filepath.Dir(sourceDir))
if runtime.GOOS == darwin {
sourceDir = filepath.Dir(filepath.Dir(filepath.Dir(sourceDir)))
}
}
err = verifyDirectory(sourceDir)
if err != nil {
Expand Down