github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/provider_mock.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/zclconf/go-cty/cty" 8 ctyjson "github.com/zclconf/go-cty/cty/json" 9 "github.com/zclconf/go-cty/cty/msgpack" 10 11 "github.com/hashicorp/terraform/internal/configs/configschema" 12 "github.com/hashicorp/terraform/internal/configs/hcl2shim" 13 "github.com/hashicorp/terraform/internal/providers" 14 ) 15 16 var _ providers.Interface = (*MockProvider)(nil) 17 18 // MockProvider implements providers.Interface but mocks out all the 19 // calls for testing purposes. 20 type MockProvider struct { 21 sync.Mutex 22 23 // Anything you want, in case you need to store extra data with the mock. 24 Meta interface{} 25 26 GetProviderSchemaCalled bool 27 GetProviderSchemaResponse *providers.GetProviderSchemaResponse 28 29 ValidateProviderConfigCalled bool 30 ValidateProviderConfigResponse *providers.ValidateProviderConfigResponse 31 ValidateProviderConfigRequest providers.ValidateProviderConfigRequest 32 ValidateProviderConfigFn func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse 33 34 ValidateResourceConfigCalled bool 35 ValidateResourceConfigTypeName string 36 ValidateResourceConfigResponse *providers.ValidateResourceConfigResponse 37 ValidateResourceConfigRequest providers.ValidateResourceConfigRequest 38 ValidateResourceConfigFn func(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse 39 40 ValidateDataResourceConfigCalled bool 41 ValidateDataResourceConfigTypeName string 42 ValidateDataResourceConfigResponse *providers.ValidateDataResourceConfigResponse 43 ValidateDataResourceConfigRequest providers.ValidateDataResourceConfigRequest 44 ValidateDataResourceConfigFn func(providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse 45 46 UpgradeResourceStateCalled bool 47 UpgradeResourceStateTypeName string 48 UpgradeResourceStateResponse *providers.UpgradeResourceStateResponse 49 UpgradeResourceStateRequest providers.UpgradeResourceStateRequest 50 UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse 51 52 ConfigureProviderCalled bool 53 ConfigureProviderResponse *providers.ConfigureProviderResponse 54 ConfigureProviderRequest providers.ConfigureProviderRequest 55 ConfigureProviderFn func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse 56 57 StopCalled bool 58 StopFn func() error 59 StopResponse error 60 61 ReadResourceCalled bool 62 ReadResourceResponse *providers.ReadResourceResponse 63 ReadResourceRequest providers.ReadResourceRequest 64 ReadResourceFn func(providers.ReadResourceRequest) providers.ReadResourceResponse 65 66 PlanResourceChangeCalled bool 67 PlanResourceChangeResponse *providers.PlanResourceChangeResponse 68 PlanResourceChangeRequest providers.PlanResourceChangeRequest 69 PlanResourceChangeFn func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse 70 71 ApplyResourceChangeCalled bool 72 ApplyResourceChangeResponse *providers.ApplyResourceChangeResponse 73 ApplyResourceChangeRequest providers.ApplyResourceChangeRequest 74 ApplyResourceChangeFn func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse 75 76 ImportResourceStateCalled bool 77 ImportResourceStateResponse *providers.ImportResourceStateResponse 78 ImportResourceStateRequest providers.ImportResourceStateRequest 79 ImportResourceStateFn func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse 80 81 ReadDataSourceCalled bool 82 ReadDataSourceResponse *providers.ReadDataSourceResponse 83 ReadDataSourceRequest providers.ReadDataSourceRequest 84 ReadDataSourceFn func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse 85 86 CloseCalled bool 87 CloseError error 88 } 89 90 func (p *MockProvider) GetProviderSchema() providers.GetProviderSchemaResponse { 91 p.Lock() 92 defer p.Unlock() 93 p.GetProviderSchemaCalled = true 94 return p.getProviderSchema() 95 } 96 97 func (p *MockProvider) getProviderSchema() providers.GetProviderSchemaResponse { 98 // This version of getProviderSchema doesn't do any locking, so it's suitable to 99 // call from other methods of this mock as long as they are already 100 // holding the lock. 101 if p.GetProviderSchemaResponse != nil { 102 return *p.GetProviderSchemaResponse 103 } 104 105 return providers.GetProviderSchemaResponse{ 106 Provider: providers.Schema{}, 107 DataSources: map[string]providers.Schema{}, 108 ResourceTypes: map[string]providers.Schema{}, 109 } 110 } 111 112 // ProviderSchema is a helper to convert from the internal GetProviderSchemaResponse to 113 // a ProviderSchema. 114 func (p *MockProvider) ProviderSchema() *ProviderSchema { 115 resp := p.getProviderSchema() 116 117 schema := &ProviderSchema{ 118 Provider: resp.Provider.Block, 119 ProviderMeta: resp.ProviderMeta.Block, 120 ResourceTypes: map[string]*configschema.Block{}, 121 DataSources: map[string]*configschema.Block{}, 122 ResourceTypeSchemaVersions: map[string]uint64{}, 123 } 124 125 for resType, s := range resp.ResourceTypes { 126 schema.ResourceTypes[resType] = s.Block 127 schema.ResourceTypeSchemaVersions[resType] = uint64(s.Version) 128 } 129 130 for dataSource, s := range resp.DataSources { 131 schema.DataSources[dataSource] = s.Block 132 } 133 134 return schema 135 } 136 137 func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { 138 p.Lock() 139 defer p.Unlock() 140 141 p.ValidateProviderConfigCalled = true 142 p.ValidateProviderConfigRequest = r 143 if p.ValidateProviderConfigFn != nil { 144 return p.ValidateProviderConfigFn(r) 145 } 146 147 if p.ValidateProviderConfigResponse != nil { 148 return *p.ValidateProviderConfigResponse 149 } 150 151 resp.PreparedConfig = r.Config 152 return resp 153 } 154 155 func (p *MockProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { 156 p.Lock() 157 defer p.Unlock() 158 159 p.ValidateResourceConfigCalled = true 160 p.ValidateResourceConfigRequest = r 161 162 // Marshall the value to replicate behavior by the GRPC protocol, 163 // and return any relevant errors 164 resourceSchema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] 165 if !ok { 166 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 167 return resp 168 } 169 170 _, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType()) 171 if err != nil { 172 resp.Diagnostics = resp.Diagnostics.Append(err) 173 return resp 174 } 175 176 if p.ValidateResourceConfigFn != nil { 177 return p.ValidateResourceConfigFn(r) 178 } 179 180 if p.ValidateResourceConfigResponse != nil { 181 return *p.ValidateResourceConfigResponse 182 } 183 184 return resp 185 } 186 187 func (p *MockProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { 188 p.Lock() 189 defer p.Unlock() 190 191 p.ValidateDataResourceConfigCalled = true 192 p.ValidateDataResourceConfigRequest = r 193 194 // Marshall the value to replicate behavior by the GRPC protocol 195 dataSchema, ok := p.getProviderSchema().DataSources[r.TypeName] 196 if !ok { 197 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 198 return resp 199 } 200 _, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) 201 if err != nil { 202 resp.Diagnostics = resp.Diagnostics.Append(err) 203 return resp 204 } 205 206 if p.ValidateDataResourceConfigFn != nil { 207 return p.ValidateDataResourceConfigFn(r) 208 } 209 210 if p.ValidateDataResourceConfigResponse != nil { 211 return *p.ValidateDataResourceConfigResponse 212 } 213 214 return resp 215 } 216 217 func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { 218 p.Lock() 219 defer p.Unlock() 220 221 if !p.ConfigureProviderCalled { 222 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before UpgradeResourceState %q", r.TypeName)) 223 return resp 224 } 225 226 schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] 227 if !ok { 228 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 229 return resp 230 } 231 232 schemaType := schema.Block.ImpliedType() 233 234 p.UpgradeResourceStateCalled = true 235 p.UpgradeResourceStateRequest = r 236 237 if p.UpgradeResourceStateFn != nil { 238 return p.UpgradeResourceStateFn(r) 239 } 240 241 if p.UpgradeResourceStateResponse != nil { 242 return *p.UpgradeResourceStateResponse 243 } 244 245 switch { 246 case r.RawStateFlatmap != nil: 247 v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) 248 if err != nil { 249 resp.Diagnostics = resp.Diagnostics.Append(err) 250 return resp 251 } 252 resp.UpgradedState = v 253 case len(r.RawStateJSON) > 0: 254 v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) 255 256 if err != nil { 257 resp.Diagnostics = resp.Diagnostics.Append(err) 258 return resp 259 } 260 resp.UpgradedState = v 261 } 262 263 return resp 264 } 265 266 func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { 267 p.Lock() 268 defer p.Unlock() 269 270 p.ConfigureProviderCalled = true 271 p.ConfigureProviderRequest = r 272 273 if p.ConfigureProviderFn != nil { 274 return p.ConfigureProviderFn(r) 275 } 276 277 if p.ConfigureProviderResponse != nil { 278 return *p.ConfigureProviderResponse 279 } 280 281 return resp 282 } 283 284 func (p *MockProvider) Stop() error { 285 // We intentionally don't lock in this one because the whole point of this 286 // method is to be called concurrently with another operation that can 287 // be cancelled. The provider itself is responsible for handling 288 // any concurrency concerns in this case. 289 290 p.StopCalled = true 291 if p.StopFn != nil { 292 return p.StopFn() 293 } 294 295 return p.StopResponse 296 } 297 298 func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { 299 p.Lock() 300 defer p.Unlock() 301 302 p.ReadResourceCalled = true 303 p.ReadResourceRequest = r 304 305 if !p.ConfigureProviderCalled { 306 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadResource %q", r.TypeName)) 307 return resp 308 } 309 310 if p.ReadResourceFn != nil { 311 return p.ReadResourceFn(r) 312 } 313 314 if p.ReadResourceResponse != nil { 315 resp = *p.ReadResourceResponse 316 317 // Make sure the NewState conforms to the schema. 318 // This isn't always the case for the existing tests. 319 schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] 320 if !ok { 321 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 322 return resp 323 } 324 325 newState, err := schema.Block.CoerceValue(resp.NewState) 326 if err != nil { 327 resp.Diagnostics = resp.Diagnostics.Append(err) 328 } 329 resp.NewState = newState 330 return resp 331 } 332 333 // otherwise just return the same state we received 334 resp.NewState = r.PriorState 335 resp.Private = r.Private 336 return resp 337 } 338 339 func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { 340 p.Lock() 341 defer p.Unlock() 342 343 if !p.ConfigureProviderCalled { 344 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before PlanResourceChange %q", r.TypeName)) 345 return resp 346 } 347 348 p.PlanResourceChangeCalled = true 349 p.PlanResourceChangeRequest = r 350 351 if p.PlanResourceChangeFn != nil { 352 return p.PlanResourceChangeFn(r) 353 } 354 355 if p.PlanResourceChangeResponse != nil { 356 return *p.PlanResourceChangeResponse 357 } 358 359 // this is a destroy plan, 360 if r.ProposedNewState.IsNull() { 361 resp.PlannedState = r.ProposedNewState 362 resp.PlannedPrivate = r.PriorPrivate 363 return resp 364 } 365 366 schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] 367 if !ok { 368 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 369 return resp 370 } 371 372 // The default plan behavior is to accept the proposed value, and mark all 373 // nil computed attributes as unknown. 374 val, err := cty.Transform(r.ProposedNewState, func(path cty.Path, v cty.Value) (cty.Value, error) { 375 // We're only concerned with known null values, which can be computed 376 // by the provider. 377 if !v.IsKnown() { 378 return v, nil 379 } 380 381 attrSchema := schema.Block.AttributeByPath(path) 382 if attrSchema == nil { 383 // this is an intermediate path which does not represent an attribute 384 return v, nil 385 } 386 387 // get the current configuration value, to detect when a 388 // computed+optional attributes has become unset 389 configVal, err := path.Apply(r.Config) 390 if err != nil { 391 return v, err 392 } 393 394 switch { 395 case attrSchema.Computed && !attrSchema.Optional && v.IsNull(): 396 // this is the easy path, this value is not yet set, and _must_ be computed 397 return cty.UnknownVal(v.Type()), nil 398 399 case attrSchema.Computed && attrSchema.Optional && !v.IsNull() && configVal.IsNull(): 400 // If an optional+computed value has gone from set to unset, it 401 // becomes computed. (this was not possible to do with legacy 402 // providers) 403 return cty.UnknownVal(v.Type()), nil 404 } 405 406 return v, nil 407 }) 408 if err != nil { 409 resp.Diagnostics = resp.Diagnostics.Append(err) 410 return resp 411 } 412 413 resp.PlannedPrivate = r.PriorPrivate 414 resp.PlannedState = val 415 416 return resp 417 } 418 419 func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { 420 p.Lock() 421 p.ApplyResourceChangeCalled = true 422 p.ApplyResourceChangeRequest = r 423 p.Unlock() 424 425 if !p.ConfigureProviderCalled { 426 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ApplyResourceChange %q", r.TypeName)) 427 return resp 428 } 429 430 if p.ApplyResourceChangeFn != nil { 431 return p.ApplyResourceChangeFn(r) 432 } 433 434 if p.ApplyResourceChangeResponse != nil { 435 return *p.ApplyResourceChangeResponse 436 } 437 438 schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] 439 if !ok { 440 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) 441 return resp 442 } 443 444 // if the value is nil, we return that directly to correspond to a delete 445 if r.PlannedState.IsNull() { 446 resp.NewState = cty.NullVal(schema.Block.ImpliedType()) 447 return resp 448 } 449 450 val, err := schema.Block.CoerceValue(r.PlannedState) 451 if err != nil { 452 resp.Diagnostics = resp.Diagnostics.Append(err) 453 return resp 454 } 455 456 // the default behavior will be to create the minimal valid apply value by 457 // setting unknowns (which correspond to computed attributes) to a zero 458 // value. 459 val, _ = cty.Transform(val, func(path cty.Path, v cty.Value) (cty.Value, error) { 460 if !v.IsKnown() { 461 ty := v.Type() 462 switch { 463 case ty == cty.String: 464 return cty.StringVal(""), nil 465 case ty == cty.Number: 466 return cty.NumberIntVal(0), nil 467 case ty == cty.Bool: 468 return cty.False, nil 469 case ty.IsMapType(): 470 return cty.MapValEmpty(ty.ElementType()), nil 471 case ty.IsListType(): 472 return cty.ListValEmpty(ty.ElementType()), nil 473 default: 474 return cty.NullVal(ty), nil 475 } 476 } 477 return v, nil 478 }) 479 480 resp.NewState = val 481 resp.Private = r.PlannedPrivate 482 483 return resp 484 } 485 486 func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { 487 p.Lock() 488 defer p.Unlock() 489 490 if !p.ConfigureProviderCalled { 491 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ImportResourceState %q", r.TypeName)) 492 return resp 493 } 494 495 p.ImportResourceStateCalled = true 496 p.ImportResourceStateRequest = r 497 if p.ImportResourceStateFn != nil { 498 return p.ImportResourceStateFn(r) 499 } 500 501 if p.ImportResourceStateResponse != nil { 502 resp = *p.ImportResourceStateResponse 503 // fixup the cty value to match the schema 504 for i, res := range resp.ImportedResources { 505 schema, ok := p.getProviderSchema().ResourceTypes[res.TypeName] 506 if !ok { 507 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", res.TypeName)) 508 return resp 509 } 510 511 var err error 512 res.State, err = schema.Block.CoerceValue(res.State) 513 if err != nil { 514 resp.Diagnostics = resp.Diagnostics.Append(err) 515 return resp 516 } 517 518 resp.ImportedResources[i] = res 519 } 520 } 521 522 return resp 523 } 524 525 func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { 526 p.Lock() 527 defer p.Unlock() 528 529 if !p.ConfigureProviderCalled { 530 resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadDataSource %q", r.TypeName)) 531 return resp 532 } 533 534 p.ReadDataSourceCalled = true 535 p.ReadDataSourceRequest = r 536 537 if p.ReadDataSourceFn != nil { 538 return p.ReadDataSourceFn(r) 539 } 540 541 if p.ReadDataSourceResponse != nil { 542 resp = *p.ReadDataSourceResponse 543 } 544 545 return resp 546 } 547 548 func (p *MockProvider) Close() error { 549 p.CloseCalled = true 550 return p.CloseError 551 }