Skip to content

Commit d75e8b7

Browse files
author
Mélanie Marques
committed
feat: add warning message if multiple variable sources
1 parent 3a1f808 commit d75e8b7

File tree

4 files changed

+143
-1
lines changed

4 files changed

+143
-1
lines changed

core/bootstrap.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,12 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result any, err error) {
236236
// Run checks after command has been executed
237237
defer func() { // if we plan to remove defer, do not forget logger is not set until cobra pre init func
238238
// Check CLI new version and api key expiration date
239-
runAfterCommandChecks(ctx, config.BuildInfo.checkVersion, checkAPIKey)
239+
runAfterCommandChecks(
240+
ctx,
241+
config.BuildInfo.checkVersion,
242+
checkAPIKey,
243+
checkIfMultipleVariableSources,
244+
)
240245
}()
241246

242247
if !config.DisableAliases {

core/checks.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package core
22

33
import (
4+
"bytes"
45
"context"
56
"fmt"
67
"os"
78
"path/filepath"
9+
"reflect"
810
"time"
911

1012
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
13+
"github.com/scaleway/scaleway-sdk-go/scw"
1114
)
1215

1316
var (
1417
apiKeyExpireTime = 24 * time.Hour
1518
lastChecksFileLocalName = "last-cli-checks"
1619
)
1720

21+
const (
22+
defaultCredentialSource = "environment variable"
23+
)
24+
1825
type AfterCommandCheckFunc func(ctx context.Context)
1926

2027
// wasFileModifiedLast24h checks whether the file has been updated during last 24 hours.
@@ -105,3 +112,43 @@ func checkAPIKey(ctx context.Context) {
105112
ExtractLogger(ctx).Warningf("Current api key expires in %s\n", expiresIn)
106113
}
107114
}
115+
116+
// checkIfMultipleVariableSources return an informative message during the CLI initialization
117+
// if there are multiple sources of configuration that could confuse the user
118+
func checkIfMultipleVariableSources(ctx context.Context) {
119+
config, err := scw.LoadConfigFromPath(ExtractConfigPath(ctx))
120+
if err != nil {
121+
return
122+
}
123+
124+
activeProfile, err := config.GetActiveProfile()
125+
if err != nil {
126+
return
127+
}
128+
129+
profileEnv := scw.LoadEnvProfile()
130+
131+
vFile := reflect.ValueOf(activeProfile).Elem()
132+
vEnv := reflect.ValueOf(profileEnv).Elem()
133+
t := vFile.Type()
134+
135+
var buffer bytes.Buffer
136+
buffer.WriteString("Checking multiple variable sources: \n")
137+
138+
for i := range t.NumField() {
139+
valFile := vFile.Field(i)
140+
valEnv := vEnv.Field(i)
141+
142+
if !valFile.IsNil() && !valEnv.IsNil() {
143+
if valFile.Elem().String() != valEnv.Elem().String() {
144+
buffer.WriteString(fmt.Sprintf(
145+
"- Variable '%s' is defined in both config.yaml and environment with different values. Using: %s.\n",
146+
t.Field(i).Name,
147+
defaultCredentialSource,
148+
))
149+
}
150+
}
151+
}
152+
153+
ExtractLogger(ctx).Warning(buffer.String())
154+
}

core/checks_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package core_test
33
import (
44
"context"
55
"errors"
6+
"os"
67
"path/filepath"
78
"reflect"
89
"strings"
@@ -86,3 +87,57 @@ func TestCheckAPIKey(t *testing.T) {
8687
},
8788
}))
8889
}
90+
91+
func TestCheckIfMultipleVariableSources(t *testing.T) {
92+
testCommands := core.NewCommands(
93+
&core.Command{
94+
Namespace: "test",
95+
ArgSpecs: core.ArgSpecs{},
96+
ArgsType: reflect.TypeOf(testType{}),
97+
Run: func(ctx context.Context, _ any) (any, error) { return "", nil },
98+
},
99+
)
100+
101+
t.Run("conflicting sources should trigger warning", core.Test(&core.TestConfig{
102+
Commands: testCommands,
103+
TmpHomeDir: true,
104+
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
105+
cfg := &scw.Config{
106+
Profile: scw.Profile{
107+
AccessKey: scw.StringPtr("SCW11111111111111111"),
108+
SecretKey: scw.StringPtr("config-secret"),
109+
DefaultProjectID: scw.StringPtr("config-project-id"),
110+
},
111+
}
112+
113+
configPath := filepath.Join(ctx.OverrideEnv["HOME"], ".config", "scw", "config.yaml")
114+
if err := cfg.SaveTo(configPath); err != nil {
115+
return err
116+
}
117+
118+
_ = os.Setenv("SCW_ACCESS_KEY", "SCW99999999999999999")
119+
_ = os.Setenv("SCW_SECRET_KEY", "env-secret")
120+
_ = os.Setenv("SCW_DEFAULT_PROJECT_ID", "config-project-id")
121+
122+
return nil
123+
},
124+
Cmd: "scw test",
125+
Check: core.TestCheckCombine(
126+
core.TestCheckExitCode(0),
127+
func(t *testing.T, ctx *core.CheckFuncCtx) {
128+
t.Helper()
129+
assert.Contains(t, ctx.LogBuffer, "Checking multiple variable sources")
130+
assert.Contains(
131+
t,
132+
ctx.LogBuffer,
133+
"Variable 'AccessKey' is defined in both config.yaml and environment with different values. Using: environment variable.",
134+
)
135+
assert.Contains(
136+
t,
137+
ctx.LogBuffer,
138+
"Variable 'SecretKey' is defined in both config.yaml and environment with different values. Using: environment variable.",
139+
)
140+
},
141+
),
142+
}))
143+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
version: 1
3+
interactions:
4+
- request:
5+
body: '{"message":"authentication is denied","method":"api_key","reason":"not_found","type":"denied_authentication"}'
6+
form: {}
7+
headers:
8+
User-Agent:
9+
- scaleway-sdk-go/v1.0.0-beta.7+dev (go1.24.4; darwin; arm64) cli-e2e-test
10+
url: https://api.scaleway.com/iam/v1alpha1/api-keys/SCWXXXXXXXXXXXXXXXXX
11+
method: GET
12+
response:
13+
body: '{"message":"authentication is denied","method":"api_key","reason":"not_found","type":"denied_authentication"}'
14+
headers:
15+
Content-Length:
16+
- "109"
17+
Content-Security-Policy:
18+
- default-src 'none'; frame-ancestors 'none'
19+
Content-Type:
20+
- application/json
21+
Date:
22+
- Tue, 22 Jul 2025 12:16:22 GMT
23+
Server:
24+
- Scaleway API Gateway (fr-par-1;edge01)
25+
Strict-Transport-Security:
26+
- max-age=63072000
27+
X-Content-Type-Options:
28+
- nosniff
29+
X-Frame-Options:
30+
- DENY
31+
X-Request-Id:
32+
- d55dda1c-064c-4670-95ff-50d7c32fe521
33+
status: 401 Unauthorized
34+
code: 401
35+
duration: ""

0 commit comments

Comments
 (0)