-
Notifications
You must be signed in to change notification settings - Fork 148
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow installing a second agent on the same machine for development (#…
…4822) * Define development mode, alter install path. * Change service name in development mode. * Make shell wrapper path conditional on development mode. * Make control socket run path account for dev mode. * Development mode automatically binds to port 0. * Refactor dev mode into separate file. Auto-detect when we were installed in development mode at runtime. * Shorten command to --develop. * Fix windows build error. * Fix using wrong config option in install. * Add run --develop command. * Add initial test for --develop. * Initial version of an integration test for --develop mode. * Wait on the watcher instead of just releasing it. * Add --develop test with base path. * Add privileged install tests with --develop * Move install tests to the same file. * Refactor develop test into function. * Invert condition to match installopts * Automatically add development tag on enroll. * Change shell wrapper path to development. * Add documentation for --develop. * Use lowercase for consistency. * Remove TODO comments. * Fix README typos. * Adjust comments. * More typo fixes. * Fix description not to mention beats. * Change windows service name to avoid collision. * Make service display name unique on Windows. * Add concept of an installation namespace. Restrict use to only the well known development namespace. * Add nolint directives. * Switch from strings.Replace to fmt.Sprintf. * Allow empty nolint directives. Some nolints are platform dependent in cross-platform code. * Enforce agent prefix. Add whitespace tests. * Fix typo. * Remove unnecessary conditional check. * Read the namespace once at startup. * Add missing license header. * Also disable staticcheck for Windows lint warnings. * Revert "Allow empty nolint directives." This reverts commit 8d36a0a. * Better handling of empty fmt strings on windows. * Fix use of hard coded service name. * Fix merge errors in integration tests. * Properly handle empty format string on Windows. * Add Address dropped in merge conflict resolution. * Fix ServiceName uses after merge. * Add --namespace installation option. * Use --namespace in integration tests. * Get integration tests to pass.
- Loading branch information
Showing
33 changed files
with
661 additions
and
242 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
internal/pkg/agent/application/paths/common_namespace.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// 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. | ||
|
||
// This file encapsulates the common paths that need to account for installation namepsaces. | ||
// Installation namespaces allow multiple agents on the same machine. | ||
package paths | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
const ( | ||
// installDirNamespaceFmt is the format of the directory agent will be installed to within the base path when using an installation namepsace. | ||
// It is $BasePath/Agent-$namespace. | ||
installDir = "Agent" | ||
installDirNamespaceSep = "-" | ||
installDirNamespacePrefix = installDir + installDirNamespaceSep | ||
installDirNamespaceFmt = installDirNamespacePrefix + "%s" | ||
|
||
// DevelopmentNamespace defines the "well known" development namespace. | ||
DevelopmentNamespace = "Development" | ||
|
||
// Service display names. Must be different from the ServiceName() on Windows. | ||
serviceDisplayName = "Elastic Agent" | ||
serviceDisplayNameNamespaceFmt = "Elastic Agent - %s" | ||
) | ||
|
||
// installNamespace is the name of the agent's current installation namepsace. | ||
var installNamespace string | ||
|
||
// SetInstallNamespace sets whether the agent is currently in or is being installed in an installation namespace. | ||
// Removes leading and trailing whitespace | ||
func SetInstallNamespace(namespace string) { | ||
installNamespace = strings.TrimSpace(namespace) | ||
} | ||
|
||
// InstallNamespace returns the name of the current installation namespace. Returns the empty string | ||
// for the default namespace. For installed agents, the namespace is parsed from the installation | ||
// directory name, since a unique directory name is required to avoid collisions between installed | ||
// agents in the same base path. Before installation, the installation namespace must be configured | ||
// using SetInstallNamespace(). | ||
func InstallNamespace() string { | ||
if installNamespace != "" { | ||
return installNamespace | ||
} | ||
|
||
if RunningInstalled() { | ||
// Parse the namespace from the directory once to ensure deterministic behavior from startup. | ||
namespace := parseNamespaceFromDir(filepath.Base(Top())) | ||
installNamespace = namespace | ||
} | ||
|
||
return "" | ||
} | ||
|
||
func parseNamespaceFromDir(dir string) string { | ||
parts := strings.SplitAfterN(dir, "-", 2) | ||
if len(parts) <= 1 { | ||
return "" | ||
} else if parts[0] != installDirNamespacePrefix { | ||
return "" | ||
} | ||
|
||
return parts[1] | ||
} | ||
|
||
// InInstallNamespace returns true if the agent is being installed in an installation namespace. | ||
func InInstallNamespace() bool { | ||
return InstallNamespace() != "" | ||
} | ||
|
||
// InstallDirNameForNamespace returns the installation directory name for a given namespace. | ||
// The installation directory name with a namespace is $BasePath/InstallDirNameForNamespace(). | ||
func InstallDirNameForNamespace(namespace string) string { | ||
if namespace == "" { | ||
return installDir | ||
} | ||
|
||
return fmt.Sprintf(installDirNamespaceFmt, namespace) | ||
} | ||
|
||
// InstallPath returns the top level directory Agent will be installed into, accounting for any namespace. | ||
func InstallPath(basePath string) string { | ||
namespace := InstallNamespace() | ||
return filepath.Join(basePath, "Elastic", InstallDirNameForNamespace(namespace)) | ||
} | ||
|
||
// ServiceName returns the service name accounting for any namespace. | ||
func ServiceName() string { | ||
namespace := InstallNamespace() | ||
if namespace == "" { | ||
return serviceName | ||
} | ||
|
||
return fmt.Sprintf(serviceNameNamespaceFmt, namespace) | ||
} | ||
|
||
// ServiceDisplayName returns the service display name accounting for any namespace. | ||
func ServiceDisplayName() string { | ||
namespace := InstallNamespace() | ||
if namespace == "" { | ||
return serviceDisplayName | ||
} | ||
|
||
return fmt.Sprintf(serviceDisplayNameNamespaceFmt, namespace) | ||
} | ||
|
||
// ShellWrapperPath returns the shell wrapper path accounting for any namespace. | ||
// The provided namespace is always lowercased for consistency. | ||
func ShellWrapperPath() string { | ||
namespace := InstallNamespace() | ||
if namespace == "" { | ||
return shellWrapperPath | ||
} | ||
|
||
return ShellWrapperPathForNamespace(namespace) | ||
} | ||
|
||
// ControlSocketRunSymlink returns the shell wrapper path accounting for any namespace. | ||
// Does not auto detect the namespace because it is used outside of agent itself in the testing framework. | ||
func ControlSocketRunSymlink(namespace string) string { | ||
if namespace == "" { | ||
return controlSocketRunSymlink | ||
} | ||
|
||
return controlSocketRunSymlinkForNamespace(namespace) | ||
} |
76 changes: 76 additions & 0 deletions
76
internal/pkg/agent/application/paths/common_namespace_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// 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 paths | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestInstallNamespace(t *testing.T) { | ||
namespace := "testing" | ||
basePath := filepath.Join("base", "path") | ||
|
||
// Add whitespace to ensure it gets removed. | ||
SetInstallNamespace(" " + namespace + "\t ") | ||
|
||
assert.Equal(t, namespace, InstallNamespace()) | ||
assert.True(t, InInstallNamespace()) | ||
assert.Equal(t, filepath.Join(basePath, "Elastic", fmt.Sprintf(installDirNamespaceFmt, namespace)), InstallPath(basePath)) | ||
assert.Equal(t, fmt.Sprintf(serviceNameNamespaceFmt, namespace), ServiceName()) | ||
assert.Equal(t, fmt.Sprintf(serviceDisplayNameNamespaceFmt, namespace), ServiceDisplayName()) | ||
assert.Equal(t, ShellWrapperPathForNamespace(namespace), ShellWrapperPath()) | ||
assert.Equal(t, controlSocketRunSymlinkForNamespace(namespace), ControlSocketRunSymlink(namespace)) | ||
} | ||
|
||
func TestInstallNoNamespace(t *testing.T) { | ||
namespace := "" | ||
basePath := filepath.Join("base", "path") | ||
SetInstallNamespace(namespace) | ||
|
||
assert.Equal(t, namespace, InstallNamespace()) | ||
assert.False(t, InInstallNamespace()) | ||
assert.Equal(t, filepath.Join(basePath, "Elastic", installDir), InstallPath(basePath)) | ||
assert.Equal(t, serviceName, ServiceName()) | ||
assert.Equal(t, serviceDisplayName, ServiceDisplayName()) | ||
assert.Equal(t, shellWrapperPath, ShellWrapperPath()) | ||
assert.Equal(t, controlSocketRunSymlink, ControlSocketRunSymlink(namespace)) | ||
} | ||
|
||
func TestParseNamespaceFromDirName(t *testing.T) { | ||
testcases := []struct { | ||
name string | ||
dir string | ||
namespace string | ||
}{ | ||
{name: "empty", dir: "", namespace: ""}, | ||
{name: "none", dir: "Agent", namespace: ""}, | ||
{name: "develop", dir: "Agent-Development", namespace: "Development"}, | ||
{name: "dashes", dir: "Agent-With-Dashes", namespace: "With-Dashes"}, | ||
{name: "special", dir: "Agent-@!$%^&*()-_+=", namespace: "@!$%^&*()-_+="}, | ||
{name: "format", dir: "Agent-%s%d%v%t", namespace: "%s%d%v%t"}, | ||
{name: "spaces", dir: "Agent- Development \t", namespace: " Development \t"}, | ||
} | ||
|
||
for _, tc := range testcases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
assert.Equalf(t, tc.namespace, parseNamespaceFromDir(tc.dir), "parsing %s", tc.dir) | ||
|
||
// Special case: if the directory is empty the install dir is the default "Agent" not "Agent-" | ||
wantDir := tc.dir | ||
if wantDir == "" { | ||
wantDir = installDir | ||
} | ||
assert.Equal(t, wantDir, InstallDirNameForNamespace(tc.namespace)) | ||
}) | ||
} | ||
} | ||
|
||
func TestParseNamespaceFromDirNameWithoutAgentPrefix(t *testing.T) { | ||
assert.Equal(t, "", parseNamespaceFromDir("Beats-Development")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.