Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: migrate meta to its own package (#2449)
* chore: migrate meta to itw own package * chore: migrate version to its own package * chore: migrate meta to its own package * fix unecessary casts * Fix * mute R014 linter for tfproviderlint
- Loading branch information
Showing
247 changed files
with
1,838 additions
and
1,845 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package meta | ||
|
||
import "errors" | ||
|
||
// ErrProjectIDNotFound is returned when no region can be detected | ||
var ErrProjectIDNotFound = errors.New("could not detect project id") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package meta | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/scaleway/scaleway-sdk-go/scw" | ||
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" | ||
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/zonal" | ||
) | ||
|
||
// terraformResourceData is an interface for *schema.ResourceData. (used for mock) | ||
type terraformResourceData interface { | ||
HasChange(string) bool | ||
GetOk(string) (interface{}, bool) | ||
Get(string) interface{} | ||
Id() string | ||
} | ||
|
||
// ExtractZone will try to guess the zone from the following: | ||
// - zone field of the resource data | ||
// - default zone from config | ||
func ExtractZone(d terraformResourceData, m interface{}) (scw.Zone, error) { | ||
rawZone, exist := d.GetOk("zone") | ||
if exist { | ||
return scw.ParseZone(rawZone.(string)) | ||
} | ||
|
||
zone, exist := m.(*Meta).ScwClient().GetDefaultZone() | ||
if exist { | ||
return zone, nil | ||
} | ||
|
||
return "", zonal.ErrZoneNotFound | ||
} | ||
|
||
// ExtractRegion will try to guess the region from the following: | ||
// - region field of the resource data | ||
// - default region from config | ||
func ExtractRegion(d terraformResourceData, m interface{}) (scw.Region, error) { | ||
rawRegion, exist := d.GetOk("region") | ||
if exist { | ||
return scw.ParseRegion(rawRegion.(string)) | ||
} | ||
|
||
region, exist := m.(*Meta).ScwClient().GetDefaultRegion() | ||
if exist { | ||
return region, nil | ||
} | ||
|
||
return "", regional.ErrRegionNotFound | ||
} | ||
|
||
// ExtractRegionWithDefault will try to guess the region from the following: | ||
// - region field of the resource data | ||
// - default region given in argument | ||
// - default region from config | ||
func ExtractRegionWithDefault(d terraformResourceData, m interface{}, defaultRegion scw.Region) (scw.Region, error) { | ||
rawRegion, exist := d.GetOk("region") | ||
if exist { | ||
return scw.ParseRegion(rawRegion.(string)) | ||
} | ||
|
||
if defaultRegion != "" { | ||
return defaultRegion, nil | ||
} | ||
|
||
region, exist := m.(*Meta).ScwClient().GetDefaultRegion() | ||
if exist { | ||
return region, nil | ||
} | ||
|
||
return "", regional.ErrRegionNotFound | ||
} | ||
|
||
// ExtractProjectID will try to guess the project id from the following: | ||
// - project_id field of the resource data | ||
// - default project id from config | ||
func ExtractProjectID(d terraformResourceData, m interface{}) (projectID string, isDefault bool, err error) { | ||
rawProjectID, exist := d.GetOk("project_id") | ||
if exist { | ||
return rawProjectID.(string), false, nil | ||
} | ||
|
||
defaultProjectID, exist := m.(*Meta).ScwClient().GetDefaultProjectID() | ||
if exist { | ||
return defaultProjectID, true, nil | ||
} | ||
|
||
return "", false, ErrProjectIDNotFound | ||
} | ||
|
||
func ExtractScwClient(m interface{}) *scw.Client { | ||
return m.(*Meta).ScwClient() | ||
} | ||
|
||
func ExtractHTTPClient(m interface{}) *http.Client { | ||
return m.(*Meta).HTTPClient() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package meta | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/scaleway/scaleway-sdk-go/scw" | ||
"github.com/scaleway/terraform-provider-scaleway/v2/internal/transport" | ||
"github.com/scaleway/terraform-provider-scaleway/v2/version" | ||
) | ||
|
||
const appendUserAgentEnvVar = "TF_APPEND_USER_AGENT" | ||
|
||
// Meta contains config and SDK clients used by resources. | ||
// | ||
// This meta value is passed into all resources. | ||
type Meta struct { | ||
// scwClient is the Scaleway SDK client. | ||
scwClient *scw.Client | ||
// httpClient can be either a regular http.Client used to make real HTTP requests | ||
// or it can be a http.Client used to record and replay cassettes which is useful | ||
// to replay recorded interactions with APIs locally | ||
httpClient *http.Client | ||
} | ||
|
||
func (m Meta) ScwClient() *scw.Client { | ||
return m.scwClient | ||
} | ||
|
||
func (m Meta) HTTPClient() *http.Client { | ||
return m.httpClient | ||
} | ||
|
||
type Config struct { | ||
ProviderSchema *schema.ResourceData | ||
TerraformVersion string | ||
ForceZone scw.Zone | ||
ForceProjectID string | ||
ForceOrganizationID string | ||
ForceAccessKey string | ||
ForceSecretKey string | ||
HTTPClient *http.Client | ||
} | ||
|
||
// providerConfigure creates the Meta object containing the SDK client. | ||
func NewMeta(ctx context.Context, config *Config) (*Meta, error) { | ||
//// | ||
// Load Profile | ||
//// | ||
profile, err := loadProfile(ctx, config.ProviderSchema) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if config.ForceZone != "" { | ||
region, err := config.ForceZone.Region() | ||
if err != nil { | ||
return nil, err | ||
} | ||
profile.DefaultRegion = scw.StringPtr(region.String()) | ||
profile.DefaultZone = scw.StringPtr(config.ForceZone.String()) | ||
} | ||
if config.ForceProjectID != "" { | ||
profile.DefaultProjectID = scw.StringPtr(config.ForceProjectID) | ||
} | ||
if config.ForceOrganizationID != "" { | ||
profile.DefaultOrganizationID = scw.StringPtr(config.ForceOrganizationID) | ||
} | ||
if config.ForceAccessKey != "" { | ||
profile.AccessKey = scw.StringPtr(config.ForceAccessKey) | ||
} | ||
if config.ForceSecretKey != "" { | ||
profile.SecretKey = scw.StringPtr(config.ForceSecretKey) | ||
} | ||
|
||
// TODO validated profile | ||
|
||
//// | ||
// Create scaleway SDK client | ||
//// | ||
opts := []scw.ClientOption{ | ||
scw.WithUserAgent(customizeUserAgent(version.Version, config.TerraformVersion)), | ||
scw.WithProfile(profile), | ||
} | ||
|
||
httpClient := &http.Client{Transport: transport.NewRetryableTransport(http.DefaultTransport)} | ||
if config.HTTPClient != nil { | ||
httpClient = config.HTTPClient | ||
} | ||
opts = append(opts, scw.WithHTTPClient(httpClient)) | ||
|
||
scwClient, err := scw.NewClient(opts...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Meta{ | ||
scwClient: scwClient, | ||
httpClient: httpClient, | ||
}, nil | ||
} | ||
|
||
func customizeUserAgent(providerVersion string, terraformVersion string) string { | ||
userAgent := fmt.Sprintf("terraform-provider/%s terraform/%s", providerVersion, terraformVersion) | ||
|
||
if appendUserAgent := os.Getenv(appendUserAgentEnvVar); appendUserAgent != "" { | ||
userAgent += " " + appendUserAgent | ||
} | ||
|
||
return userAgent | ||
} | ||
|
||
//gocyclo:ignore | ||
func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, error) { | ||
config, err := scw.LoadConfig() | ||
// If the config file do not exist, don't return an error as we may find config in ENV or flags. | ||
if _, isNotFoundError := err.(*scw.ConfigFileNotFoundError); isNotFoundError { | ||
config = &scw.Config{} | ||
} else if err != nil { | ||
return nil, err | ||
} | ||
|
||
// By default we set default zone and region to fr-par | ||
defaultZoneProfile := &scw.Profile{ | ||
DefaultRegion: scw.StringPtr(scw.RegionFrPar.String()), | ||
DefaultZone: scw.StringPtr(scw.ZoneFrPar1.String()), | ||
} | ||
|
||
activeProfile, err := config.GetActiveProfile() | ||
if err != nil { | ||
return nil, err | ||
} | ||
envProfile := scw.LoadEnvProfile() | ||
|
||
providerProfile := &scw.Profile{} | ||
if d != nil { | ||
if profileName, exist := d.GetOk("profile"); exist { | ||
profileFromConfig, err := config.GetProfile(profileName.(string)) | ||
if err == nil { | ||
providerProfile = profileFromConfig | ||
} | ||
} | ||
if accessKey, exist := d.GetOk("access_key"); exist { | ||
providerProfile.AccessKey = scw.StringPtr(accessKey.(string)) | ||
} | ||
if secretKey, exist := d.GetOk("secret_key"); exist { | ||
providerProfile.SecretKey = scw.StringPtr(secretKey.(string)) | ||
} | ||
if projectID, exist := d.GetOk("project_id"); exist { | ||
providerProfile.DefaultProjectID = scw.StringPtr(projectID.(string)) | ||
} | ||
if orgID, exist := d.GetOk("organization_id"); exist { | ||
providerProfile.DefaultOrganizationID = scw.StringPtr(orgID.(string)) | ||
} | ||
if region, exist := d.GetOk("region"); exist { | ||
providerProfile.DefaultRegion = scw.StringPtr(region.(string)) | ||
} | ||
if zone, exist := d.GetOk("zone"); exist { | ||
providerProfile.DefaultZone = scw.StringPtr(zone.(string)) | ||
} | ||
if apiURL, exist := d.GetOk("api_url"); exist { | ||
providerProfile.APIURL = scw.StringPtr(apiURL.(string)) | ||
} | ||
} | ||
|
||
profile := scw.MergeProfiles(defaultZoneProfile, activeProfile, providerProfile, envProfile) | ||
|
||
// If profile have a defaultZone but no defaultRegion we set the defaultRegion | ||
// to the one of the defaultZone | ||
if profile.DefaultZone != nil && *profile.DefaultZone != "" && | ||
(profile.DefaultRegion == nil || *profile.DefaultRegion == "") { | ||
zone := scw.Zone(*profile.DefaultZone) | ||
tflog.Debug(ctx, fmt.Sprintf("guess region from %s zone", zone)) | ||
region, err := zone.Region() | ||
if err == nil { | ||
profile.DefaultRegion = scw.StringPtr(region.String()) | ||
} else { | ||
tflog.Debug(ctx, "cannot guess region: "+err.Error()) | ||
} | ||
} | ||
return profile, nil | ||
} |
Oops, something went wrong.