Skip to content
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

[WIP] build provider_meta feature into provider #474

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ website/node_modules
.idea
*.iml
*.test
*__debug_bin

website/vendor

Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,8 @@ require (
google.golang.org/grpc v1.46.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
)

// grahams fork
// https://github.com/hashicorp/aws-sdk-go-base/tree/user-agent-from-context
replace github.com/hashicorp/aws-sdk-go-base => ../aws-sdk-go-base

29 changes: 29 additions & 0 deletions internal/acctest/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,35 @@ provider "awscc" {
return config
}

func (td *TestData) MetadataConfig() string {
config := fmt.Sprintf(`
resource %[1]q %[2]q {}
`, td.TerraformResourceType, td.ResourceLabel)

config = fmt.Sprintf(`
resource awscc_ec2_vpc drew {
cidr_block = "10.0.0.0/16"
}

terraform {
provider_meta "awscc" {
user_agent = [{
product_name = "my-test-module"
product_version = "0.0.1"
comment = "testing user-agent comment"
},
{
product_name = "2nd-user-agent"
product_version = "0.0.1"
comment = "2nd user agent"
}
]
}
}
` + config)
return config
}

// DataSourceWithEmptyResourceConfig returns a Terraform configuration for the data source and its respective resource.
func (td *TestData) DataSourceWithEmptyResourceConfig() string {
return td.EmptyConfig() + fmt.Sprintf(`
Expand Down
19 changes: 19 additions & 0 deletions internal/aws/ec2/vpc_resource_gen_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions internal/generic/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-log/tflog"
tfcloudcontrol "github.com/hashicorp/terraform-provider-awscc/internal/service/cloudcontrol"
"github.com/hashicorp/terraform-provider-awscc/internal/tfresource"
"github.com/hashicorp/terraform-provider-awscc/internal/types"
"github.com/hashicorp/terraform-provider-awscc/internal/validate"
"github.com/mattbaird/jsonpatch"
)
Expand Down Expand Up @@ -394,13 +395,29 @@ var (
idAttributePath = tftypes.NewAttributePath().WithAttributeName("id")
)

type providerMetaData struct {
UserAgent types.UserAgentProducts `tfsdk:"user_agent"`
}

func (r *resource) Create(ctx context.Context, request tfsdk.CreateResourceRequest, response *tfsdk.CreateResourceResponse) {
ctx = r.cfnTypeContext(ctx)

traceEntry(ctx, "Resource.Create")

var metadata providerMetaData

response.Diagnostics.Append(request.ProviderMeta.Get(ctx, &metadata)...)

if response.Diagnostics.HasError() {
return
}

ctx = context.WithValue(ctx, "awsbase.ContextScopedUserAgent", metadata.UserAgent.UserAgentProducts())

conn := r.provider.CloudControlApiClient(ctx)

// conn := r.provider.CloudControlApiClient(ctx)

tflog.Debug(ctx, "Request.Plan.Raw", map[string]interface{}{
"value": hclog.Fmt("%v", request.Plan.Raw),
})
Expand Down
54 changes: 34 additions & 20 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,16 +301,10 @@ type providerData struct {
Token types.String `tfsdk:"token"`
AssumeRole *assumeRoleData `tfsdk:"assume_role"`
AssumeRoleWithWebIdentity *assumeRoleWithWebIdentityData `tfsdk:"assume_role_with_web_identity"`
UserAgent []userAgentProduct `tfsdk:"user_agent"`
UserAgent cctypes.UserAgentProducts `tfsdk:"user_agent"`
terraformVersion string
}

type userAgentProduct struct {
ProductName types.String `tfsdk:"product_name"`
ProductVersion types.String `tfsdk:"product_version"`
Comment types.String `tfsdk:"comment"`
}

type assumeRoleData struct {
RoleARN types.String `tfsdk:"role_arn"`
Duration cctypes.Duration `tfsdk:"duration"`
Expand Down Expand Up @@ -462,6 +456,38 @@ func (p *AwsCloudControlApiProvider) GetDataSources(ctx context.Context) (map[st
return dataSources, diags
}

func (p *AwsCloudControlApiProvider) GetMetaSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Version: 1,
Attributes: map[string]tfsdk.Attribute{
"user_agent": {
Attributes: tfsdk.ListNestedAttributes(
map[string]tfsdk.Attribute{
"product_name": {
Type: types.StringType,
Description: "Product name. At least one of `product_name` or `comment` must be set.",
Required: true,
},
"product_version": {
Type: types.StringType,
Description: "Product version. Optional, and should only be set when `product_name` is set.",
Optional: true,
},
"comment": {
Type: types.StringType,
Description: "User-Agent comment. At least one of `comment` or `product_name` must be set.",
Optional: true,
},
},
tfsdk.ListNestedAttributesOptions{},
),
Description: "Product details to append to User-Agent string in all AWS API calls.",
Optional: true,
},
},
}, nil
}

func (p *AwsCloudControlApiProvider) CloudControlApiClient(_ context.Context) *cloudcontrol.Client {
return p.ccClient
}
Expand Down Expand Up @@ -494,7 +520,7 @@ func newCloudControlClient(ctx context.Context, pd *providerData) (*cloudcontrol
},
},
}
config.UserAgent = userAgentProducts(pd.UserAgent)
config.UserAgent = pd.UserAgent.UserAgentProducts()
if pd.MaxRetries.Null {
config.MaxRetries = defaultMaxRetries
} else {
Expand Down Expand Up @@ -568,15 +594,3 @@ func (l awsSdkContextLogger) Logf(classification logging.Classification, format
})
}
}

func userAgentProducts(products []userAgentProduct) []awsbase.UserAgentProduct {
results := make([]awsbase.UserAgentProduct, len(products))
for i, p := range products {
results[i] = awsbase.UserAgentProduct{
Name: p.ProductName.Value,
Version: p.ProductVersion.Value,
Comment: p.Comment.Value,
}
}
return results
}
46 changes: 0 additions & 46 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,6 @@ package provider

import (
"testing"

"github.com/google/go-cmp/cmp"
awsbase "github.com/hashicorp/aws-sdk-go-base/v2"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func TestProvider(t *testing.T) {}

func TestUserAgentProducts(t *testing.T) {
t.Parallel()

simpleProduct := awsbase.UserAgentProduct{Name: "simple", Version: "t", Comment: "t"}
simpleAddProduct := userAgentProduct{ProductName: types.String{Value: simpleProduct.Name}, ProductVersion: types.String{Value: simpleProduct.Version}, Comment: types.String{Value: simpleProduct.Comment}}
minimalProduct := awsbase.UserAgentProduct{Name: "minimal"}
minimalAddProduct := userAgentProduct{ProductName: types.String{Value: minimalProduct.Name}}

testcases := map[string]struct {
addProducts []userAgentProduct
expected []awsbase.UserAgentProduct
}{
"none_added": {
addProducts: []userAgentProduct{},
expected: []awsbase.UserAgentProduct{},
},
"simple_added": {
addProducts: []userAgentProduct{simpleAddProduct},
expected: []awsbase.UserAgentProduct{simpleProduct},
},
"minimal_added": {
addProducts: []userAgentProduct{minimalAddProduct},
expected: []awsbase.UserAgentProduct{minimalProduct},
},
"both_added": {
addProducts: []userAgentProduct{simpleAddProduct, minimalAddProduct},
expected: []awsbase.UserAgentProduct{simpleProduct, minimalProduct},
},
}

for name, testcase := range testcases {
name, testcase := name, testcase

t.Run(name, func(t *testing.T) {
actual := userAgentProducts(testcase.addProducts)
if !cmp.Equal(testcase.expected, actual) {
t.Errorf("expected %q, got %q", testcase.expected, actual)
}
})
}
}
26 changes: 26 additions & 0 deletions internal/types/user_agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package types

import (
awsbase "github.com/hashicorp/aws-sdk-go-base/v2"
tftypes "github.com/hashicorp/terraform-plugin-framework/types"
)

type UserAgentProducts []userAgentProduct

type userAgentProduct struct {
ProductName tftypes.String `tfsdk:"product_name"`
ProductVersion tftypes.String `tfsdk:"product_version"`
Comment tftypes.String `tfsdk:"comment"`
}

func (uap UserAgentProducts) UserAgentProducts() []awsbase.UserAgentProduct {
results := make([]awsbase.UserAgentProduct, len(uap))
for i, p := range uap {
results[i] = awsbase.UserAgentProduct{
Name: p.ProductName.Value,
Version: p.ProductVersion.Value,
Comment: p.Comment.Value,
}
}
return results
}
51 changes: 51 additions & 0 deletions internal/types/user_agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package types

import (
"testing"

"github.com/google/go-cmp/cmp"
awsbase "github.com/hashicorp/aws-sdk-go-base/v2"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func TestUserAgentProducts(t *testing.T) {
t.Parallel()

simpleProduct := awsbase.UserAgentProduct{Name: "simple", Version: "t", Comment: "t"}
simpleAddProduct := userAgentProduct{ProductName: types.String{Value: simpleProduct.Name}, ProductVersion: types.String{Value: simpleProduct.Version}, Comment: types.String{Value: simpleProduct.Comment}}
minimalProduct := awsbase.UserAgentProduct{Name: "minimal"}
minimalAddProduct := userAgentProduct{ProductName: types.String{Value: minimalProduct.Name}}

testcases := map[string]struct {
addProducts UserAgentProducts
expected []awsbase.UserAgentProduct
}{
"none_added": {
addProducts: []userAgentProduct{},
expected: []awsbase.UserAgentProduct{},
},
"simple_added": {
addProducts: []userAgentProduct{simpleAddProduct},
expected: []awsbase.UserAgentProduct{simpleProduct},
},
"minimal_added": {
addProducts: []userAgentProduct{minimalAddProduct},
expected: []awsbase.UserAgentProduct{minimalProduct},
},
"both_added": {
addProducts: []userAgentProduct{simpleAddProduct, minimalAddProduct},
expected: []awsbase.UserAgentProduct{simpleProduct, minimalProduct},
},
}

for name, testcase := range testcases {
name, testcase := name, testcase

t.Run(name, func(t *testing.T) {
actual := testcase.addProducts.UserAgentProducts()
if !cmp.Equal(testcase.expected, actual) {
t.Errorf("expected %q, got %q", testcase.expected, actual)
}
})
}
}