func installCmd()

in internal/pkg/agent/cmd/install.go [86:364]


func installCmd(streams *cli.IOStreams, cmd *cobra.Command) error {
	var err error

	if installServers, _ := cmd.Flags().GetBool(flagInstallServers); isFleetServerFlagProvided(cmd) && !installServers {
		_ = cmd.Flags().Lookup(flagInstallServers).Value.Set("true") // this can fail only when parsing bool
		fmt.Fprintf(streams.Out, "fleet-server installation detected, using --%s flag\n", flagInstallServers)
	}

	err = validateEnrollFlags(cmd)
	if err != nil {
		return fmt.Errorf("could not validate flags: %w", err)
	}

	basePath, _ := cmd.Flags().GetString(flagInstallBasePath)
	if !filepath.IsAbs(basePath) {
		return fmt.Errorf("base path [%s] is not absolute", basePath)
	}

	isAdmin, err := utils.HasRoot()
	if err != nil {
		return fmt.Errorf("unable to perform install command while checking for root/Administrator rights: %w", err)
	}
	if !isAdmin {
		return fmt.Errorf("unable to perform install command, not executed with %s permissions", utils.PermissionUser)
	}

	unprivileged, _ := cmd.Flags().GetBool(flagInstallUnprivileged)
	if unprivileged {
		fmt.Fprintln(streams.Out, "Unprivileged installation mode enabled; this feature is currently in beta.")
	}

	isDevelopmentMode, _ := cmd.Flags().GetBool(flagInstallDevelopment)
	if isDevelopmentMode {
		fmt.Fprintln(streams.Out, "Installing into development namespace; this is an experimental and currently unsupported feature.")
		// For now, development mode only installs agent in a well known namespace to allow two agents on the same machine.
		paths.SetInstallNamespace(paths.DevelopmentNamespace)
	}

	namespace, _ := cmd.Flags().GetString(flagInstallNamespace)
	if namespace != "" {
		fmt.Fprintf(streams.Out, "Installing into namespace '%s'; this is an experimental and currently unsupported feature.\n", namespace)
		// Overrides the development namespace if namespace was specified separately.
		paths.SetInstallNamespace(namespace)
	}

	topPath := paths.InstallPath(basePath)

	status, reason := install.Status(topPath)
	force, _ := cmd.Flags().GetBool("force")
	if status == install.Installed && !force {
		return fmt.Errorf("already installed at: %s", topPath)
	}

	runUninstallBinary, _ := cmd.Flags().GetBool(flagInstallRunUninstallFromBinary)
	if status == install.Installed && force && runUninstallBinary {
		fmt.Fprintln(streams.Out, "Uninstall will not be ran from the agent installed in system path, components may persist.")
	}

	nonInteractive, _ := cmd.Flags().GetBool("non-interactive")
	if nonInteractive {
		fmt.Fprintln(streams.Out, "Installing in non-interactive mode.")
	}

	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 {
		if errors.Is(err, filelock.ErrAppAlreadyRunning) {
			return fmt.Errorf("cannot perform installation as Elastic Agent is already running from this directory")
		}
		return fmt.Errorf("error obtaining lock: %w", err)
	}
	_ = locker.Unlock()

	if status == install.Broken {
		if !force && !nonInteractive {
			fmt.Fprintf(streams.Out, "Elastic Agent is installed but currently broken: %s\n", reason)
			confirm, err := cli.Confirm(fmt.Sprintf("Continuing will re-install Elastic Agent over the current installation at %s. Do you want to continue?", topPath), true)
			if err != nil {
				return fmt.Errorf("problem reading prompt response")
			}
			if !confirm {
				return fmt.Errorf("installation was cancelled by the user")
			}
		}
	} else if status != install.PackageInstall {
		if !force && !nonInteractive {
			confirm, err := cli.Confirm(fmt.Sprintf("Elastic Agent will be installed at %s and will run as a service. Do you want to continue?", topPath), true)
			if err != nil {
				return fmt.Errorf("problem reading prompt response")
			}
			if !confirm {
				return fmt.Errorf("installation was cancelled by the user")
			}
		}
	}

	enroll := true
	askEnroll := true
	url, _ := cmd.Flags().GetString("url")
	token, _ := cmd.Flags().GetString("enrollment-token")
	delayEnroll, _ := cmd.Flags().GetBool("delay-enroll")
	if url != "" && token != "" {
		askEnroll = false
	}
	fleetServer, _ := cmd.Flags().GetString("fleet-server-es")
	if fleetServer != "" || force || delayEnroll || nonInteractive {
		askEnroll = false
	}
	if askEnroll {
		confirm, err := cli.Confirm("Do you want to enroll this Agent into Fleet?", true)
		if err != nil {
			return fmt.Errorf("problem reading prompt response")
		}
		if !confirm {
			// not enrolling
			enroll = false
		}
	}
	if !askEnroll && (url == "" || token == "") && fleetServer == "" {
		// force was performed without required enrollment arguments, all done (standalone mode)
		enroll = false
	}

	if enroll && fleetServer == "" {
		if url == "" {
			if nonInteractive {
				return fmt.Errorf("missing required --url argument used to enroll the agent")
			}
			url, err = cli.ReadInput("URL you want to enroll this Agent into:")
			if err != nil {
				return fmt.Errorf("problem reading prompt response")
			}
			if url == "" {
				fmt.Fprintln(streams.Out, "Enrollment cancelled because no URL was provided.")
				return nil
			}
		}
		if token == "" {
			if nonInteractive {
				return fmt.Errorf("missing required --enrollment-token argument used to enroll the agent")
			}
			token, err = cli.ReadInput("Fleet enrollment token:")
			if err != nil {
				return fmt.Errorf("problem reading prompt response")
			}
			if token == "" {
				fmt.Fprintf(streams.Out, "Enrollment cancelled because no enrollment token was provided.\n")
				return nil
			}
		}
	}

	progBar := install.CreateAndStartNewSpinner(streams.Out, "Installing Elastic Agent...")

	log, logBuff := logger.NewInMemory("install", logp.ConsoleEncoderConfig())
	defer func() {
		if err == nil {
			return
		}
		fmt.Fprintf(os.Stderr, "Error uninstalling. Printing logs\n")
		fmt.Fprint(os.Stderr, logBuff.String())
	}()

	var ownership utils.FileOwner
	cfgFile := paths.ConfigFile()
	if status == install.Installed {
		// Uninstall the agent
		progBar.Describe(fmt.Sprintf("Uninstalling current %s", paths.ServiceDisplayName()))
		if !runUninstallBinary {
			err := execUninstall(streams, topPath, paths.BinaryName)
			if err != nil {
				progBar.Describe("Uninstall failed")
				return err
			}
		} else {
			err := install.Uninstall(cmd.Context(), cfgFile, topPath, "", log, progBar, false)
			if err != nil {
				progBar.Describe("Uninstall from binary failed")
				return err
			}
		}
		progBar.Describe("Successfully uninstalled Elastic Agent")
	}

	if status != install.PackageInstall {
		customUser, _ := cmd.Flags().GetString(flagInstallCustomUser)
		customGroup, _ := cmd.Flags().GetString(flagInstallCustomGroup)
		customPass := ""
		if runtime.GOOS == "windows" {
			customPass, _ = cmd.Flags().GetString(flagInstallCustomPass)
		}

		flavor := install.DefaultFlavor
		if installServers, _ := cmd.Flags().GetBool(flagInstallServers); installServers {
			flavor = install.FlavorServers
		}

		ownership, err = install.Install(cfgFile, topPath, unprivileged, log, progBar, streams, customUser, customGroup, customPass, flavor)
		if err != nil {
			return fmt.Errorf("error installing package: %w", err)
		}

		defer func() {
			if err != nil {
				progBar.Describe("Uninstalling")
				innerErr := install.Uninstall(cmd.Context(), cfgFile, topPath, "", log, progBar, false)
				if innerErr != nil {
					progBar.Describe("Failed to Uninstall")
				} else {
					progBar.Describe("Uninstalled")
				}
			}
		}()

		if !delayEnroll {
			progBar.Describe("Starting Service")
			err = install.StartService(topPath)
			if err != nil {
				progBar.Describe("Start Service failed, exiting...")
				fmt.Fprintf(streams.Out, "Installation failed to start '%s' service.\n", paths.ServiceName())
				return fmt.Errorf("error starting service: %w", err)
			}
			progBar.Describe("Service Started")

			defer func() {
				if err != nil {
					progBar.Describe("Stopping Service")
					innerErr := install.StopService(topPath, install.DefaultStopTimeout, install.DefaultStopInterval)
					if innerErr != nil {
						progBar.Describe("Failed to Stop Service")
					} else {
						progBar.Describe("Successfully Stopped Service")
					}
				}
			}()
		}

		fmt.Fprintf(streams.Out, "%s successfully installed, starting enrollment.\n", paths.ServiceDisplayName())
	}

	if enroll {
		enrollArgs := []string{"enroll", "--from-install"}
		enrollArgs = append(enrollArgs, buildEnrollmentFlags(cmd, url, token)...)
		enrollCmd := exec.Command(install.ExecutablePath(topPath), enrollArgs...) //nolint:gosec // it's not tainted
		enrollCmd.Stdin = os.Stdin
		enrollCmd.Stdout = os.Stdout
		enrollCmd.Stderr = os.Stderr
		err = enrollCmdExtras(enrollCmd, ownership)
		if err != nil {
			return err
		}

		progBar.Describe(fmt.Sprintf("Enrolling %s with Fleet", paths.ServiceDisplayName()))
		err = enrollCmd.Start()
		if err != nil {
			progBar.Describe("Failed to Enroll")
			return fmt.Errorf("failed to execute enroll command: %w", err)
		}
		progBar.Describe("Waiting For Enroll...")
		err = enrollCmd.Wait()
		if err != nil {
			progBar.Describe("Failed to Enroll")
			// uninstall doesn't need to be performed here the defer above will
			// catch the error and perform the uninstall
			return fmt.Errorf("enroll command failed for unknown reason: %w", err)
		}
		progBar.Describe("Enroll Completed")
	}

	progBar.Describe("Done")
	_ = progBar.Finish()
	_ = progBar.Exit()
	fmt.Fprintf(streams.Out, "\n%s has been successfully installed.\n", paths.ServiceDisplayName())
	return nil
}