func agentInit()

in google_guest_agent/instance_setup.go [81:230]


func agentInit(ctx context.Context) {
	// Actions to take on agent startup.
	//
	// All platforms:
	//  - Determine if metadata hostname can be resolved.
	//
	// On Windows:
	//  - Add route to metadata server
	// On Linux:
	//  - Generate SSH host keys (one time only).
	//  - Generate boto.cfg (one time only).
	//  - Set sysctl values.
	//  - Set scheduler values.
	//  - Run `google_optimize_local_ssd` script.
	//  - Run `google_set_multiqueue` script.
	// TODO incorporate these scripts into the agent. liamh@12-11-19
	config := cfg.Get()

	if runtime.GOOS == "windows" {
		// Try maximum for 1 min.
		policy := retry.Policy{MaxAttempts: 60, BackoffFactor: 1, Jitter: time.Second}
		err := retry.Run(ctx, policy, addMetadataRoute)
		if err != nil {
			panic(fmt.Sprintf("Failed to set metadata route: %+v", err))
		}
	} else {
		// Linux instance setup.
		defer run.Quiet(ctx, "systemd-notify", "--ready")
		defer logger.Debugf("notify systemd")

		if config.Snapshots.Enabled {
			logger.Infof("Snapshot listener enabled")
			snapshotServiceIP := config.Snapshots.SnapshotServiceIP
			snapshotServicePort := config.Snapshots.SnapshotServicePort
			timeoutInSeconds := config.Snapshots.TimeoutInSeconds
			startSnapshotListener(ctx, snapshotServiceIP, snapshotServicePort, timeoutInSeconds)
		}

		scripts := []struct {
			enabled bool
			script  string
		}{
			{config.InstanceSetup.OptimizeLocalSSD, "optimize_local_ssd"},
			{config.InstanceSetup.SetMultiqueue, "set_multiqueue"},
		}

		// These scripts are run regardless of metadata/network access and config options.
		for _, curr := range scripts {
			if !curr.enabled {
				continue
			}

			if err := run.Quiet(ctx, "google_"+curr.script); err != nil {
				logger.Warningf("Failed to run %q script: %v", "google_"+curr.script, err)
			}
		}

		// Below actions happen on every agent start. They only need to
		// run once per boot, but it's harmless to run them on every
		// boot. If this changes, we will hook these to an explicit
		// on-boot signal.

		logger.Debugf("set IO scheduler config")
		if err := setIOScheduler(); err != nil {
			logger.Warningf("Failed to set IO scheduler: %v", err)
		}

		// Allow users to opt out of below instance setup actions.
		if !config.InstanceSetup.NetworkEnabled {
			logger.Infof("InstanceSetup.network_enabled is false, skipping setup actions that require metadata")
			return
		}

		if newMetadata == nil {
			var err error
			logger.Debugf("populate metadata for the first time...")
			newMetadata, err = mdsClient.Get(ctx)
			if err != nil {
				logger.Errorf("Failed to reach MDS(all retries exhausted): %+v", err)
				logger.Infof("Falling to OS default network configuration to attempt to recover.")
				if err := network.FallbackToDefault(ctx); err != nil {
					// Just log error and attempt to continue anyway, if we can't reach MDS
					// we can't do anything.
					logger.Errorf("Failed to rollback guest-agent network configuration: %v", err)
				}
				newMetadata, err = mdsClient.Get(ctx)
				if err != nil {
					logger.Errorf("Failed to reach MDS after attempt to recover network configuration(all retries exhausted): %+v", err)
					os.Exit(1)
				}
			}
		}

		// Early setup the network configurations before we notify systemd we are done.
		runManager(ctx, addressManager)

		// Disable overcommit accounting; e2 instances only.
		parts := strings.Split(newMetadata.Instance.MachineType, "/")
		if strings.HasPrefix(parts[len(parts)-1], "e2-") {
			if err := run.Quiet(ctx, "sysctl", "vm.overcommit_memory=1"); err != nil {
				logger.Warningf("Failed to run 'sysctl vm.overcommit_memory=1': %v", err)
			}
		}

		// Check if instance ID has changed, and if so, consider this
		// the first boot of the instance.
		// TODO Also do this for windows. liamh@13-11-2019
		instanceIDFile := config.Instance.InstanceIDDir
		instanceID, err := os.ReadFile(instanceIDFile)
		if err != nil && !os.IsNotExist(err) {
			logger.Warningf("Not running first-boot actions, error reading instance ID: %v", err)
		} else {
			if string(instanceID) == "" {
				// If the file didn't exist or was empty, try legacy key from instance configs.
				instanceID = []byte(config.Instance.InstanceID)

				// Write instance ID to file for next time before moving on.
				towrite := fmt.Sprintf("%s\n", newMetadata.Instance.ID.String())
				if err := os.WriteFile(instanceIDFile, []byte(towrite), 0644); err != nil {
					logger.Warningf("Failed to write instance ID file: %v", err)
				}
			}
			if newMetadata.Instance.ID.String() != strings.TrimSpace(string(instanceID)) {
				logger.Infof("Instance ID changed, running first-boot actions")
				if config.InstanceSetup.SetHostKeys {
					if err := generateSSHKeys(ctx); err != nil {
						logger.Warningf("Failed to generate SSH keys: %v", err)
					}
				}
				if config.InstanceSetup.SetBotoConfig {
					if err := generateBotoConfig(); err != nil {
						logger.Warningf("Failed to create boto.cfg: %v", err)
					}
				}

				// Write instance ID to file.
				towrite := fmt.Sprintf("%s\n", newMetadata.Instance.ID.String())
				if err := os.WriteFile(instanceIDFile, []byte(towrite), 0644); err != nil {
					logger.Warningf("Failed to write instance ID file: %v", err)
				}
			}
		}
	}
	// Schedules jobs that need to be started before notifying systemd Agent process has started.
	// We want to generate MDS credentials as early as possible so that any process in the Guest can
	// use them. Processes may depend on the Guest Agent at startup to ensure that the credentials are
	// available for use. By generating the credentials before notifying the systemd, we ensure that
	// they are generated for any process that depends on the Guest Agent.
	agentcrypto.Init(ctx)
}