tfdiags: Allow construction of RPC-friendly Diagnostics
Due to the use of interfaces, Diagnostics is not super-friendly to the gob encoding we currently use for plugin RPC. To mitigate this, we provide a helper that converts all of the wrapped objects into a predictable flat structure that we can pre-emptively register with gob. This means that the decoded Diagnostics still has the same meaning as the original, though the original wrapped errors (if any) are lost and thus our errwrap integration won't be effective any longer.
This commit is contained in:
parent
ab5efb805c
commit
780e758f1e
|
@ -102,6 +102,24 @@ func (diags Diagnostics) HasErrors() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForRPC returns a version of the receiver that has been simplified so that
|
||||||
|
// it is friendly to RPC protocols.
|
||||||
|
//
|
||||||
|
// Currently this means that it can be serialized with encoding/gob and
|
||||||
|
// subsequently re-inflated. It may later grow to include other serialization
|
||||||
|
// formats.
|
||||||
|
//
|
||||||
|
// Note that this loses information about the original objects used to
|
||||||
|
// construct the diagnostics, so e.g. the errwrap API will not work as
|
||||||
|
// expected on an error-wrapped Diagnostics that came from ForRPC.
|
||||||
|
func (diags Diagnostics) ForRPC() Diagnostics {
|
||||||
|
ret := make(Diagnostics, len(diags))
|
||||||
|
for i := range diags {
|
||||||
|
ret[i] = makeRPCFriendlyDiag(diags[i])
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// Err flattens a diagnostics list into a single Go error, or to nil
|
// Err flattens a diagnostics list into a single Go error, or to nil
|
||||||
// if the diagnostics list does not include any error-level diagnostics.
|
// if the diagnostics list does not include any error-level diagnostics.
|
||||||
//
|
//
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package tfdiags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rpcFriendlyDiag struct {
|
||||||
|
Severity_ Severity
|
||||||
|
Summary_ string
|
||||||
|
Detail_ string
|
||||||
|
Subject_ *SourceRange
|
||||||
|
Context_ *SourceRange
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpcFriendlyDiag transforms a given diagnostic so that is more friendly to
|
||||||
|
// RPC.
|
||||||
|
//
|
||||||
|
// In particular, it currently returns an object that can be serialized and
|
||||||
|
// later re-inflated using gob. This definition may grow to include other
|
||||||
|
// serializations later.
|
||||||
|
func makeRPCFriendlyDiag(diag Diagnostic) Diagnostic {
|
||||||
|
desc := diag.Description()
|
||||||
|
source := diag.Source()
|
||||||
|
return &rpcFriendlyDiag{
|
||||||
|
Severity_: diag.Severity(),
|
||||||
|
Summary_: desc.Summary,
|
||||||
|
Detail_: desc.Detail,
|
||||||
|
Subject_: source.Subject,
|
||||||
|
Context_: source.Context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *rpcFriendlyDiag) Severity() Severity {
|
||||||
|
return d.Severity_
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *rpcFriendlyDiag) Description() Description {
|
||||||
|
return Description{
|
||||||
|
Summary: d.Summary_,
|
||||||
|
Detail: d.Detail_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *rpcFriendlyDiag) Source() Source {
|
||||||
|
return Source{
|
||||||
|
Subject: d.Subject_,
|
||||||
|
Context: d.Context_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gob.Register((*rpcFriendlyDiag)(nil))
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package tfdiags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl2/hcl"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiagnosticsForRPC(t *testing.T) {
|
||||||
|
var diags Diagnostics
|
||||||
|
diags = diags.Append(fmt.Errorf("bad"))
|
||||||
|
diags = diags.Append(SimpleWarning("less bad"))
|
||||||
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "bad bad bad",
|
||||||
|
Detail: "badily bad bad",
|
||||||
|
Subject: &hcl.Range{
|
||||||
|
Filename: "foo",
|
||||||
|
},
|
||||||
|
Context: &hcl.Range{
|
||||||
|
Filename: "bar",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
enc := gob.NewEncoder(&buf)
|
||||||
|
dec := gob.NewDecoder(&buf)
|
||||||
|
|
||||||
|
rpcDiags := diags.ForRPC()
|
||||||
|
err := enc.Encode(rpcDiags)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error on Encode: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var got Diagnostics
|
||||||
|
err = dec.Decode(&got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error on Decode: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := Diagnostics{
|
||||||
|
&rpcFriendlyDiag{
|
||||||
|
Severity_: Error,
|
||||||
|
Summary_: "bad",
|
||||||
|
},
|
||||||
|
&rpcFriendlyDiag{
|
||||||
|
Severity_: Warning,
|
||||||
|
Summary_: "less bad",
|
||||||
|
},
|
||||||
|
&rpcFriendlyDiag{
|
||||||
|
Severity_: Error,
|
||||||
|
Summary_: "bad bad bad",
|
||||||
|
Detail_: "badily bad bad",
|
||||||
|
Subject_: &SourceRange{
|
||||||
|
Filename: "foo",
|
||||||
|
},
|
||||||
|
Context_: &SourceRange{
|
||||||
|
Filename: "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue