github.com/opentofu/opentofu@v1.7.1/internal/tofu/context_functions.go (about) 1 package tofu 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/hashicorp/hcl/v2" 8 "github.com/opentofu/opentofu/internal/addrs" 9 "github.com/opentofu/opentofu/internal/configs" 10 "github.com/opentofu/opentofu/internal/providers" 11 "github.com/opentofu/opentofu/internal/tfdiags" 12 "github.com/zclconf/go-cty/cty" 13 "github.com/zclconf/go-cty/cty/function" 14 ) 15 16 // This builds a provider function using an EvalContext and some additional information 17 // This is split out of BuiltinEvalContext for testing 18 func evalContextProviderFunction(providers func(addrs.AbsProviderConfig) providers.Interface, mc *configs.Config, op walkOperation, pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) { 19 var diags tfdiags.Diagnostics 20 21 pr, ok := mc.Module.ProviderRequirements.RequiredProviders[pf.ProviderName] 22 if !ok { 23 return nil, diags.Append(&hcl.Diagnostic{ 24 Severity: hcl.DiagError, 25 Summary: "Unknown function provider", 26 Detail: fmt.Sprintf("Provider %q does not exist within the required_providers of this module", pf.ProviderName), 27 Subject: rng.ToHCL().Ptr(), 28 }) 29 } 30 31 // Very similar to transform_provider.go 32 absPc := addrs.AbsProviderConfig{ 33 Provider: pr.Type, 34 Module: mc.Path, 35 Alias: pf.ProviderAlias, 36 } 37 38 provider := providers(absPc) 39 40 if provider == nil { 41 // Configured provider (NodeApplyableProvider) not required via transform_provider.go. Instead we should use the unconfigured instance (NodeEvalableProvider) in the root. 42 43 // Make sure the alias is valid 44 validAlias := pf.ProviderAlias == "" 45 if !validAlias { 46 for _, alias := range pr.Aliases { 47 if alias.Alias == pf.ProviderAlias { 48 validAlias = true 49 break 50 } 51 } 52 if !validAlias { 53 return nil, diags.Append(&hcl.Diagnostic{ 54 Severity: hcl.DiagError, 55 Summary: "Unknown function provider", 56 Detail: fmt.Sprintf("No provider instance %q with alias %q", pf.ProviderName, pf.ProviderAlias), 57 Subject: rng.ToHCL().Ptr(), 58 }) 59 } 60 } 61 62 provider = providers(addrs.AbsProviderConfig{Provider: pr.Type}) 63 if provider == nil { 64 // This should not be possible 65 return nil, diags.Append(&hcl.Diagnostic{ 66 Severity: hcl.DiagError, 67 Summary: "BUG: Uninitialized function provider", 68 Detail: fmt.Sprintf("Provider %q has not yet been initialized", absPc.String()), 69 Subject: rng.ToHCL().Ptr(), 70 }) 71 } 72 } 73 74 // First try to look up the function from provider schema 75 schema := provider.GetProviderSchema() 76 if schema.Diagnostics.HasErrors() { 77 return nil, schema.Diagnostics 78 } 79 spec, ok := schema.Functions[pf.Function] 80 if !ok { 81 // During the validate operation, providers are not configured and therefore won't provide 82 // a comprehensive GetFunctions list 83 // Validate is built around unknown values already, we can stub in a placeholder 84 if op == walkValidate { 85 // Configured provider functions are not available during validate 86 fn := function.New(&function.Spec{ 87 Description: "Validate Placeholder", 88 VarParam: &function.Parameter{ 89 Type: cty.DynamicPseudoType, 90 AllowNull: true, 91 AllowUnknown: true, 92 AllowDynamicType: true, 93 AllowMarked: false, 94 }, 95 Type: function.StaticReturnType(cty.DynamicPseudoType), 96 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 97 return cty.UnknownVal(cty.DynamicPseudoType), nil 98 }, 99 }) 100 return &fn, nil 101 } 102 103 // The provider may be configured and present additional functions via GetFunctions 104 specs := provider.GetFunctions() 105 if specs.Diagnostics.HasErrors() { 106 return nil, specs.Diagnostics 107 } 108 109 // If the function isn't in the custom GetFunctions list, it must be undefined 110 spec, ok = specs.Functions[pf.Function] 111 if !ok { 112 return nil, diags.Append(&hcl.Diagnostic{ 113 Severity: hcl.DiagError, 114 Summary: "Function not found in provider", 115 Detail: fmt.Sprintf("Function %q was not registered by provider %q", pf.Function, absPc.String()), 116 Subject: rng.ToHCL().Ptr(), 117 }) 118 } 119 } 120 121 fn := providerFunction(pf.Function, spec, provider) 122 123 return &fn, nil 124 125 } 126 127 // Turn a provider function spec into a cty callable function 128 // This will use the instance factory to get a provider to support the 129 // function call. 130 func providerFunction(name string, spec providers.FunctionSpec, provider providers.Interface) function.Function { 131 params := make([]function.Parameter, len(spec.Parameters)) 132 for i, param := range spec.Parameters { 133 params[i] = providerFunctionParameter(param) 134 } 135 136 var varParam *function.Parameter 137 if spec.VariadicParameter != nil { 138 value := providerFunctionParameter(*spec.VariadicParameter) 139 varParam = &value 140 } 141 142 impl := func(args []cty.Value, retType cty.Type) (cty.Value, error) { 143 resp := provider.CallFunction(providers.CallFunctionRequest{ 144 Name: name, 145 Arguments: args, 146 }) 147 148 if argError, ok := resp.Error.(*providers.CallFunctionArgumentError); ok { 149 // Convert ArgumentError to cty error 150 return resp.Result, function.NewArgError(argError.FunctionArgument, errors.New(argError.Text)) 151 } 152 153 return resp.Result, resp.Error 154 } 155 156 return function.New(&function.Spec{ 157 Description: spec.Summary, 158 Params: params, 159 VarParam: varParam, 160 Type: function.StaticReturnType(spec.Return), 161 Impl: impl, 162 }) 163 164 } 165 166 // Simple mapping of function parameter spec to function parameter 167 func providerFunctionParameter(spec providers.FunctionParameterSpec) function.Parameter { 168 return function.Parameter{ 169 Name: spec.Name, 170 Description: spec.Description, 171 Type: spec.Type, 172 AllowNull: spec.AllowNullValue, 173 AllowUnknown: spec.AllowUnknownValues, 174 // I don't believe this is allowable for provider functions 175 AllowDynamicType: false, 176 // force cty to strip marks ahead of time and re-add them to the resulting object 177 // GRPC: failed: value has marks, so it cannot be serialized. 178 AllowMarked: false, 179 } 180 }