Skip to content

Commit 758140c

Browse files
authored
Merge branch 'main' into dependabot/github_actions/actions/upload-artifact-5
2 parents 3e380d8 + b690d3f commit 758140c

File tree

9 files changed

+233
-26
lines changed

9 files changed

+233
-26
lines changed

.github/workflows/update-terraform-provider.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Set up Go
2020
uses: actions/setup-go@v6
2121
with:
22-
go-version: '1.22'
22+
go-version: '1.25'
2323

2424
- name: Install golangci-lint
2525
uses: golangci/golangci-lint-action@v8

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ stringData:
128128
| `organization_id` | The [organization ID](https://console.scaleway.com/organization/settings) that will be used as default value for organization-scoped resources. |
129129
| `region` | The [region](https://developers.scaleway.com/en/quickstart/#region-and-zone) that will be used as default value for all resources. (`fr-par` if none specified) |
130130
| `zone` | The [zone](https://developers.scaleway.com/en/quickstart/#region-and-zone) that will be used as default value for all resources. (`fr-par-1` if none specified) |
131+
| `api_url` | The URL of the API |
131132

132133
### Create a ProviderConfig
133134

@@ -160,6 +161,23 @@ The `spec.secretRef` describes the parameters of the secret to use.
160161
* `name` is the name of the Kubernetes `secret` object.
161162
* `key` is the `Data` field from `kubectl describe secret`.
162163

164+
### SCW config support
165+
166+
This provider can read the standard SCW config file (`~/.config/scw/config.yaml`) and environment variables. Precedence is:
167+
168+
1. ProviderConfig credentials
169+
2. Environment variables (`SCW_*`)
170+
3. SCW config file
171+
172+
You can control behavior in `ProviderConfig.spec.scw`:
173+
174+
```yaml
175+
spec:
176+
scw:
177+
useScwConfig: true
178+
# path: /home/me/.config/scw/config.yaml
179+
# profile: myProfile
180+
```
163181
### Create a managed resource
164182

165183
1. Create a managed resource to see if the provider is properly functioning.

apis/generate.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//go:build generate
2-
// +build generate
32

43
/*
54
Copyright 2021 Upbound Inc.

apis/v1beta1/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ import (
1313
// A ProviderConfigSpec defines the desired state of a ProviderConfig.
1414
type ProviderConfigSpec struct {
1515
// Credentials required to authenticate to this provider.
16+
// You may set source: None to rely on env/file config.
1617
Credentials ProviderCredentials `json:"credentials"`
18+
19+
// Scw controls how the Scaleway shared config is discovered and selected.
20+
// Optional; if omitted, file discovery is enabled by default and the SDK
21+
// will use its active profile (env can still override).
22+
Scw *ScwConfig `json:"scw,omitempty"`
1723
}
1824

1925
// ProviderCredentials required to authenticate.
@@ -25,6 +31,18 @@ type ProviderCredentials struct {
2531
xpv1.CommonCredentialSelectors `json:",inline"`
2632
}
2733

34+
type ScwConfig struct {
35+
// UseScwConfig toggles loading of the SCW config file. Defaults to true if nil.
36+
UseScwConfig *bool `json:"useScwConfig,omitempty"`
37+
38+
// Path to a specific config file. If unset, SDK discovery is used.
39+
Path *string `json:"path,omitempty"`
40+
41+
// Profile name to select from the config file. If unset, SDK active profile is used.
42+
// Note: SCW_* environment variables still override file values.
43+
Profile *string `json:"profile,omitempty"`
44+
}
45+
2846
// A ProviderConfigStatus reflects the observed state of a ProviderConfig.
2947
type ProviderConfigStatus struct {
3048
xpv1.ProviderConfigStatus `json:",inline"`

apis/v1beta1/zz_generated.deepcopy.go

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79
1010
github.com/crossplane/upjet v1.9.0
1111
github.com/pkg/errors v0.9.1
12+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35
1213
golang.org/x/text v0.30.0
1314
gopkg.in/yaml.v3 v3.0.1
1415
k8s.io/apimachinery v0.34.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG
235235
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
236236
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
237237
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
238+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts=
239+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc=
238240
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
239241
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
240242
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=

internal/clients/scaleway.go

Lines changed: 135 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/crossplane/crossplane-runtime/pkg/resource"
1717
"github.com/pkg/errors"
1818
"github.com/scaleway/crossplane-provider-scaleway/internal/version"
19+
"github.com/scaleway/scaleway-sdk-go/scw"
1920

2021
"github.com/crossplane/upjet/pkg/terraform"
2122

@@ -27,15 +28,18 @@ const (
2728
errNoProviderConfig = "no providerConfigRef provided"
2829
errGetProviderConfig = "cannot get referenced ProviderConfig"
2930
errTrackUsage = "cannot track ProviderConfig usage"
30-
errExtractCredentials = "cannot extract credentials"
3131
errUnmarshalCredentials = "cannot unmarshal scaleway credentials as JSON"
32+
errLoadSCWConfig = "cannot load SCW config file"
33+
errGetSCWProfile = "cannot get SCW profile from config"
3234

3335
keyAccessKey = "access_key"
3436
keySecretKey = "secret_key"
3537
keyProjectID = "project_id"
3638
keyOrganizationID = "organization_id"
3739
keyRegion = "region"
3840
keyZone = "zone"
41+
keyAPIURL = "api_url"
42+
keyInsecure = "insecure"
3943
)
4044

4145
// TerraformSetupBuilder builds Terraform a terraform.SetupFn function which
@@ -48,6 +52,7 @@ func TerraformSetupBuilder(tfversion, providerSource, providerVersion string) te
4852
Source: providerSource,
4953
Version: providerVersion,
5054
},
55+
Configuration: map[string]any{},
5156
}
5257

5358
configRef := mg.GetProviderConfigReference()
@@ -64,32 +69,21 @@ func TerraformSetupBuilder(tfversion, providerSource, providerVersion string) te
6469
return ps, errors.Wrap(err, errTrackUsage)
6570
}
6671

67-
data, err := resource.CommonCredentialExtractor(ctx, pc.Spec.Credentials.Source, client, pc.Spec.Credentials.CommonCredentialSelectors)
72+
// Load Scaleway config file + Env (Env > File)
73+
profile, err := resolveScwProfile(pc)
6874
if err != nil {
69-
return ps, errors.Wrap(err, errExtractCredentials)
70-
}
71-
creds := map[string]string{}
72-
if err := json.Unmarshal(data, &creds); err != nil {
73-
return ps, errors.Wrap(err, errUnmarshalCredentials)
74-
}
75-
76-
scalewayCreds := map[string]string{}
77-
if err := json.Unmarshal(data, &scalewayCreds); err != nil {
78-
return ps, errors.Wrap(err, errUnmarshalCredentials)
75+
return ps, err
7976
}
77+
fillConfigFromProfile(ps.Configuration, profile)
8078

81-
ps.Configuration = map[string]interface{}{}
82-
for _, key := range []string{
83-
keyAccessKey,
84-
keySecretKey,
85-
keyProjectID,
86-
keyOrganizationID,
87-
keyRegion,
88-
keyZone,
89-
} {
90-
if scalewayCreds[key] != "" {
91-
ps.Configuration[key] = scalewayCreds[key]
79+
// Overlay Secret (if any). Secret > Env > File
80+
data, err := resource.CommonCredentialExtractor(ctx, pc.Spec.Credentials.Source, client, pc.Spec.Credentials.CommonCredentialSelectors)
81+
if err == nil && len(data) > 0 {
82+
vals := map[string]string{}
83+
if err := json.Unmarshal(data, &vals); err != nil {
84+
return ps, errors.Wrap(err, errUnmarshalCredentials)
9285
}
86+
overlayCredentials(ps.Configuration, vals)
9387
}
9488

9589
// Set the custom user agent
@@ -102,3 +96,121 @@ func TerraformSetupBuilder(tfversion, providerSource, providerVersion string) te
10296
return ps, nil
10397
}
10498
}
99+
100+
// overlayCredentials overlays non-empty Secret values over existing config
101+
func overlayCredentials(cfg map[string]any, vals map[string]string) {
102+
for _, k := range []string{
103+
keyAccessKey, keySecretKey, keyProjectID, keyOrganizationID,
104+
keyRegion, keyZone, keyAPIURL, keyInsecure,
105+
} {
106+
if v, ok := vals[k]; ok && v != "" {
107+
cfg[k] = v
108+
}
109+
}
110+
}
111+
112+
func resolveScwProfile(pc *v1beta1.ProviderConfig) (*scw.Profile, error) {
113+
useFile := shouldUseScwConfig(pc)
114+
115+
fileProf, err := readScwConfigProfile(pc, useFile)
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
envProf := scw.LoadEnvProfile()
121+
return scw.MergeProfiles(fileProf, envProf), nil
122+
}
123+
124+
func readScwConfigProfile(pc *v1beta1.ProviderConfig, useFile bool) (*scw.Profile, error) {
125+
if !useFile {
126+
return nil, nil
127+
}
128+
129+
cfg, err := loadScwConfigFile(pc)
130+
if err != nil {
131+
return nil, err
132+
}
133+
if cfg == nil {
134+
return nil, nil
135+
}
136+
137+
return selectScwProfile(pc, cfg)
138+
}
139+
140+
func loadScwConfigFile(pc *v1beta1.ProviderConfig) (*scw.Config, error) {
141+
var cfg *scw.Config
142+
var err error
143+
144+
if hasScwPath(pc) {
145+
cfg, err = scw.LoadConfigFromPath(*pc.Spec.Scw.Path)
146+
} else {
147+
cfg, err = scw.LoadConfig()
148+
}
149+
150+
var notFound *scw.ConfigFileNotFoundError
151+
if err != nil && !errors.As(err, &notFound) {
152+
return nil, errors.Wrap(err, errLoadSCWConfig)
153+
}
154+
155+
return cfg, nil
156+
}
157+
158+
func selectScwProfile(pc *v1beta1.ProviderConfig, cfg *scw.Config) (*scw.Profile, error) {
159+
if cfg == nil {
160+
return nil, nil
161+
}
162+
163+
if hasScwProfile(pc) {
164+
prof, err := cfg.GetProfile(*pc.Spec.Scw.Profile)
165+
if err != nil {
166+
return nil, errors.Wrap(err, errGetSCWProfile)
167+
}
168+
return prof, nil
169+
}
170+
171+
prof, err := cfg.GetActiveProfile()
172+
if err != nil {
173+
return nil, errors.Wrap(err, errGetSCWProfile)
174+
}
175+
return prof, nil
176+
}
177+
178+
func hasScwPath(pc *v1beta1.ProviderConfig) bool {
179+
return pc.Spec.Scw != nil && pc.Spec.Scw.Path != nil && *pc.Spec.Scw.Path != ""
180+
}
181+
182+
func hasScwProfile(pc *v1beta1.ProviderConfig) bool {
183+
return pc.Spec.Scw != nil && pc.Spec.Scw.Profile != nil && *pc.Spec.Scw.Profile != ""
184+
}
185+
186+
func shouldUseScwConfig(pc *v1beta1.ProviderConfig) bool {
187+
if pc.Spec.Scw != nil && pc.Spec.Scw.UseScwConfig != nil {
188+
return *pc.Spec.Scw.UseScwConfig
189+
}
190+
return true
191+
}
192+
193+
// fillConfigFromProfile copies non-empty profile fields into cfg
194+
func fillConfigFromProfile(cfg map[string]any, p *scw.Profile) {
195+
if cfg == nil || p == nil {
196+
return
197+
}
198+
199+
assign := func(key string, val *string) {
200+
if val != nil && *val != "" {
201+
cfg[key] = *val
202+
}
203+
}
204+
205+
assign(keyAccessKey, p.AccessKey)
206+
assign(keySecretKey, p.SecretKey)
207+
assign(keyProjectID, p.DefaultProjectID)
208+
assign(keyOrganizationID, p.DefaultOrganizationID)
209+
assign(keyRegion, p.DefaultRegion)
210+
assign(keyZone, p.DefaultZone)
211+
assign(keyAPIURL, p.APIURL)
212+
213+
if p.Insecure != nil {
214+
cfg[keyInsecure] = *p.Insecure
215+
}
216+
}

package/crds/scaleway.upbound.io_providerconfigs.yaml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ spec:
5252
description: A ProviderConfigSpec defines the desired state of a ProviderConfig.
5353
properties:
5454
credentials:
55-
description: Credentials required to authenticate to this provider.
55+
description: |-
56+
Credentials required to authenticate to this provider.
57+
You may set source: None to rely on env/file config.
5658
properties:
5759
env:
5860
description: |-
@@ -107,6 +109,26 @@ spec:
107109
required:
108110
- source
109111
type: object
112+
scw:
113+
description: |-
114+
Scw controls how the Scaleway shared config is discovered and selected.
115+
Optional; if omitted, file discovery is enabled by default and the SDK
116+
will use its active profile (env can still override).
117+
properties:
118+
path:
119+
description: Path to a specific config file. If unset, SDK discovery
120+
is used.
121+
type: string
122+
profile:
123+
description: |-
124+
Profile name to select from the config file. If unset, SDK active profile is used.
125+
Note: SCW_* environment variables still override file values.
126+
type: string
127+
useScwConfig:
128+
description: UseScwConfig toggles loading of the SCW config file.
129+
Defaults to true if nil.
130+
type: boolean
131+
type: object
110132
required:
111133
- credentials
112134
type: object

0 commit comments

Comments
 (0)