func setupVMWatchCommand()

in main/vmWatch.go [257:375]


func setupVMWatchCommand(s *vmWatchSettings, hEnv *handlerenv.HandlerEnvironment) (*exec.Cmd, bool, error) {
	processDirectory, err := GetProcessDirectory()
	if err != nil {
		return nil, false, err
	}

	args := []string{"--config", GetVMWatchConfigFullPath(processDirectory)}
	args = append(args, "--debug")
	args = append(args, "--heartbeat-file", GetVMWatchHeartbeatFilePath(hEnv))
	args = append(args, "--execution-environment", GetExecutionEnvironment(hEnv))

	// 0 is the default so allow that but any value below 30MB is not allowed
	if s.MemoryLimitInBytes == 0 {
		s.MemoryLimitInBytes = DefaultMaxMemoryInBytes

	}
	if s.MemoryLimitInBytes < 30000000 {
		err = fmt.Errorf("[%v] Invalid MemoryLimitInBytes specified must be at least 30000000", time.Now().UTC().Format(time.RFC3339))
		return nil, false, err
	}

	// check cpu, if 0 (default) set to the default value
	if s.MaxCpuPercentage == 0 {
		s.MaxCpuPercentage = DefaultMaxCpuPercentage
	}

	if s.MaxCpuPercentage < 0 || s.MaxCpuPercentage > 100 {
		err = fmt.Errorf("[%v] Invalid maxCpuPercentage specified must be between 0 and 100", time.Now().UTC().Format(time.RFC3339))
		return nil, false, err
	}

	args = append(args, "--memory-limit-bytes", strconv.FormatInt(s.MemoryLimitInBytes, 10))

	if s.SignalFilters != nil {
		if s.SignalFilters.DisabledSignals != nil && len(s.SignalFilters.DisabledSignals) > 0 {
			args = append(args, "--disabled-signals")
			args = append(args, strings.Join(s.SignalFilters.DisabledSignals, ":"))
		}

		if s.SignalFilters.DisabledTags != nil && len(s.SignalFilters.DisabledTags) > 0 {
			args = append(args, "--disabled-tags")
			args = append(args, strings.Join(s.SignalFilters.DisabledTags, ":"))
		}

		if s.SignalFilters.EnabledTags != nil && len(s.SignalFilters.EnabledTags) > 0 {
			args = append(args, "--enabled-tags")
			args = append(args, strings.Join(s.SignalFilters.EnabledTags, ":"))
		}

		if s.SignalFilters.EnabledOptionalSignals != nil && len(s.SignalFilters.EnabledOptionalSignals) > 0 {
			args = append(args, "--enabled-optional-signals")
			args = append(args, strings.Join(s.SignalFilters.EnabledOptionalSignals, ":"))
		}
	}

	if len(strings.TrimSpace(s.GlobalConfigUrl)) > 0 {
		args = append(args, "--global-config-url", s.GlobalConfigUrl)
	}

	args = append(args, "--disable-config-reader", strconv.FormatBool(s.DisableConfigReader))

	if s.EnvironmentAttributes != nil {
		if len(s.EnvironmentAttributes) > 0 {
			args = append(args, "--env-attributes")
			var envAttributes []string
			for k, v := range s.EnvironmentAttributes {
				envAttributes = append(envAttributes, fmt.Sprintf("%v=%v", k, v))
			}
			args = append(args, strings.Join(envAttributes, ":"))
		}
	}

	// if we are running in a dev container don't call IMDS endpoint
	if os.Getenv("RUNNING_IN_DEV_CONTAINER") != "" {
		args = append(args, "--local")
	}

	extVersion, err := GetExtensionManifestVersion()
	if err == nil {
		args = append(args, "--apphealth-version", extVersion)
	}
	var cmd *exec.Cmd
	// flag to tell the caller that further resource governance is required by assigning to cgroups after the process is started
	// default to true to that if systemd-run is not available, we will assign cgroups
	resourceGovernanceRequired := true
	// if we have systemd available, we will use that to launch the process, otherwise we will launch directly and manipulate our own cgroups
	if isSystemdAvailable() {
		systemdVersion := getSystemdVersion()

		// since systemd-run is in different paths on different distros, we will check for systemd but not use the full path
		// to systemd-run.  This is how guest agent handles it also so seems appropriate.
		systemdArgs := []string{"--scope", "-p", fmt.Sprintf("CPUQuota=%v%%", s.MaxCpuPercentage)}

		// systemd versions prior to 246 do not support MemoryMax, instead MemoryLimit should be used
		if systemdVersion < 246 {
			systemdArgs = append(systemdArgs, "-p", fmt.Sprintf("MemoryLimit=%v", s.MemoryLimitInBytes))
		} else {
			systemdArgs = append(systemdArgs, "-p", fmt.Sprintf("MemoryMax=%v", s.MemoryLimitInBytes))
		}

		// now append the env variables (--setenv is supported in all versions, -E only in newer versions)
		for _, v := range GetVMWatchEnvironmentVariables(s.ParameterOverrides, hEnv) {
			systemdArgs = append(systemdArgs, "--setenv", v)
		}
		systemdArgs = append(systemdArgs, GetVMWatchBinaryFullPath(processDirectory))
		systemdArgs = append(systemdArgs, args...)

		// since systemd-run is in different paths on different distros, we will check for systemd but not use the full path
		// to systemd-run.  This is how guest agent handles it also so seems appropriate.
		cmd = exec.Command("systemd-run", systemdArgs...)
		// cgroup assignment not required since we are using systemd-run
		resourceGovernanceRequired = false
	} else {
		cmd = exec.Command(GetVMWatchBinaryFullPath(processDirectory), args...)
		cmd.Env = GetVMWatchEnvironmentVariables(s.ParameterOverrides, hEnv)
	}

	return cmd, resourceGovernanceRequired, nil
}