mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-28 04:03:27 -04:00
Merge pull request #34603 from hashicorp/jbardin/remove-provider-funtion-warnings
provider functions can only return an error
This commit is contained in:
commit
300f66b71d
12 changed files with 2323 additions and 2135 deletions
|
|
@ -41,10 +41,13 @@ message Diagnostic {
|
|||
string summary = 2;
|
||||
string detail = 3;
|
||||
AttributePath attribute = 4;
|
||||
}
|
||||
|
||||
// function_argument is the positional function argument for aligning
|
||||
// configuration source.
|
||||
optional int64 function_argument = 5;
|
||||
message FunctionError {
|
||||
string text = 1;
|
||||
// The optional function_argument records the index position of the
|
||||
// argument which caused the error.
|
||||
optional int64 function_argument = 2;
|
||||
}
|
||||
|
||||
message AttributePath {
|
||||
|
|
@ -569,6 +572,6 @@ message CallFunction {
|
|||
}
|
||||
message Response {
|
||||
DynamicValue result = 1;
|
||||
repeated Diagnostic diagnostics = 2;
|
||||
FunctionError error = 2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,13 @@ message Diagnostic {
|
|||
string summary = 2;
|
||||
string detail = 3;
|
||||
AttributePath attribute = 4;
|
||||
}
|
||||
|
||||
// function_argument is the positional function argument for aligning
|
||||
// configuration source.
|
||||
optional int64 function_argument = 5;
|
||||
message FunctionError {
|
||||
string text = 1;
|
||||
// The optional function_argument records the index position of the
|
||||
// argument which caused the error.
|
||||
optional int64 function_argument = 2;
|
||||
}
|
||||
|
||||
message AttributePath {
|
||||
|
|
@ -549,6 +552,6 @@ message CallFunction {
|
|||
}
|
||||
message Response {
|
||||
DynamicValue result = 1;
|
||||
repeated Diagnostic diagnostics = 2;
|
||||
FunctionError error = 2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ package grpcwrap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
|
@ -444,15 +444,18 @@ func (p *provider) CallFunction(_ context.Context, req *tfplugin5.CallFunction_R
|
|||
if len(req.Arguments) != 0 {
|
||||
args = make([]cty.Value, len(req.Arguments))
|
||||
for i, rawArg := range req.Arguments {
|
||||
idx := int64(i)
|
||||
|
||||
var argTy cty.Type
|
||||
if i < len(funcSchema.Parameters) {
|
||||
argTy = funcSchema.Parameters[i].Type
|
||||
} else {
|
||||
if funcSchema.VariadicParameter == nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(
|
||||
resp.Diagnostics, fmt.Errorf("too many arguments for non-variadic function"),
|
||||
)
|
||||
|
||||
resp.Error = &tfplugin5.FunctionError{
|
||||
Text: "too many arguments for non-variadic function",
|
||||
FunctionArgument: &idx,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
argTy = funcSchema.VariadicParameter.Type
|
||||
|
|
@ -460,9 +463,13 @@ func (p *provider) CallFunction(_ context.Context, req *tfplugin5.CallFunction_R
|
|||
|
||||
argVal, err := decodeDynamicValue(rawArg, argTy)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
resp.Error = &tfplugin5.FunctionError{
|
||||
Text: err.Error(),
|
||||
FunctionArgument: &idx,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
args[i] = argVal
|
||||
}
|
||||
}
|
||||
|
|
@ -471,14 +478,26 @@ func (p *provider) CallFunction(_ context.Context, req *tfplugin5.CallFunction_R
|
|||
FunctionName: req.Name,
|
||||
Arguments: args,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, callResp.Diagnostics)
|
||||
if callResp.Diagnostics.HasErrors() {
|
||||
|
||||
if callResp.Err != nil {
|
||||
resp.Error = &tfplugin5.FunctionError{
|
||||
Text: callResp.Err.Error(),
|
||||
}
|
||||
|
||||
if argErr, ok := callResp.Err.(function.ArgError); ok {
|
||||
idx := int64(argErr.Index)
|
||||
resp.Error.FunctionArgument = &idx
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.Result, err = encodeDynamicValue(callResp.Result, funcSchema.ReturnType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
resp.Error = &tfplugin5.FunctionError{
|
||||
Text: err.Error(),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ package grpcwrap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
|
@ -445,15 +445,17 @@ func (p *provider6) CallFunction(_ context.Context, req *tfplugin6.CallFunction_
|
|||
if len(req.Arguments) != 0 {
|
||||
args = make([]cty.Value, len(req.Arguments))
|
||||
for i, rawArg := range req.Arguments {
|
||||
idx := int64(i)
|
||||
|
||||
var argTy cty.Type
|
||||
if i < len(funcSchema.Parameters) {
|
||||
argTy = funcSchema.Parameters[i].Type
|
||||
} else {
|
||||
if funcSchema.VariadicParameter == nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(
|
||||
resp.Diagnostics, fmt.Errorf("too many arguments for non-variadic function"),
|
||||
)
|
||||
resp.Error = &tfplugin6.FunctionError{
|
||||
Text: "too many arguments for non-variadic function",
|
||||
FunctionArgument: &idx,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
argTy = funcSchema.VariadicParameter.Type
|
||||
|
|
@ -461,9 +463,13 @@ func (p *provider6) CallFunction(_ context.Context, req *tfplugin6.CallFunction_
|
|||
|
||||
argVal, err := decodeDynamicValue6(rawArg, argTy)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
resp.Error = &tfplugin6.FunctionError{
|
||||
Text: err.Error(),
|
||||
FunctionArgument: &idx,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
args[i] = argVal
|
||||
}
|
||||
}
|
||||
|
|
@ -472,14 +478,25 @@ func (p *provider6) CallFunction(_ context.Context, req *tfplugin6.CallFunction_
|
|||
FunctionName: req.Name,
|
||||
Arguments: args,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, callResp.Diagnostics)
|
||||
if callResp.Diagnostics.HasErrors() {
|
||||
if callResp.Err != nil {
|
||||
resp.Error = &tfplugin6.FunctionError{
|
||||
Text: callResp.Err.Error(),
|
||||
}
|
||||
|
||||
if argErr, ok := callResp.Err.(function.ArgError); ok {
|
||||
idx := int64(argErr.Index)
|
||||
resp.Error.FunctionArgument = &idx
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.Result, err = encodeDynamicValue6(callResp.Result, funcSchema.ReturnType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
resp.Error = &tfplugin6.FunctionError{
|
||||
Text: err.Error(),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc"
|
||||
|
|
@ -751,7 +752,7 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
resp.Err = schema.Diagnostics.Err()
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -765,15 +766,15 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
// Should only get here if the caller has a bug, because we should
|
||||
// have detected earlier any attempt to call a function that the
|
||||
// provider didn't declare.
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provider has no function named %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("provider has no function named %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
if len(r.Arguments) < len(funcDecl.Parameters) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not enough arguments for function %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("not enough arguments for function %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
if funcDecl.VariadicParameter == nil && len(r.Arguments) > len(funcDecl.Parameters) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("too many arguments for function %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("too many arguments for function %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
args := make([]*proto.DynamicValue, len(r.Arguments))
|
||||
|
|
@ -787,7 +788,7 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
|
||||
argValRaw, err := msgpack.Marshal(argVal, paramDecl.Type)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.Err = err
|
||||
return resp
|
||||
}
|
||||
args[i] = &proto.DynamicValue{
|
||||
|
|
@ -800,17 +801,28 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
Arguments: args,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
// functions can only support simple errors, but use our grpcError
|
||||
// diagnostic function to format common problems is a more
|
||||
// user-friendly manner.
|
||||
resp.Err = grpcErr(err).Err()
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
|
||||
if protoResp.Error != nil {
|
||||
resp.Err = errors.New(protoResp.Error.Text)
|
||||
|
||||
// If this is a problem with a specific argument, we can wrap the error
|
||||
// in a function.ArgError
|
||||
if protoResp.Error.FunctionArgument != nil {
|
||||
resp.Err = function.NewArgError(int(*protoResp.Error.FunctionArgument), resp.Err)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
resultVal, err := decodeDynamicValue(protoResp.Result, funcDecl.ReturnType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.Err = err
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc"
|
||||
|
|
@ -740,7 +741,7 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
resp.Err = schema.Diagnostics.Err()
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -754,15 +755,15 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
// Should only get here if the caller has a bug, because we should
|
||||
// have detected earlier any attempt to call a function that the
|
||||
// provider didn't declare.
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provider has no function named %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("provider has no function named %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
if len(r.Arguments) < len(funcDecl.Parameters) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not enough arguments for function %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("not enough arguments for function %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
if funcDecl.VariadicParameter == nil && len(r.Arguments) > len(funcDecl.Parameters) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("too many arguments for function %q", r.FunctionName))
|
||||
resp.Err = fmt.Errorf("too many arguments for function %q", r.FunctionName)
|
||||
return resp
|
||||
}
|
||||
args := make([]*proto6.DynamicValue, len(r.Arguments))
|
||||
|
|
@ -776,7 +777,7 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
|
||||
argValRaw, err := msgpack.Marshal(argVal, paramDecl.Type)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.Err = err
|
||||
return resp
|
||||
}
|
||||
args[i] = &proto6.DynamicValue{
|
||||
|
|
@ -789,17 +790,28 @@ func (p *GRPCProvider) CallFunction(r providers.CallFunctionRequest) (resp provi
|
|||
Arguments: args,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
// functions can only support simple errors, but use our grpcError
|
||||
// diagnostic function to format common problems is a more
|
||||
// user-friendly manner.
|
||||
resp.Err = grpcErr(err).Err()
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
|
||||
if protoResp.Error != nil {
|
||||
resp.Err = errors.New(protoResp.Error.Text)
|
||||
|
||||
// If this is a problem with a specific argument, we can wrap the error
|
||||
// in a function.ArgError
|
||||
if protoResp.Error.FunctionArgument != nil {
|
||||
resp.Err = function.NewArgError(int(*protoResp.Error.FunctionArgument), resp.Err)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
resultVal, err := decodeDynamicValue(protoResp.Result, funcDecl.ReturnType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.Err = err
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ func (s simple) ReadDataSource(req providers.ReadDataSourceRequest) (resp provid
|
|||
|
||||
func (s simple) CallFunction(req providers.CallFunctionRequest) (resp providers.CallFunctionResponse) {
|
||||
if req.FunctionName != "noop" {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("CallFunction for undefined function %q", req.FunctionName))
|
||||
resp.Err = fmt.Errorf("CallFunction for undefined function %q", req.FunctionName)
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,11 +114,8 @@ func (d FunctionDecl) BuildFunction(providerAddr addrs.Provider, name string, re
|
|||
FunctionName: name,
|
||||
Arguments: args,
|
||||
})
|
||||
// NOTE: We don't actually have any way to surface warnings
|
||||
// from the function here, because functions just return normal
|
||||
// Go errors rather than diagnostics.
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return cty.UnknownVal(retType), resp.Diagnostics.Err()
|
||||
if resp.Err != nil {
|
||||
return cty.UnknownVal(retType), resp.Err
|
||||
}
|
||||
|
||||
if resp.Result == cty.NilVal {
|
||||
|
|
|
|||
|
|
@ -501,6 +501,8 @@ type CallFunctionResponse struct {
|
|||
// so can be left as cty.NilVal to represent the absense of a value.
|
||||
Result cty.Value
|
||||
|
||||
// Diagnostics contains any warnings or errors from the function call.
|
||||
Diagnostics tfdiags.Diagnostics
|
||||
// Err is the error value from the function call. This may be an instance
|
||||
// of function.ArgError from the go-cty package to specify a problem with a
|
||||
// specific argument.
|
||||
Err error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2536,23 +2536,23 @@ func TestContext2Validate_providerContributedFunctions(t *testing.T) {
|
|||
}
|
||||
p.CallFunctionFn = func(req providers.CallFunctionRequest) (resp providers.CallFunctionResponse) {
|
||||
if req.FunctionName != "count_e" {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("incorrect function name %q", req.FunctionName))
|
||||
resp.Err = fmt.Errorf("incorrect function name %q", req.FunctionName)
|
||||
return resp
|
||||
}
|
||||
if len(req.Arguments) != 1 {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong number of arguments %d", len(req.Arguments)))
|
||||
resp.Err = fmt.Errorf("wrong number of arguments %d", len(req.Arguments))
|
||||
return resp
|
||||
}
|
||||
if req.Arguments[0].Type() != cty.String {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong argument type %#v", req.Arguments[0].Type()))
|
||||
resp.Err = fmt.Errorf("wrong argument type %#v", req.Arguments[0].Type())
|
||||
return resp
|
||||
}
|
||||
if !req.Arguments[0].IsKnown() {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("argument is unknown"))
|
||||
resp.Err = fmt.Errorf("argument is unknown")
|
||||
return resp
|
||||
}
|
||||
if req.Arguments[0].IsNull() {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("argument is null"))
|
||||
resp.Err = fmt.Errorf("argument is null")
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue