Skip to content

Commit 4b9ba52

Browse files
authored
feat(core): data source config reader (#2590)
* poc(config): add a data source that reads user's credentials * add sources + zone and region * add tests + respect order * create separate job for ci test * rework new job + skip coverage tests * linter: make it a const * move test_config.yaml to testfixture dir
1 parent 82ede47 commit 4b9ba52

12 files changed

+3078
-9
lines changed

.github/workflows/acceptance-tests.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,24 @@ jobs:
6161
SCW_ACCESS_KEY: "SCWXXXXXXXXXXXXXFAKE"
6262
SCW_SECRET_KEY: "11111111-1111-1111-1111-111111111111"
6363
SCW_ENABLE_BETA: true
64+
test-scwconfig:
65+
strategy:
66+
fail-fast: false
67+
runs-on: ubuntu-latest
68+
steps:
69+
- name: Install Go
70+
uses: actions/setup-go@v5
71+
with:
72+
go-version: 1.22
73+
- name: Checkout
74+
uses: actions/checkout@v4
75+
- name: Run scwconfig tests
76+
run: go test -v ./internal/services/scwconfig -timeout=2m
77+
env:
78+
TF_LOG: DEBUG
79+
TF_ACC: 1
80+
TF_UPDATE_CASSETTES: false
81+
TF_TEST_DOMAIN: scaleway-terraform.com
82+
TF_TEST_DOMAIN_ZONE: tf
83+
SCW_DEBUG: 0
84+
SCW_ENABLE_BETA: true

internal/meta/meta.go

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,22 @@ import (
1313
"github.com/scaleway/terraform-provider-scaleway/v2/version"
1414
)
1515

16-
const appendUserAgentEnvVar = "TF_APPEND_USER_AGENT"
16+
const (
17+
appendUserAgentEnvVar = "TF_APPEND_USER_AGENT"
18+
CredentialsSourceEnvironment = "Environment variable"
19+
CredentialsSourceDefault = "Default"
20+
CredentialsSourceActiveProfile = "Active Profile in config.yaml"
21+
CredentialsSourceProviderProfile = "Profile defined in provider{} block"
22+
CredentialsSourceInferred = "CredentialsSourceInferred from default zone"
23+
)
24+
25+
type CredentialsSource struct {
26+
AccessKey string
27+
SecretKey string
28+
ProjectID string
29+
DefaultZone string
30+
DefaultRegion string
31+
}
1732

1833
// Meta contains config and SDK clients used by resources.
1934
//
@@ -25,6 +40,8 @@ type Meta struct {
2540
// or it can be a http.Client used to record and replay cassettes which is useful
2641
// to replay recorded interactions with APIs locally
2742
httpClient *http.Client
43+
// credentialsSource stores information about the source (env, profile, etc.) of each credential
44+
credentialsSource *CredentialsSource
2845
}
2946

3047
func (m Meta) ScwClient() *scw.Client {
@@ -35,6 +52,26 @@ func (m Meta) HTTPClient() *http.Client {
3552
return m.httpClient
3653
}
3754

55+
func (m Meta) AccessKeySource() string {
56+
return m.credentialsSource.AccessKey
57+
}
58+
59+
func (m Meta) SecretKeySource() string {
60+
return m.credentialsSource.SecretKey
61+
}
62+
63+
func (m Meta) ProjectIDSource() string {
64+
return m.credentialsSource.ProjectID
65+
}
66+
67+
func (m Meta) RegionSource() string {
68+
return m.credentialsSource.DefaultRegion
69+
}
70+
71+
func (m Meta) ZoneSource() string {
72+
return m.credentialsSource.DefaultZone
73+
}
74+
3875
type Config struct {
3976
ProviderSchema *schema.ResourceData
4077
TerraformVersion string
@@ -46,12 +83,12 @@ type Config struct {
4683
HTTPClient *http.Client
4784
}
4885

49-
// providerConfigure creates the Meta object containing the SDK client.
86+
// NewMeta creates the Meta object containing the SDK client.
5087
func NewMeta(ctx context.Context, config *Config) (*Meta, error) {
5188
////
5289
// Load Profile
5390
////
54-
profile, err := loadProfile(ctx, config.ProviderSchema)
91+
profile, credentialsSource, err := loadProfile(ctx, config.ProviderSchema)
5592
if err != nil {
5693
return nil, err
5794
}
@@ -98,8 +135,9 @@ func NewMeta(ctx context.Context, config *Config) (*Meta, error) {
98135
}
99136

100137
return &Meta{
101-
scwClient: scwClient,
102-
httpClient: httpClient,
138+
scwClient: scwClient,
139+
httpClient: httpClient,
140+
credentialsSource: credentialsSource,
103141
}, nil
104142
}
105143

@@ -114,13 +152,13 @@ func customizeUserAgent(providerVersion string, terraformVersion string) string
114152
}
115153

116154
//gocyclo:ignore
117-
func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, error) {
155+
func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, *CredentialsSource, error) {
118156
config, err := scw.LoadConfig()
119157
// If the config file do not exist, don't return an error as we may find config in ENV or flags.
120158
if _, isNotFoundError := err.(*scw.ConfigFileNotFoundError); isNotFoundError {
121159
config = &scw.Config{}
122160
} else if err != nil {
123-
return nil, err
161+
return nil, nil, err
124162
}
125163

126164
// By default we set default zone and region to fr-par
@@ -131,7 +169,7 @@ func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, err
131169

132170
activeProfile, err := config.GetActiveProfile()
133171
if err != nil {
134-
return nil, err
172+
return nil, nil, err
135173
}
136174
envProfile := scw.LoadEnvProfile()
137175

@@ -167,6 +205,7 @@ func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, err
167205
}
168206

169207
profile := scw.MergeProfiles(defaultZoneProfile, activeProfile, providerProfile, envProfile)
208+
credentialsSource := GetCredentialsSource(defaultZoneProfile, activeProfile, providerProfile, envProfile)
170209

171210
// If profile have a defaultZone but no defaultRegion we set the defaultRegion
172211
// to the one of the defaultZone
@@ -177,9 +216,61 @@ func loadProfile(ctx context.Context, d *schema.ResourceData) (*scw.Profile, err
177216
region, err := zone.Region()
178217
if err == nil {
179218
profile.DefaultRegion = scw.StringPtr(region.String())
219+
credentialsSource.DefaultRegion = CredentialsSourceInferred
180220
} else {
181221
tflog.Debug(ctx, "cannot guess region: "+err.Error())
182222
}
183223
}
184-
return profile, nil
224+
return profile, credentialsSource, nil
225+
}
226+
227+
// GetCredentialsSource infers the source of the credentials based on the priority order of the different profiles
228+
func GetCredentialsSource(defaultZoneProfile, activeProfile, providerProfile, envProfile *scw.Profile) *CredentialsSource {
229+
type SourceProfilePair struct {
230+
Source string
231+
Profile *scw.Profile
232+
}
233+
234+
profilesInOrder := []SourceProfilePair{
235+
{
236+
CredentialsSourceDefault,
237+
defaultZoneProfile,
238+
},
239+
{
240+
CredentialsSourceActiveProfile,
241+
activeProfile,
242+
},
243+
{
244+
CredentialsSourceProviderProfile,
245+
providerProfile,
246+
},
247+
{
248+
CredentialsSourceEnvironment,
249+
envProfile,
250+
},
251+
}
252+
credentialsSource := &CredentialsSource{}
253+
254+
for _, pair := range profilesInOrder {
255+
source := pair.Source
256+
profile := pair.Profile
257+
258+
if profile.AccessKey != nil {
259+
credentialsSource.AccessKey = source
260+
}
261+
if profile.SecretKey != nil {
262+
credentialsSource.SecretKey = source
263+
}
264+
if profile.DefaultProjectID != nil {
265+
credentialsSource.ProjectID = source
266+
}
267+
if profile.DefaultRegion != nil {
268+
credentialsSource.DefaultRegion = source
269+
}
270+
if profile.DefaultZone != nil {
271+
credentialsSource.DefaultZone = source
272+
}
273+
}
274+
275+
return credentialsSource
185276
}

internal/provider/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/rdb"
3737
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/redis"
3838
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/registry"
39+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/scwconfig"
3940
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/sdb"
4041
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/secret"
4142
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/tem"
@@ -238,6 +239,7 @@ func Provider(config *Config) plugin.ProviderFunc {
238239
"scaleway_block_volume": block.DataSourceVolume(),
239240
"scaleway_cockpit": cockpit.DataSourceCockpit(),
240241
"scaleway_cockpit_plan": cockpit.DataSourcePlan(),
242+
"scaleway_config": scwconfig.DataSourceConfig(),
241243
"scaleway_container": container.DataSourceContainer(),
242244
"scaleway_container_namespace": container.DataSourceNamespace(),
243245
"scaleway_documentdb_database": documentdb.DataSourceDatabase(),

0 commit comments

Comments
 (0)