github.com/crossplane/upjet@v1.3.0/pkg/controller/external_tfpluginfw_test.go (about) 1 // SPDX-FileCopyrightText: 2024 The Crossplane Authors <https://crossplane.io> 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package controller 6 7 import ( 8 "context" 9 "testing" 10 11 "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" 12 xpresource "github.com/crossplane/crossplane-runtime/pkg/resource" 13 "github.com/crossplane/crossplane-runtime/pkg/test" 14 "github.com/google/go-cmp/cmp" 15 "github.com/hashicorp/terraform-plugin-framework/datasource" 16 "github.com/hashicorp/terraform-plugin-framework/provider" 17 "github.com/hashicorp/terraform-plugin-framework/resource" 18 rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" 19 "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 20 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 21 "github.com/hashicorp/terraform-plugin-framework/tfsdk" 22 "github.com/hashicorp/terraform-plugin-framework/types" 23 "github.com/hashicorp/terraform-plugin-go/tfprotov5" 24 "github.com/hashicorp/terraform-plugin-go/tftypes" 25 "github.com/pkg/errors" 26 "sigs.k8s.io/controller-runtime/pkg/client" 27 28 "github.com/crossplane/upjet/pkg/config" 29 "github.com/crossplane/upjet/pkg/resource/fake" 30 "github.com/crossplane/upjet/pkg/terraform" 31 ) 32 33 func newBaseObject() fake.Terraformed { 34 return fake.Terraformed{ 35 Parameterizable: fake.Parameterizable{ 36 Parameters: map[string]any{ 37 "name": "example", 38 "map": map[string]any{ 39 "key": "value", 40 }, 41 "list": []any{"elem1", "elem2"}, 42 }, 43 }, 44 Observable: fake.Observable{ 45 Observation: map[string]any{}, 46 }, 47 } 48 } 49 50 func newBaseSchema() rschema.Schema { 51 return rschema.Schema{ 52 Attributes: map[string]rschema.Attribute{ 53 "name": rschema.StringAttribute{ 54 Required: true, 55 PlanModifiers: []planmodifier.String{ 56 stringplanmodifier.UseStateForUnknown(), 57 }, 58 }, 59 "id": rschema.StringAttribute{ 60 Computed: true, 61 PlanModifiers: []planmodifier.String{ 62 stringplanmodifier.UseStateForUnknown(), 63 }, 64 }, 65 "map": rschema.MapAttribute{ 66 Required: true, 67 ElementType: types.StringType, 68 }, 69 "list": rschema.ListAttribute{ 70 Required: true, 71 ElementType: types.StringType, 72 }, 73 }, 74 } 75 } 76 77 func newMockBaseTPFResource() *mockTPFResource { 78 return &mockTPFResource{ 79 SchemaMethod: func(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { 80 response.Schema = newBaseSchema() 81 }, 82 ReadMethod: func(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { 83 response.State = tfsdk.State{ 84 Raw: tftypes.Value{}, 85 Schema: nil, 86 } 87 }, 88 } 89 } 90 91 func newBaseUpjetConfig() *config.Resource { 92 return &config.Resource{ 93 TerraformPluginFrameworkResource: newMockBaseTPFResource(), 94 ExternalName: config.IdentifierFromProvider, 95 Sensitive: config.Sensitive{AdditionalConnectionDetailsFn: func(attr map[string]any) (map[string][]byte, error) { 96 return nil, nil 97 }}, 98 } 99 } 100 101 type testConfiguration struct { 102 r resource.Resource 103 cfg *config.Resource 104 obj fake.Terraformed 105 params map[string]any 106 currentStateMap map[string]any 107 plannedStateMap map[string]any 108 newStateMap map[string]any 109 110 readErr error 111 readDiags []*tfprotov5.Diagnostic 112 113 applyErr error 114 applyDiags []*tfprotov5.Diagnostic 115 116 planErr error 117 planDiags []*tfprotov5.Diagnostic 118 } 119 120 func prepareTPFExternalWithTestConfig(testConfig testConfiguration) *terraformPluginFrameworkExternalClient { 121 testConfig.cfg.TerraformPluginFrameworkResource = testConfig.r 122 schemaResp := &resource.SchemaResponse{} 123 testConfig.r.Schema(context.TODO(), resource.SchemaRequest{}, schemaResp) 124 tfValueType := schemaResp.Schema.Type().TerraformType(context.TODO()) 125 126 currentStateVal, err := protov5DynamicValueFromMap(testConfig.currentStateMap, tfValueType) 127 if err != nil { 128 panic("cannot prepare TPF") 129 } 130 plannedStateVal, err := protov5DynamicValueFromMap(testConfig.plannedStateMap, tfValueType) 131 if err != nil { 132 panic("cannot prepare TPF") 133 } 134 newStateAfterApplyVal, err := protov5DynamicValueFromMap(testConfig.newStateMap, tfValueType) 135 if err != nil { 136 panic("cannot prepare TPF") 137 } 138 return &terraformPluginFrameworkExternalClient{ 139 ts: terraform.Setup{ 140 FrameworkProvider: &mockTPFProvider{}, 141 }, 142 config: cfg, 143 logger: logTest, 144 // metricRecorder: nil, 145 opTracker: NewAsyncTracker(), 146 resource: testConfig.r, 147 server: &mockTPFProviderServer{ 148 ReadResourceFn: func(ctx context.Context, request *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) { 149 return &tfprotov5.ReadResourceResponse{ 150 NewState: currentStateVal, 151 Diagnostics: testConfig.readDiags, 152 }, testConfig.readErr 153 }, 154 PlanResourceChangeFn: func(ctx context.Context, request *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { 155 return &tfprotov5.PlanResourceChangeResponse{ 156 PlannedState: plannedStateVal, 157 Diagnostics: testConfig.planDiags, 158 }, testConfig.planErr 159 }, 160 ApplyResourceChangeFn: func(ctx context.Context, request *tfprotov5.ApplyResourceChangeRequest) (*tfprotov5.ApplyResourceChangeResponse, error) { 161 return &tfprotov5.ApplyResourceChangeResponse{ 162 NewState: newStateAfterApplyVal, 163 Diagnostics: testConfig.applyDiags, 164 }, testConfig.applyErr 165 }, 166 }, 167 params: testConfig.params, 168 planResponse: &tfprotov5.PlanResourceChangeResponse{PlannedState: plannedStateVal}, 169 resourceSchema: schemaResp.Schema, 170 resourceValueTerraformType: tfValueType, 171 } 172 } 173 174 func TestTPFConnect(t *testing.T) { 175 type args struct { 176 setupFn terraform.SetupFn 177 cfg *config.Resource 178 ots *OperationTrackerStore 179 obj fake.Terraformed 180 } 181 type want struct { 182 err error 183 } 184 cases := map[string]struct { 185 args 186 want 187 }{ 188 "Successful": { 189 args: args{ 190 setupFn: func(_ context.Context, _ client.Client, _ xpresource.Managed) (terraform.Setup, error) { 191 return terraform.Setup{ 192 FrameworkProvider: &mockTPFProvider{}, 193 }, nil 194 }, 195 cfg: newBaseUpjetConfig(), 196 obj: newBaseObject(), 197 ots: ots, 198 }, 199 }, 200 } 201 202 for name, tc := range cases { 203 t.Run(name, func(t *testing.T) { 204 c := NewTerraformPluginFrameworkConnector(nil, tc.args.setupFn, tc.args.cfg, tc.args.ots, WithTerraformPluginFrameworkLogger(logTest)) 205 _, err := c.Connect(context.TODO(), &tc.args.obj) 206 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 207 t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff) 208 } 209 }) 210 } 211 } 212 213 func TestTPFObserve(t *testing.T) { 214 type want struct { 215 obs managed.ExternalObservation 216 err error 217 } 218 cases := map[string]struct { 219 testConfiguration 220 want 221 }{ 222 "NotExists": { 223 testConfiguration: testConfiguration{ 224 r: newMockBaseTPFResource(), 225 cfg: newBaseUpjetConfig(), 226 obj: obj, 227 currentStateMap: nil, 228 plannedStateMap: map[string]any{ 229 "name": "example", 230 }, 231 params: map[string]any{ 232 "name": "example", 233 }, 234 }, 235 want: want{ 236 obs: managed.ExternalObservation{ 237 ResourceExists: false, 238 ResourceUpToDate: false, 239 ResourceLateInitialized: false, 240 ConnectionDetails: nil, 241 Diff: "", 242 }, 243 }, 244 }, 245 246 "UpToDate": { 247 testConfiguration: testConfiguration{ 248 r: newMockBaseTPFResource(), 249 cfg: newBaseUpjetConfig(), 250 obj: newBaseObject(), 251 params: map[string]any{ 252 "id": "example-id", 253 "name": "example", 254 }, 255 currentStateMap: map[string]any{ 256 "id": "example-id", 257 "name": "example", 258 }, 259 plannedStateMap: map[string]any{ 260 "id": "example-id", 261 "name": "example", 262 }, 263 }, 264 want: want{ 265 obs: managed.ExternalObservation{ 266 ResourceExists: true, 267 ResourceUpToDate: true, 268 ResourceLateInitialized: true, 269 ConnectionDetails: nil, 270 Diff: "", 271 }, 272 }, 273 }, 274 275 "LateInitialize": { 276 testConfiguration: testConfiguration{ 277 r: newMockBaseTPFResource(), 278 cfg: newBaseUpjetConfig(), 279 obj: fake.Terraformed{ 280 Parameterizable: fake.Parameterizable{ 281 Parameters: map[string]any{ 282 "name": "example", 283 "map": map[string]any{ 284 "key": "value", 285 }, 286 "list": []any{"elem1", "elem2"}, 287 }, 288 InitParameters: map[string]any{ 289 "list": []any{"elem1", "elem2", "elem3"}, 290 }, 291 }, 292 Observable: fake.Observable{ 293 Observation: map[string]any{}, 294 }, 295 }, 296 params: map[string]any{ 297 "id": "example-id", 298 }, 299 currentStateMap: map[string]any{ 300 "id": "example-id", 301 "name": "example2", 302 }, 303 plannedStateMap: map[string]any{ 304 "id": "example-id", 305 "name": "example2", 306 }, 307 }, 308 want: want{ 309 obs: managed.ExternalObservation{ 310 ResourceExists: true, 311 ResourceUpToDate: true, 312 ResourceLateInitialized: true, 313 ConnectionDetails: nil, 314 Diff: "", 315 }, 316 }, 317 }, 318 } 319 320 for name, tc := range cases { 321 t.Run(name, func(t *testing.T) { 322 tpfExternal := prepareTPFExternalWithTestConfig(tc.testConfiguration) 323 observation, err := tpfExternal.Observe(context.TODO(), &tc.testConfiguration.obj) 324 if diff := cmp.Diff(tc.want.obs, observation); diff != "" { 325 t.Errorf("\n%s\nObserve(...): -want observation, +got observation:\n", diff) 326 } 327 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 328 t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff) 329 } 330 }) 331 } 332 } 333 334 func TestTPFCreate(t *testing.T) { 335 type want struct { 336 err error 337 } 338 cases := map[string]struct { 339 testConfiguration 340 want 341 }{ 342 "Successful": { 343 testConfiguration: testConfiguration{ 344 r: newMockBaseTPFResource(), 345 cfg: newBaseUpjetConfig(), 346 obj: obj, 347 currentStateMap: nil, 348 plannedStateMap: map[string]any{ 349 "name": "example", 350 }, 351 params: map[string]any{ 352 "name": "example", 353 }, 354 newStateMap: map[string]any{ 355 "name": "example", 356 "id": "example-id", 357 }, 358 }, 359 }, 360 "EmptyStateAfterCreation": { 361 testConfiguration: testConfiguration{ 362 r: newMockBaseTPFResource(), 363 cfg: newBaseUpjetConfig(), 364 obj: obj, 365 currentStateMap: nil, 366 plannedStateMap: map[string]any{ 367 "name": "example", 368 }, 369 params: map[string]any{ 370 "name": "example", 371 }, 372 newStateMap: nil, 373 }, 374 want: want{ 375 err: errors.New("new state is empty after creation"), 376 }, 377 }, 378 "ApplyWithError": { 379 testConfiguration: testConfiguration{ 380 r: newMockBaseTPFResource(), 381 cfg: newBaseUpjetConfig(), 382 obj: obj, 383 currentStateMap: nil, 384 plannedStateMap: map[string]any{ 385 "name": "example", 386 }, 387 params: map[string]any{ 388 "name": "example", 389 }, 390 newStateMap: nil, 391 applyErr: errors.New("foo error"), 392 }, 393 want: want{ 394 err: errors.Wrap(errors.New("foo error"), "cannot create resource"), 395 }, 396 }, 397 "ApplyWithDiags": { 398 testConfiguration: testConfiguration{ 399 r: newMockBaseTPFResource(), 400 cfg: newBaseUpjetConfig(), 401 obj: obj, 402 currentStateMap: nil, 403 plannedStateMap: map[string]any{ 404 "name": "example", 405 }, 406 params: map[string]any{ 407 "name": "example", 408 }, 409 newStateMap: nil, 410 applyDiags: []*tfprotov5.Diagnostic{ 411 { 412 Severity: tfprotov5.DiagnosticSeverityError, 413 Summary: "foo summary", 414 Detail: "foo detail", 415 }, 416 }, 417 }, 418 want: want{ 419 err: errors.Wrap(errors.New("foo summary: foo detail"), "resource creation call returned error diags"), 420 }, 421 }, 422 } 423 for name, tc := range cases { 424 t.Run(name, func(t *testing.T) { 425 tpfExternal := prepareTPFExternalWithTestConfig(tc.testConfiguration) 426 _, err := tpfExternal.Create(context.TODO(), &tc.testConfiguration.obj) 427 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 428 t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff) 429 } 430 }) 431 } 432 } 433 434 func TestTPFUpdate(t *testing.T) { 435 type want struct { 436 err error 437 } 438 cases := map[string]struct { 439 testConfiguration 440 want 441 }{ 442 "Successful": { 443 testConfiguration: testConfiguration{ 444 r: newMockBaseTPFResource(), 445 cfg: newBaseUpjetConfig(), 446 obj: newBaseObject(), 447 currentStateMap: map[string]any{ 448 "name": "example", 449 "id": "example-id", 450 }, 451 plannedStateMap: map[string]any{ 452 "name": "example-updated", 453 "id": "example-id", 454 }, 455 params: map[string]any{ 456 "name": "example-updated", 457 }, 458 newStateMap: map[string]any{ 459 "name": "example-updated", 460 "id": "example-id", 461 }, 462 }, 463 }, 464 } 465 for name, tc := range cases { 466 t.Run(name, func(t *testing.T) { 467 tpfExternal := prepareTPFExternalWithTestConfig(tc.testConfiguration) 468 _, err := tpfExternal.Update(context.TODO(), &tc.testConfiguration.obj) 469 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 470 t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff) 471 } 472 }) 473 } 474 } 475 476 func TestTPFDelete(t *testing.T) { 477 478 type want struct { 479 err error 480 } 481 cases := map[string]struct { 482 testConfiguration 483 want 484 }{ 485 "Successful": { 486 testConfiguration: testConfiguration{ 487 r: newMockBaseTPFResource(), 488 cfg: newBaseUpjetConfig(), 489 obj: newBaseObject(), 490 currentStateMap: map[string]any{ 491 "name": "example", 492 "id": "example-id", 493 }, 494 plannedStateMap: nil, 495 params: map[string]any{ 496 "name": "example", 497 }, 498 newStateMap: nil, 499 }, 500 }, 501 } 502 for name, tc := range cases { 503 t.Run(name, func(t *testing.T) { 504 tpfExternal := prepareTPFExternalWithTestConfig(tc.testConfiguration) 505 err := tpfExternal.Delete(context.TODO(), &tc.testConfiguration.obj) 506 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 507 t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff) 508 } 509 }) 510 } 511 } 512 513 // Mocks 514 515 var _ resource.Resource = &mockTPFResource{} 516 var _ tfprotov5.ProviderServer = &mockTPFProviderServer{} 517 var _ provider.Provider = &mockTPFProvider{} 518 519 type mockTPFProviderServer struct { 520 GetMetadataFn func(ctx context.Context, request *tfprotov5.GetMetadataRequest) (*tfprotov5.GetMetadataResponse, error) 521 GetProviderSchemaFn func(ctx context.Context, request *tfprotov5.GetProviderSchemaRequest) (*tfprotov5.GetProviderSchemaResponse, error) 522 PrepareProviderConfigFn func(ctx context.Context, request *tfprotov5.PrepareProviderConfigRequest) (*tfprotov5.PrepareProviderConfigResponse, error) 523 ConfigureProviderFn func(ctx context.Context, request *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) 524 StopProviderFn func(ctx context.Context, request *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) 525 ValidateResourceTypeConfigFn func(ctx context.Context, request *tfprotov5.ValidateResourceTypeConfigRequest) (*tfprotov5.ValidateResourceTypeConfigResponse, error) 526 UpgradeResourceStateFn func(ctx context.Context, request *tfprotov5.UpgradeResourceStateRequest) (*tfprotov5.UpgradeResourceStateResponse, error) 527 ReadResourceFn func(ctx context.Context, request *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) 528 PlanResourceChangeFn func(ctx context.Context, request *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) 529 ApplyResourceChangeFn func(ctx context.Context, request *tfprotov5.ApplyResourceChangeRequest) (*tfprotov5.ApplyResourceChangeResponse, error) 530 ImportResourceStateFn func(ctx context.Context, request *tfprotov5.ImportResourceStateRequest) (*tfprotov5.ImportResourceStateResponse, error) 531 ValidateDataSourceConfigFn func(ctx context.Context, request *tfprotov5.ValidateDataSourceConfigRequest) (*tfprotov5.ValidateDataSourceConfigResponse, error) 532 ReadDataSourceFn func(ctx context.Context, request *tfprotov5.ReadDataSourceRequest) (*tfprotov5.ReadDataSourceResponse, error) 533 } 534 535 func (m *mockTPFProviderServer) GetMetadata(_ context.Context, _ *tfprotov5.GetMetadataRequest) (*tfprotov5.GetMetadataResponse, error) { 536 // TODO implement me 537 panic("implement me") 538 } 539 540 func (m *mockTPFProviderServer) GetProviderSchema(_ context.Context, _ *tfprotov5.GetProviderSchemaRequest) (*tfprotov5.GetProviderSchemaResponse, error) { 541 // TODO implement me 542 panic("implement me") 543 } 544 545 func (m *mockTPFProviderServer) PrepareProviderConfig(_ context.Context, _ *tfprotov5.PrepareProviderConfigRequest) (*tfprotov5.PrepareProviderConfigResponse, error) { 546 // TODO implement me 547 panic("implement me") 548 } 549 550 func (m *mockTPFProviderServer) ConfigureProvider(_ context.Context, _ *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) { 551 // TODO implement me 552 panic("implement me") 553 } 554 555 func (m *mockTPFProviderServer) StopProvider(_ context.Context, _ *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) { 556 // TODO implement me 557 panic("implement me") 558 } 559 560 func (m *mockTPFProviderServer) ValidateResourceTypeConfig(_ context.Context, _ *tfprotov5.ValidateResourceTypeConfigRequest) (*tfprotov5.ValidateResourceTypeConfigResponse, error) { 561 // TODO implement me 562 panic("implement me") 563 } 564 565 func (m *mockTPFProviderServer) UpgradeResourceState(_ context.Context, _ *tfprotov5.UpgradeResourceStateRequest) (*tfprotov5.UpgradeResourceStateResponse, error) { 566 // TODO implement me 567 panic("implement me") 568 } 569 570 func (m *mockTPFProviderServer) ReadResource(ctx context.Context, request *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) { 571 if m.ReadResourceFn == nil { 572 return nil, nil 573 } 574 return m.ReadResourceFn(ctx, request) 575 } 576 577 func (m *mockTPFProviderServer) PlanResourceChange(ctx context.Context, request *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { 578 if m.PlanResourceChangeFn == nil { 579 return nil, nil 580 } 581 return m.PlanResourceChangeFn(ctx, request) 582 } 583 584 func (m *mockTPFProviderServer) ApplyResourceChange(ctx context.Context, request *tfprotov5.ApplyResourceChangeRequest) (*tfprotov5.ApplyResourceChangeResponse, error) { 585 if m.ApplyResourceChangeFn == nil { 586 return nil, nil 587 } 588 return m.ApplyResourceChangeFn(ctx, request) 589 } 590 591 func (m *mockTPFProviderServer) ImportResourceState(_ context.Context, _ *tfprotov5.ImportResourceStateRequest) (*tfprotov5.ImportResourceStateResponse, error) { 592 // TODO implement me 593 panic("implement me") 594 } 595 596 func (m *mockTPFProviderServer) ValidateDataSourceConfig(_ context.Context, _ *tfprotov5.ValidateDataSourceConfigRequest) (*tfprotov5.ValidateDataSourceConfigResponse, error) { 597 // TODO implement me 598 panic("implement me") 599 } 600 601 func (m *mockTPFProviderServer) ReadDataSource(_ context.Context, _ *tfprotov5.ReadDataSourceRequest) (*tfprotov5.ReadDataSourceResponse, error) { 602 // TODO implement me 603 panic("implement me") 604 } 605 606 type mockTPFProvider struct { 607 // Provider interface methods 608 MetadataMethod func(context.Context, provider.MetadataRequest, *provider.MetadataResponse) 609 ConfigureMethod func(context.Context, provider.ConfigureRequest, *provider.ConfigureResponse) 610 SchemaMethod func(context.Context, provider.SchemaRequest, *provider.SchemaResponse) 611 DataSourcesMethod func(context.Context) []func() datasource.DataSource 612 ResourcesMethod func(context.Context) []func() resource.Resource 613 } 614 615 // Configure satisfies the provider.Provider interface. 616 func (p *mockTPFProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { 617 if p == nil || p.ConfigureMethod == nil { 618 return 619 } 620 621 p.ConfigureMethod(ctx, req, resp) 622 } 623 624 // DataSources satisfies the provider.Provider interface. 625 func (p *mockTPFProvider) DataSources(ctx context.Context) []func() datasource.DataSource { 626 if p == nil || p.DataSourcesMethod == nil { 627 return nil 628 } 629 630 return p.DataSourcesMethod(ctx) 631 } 632 633 // Metadata satisfies the provider.Provider interface. 634 func (p *mockTPFProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { 635 if p == nil || p.MetadataMethod == nil { 636 return 637 } 638 639 p.MetadataMethod(ctx, req, resp) 640 } 641 642 // Schema satisfies the provider.Provider interface. 643 func (p *mockTPFProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { 644 if p == nil || p.SchemaMethod == nil { 645 return 646 } 647 648 p.SchemaMethod(ctx, req, resp) 649 } 650 651 // Resources satisfies the provider.Provider interface. 652 func (p *mockTPFProvider) Resources(ctx context.Context) []func() resource.Resource { 653 if p == nil || p.ResourcesMethod == nil { 654 return nil 655 } 656 657 return p.ResourcesMethod(ctx) 658 } 659 660 type mockTPFResource struct { 661 // Resource interface methods 662 MetadataMethod func(context.Context, resource.MetadataRequest, *resource.MetadataResponse) 663 SchemaMethod func(context.Context, resource.SchemaRequest, *resource.SchemaResponse) 664 CreateMethod func(context.Context, resource.CreateRequest, *resource.CreateResponse) 665 DeleteMethod func(context.Context, resource.DeleteRequest, *resource.DeleteResponse) 666 ReadMethod func(context.Context, resource.ReadRequest, *resource.ReadResponse) 667 UpdateMethod func(context.Context, resource.UpdateRequest, *resource.UpdateResponse) 668 } 669 670 // Metadata satisfies the resource.Resource interface. 671 func (r *mockTPFResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 672 if r.MetadataMethod == nil { 673 return 674 } 675 676 r.MetadataMethod(ctx, req, resp) 677 } 678 679 // Schema satisfies the resource.Resource interface. 680 func (r *mockTPFResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 681 if r.SchemaMethod == nil { 682 return 683 } 684 685 r.SchemaMethod(ctx, req, resp) 686 } 687 688 // Create satisfies the resource.Resource interface. 689 func (r *mockTPFResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 690 if r.CreateMethod == nil { 691 return 692 } 693 694 r.CreateMethod(ctx, req, resp) 695 } 696 697 // Delete satisfies the resource.Resource interface. 698 func (r *mockTPFResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 699 if r.DeleteMethod == nil { 700 return 701 } 702 703 r.DeleteMethod(ctx, req, resp) 704 } 705 706 // Read satisfies the resource.Resource interface. 707 func (r *mockTPFResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 708 if r.ReadMethod == nil { 709 return 710 } 711 712 r.ReadMethod(ctx, req, resp) 713 } 714 715 // Update satisfies the resource.Resource interface. 716 func (r *mockTPFResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 717 if r.UpdateMethod == nil { 718 return 719 } 720 721 r.UpdateMethod(ctx, req, resp) 722 }