cmd/ops_agent_uap_plugin/plugin.go (89 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 import ( "context" "flag" "fmt" "net" "os" "os/exec" "path/filepath" "sync" "buf.build/go/protoyaml" "google.golang.org/grpc" "google.golang.org/grpc/reflection" pb "github.com/GoogleCloudPlatform/ops-agent/cmd/ops_agent_uap_plugin/google_guest_agent/plugin" ) var ( // protocol is the protocol to use tcp/uds. protocol string // address is the address to start server listening on. address string // logfile is the path to the log file to capture error logs. logfile string ) // RunCommandFunc defines a function type that takes an exec.Cmd and returns // its output and error. This abstraction is introduced // primarily to facilitate testing by allowing the injection of mock // implementations. type RunCommandFunc func(cmd *exec.Cmd) (string, error) // PluginServer implements the plugin RPC server interface. type OpsAgentPluginServer struct { pb.UnimplementedGuestAgentPluginServer server *grpc.Server // mu protects the cancel field. mu sync.Mutex cancel context.CancelFunc runCommand RunCommandFunc } func init() { flag.StringVar(&protocol, "protocol", "", "protocol to use uds/tcp") flag.StringVar(&address, "address", "", "address to start server listening on") flag.StringVar(&logfile, "errorlogfile", "", "path to the error log file") } func main() { flag.Parse() if _, err := os.Stat(address); err == nil { if err := os.RemoveAll(address); err != nil { // Unix sockets must be unlinked (listener.Close()) before // being reused again. If file already exist bind can fail. fmt.Fprintf(os.Stderr, "Failed to remove %q: %v\n", address, err) os.Exit(1) } } listener, err := net.Listen(protocol, address) if err != nil { fmt.Fprintf(os.Stderr, "Failed to start listening on %q using %q: %v\n", address, protocol, err) os.Exit(1) } defer listener.Close() // This is the grpc server in communication with the Guest Agent. server := grpc.NewServer() defer server.GracefulStop() ps := &OpsAgentPluginServer{server: server, runCommand: runCommand} // Successfully registering the server and starting to listen on the address // offered mean Guest Agent was successful in installing/launching the plugin // & will manage the lifecycle (start, stop, or revision change) here onwards. pb.RegisterGuestAgentPluginServer(server, ps) reflection.Register(server) if err := server.Serve(listener); err != nil { fmt.Fprintf(os.Stderr, "Exiting, cannot continue serving: %v\n", err) os.Exit(1) } } func writeCustomConfigToFile(req *pb.StartRequest, configPath string) error { customConfig := []byte{} switch req.GetServiceConfig().(type) { case *pb.StartRequest_StringConfig: customConfig = []byte(req.GetStringConfig()) case *pb.StartRequest_StructConfig: structConfig := req.GetStructConfig() yamlBytes, err := protoyaml.Marshal(structConfig) if err != nil { return fmt.Errorf("failed to parse the custom Ops Agent config: %v", err) } customConfig = yamlBytes } if len(customConfig) > 0 { parentDir := filepath.Dir(configPath) if _, err := os.Stat(parentDir); os.IsNotExist(err) { err := os.MkdirAll(parentDir, 0755) if err != nil { return fmt.Errorf("failed to create parent directory %s: %v", parentDir, err) } } file, err := os.OpenFile(configPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return fmt.Errorf("failed to open the config.yaml file at location: %s, error: %v", configPath, err) } defer file.Close() if _, err := file.Write(customConfig); err != nil { return fmt.Errorf("failed to write to the config.yaml file at location: %s, error: %v", configPath, err) } } return nil }