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
}