kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/plugin6/grpc_error.go (about)

     1  package plugin6
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"runtime"
     7  
     8  	"kubeform.dev/terraform-backend-sdk/tfdiags"
     9  	"google.golang.org/grpc/codes"
    10  	"google.golang.org/grpc/status"
    11  )
    12  
    13  // grpcErr extracts some known error types and formats them into better
    14  // representations for core. This must only be called from plugin methods.
    15  // Since we don't use RPC status errors for the plugin protocol, these do not
    16  // contain any useful details, and we can return some text that at least
    17  // indicates the plugin call and possible error condition.
    18  func grpcErr(err error) (diags tfdiags.Diagnostics) {
    19  	if err == nil {
    20  		return
    21  	}
    22  
    23  	// extract the method name from the caller.
    24  	pc, _, _, ok := runtime.Caller(1)
    25  	if !ok {
    26  		logger.Error("unknown grpc call", "error", err)
    27  		return diags.Append(err)
    28  	}
    29  
    30  	f := runtime.FuncForPC(pc)
    31  
    32  	// Function names will contain the full import path. Take the last
    33  	// segment, which will let users know which method was being called.
    34  	_, requestName := path.Split(f.Name())
    35  
    36  	// Here we can at least correlate the error in the logs to a particular binary.
    37  	logger.Error(requestName, "error", err)
    38  
    39  	// TODO: while this expands the error codes into somewhat better messages,
    40  	// this still does not easily link the error to an actual user-recognizable
    41  	// plugin. The grpc plugin does not know its configured name, and the
    42  	// errors are in a list of diagnostics, making it hard for the caller to
    43  	// annotate the returned errors.
    44  	switch status.Code(err) {
    45  	case codes.Unavailable:
    46  		// This case is when the plugin has stopped running for some reason,
    47  		// and is usually the result of a crash.
    48  		diags = diags.Append(tfdiags.Sourceless(
    49  			tfdiags.Error,
    50  			"Plugin did not respond",
    51  			fmt.Sprintf("The plugin encountered an error, and failed to respond to the %s call. "+
    52  				"The plugin logs may contain more details.", requestName),
    53  		))
    54  	case codes.Canceled:
    55  		diags = diags.Append(tfdiags.Sourceless(
    56  			tfdiags.Error,
    57  			"Request cancelled",
    58  			fmt.Sprintf("The %s request was cancelled.", requestName),
    59  		))
    60  	case codes.Unimplemented:
    61  		diags = diags.Append(tfdiags.Sourceless(
    62  			tfdiags.Error,
    63  			"Unsupported plugin method",
    64  			fmt.Sprintf("The %s method is not supported by this plugin.", requestName),
    65  		))
    66  	default:
    67  		diags = diags.Append(tfdiags.Sourceless(
    68  			tfdiags.Error,
    69  			"Plugin error",
    70  			fmt.Sprintf("The plugin returned an unexpected error from %s: %v", requestName, err),
    71  		))
    72  	}
    73  	return
    74  }