Skip to content

Commit 4179bf0

Browse files
authored
tfprotov5+tfprotov6: Initial provider defined functions implementation (#351)
Reference: hashicorp/terraform#34383 Reference: #353 The next version of the plugin protocol (5.5/6.5) includes support for provider defined functions. This change introduces the initial implementation of that support including: - Updated Protocol Buffers definitions - Re-generated Protocol Buffers Go code - Initial implementations of `tfprotov5` and `tfprotov6` package abstractions and wiring between those abstractions and the Protocol Buffers generated Go code - Initial implementations of `tfprotov5/tf5server` and `tfprotov6/tf6server` for the new `GetFunctions` and `CallFunction` RPCs This temporarily will not require `ProviderServer` implementations to include `FunctionServer` implementation, however that change will occur in a subsequent release.
1 parent 60527ee commit 4179bf0

28 files changed

+4722
-1539
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'tfprotov5+tfprotov6: Upgraded protocols and added types to support
3+
provider-defined functions'
4+
time: 2023-10-26T16:43:00.024481-04:00
5+
custom:
6+
Issue: "351"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: NOTES
2+
body: 'tfprotov5+tfprotov6: An upcoming release will require the FunctionServer
3+
implementation as part of ProviderServer.'
4+
time: 2023-10-26T16:43:59.845786-04:00
5+
custom:
6+
Issue: "351"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: NOTES
2+
body: Provider-defined function support is in technical preview and offered without
3+
compatibility promises until Terraform 1.8 is generally available.
4+
time: 2023-12-14T08:34:37.329545-05:00
5+
custom:
6+
Issue: "351"

internal/logging/keys.go

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ const (
1111
// Attribute of the diagnostic being logged.
1212
KeyDiagnosticAttribute = "diagnostic_attribute"
1313

14+
// Function Argument of the diagnostic being logged.
15+
KeyDiagnosticFunctionArgument = "diagnostic_function_argument"
16+
1417
// Number of the error diagnostics.
1518
KeyDiagnosticErrorCount = "diagnostic_error_count"
1619

tfprotov5/diagnostic.go

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ type Diagnostic struct {
4242
// indicate that the problem is with a certain field in the resource,
4343
// which helps users find the source of the problem.
4444
Attribute *tftypes.AttributePath
45+
46+
// FunctionArgument is the positional function argument for aligning
47+
// configuration source.
48+
FunctionArgument *int64
4549
}
4650

4751
// DiagnosticSeverity represents different classes of Diagnostic which affect

tfprotov5/function.go

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfprotov5
5+
6+
import (
7+
"context"
8+
9+
"github.com/hashicorp/terraform-plugin-go/tftypes"
10+
)
11+
12+
// Function describes the definition of a function. Result must be defined.
13+
type Function struct {
14+
// Parameters is the ordered list of positional function parameters.
15+
Parameters []*FunctionParameter
16+
17+
// VariadicParameter is an optional final parameter which accepts zero or
18+
// more argument values, in which Terraform will send an ordered list of the
19+
// parameter type.
20+
VariadicParameter *FunctionParameter
21+
22+
// Return is the function result.
23+
Return *FunctionReturn
24+
25+
// Summary is the shortened human-readable documentation for the function.
26+
Summary string
27+
28+
// Description is the longer human-readable documentation for the function.
29+
Description string
30+
31+
// DescriptionKind indicates the formatting and encoding that the
32+
// Description field is using.
33+
DescriptionKind StringKind
34+
35+
// DeprecationMessage is the human-readable documentation if the function
36+
// is deprecated. This message should be practitioner oriented to explain
37+
// how their configuration should be updated.
38+
DeprecationMessage string
39+
}
40+
41+
// FunctionMetadata describes metadata for a function in the GetMetadata RPC.
42+
type FunctionMetadata struct {
43+
// Name is the name of the function.
44+
Name string
45+
}
46+
47+
// FunctionParameter describes the definition of a function parameter. Type must
48+
// be defined.
49+
type FunctionParameter struct {
50+
// AllowNullValue when enabled denotes that a null argument value can be
51+
// passed to the provider. When disabled, Terraform returns an error if the
52+
// argument value is null.
53+
AllowNullValue bool
54+
55+
// AllowUnknownValues when enabled denotes that any unknown argument value
56+
// (recursively checked for collections) can be passed to the provider. When
57+
// disabled and an unknown value is present, Terraform skips the function
58+
// call entirely and returns an unknown value result from the function.
59+
AllowUnknownValues bool
60+
61+
// Description is the human-readable documentation for the parameter.
62+
Description string
63+
64+
// DescriptionKind indicates the formatting and encoding that the
65+
// Description field is using.
66+
DescriptionKind StringKind
67+
68+
// Name is the human-readable display name for the parameter. Parameters
69+
// are by definition positional and this name is only used in documentation.
70+
Name string
71+
72+
// Type indicates the type of data the parameter expects.
73+
Type tftypes.Type
74+
}
75+
76+
// FunctionReturn describes the definition of a function result. Type must be
77+
// defined.
78+
type FunctionReturn struct {
79+
// Type indicates the type of return data.
80+
Type tftypes.Type
81+
}
82+
83+
// FunctionServer is an interface containing the methods a function
84+
// implementation needs to fill.
85+
type FunctionServer interface {
86+
// CallFunction is called when Terraform wants to execute the logic of a
87+
// function referenced in the configuration.
88+
CallFunction(context.Context, *CallFunctionRequest) (*CallFunctionResponse, error)
89+
90+
// GetFunctions is called when Terraform wants to lookup which functions a
91+
// provider supports when not calling GetProviderSchema.
92+
GetFunctions(context.Context, *GetFunctionsRequest) (*GetFunctionsResponse, error)
93+
}
94+
95+
// CallFunctionRequest is the request Terraform sends when it wants to execute
96+
// the logic of function referenced in the configuration.
97+
type CallFunctionRequest struct {
98+
// Name is the function name being called.
99+
Name string
100+
101+
// Arguments is the configuration value of each argument the practitioner
102+
// supplied for the function call. The ordering and value of each element
103+
// matches the function parameters and their associated type. If the
104+
// function definition includes a final variadic parameter, its value is an
105+
// ordered list of the variadic parameter type.
106+
Arguments []*DynamicValue
107+
}
108+
109+
// CallFunctionResponse is the response from the provider with the result of
110+
// executing the logic of the function.
111+
type CallFunctionResponse struct {
112+
// Diagnostics report errors or warnings related to the execution of the
113+
// function logic. Returning an empty slice indicates a successful response
114+
// with no warnings or errors presented to practitioners.
115+
Diagnostics []*Diagnostic
116+
117+
// Result is the return value from the called function, matching the result
118+
// type in the function definition.
119+
Result *DynamicValue
120+
}
121+
122+
// GetFunctionsRequest is the request Terraform sends when it wants to lookup
123+
// which functions a provider supports when not calling GetProviderSchema.
124+
type GetFunctionsRequest struct{}
125+
126+
// GetFunctionsResponse is the response from the provider about the implemented
127+
// functions.
128+
type GetFunctionsResponse struct {
129+
// Diagnostics report errors or warnings related to the provider
130+
// implementation. Returning an empty slice indicates a successful response
131+
// with no warnings or errors presented to practitioners.
132+
Diagnostics []*Diagnostic
133+
134+
// Functions is a map of function names to their definition.
135+
//
136+
// Unlike data resources and managed resources, the name should NOT be
137+
// prefixed with the provider name and an underscore. Configuration
138+
// references to functions use a separate namespacing syntax that already
139+
// includes the provider name.
140+
Functions map[string]*Function
141+
}

tfprotov5/internal/diag/diagnostics.go

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ func (d Diagnostics) Log(ctx context.Context) {
5353
diagnosticFields[logging.KeyDiagnosticAttribute] = diagnostic.Attribute.String()
5454
}
5555

56+
if diagnostic.FunctionArgument != nil {
57+
diagnosticFields[logging.KeyDiagnosticFunctionArgument] = *diagnostic.FunctionArgument
58+
}
59+
5660
switch diagnostic.Severity {
5761
case tfprotov5.DiagnosticSeverityError:
5862
logging.ProtocolError(ctx, "Response contains error diagnostic", diagnosticFields)
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto
5+
6+
import (
7+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
8+
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
9+
)
10+
11+
func CallFunctionRequest(in *tfplugin5.CallFunction_Request) (*tfprotov5.CallFunctionRequest, error) {
12+
if in == nil {
13+
return nil, nil
14+
}
15+
16+
resp := &tfprotov5.CallFunctionRequest{
17+
Arguments: make([]*tfprotov5.DynamicValue, 0, len(in.Arguments)),
18+
Name: in.Name,
19+
}
20+
21+
for _, argument := range in.Arguments {
22+
resp.Arguments = append(resp.Arguments, DynamicValue(argument))
23+
}
24+
25+
return resp, nil
26+
}
27+
28+
func GetFunctionsRequest(in *tfplugin5.GetFunctions_Request) (*tfprotov5.GetFunctionsRequest, error) {
29+
if in == nil {
30+
return nil, nil
31+
}
32+
33+
resp := &tfprotov5.GetFunctionsRequest{}
34+
35+
return resp, nil
36+
}

0 commit comments

Comments
 (0)