func mainErr()

in integration_test/soak_test/cmd/launcher/main.go [107:236]


func mainErr() error {
	defer gce.CleanupKeysOrDie()

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)
	defer cancel()

	// Log to stderr.
	logger := log.Default()

	if distro == "" {
		return errors.New("Env variable DISTRO cannot be empty")
	}
	if ttl == "" {
		return errors.New("Env variable TTL cannot be empty")
	}

	// Create the VM.
	options := gce.VMOptions{
		ImageSpec:   distro,
		TimeToLive:  ttl,
		Name:        vmName,
		MachineType: "e2-standard-16",
		Metadata: map[string]string{
			// This is to avoid Windows updates and reboots (b/295165549), and
			// also to avoid throughput blips when the OS Config agent runs
			// periodically.
			"osconfig-disabled-features": "tasks",
		},
		ExtraCreateArguments: []string{"--boot-disk-size=4000GB"},
	}
	vm, err := gce.CreateInstance(ctx, logger, options)
	if err != nil {
		return err
	}

	if gce.IsWindows(vm.ImageSpec) {
		if _, err := pauseWindowsUpdates(ctx, logger, vm); err != nil {
			return err
		}
	}

	debugLogPath := "/tmp/log_generator.log"

	// Install the Ops Agent with a config telling it to watch logPath,
	// and debugLogPath for debugging.
	config := fmt.Sprintf(`logging:
  receivers:
    mylog_source:
      type: files
      include_paths:
      - %s
    generator_debug_logs:
      type: files
      include_paths:
      - %s
  exporters:
    google:
      type: google_cloud_logging
  service:
    pipelines:
      my_pipeline:
        receivers:
        - mylog_source
        - generator_debug_logs
        exporters: [google]
`, logPath, debugLogPath)
	if err := agents.SetupOpsAgent(ctx, logger, vm, config); err != nil {
		return err
	}

	// Install Python.
	// TODO: Consider shipping over a prebuilt binary so that we don't need to
	// install Python.
	if gce.IsWindows(vm.ImageSpec) {
		installPython := `$tempDir = "/tmp"
mkdir $tempDir

$pythonUrl = 'https://www.python.org/ftp/python/3.11.2/python-3.11.2.exe'
$pythonInstallerName = $pythonUrl -replace '.*/'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($pythonUrl, "$tempDir\$pythonInstallerName")

$pythonInstallDir = "$env:SystemDrive\Python"
$pythonPath = "$pythonInstallDir\python.exe"
Start-Process "$tempDir\$pythonInstallerName" -Wait -ArgumentList "/quiet TargetDir=$pythonInstallDir InstallAllUsers=1"
`
		if _, err := gce.RunRemotely(ctx, logger, vm, installPython); err != nil {
			return fmt.Errorf("Could not install Python: %w", err)
		}
	} else {
		if err := agents.InstallPackages(ctx, logger, vm, []string{"python3"}); err != nil {
			return err
		}
	}
	// Upload log_generator.py.
	if err := gce.UploadContent(ctx, logger, vm, strings.NewReader(logGeneratorSource), logGeneratorPath); err != nil {
		return err
	}

	// Start log_generator.py asynchronously.
	var startLogGenerator string
	if gce.IsWindows(vm.ImageSpec) {
		// The best way I've found to start a process asynchronously. One downside
		// is that standard output and standard error are lost.
		startLogGenerator = fmt.Sprintf(`Invoke-WmiMethod -ComputerName . -Class Win32_Process -Name Create -ArgumentList "$env:SystemDrive\Python\python.exe %v --log-size-in-bytes=%v --log-rate=%v --log-write-type=file --file-path=%v"`, logGeneratorPath, logSizeInBytes, logRate, logPath)
	} else {
		startLogGenerator = fmt.Sprintf(`nohup python3 %v \
  --log-size-in-bytes="%v" \
  --log-rate="%v" \
  --log-write-type=file \
  --file-path="%v" \
  &> %v &
`, logGeneratorPath, logSizeInBytes, logRate, logPath, debugLogPath)
	}
	if _, err := gce.RunRemotely(ctx, logger, vm, startLogGenerator); err != nil {
		return err
	}

	// Print log_generator log files to debug startup errors.
	// These log files are unfortunately not available on Windows.
	if !gce.IsWindows(vm.ImageSpec) {
		time.Sleep(5 * time.Second)

		if _, err := gce.RunRemotely(ctx, logger, vm, "cat "+debugLogPath); err != nil {
			return err
		}
	}
	return nil
}