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  }