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

add module_name provider metadata #255

Merged
merged 4 commits into from
Nov 22, 2022
Merged
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
96 changes: 69 additions & 27 deletions equinix/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/equinix/terraform-provider-equinix/version"
"github.com/hashicorp/go-retryablehttp"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/meta"
"github.com/packethost/packngo"
xoauth2 "golang.org/x/oauth2"
Expand Down Expand Up @@ -62,8 +63,9 @@ Original Error:`
)

var (
DefaultBaseURL = "https://api.equinix.com"
DefaultTimeout = 30
DefaultBaseURL = "https://api.equinix.com"
DefaultTimeout = 30
redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`)
)

// Config is the configuration structure used to instantiate the Equinix
Expand All @@ -83,6 +85,10 @@ type Config struct {
ne ne.Client
metal *packngo.Client

ecxUserAgent string
neUserAgent string
metalUserAgent string

terraformVersion string
}

Expand Down Expand Up @@ -122,11 +128,13 @@ func (c *Config) Load(ctx context.Context) error {
ecxClient.SetPageSize(c.PageSize)
neClient.SetPageSize(c.PageSize)
}
c.ecxUserAgent = c.fullUserAgent("equinix/ecx-go")
ecxClient.SetHeaders(map[string]string{
"User-agent": c.fullUserAgent("equinix/ecx-go"),
"User-agent": c.ecxUserAgent,
})
c.neUserAgent = c.fullUserAgent("equinix/ecx-go")
neClient.SetHeaders(map[string]string{
"User-agent": c.fullUserAgent("equinix/ne-go"),
"User-agent": c.neUserAgent,
})

c.ecx = ecxClient
Expand All @@ -136,15 +144,33 @@ func (c *Config) Load(ctx context.Context) error {
return nil
}

// NewMetalClient returns a new client for accessing Equinix Metal's API.
func (c *Config) NewMetalClient() *packngo.Client {
transport := http.DefaultTransport
// transport = &DumpTransport{http.DefaultTransport} // Debug only
transport = logging.NewTransport("Equinix Metal", transport)
retryClient := retryablehttp.NewClient()
retryClient.HTTPClient.Transport = transport
retryClient.RetryMax = c.MaxRetries
retryClient.RetryWaitMin = time.Second
retryClient.RetryWaitMax = c.MaxRetryWait
retryClient.CheckRetry = MetalRetryPolicy
standardClient := retryClient.StandardClient()
baseURL, _ := url.Parse(c.BaseURL)
baseURL.Path = path.Join(baseURL.Path, metalBasePath) + "/"
client, _ := packngo.NewClientWithBaseURL(consumerToken, c.AuthToken, standardClient, baseURL.String())
client.UserAgent = c.fullUserAgent(client.UserAgent)
c.metalUserAgent = client.UserAgent
return client
}

func (c *Config) requestTimeout() time.Duration {
if c.RequestTimeout == 0 {
return 5 * time.Second
}
return c.RequestTimeout
}

var redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`)

func MetalRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
if ctx.Err() != nil {
return false, ctx.Err()
Expand All @@ -162,7 +188,6 @@ func MetalRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool
return false, nil
}
}

// The error is likely recoverable so retry.
return true, nil
}
Expand All @@ -184,28 +209,45 @@ func terraformUserAgent(version string) string {
return ua
}

func (c *Config) addModuleToECXUserAgent(client *ecx.Client, d *schema.ResourceData) {
cli := *client
rc := cli.(*ecx.RestClient)
rc.SetHeader("User-agent", generateModuleUserAgentString(d, c.ecxUserAgent))
*client = rc
}

func (c *Config) addModuleToNEUserAgent(client *ne.Client, d *schema.ResourceData) {
cli := *client
rc := cli.(*ne.RestClient)
rc.SetHeader("User-agent", generateModuleUserAgentString(d, c.neUserAgent))
*client = rc
}

// TODO (ocobleseqx) - known issue, Metal services are initialized using the metal client pointer
// if two or more modules in same project interact with metal resources they will override
// the UserAgent resulting in swapped UserAgent.
// This can be fixed by letting the headers be overwritten on the initialized Packngo ServiceOp
// clients on a query-by-query basis.
func (c *Config) addModuleToMetalUserAgent(d *schema.ResourceData) {
c.metal.UserAgent = generateModuleUserAgentString(d, c.metalUserAgent)
}

func generateModuleUserAgentString(d *schema.ResourceData, baseUserAgent string) string {
var m providerMeta
err := d.GetProviderMeta(&m)
if err != nil {
log.Printf("[WARN] error retrieving provider_meta")
return baseUserAgent
}

if m.ModuleName != "" {
return strings.Join([]string{m.ModuleName, baseUserAgent}, " ")
}
return baseUserAgent
}

func (c *Config) fullUserAgent(suffix string) string {
tfUserAgent := terraformUserAgent(c.terraformVersion)
userAgent := fmt.Sprintf("%s terraform-provider-equinix/%s %s", tfUserAgent, version.ProviderVersion, suffix)
return strings.TrimSpace(userAgent)
}

// NewMetalClient returns a new client for accessing Equinix Metal's API.
func (c *Config) NewMetalClient() *packngo.Client {
transport := http.DefaultTransport
// transport = &DumpTransport{http.DefaultTransport} // Debug only
transport = logging.NewTransport("Equinix Metal", transport)
retryClient := retryablehttp.NewClient()
retryClient.HTTPClient.Transport = transport
retryClient.RetryMax = c.MaxRetries
retryClient.RetryWaitMin = time.Second
retryClient.RetryWaitMax = c.MaxRetryWait
retryClient.CheckRetry = MetalRetryPolicy
standardClient := retryClient.StandardClient()
baseURL, _ := url.Parse(c.BaseURL)
baseURL.Path = path.Join(baseURL.Path, metalBasePath) + "/"
client, _ := packngo.NewClientWithBaseURL(consumerToken, c.AuthToken, standardClient, baseURL.String())
client.UserAgent = c.fullUserAgent(client.UserAgent)

return client
}
2 changes: 1 addition & 1 deletion equinix/data_source_ecx_l2_sellerprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func dataSourceECXL2SellerProfile() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceECXL2SellerProfileRead,
Description: "Use this data source to get details of Equinix Fabric layer 2 seller profile with a given name and / or organization",
Schema: createECXL2SellerProfileSchema(),
Schema: createECXL2SellerProfileSchema(),
}
}

Expand Down
6 changes: 6 additions & 0 deletions equinix/data_source_metal_project_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ func TestAccDataSourceMetalProject_basic(t *testing.T) {

func testAccDataSourceMetalProject_basic(r string) string {
return fmt.Sprintf(`
terraform {
ocobles marked this conversation as resolved.
Show resolved Hide resolved
provider_meta "equinix" {
module_name = "test"
}
}

resource "equinix_metal_project" "foobar" {
name = "tfacc-project-%s"
bgp_config {
Expand Down
4 changes: 4 additions & 0 deletions equinix/helpers_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ func waitForDeviceAttribute(d *schema.ResourceData, targets []string, pending []
Target: targets,
Refresh: func() (interface{}, string, error) {
client := meta.(*Config).metal
client.UserAgent = generateModuleUserAgentString(d, client.UserAgent)

device, _, err := client.Devices.Get(d.Id(), &packngo.GetOptions{Includes: []string{"project"}})
if err == nil {
retAttrVal := device.State
Expand Down Expand Up @@ -215,7 +217,9 @@ func waitForDeviceAttribute(d *schema.ResourceData, targets []string, pending []

// powerOnAndWait Powers on the device and waits for it to be active.
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
meta.(*Config).addModuleToMetalUserAgent(d)
client := meta.(*Config).metal

_, err := client.Devices.PowerOn(d.Id())
if err != nil {
return friendlyError(err)
Expand Down
1 change: 1 addition & 0 deletions equinix/port_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type ClientPortResource struct {
}

func getClientPortResource(d *schema.ResourceData, meta interface{}) (*ClientPortResource, *packngo.Response, error) {
meta.(*Config).addModuleToMetalUserAgent(d)
client := meta.(*Config).metal

port_id := d.Get("port_id").(string)
Expand Down
14 changes: 14 additions & 0 deletions equinix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ func Provider() *schema.Provider {
"equinix_metal_port_vlan_attachment": resourceMetalPortVlanAttachment(),
"equinix_metal_gateway": resourceMetalGateway(),
},
ProviderMetaSchema: map[string]*schema.Schema{
"module_name": {
Type: schema.TypeString,
Optional: true,
},
},
}

provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
Expand All @@ -170,6 +176,10 @@ func Provider() *schema.Provider {
return provider
}

type providerMeta struct {
ModuleName string `cty:"module_name"`
}

func configureProvider(ctx context.Context, d *schema.ResourceData, p *schema.Provider) (interface{}, diag.Diagnostics) {
mrws := d.Get("max_retry_wait_seconds").(int)
rt := d.Get("request_timeout").(int)
Expand All @@ -185,7 +195,11 @@ func configureProvider(ctx context.Context, d *schema.ResourceData, p *schema.Pr
MaxRetries: d.Get("max_retries").(int),
MaxRetryWait: time.Duration(mrws) * time.Second,
}
meta := providerMeta{}

if err := d.GetProviderMeta(&meta); err != nil {
return nil, diag.FromErr(err)
}
config.terraformVersion = p.TerraformVersion
if config.terraformVersion == "" {
// Terraform 0.12 introduced this field to the protocol
Expand Down
Loading