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