| package plugin |
| |
| import ( |
| "fmt" |
| "path" |
| "runtime" |
| |
| "github.com/hashicorp/terraform/internal/tfdiags" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/grpc/status" |
| ) |
| |
| // grpcErr extracts some known error types and formats them into better |
| // representations for core. This must only be called from plugin methods. |
| // Since we don't use RPC status errors for the plugin protocol, these do not |
| // contain any useful details, and we can return some text that at least |
| // indicates the plugin call and possible error condition. |
| func grpcErr(err error) (diags tfdiags.Diagnostics) { |
| if err == nil { |
| return |
| } |
| |
| // extract the method name from the caller. |
| pc, _, _, ok := runtime.Caller(1) |
| if !ok { |
| logger.Error("unknown grpc call", "error", err) |
| return diags.Append(err) |
| } |
| |
| f := runtime.FuncForPC(pc) |
| |
| // Function names will contain the full import path. Take the last |
| // segment, which will let users know which method was being called. |
| _, requestName := path.Split(f.Name()) |
| |
| // Here we can at least correlate the error in the logs to a particular binary. |
| logger.Error(requestName, "error", err) |
| |
| // TODO: while this expands the error codes into somewhat better messages, |
| // this still does not easily link the error to an actual user-recognizable |
| // plugin. The grpc plugin does not know its configured name, and the |
| // errors are in a list of diagnostics, making it hard for the caller to |
| // annotate the returned errors. |
| switch status.Code(err) { |
| case codes.Unavailable: |
| // This case is when the plugin has stopped running for some reason, |
| // and is usually the result of a crash. |
| diags = diags.Append(tfdiags.WholeContainingBody( |
| tfdiags.Error, |
| "Plugin did not respond", |
| fmt.Sprintf("The plugin encountered an error, and failed to respond to the %s call. "+ |
| "The plugin logs may contain more details.", requestName), |
| )) |
| case codes.Canceled: |
| diags = diags.Append(tfdiags.WholeContainingBody( |
| tfdiags.Error, |
| "Request cancelled", |
| fmt.Sprintf("The %s request was cancelled.", requestName), |
| )) |
| case codes.Unimplemented: |
| diags = diags.Append(tfdiags.WholeContainingBody( |
| tfdiags.Error, |
| "Unsupported plugin method", |
| fmt.Sprintf("The %s method is not supported by this plugin.", requestName), |
| )) |
| default: |
| diags = diags.Append(tfdiags.WholeContainingBody( |
| tfdiags.Error, |
| "Plugin error", |
| fmt.Sprintf("The plugin returned an unexpected error from %s: %v", requestName, err), |
| )) |
| } |
| return |
| } |