Merge pull request #28275 from hashicorp/jbardin/diagnostic-addresses
Add addresses to diagnostics
This commit is contained in:
commit
b7fb533bd2
|
@ -39,7 +39,7 @@ func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend {
|
|||
diags = diags.Append(decDiags)
|
||||
|
||||
newObj, valDiags := b.PrepareConfig(obj)
|
||||
diags = diags.Append(valDiags.InConfigBody(c))
|
||||
diags = diags.Append(valDiags.InConfigBody(c, ""))
|
||||
|
||||
if len(diags) != 0 {
|
||||
t.Fatal(diags.ErrWithWarnings())
|
||||
|
@ -49,7 +49,7 @@ func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend {
|
|||
|
||||
confDiags := b.Configure(obj)
|
||||
if len(confDiags) != 0 {
|
||||
confDiags = confDiags.InConfigBody(c)
|
||||
confDiags = confDiags.InConfigBody(c, "")
|
||||
t.Fatal(confDiags.ErrWithWarnings())
|
||||
}
|
||||
|
||||
|
|
|
@ -213,6 +213,10 @@ func DiagnosticWarningsCompact(diags tfdiags.Diagnostics, color *colorstring.Col
|
|||
}
|
||||
|
||||
func appendSourceSnippets(buf *bytes.Buffer, diag *viewsjson.Diagnostic, color *colorstring.Colorize) {
|
||||
if diag.Address != "" {
|
||||
fmt.Fprintf(buf, " with %s,\n", diag.Address)
|
||||
}
|
||||
|
||||
if diag.Range == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1086,13 +1086,13 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
|
|||
}
|
||||
|
||||
newVal, validateDiags := b.PrepareConfig(configVal)
|
||||
diags = diags.Append(validateDiags.InConfigBody(c.Config))
|
||||
diags = diags.Append(validateDiags.InConfigBody(c.Config, ""))
|
||||
if validateDiags.HasErrors() {
|
||||
return nil, cty.NilVal, diags
|
||||
}
|
||||
|
||||
configureDiags := b.Configure(newVal)
|
||||
diags = diags.Append(configureDiags.InConfigBody(c.Config))
|
||||
diags = diags.Append(configureDiags.InConfigBody(c.Config, ""))
|
||||
|
||||
return b, configVal, diags
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ type Diagnostic struct {
|
|||
Severity string `json:"severity"`
|
||||
Summary string `json:"summary"`
|
||||
Detail string `json:"detail"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Range *DiagnosticRange `json:"range,omitempty"`
|
||||
Snippet *DiagnosticSnippet `json:"snippet,omitempty"`
|
||||
}
|
||||
|
@ -124,6 +125,7 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost
|
|||
Severity: sev,
|
||||
Summary: desc.Summary,
|
||||
Detail: desc.Detail,
|
||||
Address: desc.Address,
|
||||
}
|
||||
|
||||
sourceRefs := diag.Source()
|
||||
|
|
|
@ -393,7 +393,7 @@ func TestProtoDiagnostics_emptyAttributePath(t *testing.T) {
|
|||
if parseDiags.HasErrors() {
|
||||
t.Fatal(parseDiags)
|
||||
}
|
||||
diags := tfDiags.InConfigBody(f.Body)
|
||||
diags := tfDiags.InConfigBody(f.Body, "")
|
||||
|
||||
if len(tfDiags) != 1 {
|
||||
t.Fatalf("expected 1 diag, got %d", len(tfDiags))
|
||||
|
|
|
@ -45,26 +45,26 @@ func grpcErr(err error) (diags tfdiags.Diagnostics) {
|
|||
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.Sourceless(
|
||||
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.Sourceless(
|
||||
diags = diags.Append(tfdiags.WholeContainingBody(
|
||||
tfdiags.Error,
|
||||
"Request cancelled",
|
||||
fmt.Sprintf("The %s request was cancelled.", requestName),
|
||||
))
|
||||
case codes.Unimplemented:
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
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.Sourceless(
|
||||
diags = diags.Append(tfdiags.WholeContainingBody(
|
||||
tfdiags.Error,
|
||||
"Plugin error",
|
||||
fmt.Sprintf("The plugin returned an unexpected error from %s: %v", requestName, err),
|
||||
|
|
|
@ -167,14 +167,14 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov
|
|||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid provider configuration",
|
||||
fmt.Sprintf(providerConfigErr, configDiags.InConfigBody(configBody).Err(), n.Addr.Provider),
|
||||
fmt.Sprintf(providerConfigErr, configDiags.InConfigBody(configBody, n.Addr.String()).Err(), n.Addr.Provider),
|
||||
))
|
||||
return diags
|
||||
} else {
|
||||
return diags.Append(configDiags.InConfigBody(configBody))
|
||||
return diags.Append(configDiags.InConfigBody(configBody, n.Addr.String()))
|
||||
}
|
||||
}
|
||||
diags = diags.Append(configDiags.InConfigBody(configBody))
|
||||
diags = diags.Append(configDiags.InConfigBody(configBody, n.Addr.String()))
|
||||
|
||||
return diags
|
||||
}
|
||||
|
|
|
@ -625,8 +625,9 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
Config: unmarkedConfigVal,
|
||||
},
|
||||
)
|
||||
|
||||
if validateResp.Diagnostics.HasErrors() {
|
||||
diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config))
|
||||
diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
||||
return plan, state, diags
|
||||
}
|
||||
|
||||
|
@ -659,7 +660,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
PriorPrivate: priorPrivate,
|
||||
ProviderMeta: metaConfigVal,
|
||||
})
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
||||
if diags.HasErrors() {
|
||||
return plan, state, diags
|
||||
}
|
||||
|
@ -869,7 +870,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// Consequently, we break from the usual pattern here and only
|
||||
// append these new diagnostics if there's at least one error inside.
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
||||
return plan, state, diags
|
||||
}
|
||||
plannedNewVal = resp.PlannedState
|
||||
|
@ -1195,7 +1196,7 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
},
|
||||
)
|
||||
if validateResp.Diagnostics.HasErrors() {
|
||||
return newVal, validateResp.Diagnostics.InConfigBody(config.Config)
|
||||
return newVal, validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String())
|
||||
}
|
||||
|
||||
// If we get down here then our configuration is complete and we're read
|
||||
|
@ -1207,7 +1208,7 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
Config: configVal,
|
||||
ProviderMeta: metaConfigVal,
|
||||
})
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
||||
if diags.HasErrors() {
|
||||
return newVal, diags
|
||||
}
|
||||
|
@ -1741,7 +1742,7 @@ func (n *NodeAbstractResourceInstance) applyProvisioners(ctx EvalContext, state
|
|||
Connection: unmarkedConnInfo,
|
||||
UIOutput: &output,
|
||||
})
|
||||
applyDiags := resp.Diagnostics.InConfigBody(prov.Config)
|
||||
applyDiags := resp.Diagnostics.InConfigBody(prov.Config, n.Addr.String())
|
||||
|
||||
// Call post hook
|
||||
hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
|
@ -1907,7 +1908,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
})
|
||||
applyDiags := resp.Diagnostics
|
||||
if applyConfig != nil {
|
||||
applyDiags = applyDiags.InConfigBody(applyConfig.Config)
|
||||
applyDiags = applyDiags.InConfigBody(applyConfig.Config, n.Addr.String())
|
||||
}
|
||||
diags = diags.Append(applyDiags)
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
}
|
||||
|
||||
resp := provider.ValidateResourceConfig(req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config))
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
|
||||
case addrs.DataResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
|
@ -409,7 +409,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
}
|
||||
|
||||
resp := provider.ValidateDataResourceConfig(req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config))
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
}
|
||||
|
||||
return diags
|
||||
|
|
|
@ -19,17 +19,20 @@ import (
|
|||
|
||||
// contextualFromConfig is an interface type implemented by diagnostic types
|
||||
// that can elaborate themselves when given information about the configuration
|
||||
// body they are embedded in.
|
||||
// body they are embedded in, as well as the runtime address associated with
|
||||
// that configuration.
|
||||
//
|
||||
// Usually this entails extracting source location information in order to
|
||||
// populate the "Subject" range.
|
||||
type contextualFromConfigBody interface {
|
||||
ElaborateFromConfigBody(hcl.Body) Diagnostic
|
||||
ElaborateFromConfigBody(hcl.Body, string) Diagnostic
|
||||
}
|
||||
|
||||
// InConfigBody returns a copy of the receiver with any config-contextual
|
||||
// diagnostics elaborated in the context of the given body.
|
||||
func (diags Diagnostics) InConfigBody(body hcl.Body) Diagnostics {
|
||||
// diagnostics elaborated in the context of the given body. An optional address
|
||||
// argument may be added to indicate which instance of the configuration the
|
||||
// error related to.
|
||||
func (diags Diagnostics) InConfigBody(body hcl.Body, addr string) Diagnostics {
|
||||
if len(diags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -37,7 +40,7 @@ func (diags Diagnostics) InConfigBody(body hcl.Body) Diagnostics {
|
|||
ret := make(Diagnostics, len(diags))
|
||||
for i, srcDiag := range diags {
|
||||
if cd, isCD := srcDiag.(contextualFromConfigBody); isCD {
|
||||
ret[i] = cd.ElaborateFromConfigBody(body)
|
||||
ret[i] = cd.ElaborateFromConfigBody(body, addr)
|
||||
} else {
|
||||
ret[i] = srcDiag
|
||||
}
|
||||
|
@ -112,7 +115,12 @@ type attributeDiagnostic struct {
|
|||
// source location information is still available, for more accuracy. This
|
||||
// is not always possible due to system architecture, so this serves as a
|
||||
// "best effort" fallback behavior for such situations.
|
||||
func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
|
||||
func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic {
|
||||
// don't change an existing address
|
||||
if d.address == "" {
|
||||
d.address = addr
|
||||
}
|
||||
|
||||
if len(d.attrPath) < 1 {
|
||||
// Should never happen, but we'll allow it rather than crashing.
|
||||
return d
|
||||
|
@ -353,7 +361,12 @@ type wholeBodyDiagnostic struct {
|
|||
subject *SourceRange // populated only after ElaborateFromConfigBody
|
||||
}
|
||||
|
||||
func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
|
||||
func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic {
|
||||
// don't change an existing address
|
||||
if d.address == "" {
|
||||
d.address = addr
|
||||
}
|
||||
|
||||
if d.subject != nil {
|
||||
// Don't modify an already-elaborated diagnostic.
|
||||
return d
|
||||
|
|
|
@ -185,6 +185,7 @@ simple_attr = "val"
|
|||
diagnosticBase: diagnosticBase{
|
||||
summary: "preexisting",
|
||||
detail: "detail",
|
||||
address: "original",
|
||||
},
|
||||
subject: &SourceRange{
|
||||
Filename: "somewhere_else.tf",
|
||||
|
@ -535,9 +536,22 @@ simple_attr = "val"
|
|||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d:%s", i, tc.Diag.Description()), func(t *testing.T) {
|
||||
var diags Diagnostics
|
||||
|
||||
origAddr := tc.Diag.Description().Address
|
||||
diags = diags.Append(tc.Diag)
|
||||
gotDiags := diags.InConfigBody(f.Body)
|
||||
|
||||
gotDiags := diags.InConfigBody(f.Body, "test.addr")
|
||||
gotRange := gotDiags[0].Source().Subject
|
||||
gotAddr := gotDiags[0].Description().Address
|
||||
|
||||
switch {
|
||||
case origAddr != "":
|
||||
if gotAddr != origAddr {
|
||||
t.Errorf("original diagnostic address modified from %s to %s", origAddr, gotAddr)
|
||||
}
|
||||
case gotAddr != "test.addr":
|
||||
t.Error("missing detail address")
|
||||
}
|
||||
|
||||
for _, problem := range deep.Equal(gotRange, tc.ExpectedRange) {
|
||||
t.Error(problem)
|
||||
|
|
|
@ -25,6 +25,7 @@ const (
|
|||
)
|
||||
|
||||
type Description struct {
|
||||
Address string
|
||||
Summary string
|
||||
Detail string
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ type diagnosticBase struct {
|
|||
severity Severity
|
||||
summary string
|
||||
detail string
|
||||
address string
|
||||
}
|
||||
|
||||
func (d diagnosticBase) Severity() Severity {
|
||||
|
@ -19,6 +20,7 @@ func (d diagnosticBase) Description() Description {
|
|||
return Description{
|
||||
Summary: d.summary,
|
||||
Detail: d.detail,
|
||||
Address: d.address,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue