diff --git a/internal/config/client.go b/internal/config/client.go index 509efbbcf7..ecbfb168f1 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -72,6 +72,7 @@ type Config struct { RealmBaseURL string TerraformVersion string PreviewV2AdvancedClusterEnabled bool + AnalyticsEnabled bool } type AssumeRole struct { @@ -109,10 +110,8 @@ func (c *Config) NewClient(ctx context.Context) (any, error) { // Don't change logging.NewTransport to NewSubsystemLoggingHTTPTransport until all resources are in TPF. tfLoggingTransport := logging.NewTransport("Atlas", digestTransport) // Add UserAgentExtra fields to the User-Agent header, see wrapper_provider_server.go - userAgentTransport := UserAgentTransport{ - Transport: tfLoggingTransport, - } - client := &http.Client{Transport: &userAgentTransport} + userAgentTransport := NewUserAgentTransport(tfLoggingTransport, c.AnalyticsEnabled) + client := &http.Client{Transport: userAgentTransport.Transport} optsAtlas := []matlasClient.ClientOpt{matlasClient.SetUserAgent(userAgent(c))} if c.BaseURL != "" { diff --git a/internal/config/resource_base.go b/internal/config/resource_base.go index 830883075b..60788c6808 100644 --- a/internal/config/resource_base.go +++ b/internal/config/resource_base.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" ) const ( @@ -18,11 +20,22 @@ const ( // - 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 ProviderMeta struct { + ScriptLocation types.String `tfsdk:"script_location"` +} + type RSCommon struct { Client *MongoDBClient ResourceName string } +func (r *RSCommon) ReadProviderMetaCreate(ctx context.Context, req *resource.CreateRequest, diags *diag.Diagnostics) ProviderMeta { + var meta ProviderMeta + diags.Append(req.ProviderMeta.Get(ctx, &meta)...) + return meta +} + func (r *RSCommon) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = fmt.Sprintf("%s_%s", req.ProviderTypeName, r.ResourceName) } diff --git a/internal/config/transport.go b/internal/config/transport.go index 72b2cf98c6..b18577756c 100644 --- a/internal/config/transport.go +++ b/internal/config/transport.go @@ -101,9 +101,20 @@ func AddUserAgentExtra(ctx context.Context, extra UserAgentExtra) context.Contex // UserAgentTransport wraps an http.RoundTripper to add User-Agent header with additional metadata. type UserAgentTransport struct { Transport http.RoundTripper + Enabled bool +} + +func NewUserAgentTransport(transport http.RoundTripper, enabled bool) *UserAgentTransport { + return &UserAgentTransport{ + Transport: transport, + Enabled: enabled, + } } func (t *UserAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if !t.Enabled { + return t.Transport.RoundTrip(req) + } extra := ReadUserAgentExtra(req.Context()) if extra != nil { userAgent := req.Header.Get(UserAgentHeader) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 64aa154d71..9a9e9740e6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -74,6 +75,7 @@ type tfMongodbAtlasProviderModel struct { AwsSecretAccessKeyID types.String `tfsdk:"aws_secret_access_key"` AwsSessionToken types.String `tfsdk:"aws_session_token"` IsMongodbGovCloud types.Bool `tfsdk:"is_mongodbgov_cloud"` + EnableAnalytics types.Bool `tfsdk:"enable_analytics"` } type tfAssumeRoleModel struct { @@ -105,6 +107,17 @@ func (p *MongodbtlasProvider) Metadata(ctx context.Context, req provider.Metadat resp.Version = version.ProviderVersion } +func (p *MongodbtlasProvider) MetaSchema(ctx context.Context, req provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { + resp.Schema = metaschema.Schema{ + Attributes: map[string]metaschema.Attribute{ + "script_location": metaschema.StringAttribute{ + Description: "Example metadata field for analytics/usage.", + Optional: true, + }, + }, + } +} + func (p *MongodbtlasProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Blocks: map[string]schema.Block{ @@ -156,6 +169,10 @@ func (p *MongodbtlasProvider) Schema(ctx context.Context, req provider.SchemaReq Optional: true, Description: "AWS Security Token Service provided session token.", }, + "enable_analytics": schema.BoolAttribute{ + Optional: true, + Description: "Allow extra user agent headers such as script_location specified in provider_meta blocks.", + }, }, } } @@ -245,6 +262,7 @@ func (p *MongodbtlasProvider) Configure(ctx context.Context, req provider.Config RealmBaseURL: data.RealmBaseURL.ValueString(), TerraformVersion: req.TerraformVersion, PreviewV2AdvancedClusterEnabled: config.PreviewProviderV2AdvancedCluster(), + AnalyticsEnabled: data.EnableAnalytics.ValueBool(), } var assumeRoles []tfAssumeRoleModel diff --git a/internal/provider/provider_sdk2.go b/internal/provider/provider_sdk2.go index b81132686c..8d58f2e08c 100644 --- a/internal/provider/provider_sdk2.go +++ b/internal/provider/provider_sdk2.go @@ -126,11 +126,23 @@ func NewSdkV2Provider() *schema.Provider { Optional: true, Description: "AWS Security Token Service provided session token.", }, + "enable_analytics": { + Type: schema.TypeBool, + Optional: true, + Description: "Allow extra user agent headers such as script_location specified in provider_meta blocks.", + }, }, DataSourcesMap: getDataSourcesMap(), ResourcesMap: getResourcesMap(), } provider.ConfigureContextFunc = providerConfigure(provider) + provider.ProviderMetaSchema = map[string]*schema.Schema{ + "script_location": { + Type: schema.TypeString, + Description: "Example metadata field for analytics/usage.", + Optional: true, + }, + } return provider } @@ -287,6 +299,7 @@ func providerConfigure(provider *schema.Provider) func(ctx context.Context, d *s BaseURL: d.Get("base_url").(string), RealmBaseURL: d.Get("realm_base_url").(string), TerraformVersion: provider.TerraformVersion, + AnalyticsEnabled: d.Get("enable_analytics").(bool), } assumeRoleValue, ok := d.GetOk("assume_role") diff --git a/internal/service/organization/resource_organization.go b/internal/service/organization/resource_organization.go index 61bfabba3a..9e3b14a5ee 100644 --- a/internal/service/organization/resource_organization.go +++ b/internal/service/organization/resource_organization.go @@ -122,6 +122,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag. PrivateKey: *organization.ApiKey.PrivateKey, BaseURL: meta.(*config.MongoDBClient).Config.BaseURL, TerraformVersion: meta.(*config.MongoDBClient).Config.TerraformVersion, + AnalyticsEnabled: meta.(*config.MongoDBClient).Config.AnalyticsEnabled, } clients, _ := cfg.NewClient(ctx) @@ -163,6 +164,7 @@ func resourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Di PrivateKey: d.Get("private_key").(string), BaseURL: meta.(*config.MongoDBClient).Config.BaseURL, TerraformVersion: meta.(*config.MongoDBClient).Config.TerraformVersion, + AnalyticsEnabled: meta.(*config.MongoDBClient).Config.AnalyticsEnabled, } clients, _ := cfg.NewClient(ctx) @@ -222,6 +224,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag. PrivateKey: d.Get("private_key").(string), BaseURL: meta.(*config.MongoDBClient).Config.BaseURL, TerraformVersion: meta.(*config.MongoDBClient).Config.TerraformVersion, + AnalyticsEnabled: meta.(*config.MongoDBClient).Config.AnalyticsEnabled, } clients, _ := cfg.NewClient(ctx) @@ -262,6 +265,7 @@ func resourceDelete(ctx context.Context, d *schema.ResourceData, meta any) diag. PrivateKey: d.Get("private_key").(string), BaseURL: meta.(*config.MongoDBClient).Config.BaseURL, TerraformVersion: meta.(*config.MongoDBClient).Config.TerraformVersion, + AnalyticsEnabled: meta.(*config.MongoDBClient).Config.AnalyticsEnabled, } clients, _ := cfg.NewClient(ctx) diff --git a/internal/service/organization/resource_organization_test.go b/internal/service/organization/resource_organization_test.go index ae3dec37a4..e65eace263 100644 --- a/internal/service/organization/resource_organization_test.go +++ b/internal/service/organization/resource_organization_test.go @@ -353,9 +353,10 @@ func getTestClientWithNewOrgCreds(rs *terraform.ResourceState) (*admin.APIClient } cfg := config.Config{ - PublicKey: rs.Primary.Attributes["public_key"], - PrivateKey: rs.Primary.Attributes["private_key"], - BaseURL: acc.MongoDBClient.Config.BaseURL, + PublicKey: rs.Primary.Attributes["public_key"], + PrivateKey: rs.Primary.Attributes["private_key"], + BaseURL: acc.MongoDBClient.Config.BaseURL, + AnalyticsEnabled: acc.MongoDBClient.Config.AnalyticsEnabled, } ctx := context.Background() diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index f54db63baf..45ab86f0ca 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -59,10 +59,13 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp var limits []TFLimitModel connV2 := r.Client.AtlasV2 + diags := &resp.Diagnostics + meta := r.ReadProviderMetaCreate(ctx, &req, diags) + scriptLocation := meta.ScriptLocation + fmt.Println("found script location: " + scriptLocation.ValueString()) - diags := req.Plan.Get(ctx, &projectPlan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { + diags.Append(req.Plan.Get(ctx, &projectPlan)...) + if diags.HasError() { return } projectGroup := &admin.Group{ @@ -174,17 +177,16 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp filteredLimits := FilterUserDefinedLimits(projectProps.Limits, limits) projectProps.Limits = filteredLimits - projectPlanNew, diags := NewTFProjectResourceModel(ctx, projectRes, *projectProps) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { + projectPlanNew, localDiags := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + diags.Append(localDiags...) + if diags.HasError() { return } updatePlanFromConfig(projectPlanNew, &projectPlan) // set state to fully populated data - diags = resp.State.Set(ctx, projectPlanNew) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { + diags.Append(resp.State.Set(ctx, projectPlanNew)...) + if diags.HasError() { return } } diff --git a/internal/testutil/acc/factory.go b/internal/testutil/acc/factory.go index 0de10587d4..3142361e9b 100644 --- a/internal/testutil/acc/factory.go +++ b/internal/testutil/acc/factory.go @@ -43,9 +43,10 @@ func ConnV220241113() *admin20241113.APIClient { func ConnV2UsingGov() *admin.APIClient { cfg := config.Config{ - PublicKey: os.Getenv("MONGODB_ATLAS_GOV_PUBLIC_KEY"), - PrivateKey: os.Getenv("MONGODB_ATLAS_GOV_PRIVATE_KEY"), - BaseURL: os.Getenv("MONGODB_ATLAS_GOV_BASE_URL"), + PublicKey: os.Getenv("MONGODB_ATLAS_GOV_PUBLIC_KEY"), + PrivateKey: os.Getenv("MONGODB_ATLAS_GOV_PRIVATE_KEY"), + BaseURL: os.Getenv("MONGODB_ATLAS_GOV_BASE_URL"), + AnalyticsEnabled: true, } client, _ := cfg.NewClient(context.Background()) return client.(*config.MongoDBClient).AtlasV2 @@ -58,10 +59,11 @@ func init() { }, } cfg := config.Config{ - PublicKey: os.Getenv("MONGODB_ATLAS_PUBLIC_KEY"), - PrivateKey: os.Getenv("MONGODB_ATLAS_PRIVATE_KEY"), - BaseURL: os.Getenv("MONGODB_ATLAS_BASE_URL"), - RealmBaseURL: os.Getenv("MONGODB_REALM_BASE_URL"), + PublicKey: os.Getenv("MONGODB_ATLAS_PUBLIC_KEY"), + PrivateKey: os.Getenv("MONGODB_ATLAS_PRIVATE_KEY"), + BaseURL: os.Getenv("MONGODB_ATLAS_BASE_URL"), + RealmBaseURL: os.Getenv("MONGODB_REALM_BASE_URL"), + AnalyticsEnabled: true, } client, _ := cfg.NewClient(context.Background()) MongoDBClient = client.(*config.MongoDBClient)