Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfprotov5+tfprotov6: Initial provider defined functions implementation #351

Merged
merged 5 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231026-164300.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'tfprotov5: Upgraded protocol to 5.5, which implements support for provider
defined functions'
time: 2023-10-26T16:43:00.024481-04:00
custom:
Issue: "351"
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231026-164327.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'tfprotov6: Upgraded protocol to 6.5, which implements support for provider
defined functions'
time: 2023-10-26T16:43:27.221334-04:00
custom:
Issue: "351"
7 changes: 7 additions & 0 deletions .changes/unreleased/NOTES-20231026-164359.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: NOTES
body: 'all: If using terraform-plugin-framework, terraform-plugin-mux, or terraform-plugin-sdk,
only upgrade this Go module as part of upgrading those Go modules or you may receive
a `missing method CallFunction` or `missing method GetFunctions` error when compiling'
time: 2023-10-26T16:43:59.845786-04:00
custom:
Issue: "351"
3 changes: 3 additions & 0 deletions internal/logging/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const (
// Attribute of the diagnostic being logged.
KeyDiagnosticAttribute = "diagnostic_attribute"

// Function Argument of the diagnostic being logged.
KeyDiagnosticFunctionArgument = "diagnostic_function_argument"

// Number of the error diagnostics.
KeyDiagnosticErrorCount = "diagnostic_error_count"

Expand Down
4 changes: 4 additions & 0 deletions tfprotov5/diagnostic.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ type Diagnostic struct {
// indicate that the problem is with a certain field in the resource,
// which helps users find the source of the problem.
Attribute *tftypes.AttributePath

// FunctionArgument is the positional function argument for aligning
// configuration source.
FunctionArgument *int64
}

// DiagnosticSeverity represents different classes of Diagnostic which affect
Expand Down
141 changes: 141 additions & 0 deletions tfprotov5/function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfprotov5

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// Function describes the definition of a function. Result must be defined.
type Function struct {
// Parameters is the ordered list of positional function parameters.
Parameters []*FunctionParameter

// VariadicParameter is an optional final parameter which accepts zero or
// more argument values, in which Terraform will send an ordered list of the
// parameter type.
VariadicParameter *FunctionParameter

// Return is the function result.
Return *FunctionReturn

// Summary is the shortened human-readable documentation for the function.
Summary string

// Description is the longer human-readable documentation for the function.
Description string

// DescriptionKind indicates the formatting and encoding that the
// Description field is using.
DescriptionKind StringKind

// DeprecationMessage is the human-readable documentation if the function
// is deprecated. This message should be practitioner oriented to explain
// how their configuration should be updated.
DeprecationMessage string
}

// FunctionMetadata describes metadata for a function in the GetMetadata RPC.
type FunctionMetadata struct {
// Name is the name of the function.
Name string
}

// FunctionParameter describes the definition of a function parameter. Type must
// be defined.
type FunctionParameter struct {
// AllowNullValue when enabled denotes that a null argument value can be
// passed to the provider. When disabled, Terraform returns an error if the
// argument value is null.
AllowNullValue bool

// AllowUnknownValues when enabled denotes that any unknown argument value
// (recursively checked for collections) can be passed to the provider. When
// disabled and an unknown value is present, Terraform skips the function
// call entirely and returns an unknown value result from the function.
AllowUnknownValues bool

// Description is the human-readable documentation for the parameter.
Description string

// DescriptionKind indicates the formatting and encoding that the
// Description field is using.
DescriptionKind StringKind

// Name is the human-readable display name for the parameter. Parameters
// are by definition positional and this name is only used in documentation.
Name string

// Type indicates the type of data the parameter expects.
Type tftypes.Type
}

// FunctionReturn describes the definition of a function result. Type must be
// defined.
type FunctionReturn struct {
// Type indicates the type of return data.
Type tftypes.Type
}

// FunctionServer is an interface containing the methods a function
// implementation needs to fill.
type FunctionServer interface {
// CallFunction is called when Terraform wants to execute the logic of a
// function referenced in the configuration.
CallFunction(context.Context, *CallFunctionRequest) (*CallFunctionResponse, error)

// GetFunctions is called when Terraform wants to lookup which functions a
// provider supports when not calling GetProviderSchema.
GetFunctions(context.Context, *GetFunctionsRequest) (*GetFunctionsResponse, error)
}

// CallFunctionRequest is the request Terraform sends when it wants to execute
// the logic of function referenced in the configuration.
type CallFunctionRequest struct {
// Name is the function name being called.
Name string

// Arguments is the configuration value of each argument the practitioner
// supplied for the function call. The ordering and value of each element
// matches the function parameters and their associated type. If the
// function definition includes a final variadic parameter, its value is an
// ordered list of the variadic parameter type.
Arguments []*DynamicValue
}

// CallFunctionResponse is the response from the provider with the result of
// executing the logic of the function.
type CallFunctionResponse struct {
// Diagnostics report errors or warnings related to the execution of the
// function logic. Returning an empty slice indicates a successful response
// with no warnings or errors presented to practitioners.
Diagnostics []*Diagnostic

// Result is the return value from the called function, matching the result
// type in the function definition.
Result *DynamicValue
}

// GetFunctionsRequest is the request Terraform sends when it wants to lookup
// which functions a provider supports when not calling GetProviderSchema.
type GetFunctionsRequest struct{}

// GetFunctionsResponse is the response from the provider about the implemented
// functions.
type GetFunctionsResponse struct {
// Diagnostics report errors or warnings related to the provider
// implementation. Returning an empty slice indicates a successful response
// with no warnings or errors presented to practitioners.
Diagnostics []*Diagnostic

// Functions is a map of function names to their definition.
//
// Unlike data resources and managed resources, the name should NOT be
// prefixed with the provider name and an underscore. Configuration
// references to functions use a separate namespacing syntax that already
// includes the provider name.
Functions map[string]*Function
}
4 changes: 4 additions & 0 deletions tfprotov5/internal/diag/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ func (d Diagnostics) Log(ctx context.Context) {
diagnosticFields[logging.KeyDiagnosticAttribute] = diagnostic.Attribute.String()
}

if diagnostic.FunctionArgument != nil {
diagnosticFields[logging.KeyDiagnosticFunctionArgument] = *diagnostic.FunctionArgument
}

switch diagnostic.Severity {
case tfprotov5.DiagnosticSeverityError:
logging.ProtocolError(ctx, "Response contains error diagnostic", diagnosticFields)
Expand Down
36 changes: 36 additions & 0 deletions tfprotov5/internal/fromproto/function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto

import (
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
)

func CallFunctionRequest(in *tfplugin5.CallFunction_Request) (*tfprotov5.CallFunctionRequest, error) {
if in == nil {
return nil, nil
}

resp := &tfprotov5.CallFunctionRequest{
Arguments: make([]*tfprotov5.DynamicValue, 0, len(in.Arguments)),
Name: in.Name,
}

for _, argument := range in.Arguments {
resp.Arguments = append(resp.Arguments, DynamicValue(argument))
}

return resp, nil
}

func GetFunctionsRequest(in *tfplugin5.GetFunctions_Request) (*tfprotov5.GetFunctionsRequest, error) {
if in == nil {
return nil, nil
}

resp := &tfprotov5.GetFunctionsRequest{}

return resp, nil
}
Loading
Loading