Skip to content

Commit

Permalink
Alter agent install to detect installation from package (#30289)
Browse files Browse the repository at this point in the history
* Alter agent install to detect installation from package

Alter the elastic-agent install command to not run the installation or
uninstall steps (on failure) when the agent has been installed via the
package manager of a Linux os. Instead the enroll command will be
executed.

* Check package manager by running which instead of looking for file

* Update Vagrantfile

Co-authored-by: Pier-Hugues Pellerin <phpellerin@gmail.com>

* Remove commented question

Co-authored-by: Pier-Hugues Pellerin <phpellerin@gmail.com>
  • Loading branch information
michel-laterman and ph authored Feb 24, 2022
1 parent b1d41b1 commit bc84d77
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 24 deletions.
6 changes: 4 additions & 2 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ TEST_BOXES = [
{:name => "amazon2", :box => "bento/amazonlinux-2", :platform => "centos"},

# Unsupported platforms
{:name => "opensuse153", :box => "bento/opensuse-leap-15.3", :platform => "opensuse"},
{:name => "sles12", :box => "elastic/sles-12-x86_64", :platform => "sles"},
{:name => "solaris", :box => "https://s3.amazonaws.com/beats-files/vagrant/beats-solaris-11.2-virtualbox-2016-11-02_1603.box", :platform => "unix"},
{:name => "freebsd", :box => "bento/freebsd-13", :platform => "freebsd", :extras => "pkg install -y -q bash && chsh -s bash vagrant"},
{:name => "openbsd", :box => "generic/openbsd6", :platform => "openbsd", :extras => "sudo pkg_add go"},
Expand Down Expand Up @@ -103,14 +105,14 @@ Vagrant.configure("2") do |config|
end
end

# Freebsd
# Freebsd
if node[:platform] == "freebsd"
nodeconfig.vm.provision "shell", path: "dev-tools/vagrant_scripts/unixProvision.sh", args: "gvm amd64 freebsd #{GO_VERSION}", privileged: false
nodeconfig.vm.provision "shell", inline: "sudo mount -t linprocfs /dev/null /proc", privileged: false
end

# gvm install
if node[:platform] == "centos" or node[:platform] == "ubuntu" or node[:platform] == "debian" or node[:platorm] == "archlinux"
if [:centos, :ubuntu, :debian, :archlinux, :opensuse, :sles].include?(node[:platform].to_sym)
nodeconfig.vm.provision "shell", type: "shell", path: "dev-tools/vagrant_scripts/unixProvision.sh", args: "gvm amd64 linux #{GO_VERSION}", privileged: false
end

Expand Down
10 changes: 9 additions & 1 deletion dev-tools/vagrant_scripts/unixProvision.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ function archProvision () {
pacman -Sy && pacman -S --noconfirm make gcc python python-pip git
}

function suseProvision() {
zypper refresh
zypper install -y make gcc git python3 python3-pip python3-virtualenv git rpm-devel
}

case "${PROVISION_TYPE}" in
"gvm")
gvmProvision $ARCH $OS $GO_VERSION
Expand All @@ -61,6 +66,9 @@ case "${PROVISION_TYPE}" in
"debian" | "ubuntu")
debProvision
;;
"opensuse" | "sles")
suseProvision
;;
*)
echo "No Extra provisioning steps for this platform"
esac
esac
1 change: 1 addition & 0 deletions x-pack/elastic-agent/CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,4 @@
- Discover changes in Kubernetes nodes metadata as soon as they happen. {pull}23139[23139]
- Add results of inspect output command into archive produced by diagnostics collect. {pull}29902[29902]
- Add support for loading input configuration from external configuration files in standalone mode. You can load inputs from YAML configuration files under the folder `{path.config}/inputs.d`. {pull}30087[30087]
- Install command will skip install/uninstall steps when installation via package is detected on Linux distros. {pull}30289[30289]
49 changes: 29 additions & 20 deletions x-pack/elastic-agent/pkg/agent/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
return fmt.Errorf("already installed at: %s", paths.InstallPath)
}

if status == install.PackageInstall {
fmt.Fprintf(streams.Out, "Installed as a system package, installation will not be altered.\n")
}

// check the lock to ensure that elastic-agent is not already running in this directory
locker := filelock.NewAppLocker(paths.Data(), paths.AgentLockFileName)
if err := locker.TryLock(); err != nil {
Expand All @@ -80,7 +84,7 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
return fmt.Errorf("installation was cancelled by the user")
}
}
} else {
} else if status != install.PackageInstall {
if !force {
confirm, err := cli.Confirm(fmt.Sprintf("Elastic Agent will be installed at %s and will run as a service. Do you want to continue?", paths.InstallPath), true)
if err != nil {
Expand Down Expand Up @@ -141,30 +145,33 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
}
}
}
cfgFile := paths.ConfigFile()
err = install.Install(cfgFile)
if err != nil {
return err
}

defer func() {
if err != nil {
install.Uninstall(cfgFile)
}
}()

if !delayEnroll {
err = install.StartService()
cfgFile := paths.ConfigFile()
if status != install.PackageInstall {
err = install.Install(cfgFile)
if err != nil {
fmt.Fprintf(streams.Out, "Installation failed to start Elastic Agent service.\n")
return err
}

defer func() {
if err != nil {
install.StopService()
install.Uninstall(cfgFile)
}
}()

if !delayEnroll {
err = install.StartService()
if err != nil {
fmt.Fprintf(streams.Out, "Installation failed to start Elastic Agent service.\n")
return err
}

defer func() {
if err != nil {
install.StopService()
}
}()
}
}

if enroll {
Expand All @@ -180,10 +187,12 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command, args []string) error
}
err = enrollCmd.Wait()
if err != nil {
install.Uninstall(cfgFile)
exitErr, ok := err.(*exec.ExitError)
if ok {
return fmt.Errorf("enroll command failed with exit code: %d", exitErr.ExitCode())
if status != install.PackageInstall {
install.Uninstall(cfgFile)
exitErr, ok := err.(*exec.ExitError)
if ok {
return fmt.Errorf("enroll command failed with exit code: %d", exitErr.ExitCode())
}
}
return fmt.Errorf("enroll command failed for unknown reason: %s", err)
}
Expand Down
50 changes: 50 additions & 0 deletions x-pack/elastic-agent/pkg/agent/install/install_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,58 @@

package install

import (
"os/exec"
"runtime"
"strings"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths"
)

// postInstall performs post installation for unix-based systems.
func postInstall() error {
// do nothing
return nil
}

func checkPackageInstall() bool {
if runtime.GOOS != "linux" {
return false
}

// NOTE searching for english words might not be a great idea as far as portability goes.
// list all installed packages then search for paths.BinaryName?
// dpkg is strange as the remove and purge processes leads to the package bing isted after a remove, but not after a purge

// check debian based systems (or systems that use dpkg)
// If the package has been installed, the status starts with "install"
// If the package has been removed (but not pruged) status starts with "deinstall"
// If purged or never installed, rc is 1
if _, err := exec.Command("which", "dpkg-query").Output(); err == nil {
out, err := exec.Command("dpkg-query", "-W", "-f", "${Status}", paths.BinaryName).Output()
if err != nil {
return false
}
if strings.HasPrefix(string(out), "deinstall") {
return false
}
return true
}

// check rhel and sles based systems (or systems that use rpm)
// if package has been installed query retuns with a list of associated files.
// otherwise if uninstalled, or has never been installled status ends with "not installed"
if _, err := exec.Command("which", "rpm").Output(); err == nil {
out, err := exec.Command("rpm", "-q", paths.BinaryName, "--state").Output()
if err != nil {
return false
}
if strings.HasSuffix(string(out), "not installed") {
return false
}
return true

}

return false
}
6 changes: 6 additions & 0 deletions x-pack/elastic-agent/pkg/agent/install/install_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ func postInstall() error {

return nil
}

// checkPackageInstall is used for unix based systems to see if the Elastic-Agent was installed through a package manager.
// returns false
func checkPackageInstall() bool {
return false
}
10 changes: 9 additions & 1 deletion x-pack/elastic-agent/pkg/agent/install/installed.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,24 @@ type StatusType int
const (
// NotInstalled returned when Elastic Agent is not installed.
NotInstalled StatusType = iota
// Installed returned when Elastic Agent is installed currectly.
// Installed returned when Elastic Agent is installed correctly.
Installed
// Broken returned when Elastic Agent is installed but broken.
Broken
// PackageInstall returned when the Elastic agent has been installed through a package manager (deb/rpm)
PackageInstall
)

// Status returns the installation status of Agent.
func Status() (StatusType, string) {
expected := filepath.Join(paths.InstallPath, paths.BinaryName)
status, reason := checkService()
if checkPackageInstall() {
if status == Installed {
return PackageInstall, "service running"
}
return PackageInstall, "service not running"
}
_, err := os.Stat(expected)
if os.IsNotExist(err) {
if status == Installed {
Expand Down

0 comments on commit bc84d77

Please sign in to comment.