cmd/metadata_script_runner_compat/metadata_script_runner_compat.go (84 lines of code) (raw):

// Copyright 2025 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 entry point for the metadata script runner compat binary. // It is a wrapper that either runs the metadata script runner or the legacy // metadata script runner based on core plugin configuration. package main import ( "context" "fmt" "os" "time" "github.com/GoogleCloudPlatform/galog" "github.com/GoogleCloudPlatform/google-guest-agent/internal/cfg" "github.com/GoogleCloudPlatform/google-guest-agent/internal/logger" "github.com/GoogleCloudPlatform/google-guest-agent/internal/metadata" "github.com/GoogleCloudPlatform/google-guest-agent/internal/run" "github.com/GoogleCloudPlatform/google-guest-agent/internal/utils/file" ) var ( // version is the version of the binary. version = "unknown" ) const ( // galogShutdownTimeout is the period of time we should wait galog to // shutdown. galogShutdownTimeout = time.Second ) func main() { ctx := context.Background() if err := cfg.Load(nil); err != nil { fmt.Fprintln(os.Stderr, "Failed to load instance config:", err) os.Exit(1) } coreCfg := cfg.Retrieve().Core logOpts := logger.Options{ Ident: "google_compat_metadata_script_runner", CloudIdent: "GCEGuestCompatMetadataScriptRunner", ProgramVersion: version, LogFile: coreCfg.LogFile, Level: coreCfg.LogLevel, Verbosity: coreCfg.LogVerbosity, } if err := logger.Init(ctx, logOpts); err != nil { fmt.Fprintln(os.Stderr, "Failed to initialize logger:", err) os.Exit(1) } defer galog.Shutdown(galogShutdownTimeout) if len(os.Args) != 2 { galog.Fatalf("No valid event type (%v) provided, usage: %s <startup|shutdown|specialize>", os.Args, os.Args[0]) } if err := launchScriptRunner(ctx, metadata.New(), os.Args[1]); err != nil { galog.Fatalf("Failed to launch script runner: %v", err) } galog.Infof("Successfully launched script runner") } func launchScriptRunner(ctx context.Context, mdsClient metadata.MDSClientInterface, event string) error { var enabled bool opts := run.Options{ Name: metadataScriptRunnerLegacy, OutputType: run.OutputStream, Args: []string{event}, } mds, err := mdsClient.Get(ctx) if err != nil { galog.Warnf("Failed to fetch MDS descriptor: [%v], falling back to legacy script runner", err) } else { if enabled = mds.HasCorePluginEnabled(); enabled { opts.Name = metadataScriptRunnerNew } } if !file.Exists(metadataScriptRunnerLegacy, file.TypeFile) { galog.Infof("Script runner binary %q not found, running in test environment, overriding to new script runner", metadataScriptRunnerLegacy) opts.Name = metadataScriptRunnerNew } galog.Infof("Enable core plugin set to: [%t], launching script runner for event %q from %q", enabled, event, opts.Name) res, err := run.WithContext(ctx, opts) if err != nil { return fmt.Errorf("failed to run script runner: %v", err) } streams := res.OutputScanners // Go routines will exit once all output is consumed. Run library guarantees // that all channels are closed after use. go func() { for line := range streams.StdOut { galog.Info(line) } }() go func() { for line := range streams.StdErr { galog.Error(line) } }() return <-streams.Result }