github.com/opentofu/opentofu@v1.7.1/internal/grpcwrap/provider6.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package grpcwrap 7 8 import ( 9 "context" 10 11 "github.com/opentofu/opentofu/internal/plugin6/convert" 12 "github.com/opentofu/opentofu/internal/providers" 13 "github.com/opentofu/opentofu/internal/tfplugin6" 14 "github.com/zclconf/go-cty/cty" 15 ctyjson "github.com/zclconf/go-cty/cty/json" 16 "github.com/zclconf/go-cty/cty/msgpack" 17 ) 18 19 // New wraps a providers.Interface to implement a grpc ProviderServer using 20 // plugin protocol v6. This is useful for creating a test binary out of an 21 // internal provider implementation. 22 func Provider6(p providers.Interface) tfplugin6.ProviderServer { 23 return &provider6{ 24 provider: p, 25 schema: p.GetProviderSchema(), 26 } 27 } 28 29 type provider6 struct { 30 provider providers.Interface 31 schema providers.GetProviderSchemaResponse 32 } 33 34 func (p *provider6) GetMetadata(context.Context, *tfplugin6.GetMetadata_Request) (*tfplugin6.GetMetadata_Response, error) { 35 panic("Not Implemented") 36 } 37 38 func (p *provider6) GetProviderSchema(_ context.Context, req *tfplugin6.GetProviderSchema_Request) (*tfplugin6.GetProviderSchema_Response, error) { 39 resp := &tfplugin6.GetProviderSchema_Response{ 40 ResourceSchemas: make(map[string]*tfplugin6.Schema), 41 DataSourceSchemas: make(map[string]*tfplugin6.Schema), 42 } 43 44 resp.Provider = &tfplugin6.Schema{ 45 Block: &tfplugin6.Schema_Block{}, 46 } 47 if p.schema.Provider.Block != nil { 48 resp.Provider.Block = convert.ConfigSchemaToProto(p.schema.Provider.Block) 49 } 50 51 resp.ProviderMeta = &tfplugin6.Schema{ 52 Block: &tfplugin6.Schema_Block{}, 53 } 54 if p.schema.ProviderMeta.Block != nil { 55 resp.ProviderMeta.Block = convert.ConfigSchemaToProto(p.schema.ProviderMeta.Block) 56 } 57 58 for typ, res := range p.schema.ResourceTypes { 59 resp.ResourceSchemas[typ] = &tfplugin6.Schema{ 60 Version: res.Version, 61 Block: convert.ConfigSchemaToProto(res.Block), 62 } 63 } 64 for typ, dat := range p.schema.DataSources { 65 resp.DataSourceSchemas[typ] = &tfplugin6.Schema{ 66 Version: dat.Version, 67 Block: convert.ConfigSchemaToProto(dat.Block), 68 } 69 } 70 71 resp.ServerCapabilities = &tfplugin6.ServerCapabilities{ 72 PlanDestroy: p.schema.ServerCapabilities.PlanDestroy, 73 } 74 75 // include any diagnostics from the original GetSchema call 76 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics) 77 78 return resp, nil 79 } 80 81 func (p *provider6) ValidateProviderConfig(_ context.Context, req *tfplugin6.ValidateProviderConfig_Request) (*tfplugin6.ValidateProviderConfig_Response, error) { 82 resp := &tfplugin6.ValidateProviderConfig_Response{} 83 ty := p.schema.Provider.Block.ImpliedType() 84 85 configVal, err := decodeDynamicValue6(req.Config, ty) 86 if err != nil { 87 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 88 return resp, nil 89 } 90 91 prepareResp := p.provider.ValidateProviderConfig(providers.ValidateProviderConfigRequest{ 92 Config: configVal, 93 }) 94 95 // the PreparedConfig value is no longer used 96 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, prepareResp.Diagnostics) 97 return resp, nil 98 } 99 100 func (p *provider6) ValidateResourceConfig(_ context.Context, req *tfplugin6.ValidateResourceConfig_Request) (*tfplugin6.ValidateResourceConfig_Response, error) { 101 resp := &tfplugin6.ValidateResourceConfig_Response{} 102 ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() 103 104 configVal, err := decodeDynamicValue6(req.Config, ty) 105 if err != nil { 106 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 107 return resp, nil 108 } 109 110 validateResp := p.provider.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ 111 TypeName: req.TypeName, 112 Config: configVal, 113 }) 114 115 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) 116 return resp, nil 117 } 118 119 func (p *provider6) ValidateDataResourceConfig(_ context.Context, req *tfplugin6.ValidateDataResourceConfig_Request) (*tfplugin6.ValidateDataResourceConfig_Response, error) { 120 resp := &tfplugin6.ValidateDataResourceConfig_Response{} 121 ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() 122 123 configVal, err := decodeDynamicValue6(req.Config, ty) 124 if err != nil { 125 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 126 return resp, nil 127 } 128 129 validateResp := p.provider.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ 130 TypeName: req.TypeName, 131 Config: configVal, 132 }) 133 134 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) 135 return resp, nil 136 } 137 138 func (p *provider6) UpgradeResourceState(_ context.Context, req *tfplugin6.UpgradeResourceState_Request) (*tfplugin6.UpgradeResourceState_Response, error) { 139 resp := &tfplugin6.UpgradeResourceState_Response{} 140 ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() 141 142 upgradeResp := p.provider.UpgradeResourceState(providers.UpgradeResourceStateRequest{ 143 TypeName: req.TypeName, 144 Version: req.Version, 145 RawStateJSON: req.RawState.Json, 146 }) 147 148 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics) 149 if upgradeResp.Diagnostics.HasErrors() { 150 return resp, nil 151 } 152 153 dv, err := encodeDynamicValue6(upgradeResp.UpgradedState, ty) 154 if err != nil { 155 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 156 return resp, nil 157 } 158 159 resp.UpgradedState = dv 160 161 return resp, nil 162 } 163 164 func (p *provider6) ConfigureProvider(_ context.Context, req *tfplugin6.ConfigureProvider_Request) (*tfplugin6.ConfigureProvider_Response, error) { 165 resp := &tfplugin6.ConfigureProvider_Response{} 166 ty := p.schema.Provider.Block.ImpliedType() 167 168 configVal, err := decodeDynamicValue6(req.Config, ty) 169 if err != nil { 170 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 171 return resp, nil 172 } 173 174 configureResp := p.provider.ConfigureProvider(providers.ConfigureProviderRequest{ 175 TerraformVersion: req.TerraformVersion, 176 Config: configVal, 177 }) 178 179 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, configureResp.Diagnostics) 180 return resp, nil 181 } 182 183 func (p *provider6) ReadResource(_ context.Context, req *tfplugin6.ReadResource_Request) (*tfplugin6.ReadResource_Response, error) { 184 resp := &tfplugin6.ReadResource_Response{} 185 ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() 186 187 stateVal, err := decodeDynamicValue6(req.CurrentState, ty) 188 if err != nil { 189 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 190 return resp, nil 191 } 192 193 metaTy := p.schema.ProviderMeta.Block.ImpliedType() 194 metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) 195 if err != nil { 196 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 197 return resp, nil 198 } 199 200 readResp := p.provider.ReadResource(providers.ReadResourceRequest{ 201 TypeName: req.TypeName, 202 PriorState: stateVal, 203 Private: req.Private, 204 ProviderMeta: metaVal, 205 }) 206 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) 207 if readResp.Diagnostics.HasErrors() { 208 return resp, nil 209 } 210 resp.Private = readResp.Private 211 212 dv, err := encodeDynamicValue6(readResp.NewState, ty) 213 if err != nil { 214 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 215 return resp, nil 216 } 217 resp.NewState = dv 218 219 return resp, nil 220 } 221 222 func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanResourceChange_Request) (*tfplugin6.PlanResourceChange_Response, error) { 223 resp := &tfplugin6.PlanResourceChange_Response{} 224 ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() 225 226 priorStateVal, err := decodeDynamicValue6(req.PriorState, ty) 227 if err != nil { 228 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 229 return resp, nil 230 } 231 232 proposedStateVal, err := decodeDynamicValue6(req.ProposedNewState, ty) 233 if err != nil { 234 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 235 return resp, nil 236 } 237 238 configVal, err := decodeDynamicValue6(req.Config, ty) 239 if err != nil { 240 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 241 return resp, nil 242 } 243 244 metaTy := p.schema.ProviderMeta.Block.ImpliedType() 245 metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) 246 if err != nil { 247 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 248 return resp, nil 249 } 250 251 planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{ 252 TypeName: req.TypeName, 253 PriorState: priorStateVal, 254 ProposedNewState: proposedStateVal, 255 Config: configVal, 256 PriorPrivate: req.PriorPrivate, 257 ProviderMeta: metaVal, 258 }) 259 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics) 260 if planResp.Diagnostics.HasErrors() { 261 return resp, nil 262 } 263 264 resp.PlannedPrivate = planResp.PlannedPrivate 265 266 resp.PlannedState, err = encodeDynamicValue6(planResp.PlannedState, ty) 267 if err != nil { 268 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 269 return resp, nil 270 } 271 272 for _, path := range planResp.RequiresReplace { 273 resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path)) 274 } 275 276 return resp, nil 277 } 278 279 func (p *provider6) ApplyResourceChange(_ context.Context, req *tfplugin6.ApplyResourceChange_Request) (*tfplugin6.ApplyResourceChange_Response, error) { 280 resp := &tfplugin6.ApplyResourceChange_Response{} 281 ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() 282 283 priorStateVal, err := decodeDynamicValue6(req.PriorState, ty) 284 if err != nil { 285 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 286 return resp, nil 287 } 288 289 plannedStateVal, err := decodeDynamicValue6(req.PlannedState, ty) 290 if err != nil { 291 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 292 return resp, nil 293 } 294 295 configVal, err := decodeDynamicValue6(req.Config, ty) 296 if err != nil { 297 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 298 return resp, nil 299 } 300 301 metaTy := p.schema.ProviderMeta.Block.ImpliedType() 302 metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) 303 if err != nil { 304 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 305 return resp, nil 306 } 307 308 applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ 309 TypeName: req.TypeName, 310 PriorState: priorStateVal, 311 PlannedState: plannedStateVal, 312 Config: configVal, 313 PlannedPrivate: req.PlannedPrivate, 314 ProviderMeta: metaVal, 315 }) 316 317 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics) 318 if applyResp.Diagnostics.HasErrors() { 319 return resp, nil 320 } 321 resp.Private = applyResp.Private 322 323 resp.NewState, err = encodeDynamicValue6(applyResp.NewState, ty) 324 if err != nil { 325 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 326 return resp, nil 327 } 328 329 return resp, nil 330 } 331 332 func (p *provider6) ImportResourceState(_ context.Context, req *tfplugin6.ImportResourceState_Request) (*tfplugin6.ImportResourceState_Response, error) { 333 resp := &tfplugin6.ImportResourceState_Response{} 334 335 importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{ 336 TypeName: req.TypeName, 337 ID: req.Id, 338 }) 339 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics) 340 341 for _, res := range importResp.ImportedResources { 342 ty := p.schema.ResourceTypes[res.TypeName].Block.ImpliedType() 343 state, err := encodeDynamicValue6(res.State, ty) 344 if err != nil { 345 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 346 continue 347 } 348 349 resp.ImportedResources = append(resp.ImportedResources, &tfplugin6.ImportResourceState_ImportedResource{ 350 TypeName: res.TypeName, 351 State: state, 352 Private: res.Private, 353 }) 354 } 355 356 return resp, nil 357 } 358 359 func (p *provider6) MoveResourceState(context.Context, *tfplugin6.MoveResourceState_Request) (*tfplugin6.MoveResourceState_Response, error) { 360 panic("Not Implemented") 361 } 362 363 func (p *provider6) ReadDataSource(_ context.Context, req *tfplugin6.ReadDataSource_Request) (*tfplugin6.ReadDataSource_Response, error) { 364 resp := &tfplugin6.ReadDataSource_Response{} 365 ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() 366 367 configVal, err := decodeDynamicValue6(req.Config, ty) 368 if err != nil { 369 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 370 return resp, nil 371 } 372 373 metaTy := p.schema.ProviderMeta.Block.ImpliedType() 374 metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) 375 if err != nil { 376 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 377 return resp, nil 378 } 379 380 readResp := p.provider.ReadDataSource(providers.ReadDataSourceRequest{ 381 TypeName: req.TypeName, 382 Config: configVal, 383 ProviderMeta: metaVal, 384 }) 385 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) 386 if readResp.Diagnostics.HasErrors() { 387 return resp, nil 388 } 389 390 resp.State, err = encodeDynamicValue6(readResp.State, ty) 391 if err != nil { 392 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 393 return resp, nil 394 } 395 396 return resp, nil 397 } 398 399 func (p *provider6) StopProvider(context.Context, *tfplugin6.StopProvider_Request) (*tfplugin6.StopProvider_Response, error) { 400 resp := &tfplugin6.StopProvider_Response{} 401 err := p.provider.Stop() 402 if err != nil { 403 resp.Error = err.Error() 404 } 405 return resp, nil 406 } 407 408 func (p *provider6) GetFunctions(context.Context, *tfplugin6.GetFunctions_Request) (*tfplugin6.GetFunctions_Response, error) { 409 panic("Not Implemented") 410 } 411 412 func (p *provider6) CallFunction(context.Context, *tfplugin6.CallFunction_Request) (*tfplugin6.CallFunction_Response, error) { 413 panic("Not Implemented") 414 } 415 416 // decode a DynamicValue from either the JSON or MsgPack encoding. 417 func decodeDynamicValue6(v *tfplugin6.DynamicValue, ty cty.Type) (cty.Value, error) { 418 // always return a valid value 419 var err error 420 res := cty.NullVal(ty) 421 if v == nil { 422 return res, nil 423 } 424 425 switch { 426 case len(v.Msgpack) > 0: 427 res, err = msgpack.Unmarshal(v.Msgpack, ty) 428 case len(v.Json) > 0: 429 res, err = ctyjson.Unmarshal(v.Json, ty) 430 } 431 return res, err 432 } 433 434 // encode a cty.Value into a DynamicValue msgpack payload. 435 func encodeDynamicValue6(v cty.Value, ty cty.Type) (*tfplugin6.DynamicValue, error) { 436 mp, err := msgpack.Marshal(v, ty) 437 return &tfplugin6.DynamicValue{ 438 Msgpack: mp, 439 }, err 440 }