-
Notifications
You must be signed in to change notification settings - Fork 208
chore: Adds provider_meta support to the provider #3618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 13 commits
d020917
d9cfbe8
674267c
73277ba
b4a6333
3c04f7d
2216dbf
9d22131
c78545c
aef3be7
b6a75e4
8518f30
d79c43b
716023e
ac083fc
b39c792
62e9334
29a4e0f
7b690f1
5861f3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,19 +6,53 @@ import ( | |
|
|
||
| "github.com/hashicorp/terraform-plugin-framework/datasource" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource" | ||
| "github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
| "github.com/hashicorp/terraform-plugin-framework/types" | ||
| ) | ||
|
|
||
| const ( | ||
| errorConfigureSummary = "Unexpected Resource Configure Type" | ||
| errorConfigure = "expected *MongoDBClient, got: %T. Please report this issue to the provider developers" | ||
| ) | ||
|
|
||
| type ProviderMeta struct { | ||
| ModuleName types.String `tfsdk:"module_name"` | ||
| ModuleVersion types.String `tfsdk:"module_version"` | ||
| UserAgentExtra types.Map `tfsdk:"user_agent_extra"` | ||
| } | ||
|
|
||
| type ImplementedResource interface { | ||
| resource.ResourceWithImportState | ||
| // Additional methods such as upgrade state & plan modifier are optional | ||
| SetClient(*MongoDBClient) | ||
| GetName() string | ||
| } | ||
|
|
||
| func AnalyticsResourceFunc(iResource resource.Resource) func() resource.Resource { | ||
| a := func() resource.Resource { | ||
| commonResource, ok := iResource.(ImplementedResource) | ||
|
||
| if ok { | ||
| return analyticsResource(commonResource) | ||
| } | ||
| return iResource | ||
| } | ||
| return a | ||
| } | ||
|
|
||
| func analyticsResource(iResource ImplementedResource) resource.Resource { | ||
| return &RSCommon{ | ||
| ResourceName: iResource.GetName(), | ||
| ImplementedResource: iResource, | ||
| } | ||
| } | ||
|
|
||
| // RSCommon is used as an embedded struct for all framework resources. Implements the following plugin-framework defined functions: | ||
| // - Metadata | ||
| // - Configure | ||
| // Client is left empty and populated by the framework when envoking Configure method. | ||
| // ResourceName must be defined when creating an instance of a resource. | ||
| type RSCommon struct { | ||
| ImplementedResource | ||
| Client *MongoDBClient | ||
| ResourceName string | ||
| } | ||
|
|
@@ -33,9 +67,117 @@ func (r *RSCommon) Configure(ctx context.Context, req resource.ConfigureRequest, | |
| resp.Diagnostics.AddError(errorConfigureSummary, err.Error()) | ||
| return | ||
| } | ||
| r.ImplementedResource.SetClient(client) | ||
| } | ||
|
|
||
| func (r *RSCommon) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
| extra := r.asUserAgentExtra(ctx, UserAgentOperationValueCreate, req.ProviderMeta) | ||
| ctx = AddUserAgentExtra(ctx, extra) | ||
| r.ImplementedResource.Create(ctx, req, resp) | ||
| } | ||
|
|
||
| func (r *RSCommon) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
| extra := r.asUserAgentExtra(ctx, UserAgentOperationValueRead, req.ProviderMeta) | ||
| ctx = AddUserAgentExtra(ctx, extra) | ||
| r.ImplementedResource.Read(ctx, req, resp) | ||
| } | ||
|
|
||
| func (r *RSCommon) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
| extra := r.asUserAgentExtra(ctx, UserAgentOperationValueUpdate, req.ProviderMeta) | ||
| ctx = AddUserAgentExtra(ctx, extra) | ||
| r.ImplementedResource.Update(ctx, req, resp) | ||
| } | ||
|
|
||
| func (r *RSCommon) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
| extra := r.asUserAgentExtra(ctx, UserAgentOperationValueDelete, req.ProviderMeta) | ||
| ctx = AddUserAgentExtra(ctx, extra) | ||
| r.ImplementedResource.Delete(ctx, req, resp) | ||
| } | ||
|
|
||
| // Optional interfaces for resource.Resource | ||
| func (r *RSCommon) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
| // req resource.ImportStateRequest doesn't have ProviderMeta | ||
| ctx = AddUserAgentExtra(ctx, UserAgentExtra{ | ||
| Name: r.ResourceName, | ||
| Operation: UserAgentOperationValueImport, | ||
| }) | ||
| r.ImplementedResource.ImportState(ctx, req, resp) | ||
| } | ||
|
|
||
| func (r *RSCommon) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { | ||
| resourceWithModifier, ok := r.ImplementedResource.(resource.ResourceWithModifyPlan) | ||
| if !ok { | ||
| return | ||
| } | ||
| extra := r.asUserAgentExtra(ctx, UserAgentOperationValuePlanModify, req.ProviderMeta) | ||
| ctx = AddUserAgentExtra(ctx, extra) | ||
| resourceWithModifier.ModifyPlan(ctx, req, resp) | ||
| } | ||
|
|
||
| func (r *RSCommon) MoveState(ctx context.Context) []resource.StateMover { | ||
| resourceWithMoveState, ok := r.ImplementedResource.(resource.ResourceWithMoveState) | ||
| if !ok { | ||
| return nil | ||
| } | ||
| ctx = AddUserAgentExtra(ctx, UserAgentExtra{ | ||
| Name: r.ResourceName, | ||
| Operation: UserAgentOperationValueMoveState, | ||
| }) | ||
| return resourceWithMoveState.MoveState(ctx) | ||
| } | ||
|
|
||
| func (r *RSCommon) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { | ||
| resourceWithUpgradeState, ok := r.ImplementedResource.(resource.ResourceWithUpgradeState) | ||
| if !ok { | ||
| return nil | ||
| } | ||
| ctx = AddUserAgentExtra(ctx, UserAgentExtra{ | ||
| Name: r.ResourceName, | ||
| Operation: UserAgentOperationValueUpgradeState, | ||
| }) | ||
| return resourceWithUpgradeState.UpgradeState(ctx) | ||
| } | ||
|
|
||
| // Extra methods not found on resource.Resource | ||
| func (r *RSCommon) GetName() string { | ||
| return r.ResourceName | ||
| } | ||
|
|
||
| func (r *RSCommon) SetClient(client *MongoDBClient) { | ||
| r.Client = client | ||
| } | ||
|
|
||
| func (r *RSCommon) asUserAgentExtra(ctx context.Context, reqOperation string, reqProviderMeta tfsdk.Config) UserAgentExtra { | ||
| var meta ProviderMeta | ||
| uaExtra := UserAgentExtra{ | ||
| Name: r.ResourceName, | ||
| Operation: reqOperation, | ||
| } | ||
| if reqProviderMeta.Raw.IsNull() { | ||
| return uaExtra | ||
| } | ||
| diags := reqProviderMeta.Get(ctx, &meta) | ||
| if diags.HasError() { | ||
| return uaExtra | ||
| } | ||
|
|
||
| extrasLen := len(meta.UserAgentExtra.Elements()) | ||
| userExtras := make(map[string]types.String, extrasLen) | ||
| diags.Append(meta.UserAgentExtra.ElementsAs(ctx, &userExtras, false)...) | ||
| if diags.HasError() { | ||
| return uaExtra | ||
| } | ||
| userExtrasString := make(map[string]string, extrasLen) | ||
| for k, v := range userExtras { | ||
| userExtrasString[k] = v.ValueString() | ||
| } | ||
| return uaExtra.Combine(UserAgentExtra{ | ||
| Extras: userExtrasString, | ||
| ModuleName: meta.ModuleName.ValueString(), | ||
| ModuleVersion: meta.ModuleVersion.ValueString(), | ||
| }) | ||
| } | ||
|
|
||
| // DSCommon is used as an embedded struct for all framework data sources. Implements the following plugin-framework defined functions: | ||
| // - Metadata | ||
| // - Configure | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| package config | ||
|
|
||
| import ( | ||
| "context" | ||
| "log" | ||
|
|
||
| "github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
| "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
| ) | ||
|
|
||
| func NewAnalyticsResourceSDKv2(d *schema.Resource, name string) *schema.Resource { | ||
| analyticsResource := &AnalyticsResourceSDKv2{ | ||
| resource: d, | ||
| name: name, | ||
| } | ||
| /* | ||
| We are not initializing deprecated fields, for example Update to avoid the message: | ||
| resource mongodbatlas_cloud_backup_snapshot: All fields are ForceNew or Computed w/out Optional, Update is superfluous | ||
|
|
||
| Ensure no deprecated fields are used by running `staticcheck ./internal/service/... | grep -v 'd.GetOkExists'` and looking for (SA1019) | ||
| GetOkExists we are using in many places; therefore, we use -v (invert match) to filter out lines with different deprecations | ||
| Example line: | ||
| internal/service/cluster/model_cluster.go:306:14: d.GetOkExists is deprecated: usage is discouraged due to undefined behaviors and may be removed in a future version of the SDK (SA1019) | ||
| */ | ||
| resource := &schema.Resource{ | ||
| CustomizeDiff: d.CustomizeDiff, | ||
| DeprecationMessage: d.DeprecationMessage, | ||
| Description: d.Description, | ||
| EnableLegacyTypeSystemApplyErrors: d.EnableLegacyTypeSystemApplyErrors, | ||
| EnableLegacyTypeSystemPlanErrors: d.EnableLegacyTypeSystemPlanErrors, | ||
| Identity: d.Identity, | ||
| ResourceBehavior: d.ResourceBehavior, | ||
| Schema: d.Schema, | ||
| SchemaFunc: d.SchemaFunc, | ||
| SchemaVersion: d.SchemaVersion, | ||
| StateUpgraders: d.StateUpgraders, | ||
| Timeouts: d.Timeouts, | ||
| UpdateWithoutTimeout: d.UpdateWithoutTimeout, | ||
| UseJSONNumber: d.UseJSONNumber, | ||
| ValidateRawResourceConfigFuncs: d.ValidateRawResourceConfigFuncs, | ||
| } | ||
| importer := d.Importer | ||
| if importer != nil { | ||
| resource.Importer = &schema.ResourceImporter{ | ||
| StateContext: analyticsResource.resourceImport, | ||
| } | ||
| } | ||
| // CreateContext or CreateWithoutTimeout, cannot use both | ||
| if d.CreateContext != nil { | ||
| resource.CreateContext = analyticsResource.CreateContext | ||
| } | ||
| if d.CreateWithoutTimeout != nil { | ||
| resource.CreateWithoutTimeout = analyticsResource.CreateWithoutTimeout | ||
| } | ||
| // ReadContext or ReadWithoutTimeout, cannot use both | ||
| if d.ReadContext != nil { | ||
| resource.ReadContext = analyticsResource.ReadContext | ||
| } | ||
| if d.ReadWithoutTimeout != nil { | ||
| resource.ReadWithoutTimeout = analyticsResource.ReadWithoutTimeout | ||
| } | ||
| // UpdateContext is not set on all resources | ||
| if d.UpdateContext != nil { | ||
| resource.UpdateContext = analyticsResource.UpdateContext | ||
| } | ||
| if d.UpdateWithoutTimeout != nil { | ||
| resource.UpdateWithoutTimeout = analyticsResource.UpdateWithoutTimeout | ||
| } | ||
| // DeleteContext or DeleteWithoutTimeout, cannot use both | ||
| if d.DeleteContext != nil { | ||
| resource.DeleteContext = analyticsResource.DeleteContext | ||
| } | ||
| if d.DeleteWithoutTimeout != nil { | ||
| resource.DeleteWithoutTimeout = analyticsResource.DeleteWithoutTimeout | ||
| } | ||
| return resource | ||
| } | ||
|
|
||
| type ProviderMetaSDKv2 struct { | ||
| UserAgentExtra map[string]string `cty:"user_agent_extra"` | ||
| ModuleName *string `cty:"module_name"` | ||
| ModuleVersion *string `cty:"module_version"` | ||
| } | ||
|
|
||
| type AnalyticsResourceSDKv2 struct { | ||
| resource *schema.Resource | ||
| name string | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) CreateContext(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.CreateContext(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueCreate) | ||
| return a.resource.CreateContext(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) CreateWithoutTimeout(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.CreateWithoutTimeout(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueCreate) | ||
| return a.resource.CreateWithoutTimeout(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) ReadWithoutTimeout(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.ReadWithoutTimeout(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueRead) | ||
| return a.resource.ReadWithoutTimeout(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) ReadContext(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.ReadContext(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueRead) | ||
| return a.resource.ReadContext(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) UpdateContext(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.UpdateContext(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueUpdate) | ||
| return a.resource.UpdateContext(ctx, r, m) | ||
| } | ||
| func (a *AnalyticsResourceSDKv2) UpdateWithoutTimeout(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.UpdateWithoutTimeout(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueUpdate) | ||
| return a.resource.UpdateWithoutTimeout(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) DeleteContext(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.DeleteContext(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueDelete) | ||
| return a.resource.DeleteContext(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) DeleteWithoutTimeout(ctx context.Context, r *schema.ResourceData, m interface{}) diag.Diagnostics { | ||
| meta, err := parseProviderMeta(r) | ||
| if err != nil { | ||
| return a.resource.DeleteWithoutTimeout(ctx, r, m) | ||
| } | ||
| ctx = a.updateContextWithProviderMeta(ctx, meta, UserAgentOperationValueDelete) | ||
| return a.resource.DeleteWithoutTimeout(ctx, r, m) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) resourceImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { | ||
| // Import doesn't have providerMeta | ||
| ctx = AddUserAgentExtra(ctx, UserAgentExtra{ | ||
| Name: a.name, | ||
| Operation: UserAgentOperationValueImport, | ||
| }) | ||
| return a.resource.Importer.StateContext(ctx, d, meta) | ||
| } | ||
|
|
||
| func (a *AnalyticsResourceSDKv2) updateContextWithProviderMeta(ctx context.Context, meta ProviderMetaSDKv2, operationName string) context.Context { | ||
| moduleName := "" | ||
| if meta.ModuleName != nil { | ||
| moduleName = *meta.ModuleName | ||
| } | ||
| moduleVersion := "" | ||
| if meta.ModuleVersion != nil { | ||
| moduleVersion = *meta.ModuleVersion | ||
| } | ||
|
|
||
| uaExtra := UserAgentExtra{ | ||
| Name: a.name, | ||
| Operation: operationName, | ||
| Extras: meta.UserAgentExtra, | ||
| ModuleName: moduleName, | ||
| ModuleVersion: moduleVersion, | ||
| } | ||
| ctx = AddUserAgentExtra(ctx, uaExtra) | ||
| return ctx | ||
| } | ||
|
|
||
| func parseProviderMeta(r *schema.ResourceData) (ProviderMetaSDKv2, error) { | ||
| meta := ProviderMetaSDKv2{} | ||
| err := r.GetProviderMeta(&meta) | ||
| if err != nil { | ||
| log.Printf("[WARN] failed to decode provider meta: %s, meta: %v", err, meta) | ||
| } | ||
| return meta, err | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.