cmd/google_guest_agent/google_guest_agent.go (122 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package main is the google_guest_agent binary. package main import ( "context" "flag" "fmt" "os" "runtime" "time" "github.com/GoogleCloudPlatform/galog" "github.com/GoogleCloudPlatform/google-guest-agent/cmd/google_guest_agent/setup" "github.com/GoogleCloudPlatform/google-guest-agent/internal/cfg" "github.com/GoogleCloudPlatform/google-guest-agent/internal/daemon" "github.com/GoogleCloudPlatform/google-guest-agent/internal/events" "github.com/GoogleCloudPlatform/google-guest-agent/internal/logger" "github.com/GoogleCloudPlatform/google-guest-agent/internal/metadata" "github.com/GoogleCloudPlatform/google-guest-agent/internal/plugin/config" "github.com/GoogleCloudPlatform/google-guest-agent/internal/service" "github.com/GoogleCloudPlatform/google-guest-agent/internal/utils/file" ) var ( // logOpts holds the logger options. It's mapped to command line flags. logOpts = logger.Options{ Ident: logger.ManagerLocalLoggerIdent, Prefix: logger.ManagerLogPrefix, CloudIdent: logger.ManagerCloudLoggingLogID, } // version is the version of the binary. version = "unknown" // forceOnDemandPlugins is the flag to force on-demand plugins, it takes // precedence over the config. forceOnDemandPlugins = false // corePluginPath is the path to core plugin binary. corePluginPath = "" // skipCorePlugin determines if core plugin initialization should be skipped. // Core plugin is not yet supported and must be set to true by default. skipCorePlugin = true ) const ( // galogShutdownTimeout is the period of time we should wait galog to // shutdown. galogShutdownTimeout = time.Second // defaultLinuxCorePath is the default path where core plugin is installed on Linux. defaultLinuxCorePath = "/usr/lib/google/guest_agent/core_plugin" // defaultWindowsCorePath is the default path where core plugin is installed on Windows. defaultWindowsCorePath = `C:\Program Files\Google\Compute Engine\agent\CorePlugin.exe` ) func setupFlags() { // Log flags. flag.StringVar(&logOpts.LogFile, "logfile", cfg.Retrieve().Core.LogFile, "path to the log file") flag.BoolVar(&logOpts.LogToStderr, "logtostderr", false, "write logs to stderr") flag.BoolVar(&logOpts.LogToCloudLogging, "logtocloud", true, "write logs to cloud logging") flag.IntVar(&logOpts.Level, "loglevel", cfg.Retrieve().Core.LogLevel, "log level: "+galog.ValidLevels()) flag.IntVar(&logOpts.Verbosity, "logverbosity", cfg.Retrieve().Core.LogVerbosity, "log verbosity") // On-demand plugins flags. flag.BoolVar(&forceOnDemandPlugins, "on_demand_plugins", false, "force on-demand plugins (even if disabled on the configuration)") // Core plugin flags. flag.StringVar(&corePluginPath, "core_plugin_path", entryPath(), "path to core plugin binary") flag.BoolVar(&skipCorePlugin, "core_plugins", true, "skip core plugin installation") flag.Parse() } // entryPath returns the path from where core plugin should be started. func entryPath() string { if runtime.GOOS == "windows" { return defaultWindowsCorePath } return defaultLinuxCorePath } // readExtraConfig reads the extra config from file set in environment variable. func readExtraConfig() ([]byte, error) { var configs []byte configPath := os.Getenv("GUEST_AGENT_EXTRA_CONFIG") if configPath == "" { // No extra config found, return. return configs, nil } return os.ReadFile(configPath) } func main() { extraCfg, err := readExtraConfig() if err != nil { fmt.Fprintln(os.Stderr, "Failed to read extra config:", err) os.Exit(1) } if err := cfg.Load(extraCfg); err != nil { fmt.Fprintln(os.Stderr, "Failed to load config:", err) os.Exit(1) } setupFlags() ctx, cancel := context.WithCancel(context.Background()) logOpts.ProgramVersion = version logOpts.ACSClientDebugLogging = cfg.Retrieve().ACS.ClientDebugLogging if err := logger.Init(ctx, logOpts); err != nil { fmt.Fprintln(os.Stderr, "Failed to initialize logger:", err) os.Exit(1) } if err := service.Init(ctx, func() { galog.Info("Google Guest Agent Leaving (canceling context)...") galog.Shutdown(galogShutdownTimeout) cancel() }, daemon.GuestAgentManager); err != nil { galog.Fatalf("Failed to initialize service manager: %s", err) } // MDS watcher is disabled in test environment as its not accessible. It must // not be set otherwise. if os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR") != "" { galog.Infof("MDS watcher is disabled in config, skipping MDS watcher initialization") } else { if err := events.FetchManager().AddWatcher(ctx, metadata.NewWatcher()); err != nil { galog.Fatalf("Failed to add metadata watcher: %v", err) } } opts := setup.Config{Version: version, CorePluginPath: corePluginPath, SkipCorePlugin: ignoreCorePlugin()} // ACS watcher requires ACS client enabled. if (forceOnDemandPlugins || cfg.Retrieve().Core.OnDemandPlugins) && cfg.Retrieve().Core.ACSClient { opts.EnableACSWatcher = true } galog.Infof("Initializing Google Guest Agent...") if err := setup.Run(ctx, opts); err != nil { galog.Fatalf("Failed to initialize Guest Agent with required Core Plugin: %v", err) } if err := events.FetchManager().Run(ctx); err != nil { galog.Fatalf("Failed to run events manager: %v", err) } } // ignoreCorePlugin returns true if core plugin should be skipped. func ignoreCorePlugin() bool { binaryPath := agentBinaryPath() // This is a configuration guardrail to see if the guest agent binary is // present. If it is not present, we enable the core plugin. Ignore this // check in test environment as binary path is expected to be not present. if !file.Exists(binaryPath, file.TypeFile) && os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR") == "" { galog.Infof("Guest agent binary %q not found, enabling core plugin", binaryPath) return false } // If core plugin config is written in config file, use that. Otherwise, use // the command line flag. Test environment do rely on the command line flag. if config.IsConfigFilePresent() { galog.Infof("Core plugin config file is present, setting skipCorePlugin to [%t]", skipCorePlugin) return !config.IsCorePluginEnabled() } return skipCorePlugin } // agentBinaryPath returns the path to the guest agent binary based on the OS. func agentBinaryPath() string { if runtime.GOOS == "windows" { return `C:\Program Files\Google\Compute Engine\agent\GCEWindowsAgent.exe` } return "/usr/bin/google_guest_agent" }