github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/cloudplugin/cloudplugin1/grpc_client.go (about) 1 package cloudplugin1 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "log" 8 9 "github.com/terramate-io/tf/cloudplugin" 10 "github.com/terramate-io/tf/cloudplugin/cloudproto1" 11 ) 12 13 // GRPCCloudClient is the client interface for interacting with terraform-cloudplugin 14 type GRPCCloudClient struct { 15 client cloudproto1.CommandServiceClient 16 context context.Context 17 } 18 19 // Proof that GRPCCloudClient fulfills the go-plugin interface 20 var _ cloudplugin.Cloud1 = GRPCCloudClient{} 21 22 // Execute sends the client Execute request and waits for the plugin to return 23 // an exit code response before returning 24 func (c GRPCCloudClient) Execute(args []string, stdout, stderr io.Writer) int { 25 client, err := c.client.Execute(c.context, &cloudproto1.CommandRequest{ 26 Args: args, 27 }) 28 29 if err != nil { 30 fmt.Fprint(stderr, err.Error()) 31 return 1 32 } 33 34 for { 35 // cloudplugin streams output as multiple CommandResponse value. Each 36 // value will either contain stdout bytes, stderr bytes, or an exit code. 37 response, err := client.Recv() 38 if err == io.EOF { 39 log.Print("[DEBUG] received EOF from cloudplugin") 40 break 41 } else if err != nil { 42 fmt.Fprintf(stderr, "Failed to receive command response from cloudplugin: %s", err) 43 return 1 44 } 45 46 if bytes := response.GetStdout(); len(bytes) > 0 { 47 _, err := fmt.Fprint(stdout, string(bytes)) 48 if err != nil { 49 log.Printf("[ERROR] Failed to write cloudplugin output to stdout: %s", err) 50 return 1 51 } 52 } else if bytes := response.GetStderr(); len(bytes) > 0 { 53 fmt.Fprint(stderr, string(bytes)) 54 if err != nil { 55 log.Printf("[ERROR] Failed to write cloudplugin output to stderr: %s", err) 56 return 1 57 } 58 } else { 59 exitCode := response.GetExitCode() 60 log.Printf("[TRACE] received exit code: %d", exitCode) 61 if exitCode < 0 || exitCode > 255 { 62 log.Printf("[ERROR] cloudplugin returned an invalid error code %d", exitCode) 63 return 255 64 } 65 return int(exitCode) 66 } 67 } 68 69 // This should indicate a bug in the plugin 70 fmt.Fprint(stderr, "cloudplugin exited without responding with an error code") 71 return 1 72 }