func Install()

in internal/pkg/agent/install/install.go [43:184]


func Install(cfgFile, topPath string, unprivileged bool, log *logp.Logger, pt *progressbar.ProgressBar, streams *cli.IOStreams, customUser, customGroup, userPassword string, flavor string) (utils.FileOwner, error) {
	dir, err := findDirectory()
	if err != nil {
		return utils.FileOwner{}, errors.New(err, "failed to discover the source directory for installation", errors.TypeFilesystem)
	}

	var ownership utils.FileOwner
	username := ""
	groupName := ""
	password := ""
	if unprivileged {
		username, password = UnprivilegedUser(customUser, userPassword)
		groupName = UnprivilegedGroup(customGroup)
		ownership, err = EnsureUserAndGroup(username, groupName, pt, username == ElasticUsername && password == "") // force create only elastic user
		if err != nil {
			// error context already added by EnsureUserAndGroup
			return utils.FileOwner{}, err

		}
	}

	err = setupInstallPath(topPath, ownership)
	if err != nil {
		return utils.FileOwner{}, fmt.Errorf("error setting up install path: %w", err)
	}

	manifest, err := readPackageManifest(dir)
	if err != nil {
		return utils.FileOwner{}, fmt.Errorf("reading package manifest: %w", err)
	}

	pathMappings := manifest.Package.PathMappings

	pt.Describe("Copying install files")
	copyConcurrency := calculateCopyConcurrency(streams)

	skipFn := func(relPath string) bool { return false }
	if flavor != "" {
		flavorDefinition, err := Flavor(flavor, "", manifest.Package.Flavors)
		if err != nil {
			return utils.FileOwner{}, err
		}
		skipFn, err = SkipComponentsPathFn(paths.VersionedHome(dir), flavorDefinition)
		if err != nil {
			return utils.FileOwner{}, err
		}
	}

	err = copyFiles(copyConcurrency, pathMappings, dir, topPath, skipFn)
	if err != nil {
		pt.Describe("Error copying files")
		return utils.FileOwner{}, err
	}

	if err := markFlavor(topPath, flavor); err != nil {
		return utils.FileOwner{}, fmt.Errorf("failed marking flavor %q at %q: %w", flavor, topPath, err)
	}

	pt.Describe("Successfully copied files")

	// place shell wrapper, if present on platform
	if paths.ShellWrapperPath() != "" {
		pathDir := filepath.Dir(paths.ShellWrapperPath())
		err = os.MkdirAll(pathDir, 0755)
		if err != nil {
			return utils.FileOwner{}, errors.New(
				err,
				fmt.Sprintf("failed to create directory (%s) for shell wrapper (%s)", pathDir, paths.ShellWrapperPath()),
				errors.M("directory", pathDir))
		}
		// Install symlink for darwin instead of the wrapper script.
		// Elastic-agent should be first process that launchd starts in order to be able to grant
		// the Full-Disk Access (FDA) to the agent and it's child processes.
		// This is specifically important for osquery FDA permissions at the moment.
		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 utils.FileOwner{}, errors.New(
						err,
						fmt.Sprintf("failed to remove (%s)", paths.ShellWrapperPath()),
						errors.M("destination", paths.ShellWrapperPath()))
				}
			}
			err = os.Symlink(filepath.Join(topPath, paths.BinaryName), paths.ShellWrapperPath())
			if err != nil {
				return utils.FileOwner{}, errors.New(
					err,
					fmt.Sprintf("failed to create elastic-agent symlink (%s)", paths.ShellWrapperPath()),
					errors.M("destination", paths.ShellWrapperPath()))
			}
		} else {
			// We use strings.Replace instead of fmt.Sprintf here because, with the
			// latter, govet throws a false positive error here: "fmt.Sprintf call has
			// arguments but no formatting directives".
			shellWrapper := strings.Replace(paths.ShellWrapperFmt, "%s", topPath, -1)
			err = os.WriteFile(paths.ShellWrapperPath(), []byte(shellWrapper), 0755)
			if err != nil {
				return utils.FileOwner{}, errors.New(
					err,
					fmt.Sprintf("failed to write shell wrapper (%s)", paths.ShellWrapperPath()),
					errors.M("destination", paths.ShellWrapperPath()))
			}
		}
	}

	// post install (per platform)
	err = postInstall(topPath)
	if err != nil {
		return ownership, fmt.Errorf("error running post-install steps: %w", err)
	}

	// fix permissions
	err = perms.FixPermissions(topPath, perms.WithOwnership(ownership))
	if err != nil {
		return ownership, fmt.Errorf("failed to perform permission changes on path %s: %w", topPath, err)
	}
	if paths.ShellWrapperPath() != "" {
		err = perms.FixPermissions(paths.ShellWrapperPath(), perms.WithOwnership(ownership))
		if err != nil {
			return ownership, fmt.Errorf("failed to perform permission changes on path %s: %w", paths.ShellWrapperPath(), err)
		}
	}

	// install service
	pt.Describe("Installing service")
	// ensure that service is removed
	err = EnsureServiceRemoved(30*time.Second, 250*time.Millisecond, paths.ServiceName())
	if err != nil {
		pt.Describe(fmt.Sprintf("Failed to ensure service does not exist: %s", err.Error()))
	}
	// install service
	err = InstallService(topPath, ownership, username, groupName, password)
	if err != nil {
		pt.Describe("Failed to install service")
		// error context already added by InstallService
		return ownership, err
	}
	pt.Describe("Installed service")

	return ownership, nil
}