Skip to content

Commit 4216b26

Browse files
authored
feat(config): add profile activate command (#1206)
1 parent 59677b5 commit 4216b26

File tree

14 files changed

+175
-70
lines changed

14 files changed

+175
-70
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Mark a profile as active in the config file
4+
5+
USAGE:
6+
scw config profile activate <profile-name ...> [arg=value ...]
7+
8+
ARGS:
9+
profile-name
10+
11+
FLAGS:
12+
-h, --help help for activate
13+
14+
GLOBAL FLAGS:
15+
-c, --config string The path to the config file
16+
-D, --debug Enable debug mode
17+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
18+
-p, --profile string The config profile to use

cmd/scw/testdata/test-all-usage-config-profile-usage.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ USAGE:
66
scw config profile <command>
77

88
AVAILABLE COMMANDS:
9+
activate Mark a profile as active in the config file
910
delete Delete a profile from the config file
1011

1112
FLAGS:

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ require (
2121
github.com/mattn/go-colorable v0.1.4
2222
github.com/mattn/go-isatty v0.0.11
2323
github.com/pkg/errors v0.9.1 // indirect
24-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200707130522-abc4aeb2a4e6
24+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200710161155-10382899255f
2525
github.com/sergi/go-diff v1.0.0 // indirect
2626
github.com/spf13/cobra v0.0.5
2727
github.com/spf13/pflag v1.0.5

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200624111939-0a4e128e532e
7575
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200624111939-0a4e128e532e/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
7676
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200707130522-abc4aeb2a4e6 h1:mCYMQVdy3ciDx7jtDnRuxTk9IUB525PhZYkCTjMWQUI=
7777
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200707130522-abc4aeb2a4e6/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
78+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200710161155-10382899255f h1:FHSh2peVlKEecLqHX/8uevrWqeua68LbVBOzIhC0HBw=
79+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200710161155-10382899255f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
7880
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
7981
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
8082
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package core
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/scaleway/scaleway-sdk-go/scw"
8+
)
9+
10+
func AutocompleteProfileName() AutoCompleteArgFunc {
11+
return func(ctx context.Context, prefix string) AutocompleteSuggestions {
12+
res := AutocompleteSuggestions(nil)
13+
configPath := ExtractConfigPath(ctx)
14+
config, err := scw.LoadConfigFromPath(configPath)
15+
if err != nil {
16+
return res
17+
}
18+
19+
for profileName := range config.Profiles {
20+
if strings.HasPrefix(profileName, prefix) {
21+
res = append(res, profileName)
22+
}
23+
}
24+
25+
if strings.HasPrefix(scw.DefaultProfileName, prefix) {
26+
res = append(res, scw.DefaultProfileName)
27+
}
28+
return res
29+
}
30+
}

internal/core/client.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,9 @@ func createClient(httpClient *http.Client, buildInfo *BuildInfo, profileName str
2525
return nil, err
2626
}
2727

28-
var activeProfile *scw.Profile
29-
30-
if profileName != "" {
31-
activeProfile, err = config.GetProfile(profileName)
32-
if err != nil {
33-
return nil, err
34-
}
35-
} else {
36-
activeProfile, err = config.GetActiveProfile()
37-
if err != nil {
38-
return nil, err
39-
}
28+
activeProfile, err := config.GetProfile(profileName)
29+
if err != nil {
30+
return nil, err
4031
}
4132

4233
envProfile := scw.LoadEnvProfile()

internal/core/context.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,25 @@ func ExtractStdin(ctx context.Context) io.Reader {
109109
}
110110

111111
func ExtractProfileName(ctx context.Context) string {
112+
// Handle profile flag -p
112113
if extractMeta(ctx).ProfileFlag != "" {
113114
return extractMeta(ctx).ProfileFlag
114115
}
115-
return ExtractEnv(ctx, scw.ScwActiveProfileEnv)
116+
117+
// Handle SCW_PROFILE env variable
118+
if env := ExtractEnv(ctx, scw.ScwActiveProfileEnv); env != "" {
119+
return env
120+
}
121+
122+
// Handle active_profile in config file
123+
configPath := ExtractConfigPath(ctx)
124+
config, err := scw.LoadConfigFromPath(configPath)
125+
if err == nil && config.ActiveProfile != nil {
126+
return *config.ActiveProfile
127+
}
128+
129+
// Return default profile name
130+
return scw.DefaultProfileName
116131
}
117132

118133
func ExtractHTTPClient(ctx context.Context) *http.Client {
@@ -139,7 +154,7 @@ func ReloadClient(ctx context.Context) error {
139154
if meta.isClientFromBootstrapConfig {
140155
return nil
141156
}
142-
meta.Client, err = createClient(meta.httpClient, meta.BuildInfo, "")
157+
meta.Client, err = createClient(meta.httpClient, meta.BuildInfo, ExtractProfileName(ctx))
143158
return err
144159
}
145160

internal/namespaces/config/commands.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7-
"path"
87
"reflect"
98

109
"github.com/scaleway/scaleway-sdk-go/validation"
@@ -27,6 +26,7 @@ func GetCommands() *core.Commands {
2726
configDumpCommand(),
2827
configProfileCommand(),
2928
configDeleteProfileCommand(),
29+
configActivateProfileCommand(),
3030
configResetCommand(),
3131
)
3232
}
@@ -128,7 +128,7 @@ func configGetCommand() *core.Command {
128128
},
129129
},
130130
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
131-
config, err := scw.LoadConfigFromPath(extractConfigPath(ctx))
131+
config, err := scw.LoadConfigFromPath(core.ExtractConfigPath(ctx))
132132
if err != nil {
133133
return nil, err
134134
}
@@ -254,7 +254,7 @@ The only allowed attributes are access_key, secret_key, default_organization_id,
254254
args := argsI.(*scw.Profile)
255255

256256
// Execute
257-
configPath := extractConfigPath(ctx)
257+
configPath := core.ExtractConfigPath(ctx)
258258
config, err := scw.LoadConfigFromPath(configPath)
259259
if err != nil {
260260
return nil, err
@@ -263,7 +263,7 @@ The only allowed attributes are access_key, secret_key, default_organization_id,
263263
// send_telemetry is the only key that is not in a profile but in the config object directly
264264
profileName := core.ExtractProfileName(ctx)
265265
profile := &config.Profile
266-
if profileName != "" {
266+
if profileName != scw.DefaultProfileName {
267267
var exist bool
268268
profile, exist = config.Profiles[profileName]
269269
if !exist {
@@ -319,7 +319,7 @@ func configUnsetCommand() *core.Command {
319319
},
320320
},
321321
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
322-
configPath := extractConfigPath(ctx)
322+
configPath := core.ExtractConfigPath(ctx)
323323
config, err := scw.LoadConfigFromPath(configPath)
324324
if err != nil {
325325
return nil, err
@@ -365,7 +365,7 @@ func configDumpCommand() *core.Command {
365365
},
366366
},
367367
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
368-
configPath := extractConfigPath(ctx)
368+
configPath := core.ExtractConfigPath(ctx)
369369
config, err := scw.LoadConfigFromPath(configPath)
370370
if err != nil {
371371
return nil, err
@@ -406,7 +406,7 @@ func configDeleteProfileCommand() *core.Command {
406406
},
407407
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
408408
profileName := argsI.(*configDeleteProfileArgs).Name
409-
configPath := extractConfigPath(ctx)
409+
configPath := core.ExtractConfigPath(ctx)
410410
config, err := scw.LoadConfigFromPath(configPath)
411411
if err != nil {
412412
return nil, err
@@ -428,6 +428,56 @@ func configDeleteProfileCommand() *core.Command {
428428
}
429429
}
430430

431+
// configActivateProfileCommand mark a profile as active
432+
func configActivateProfileCommand() *core.Command {
433+
type configActiveProfileArgs struct {
434+
ProfileName string
435+
}
436+
437+
return &core.Command{
438+
Short: `Mark a profile as active in the config file`,
439+
Namespace: "config",
440+
Resource: "profile",
441+
Verb: "activate",
442+
AllowAnonymousClient: true,
443+
ArgsType: reflect.TypeOf(configActiveProfileArgs{}),
444+
ArgSpecs: core.ArgSpecs{
445+
{
446+
Name: "profile-name",
447+
Required: true,
448+
Positional: true,
449+
AutoCompleteFunc: core.AutocompleteProfileName(),
450+
},
451+
},
452+
Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
453+
profileName := argsI.(*configActiveProfileArgs).ProfileName
454+
configPath := core.ExtractConfigPath(ctx)
455+
config, err := scw.LoadConfigFromPath(configPath)
456+
if err != nil {
457+
return nil, err
458+
}
459+
460+
if profileName == scw.DefaultProfileName {
461+
config.ActiveProfile = nil
462+
} else {
463+
if _, exists := config.Profiles[profileName]; !exists {
464+
return nil, unknownProfileError(profileName)
465+
}
466+
config.ActiveProfile = &profileName
467+
}
468+
469+
err = config.SaveTo(configPath)
470+
if err != nil {
471+
return nil, err
472+
}
473+
474+
return &core.SuccessResult{
475+
Message: fmt.Sprintf("successfully activate profile %s", profileName),
476+
}, nil
477+
},
478+
}
479+
}
480+
431481
// configResetCommand resets the config
432482
func configResetCommand() *core.Command {
433483
type configResetArgs struct{}
@@ -498,14 +548,11 @@ func getProfileKeys() []string {
498548
return keys
499549
}
500550

501-
// This func should be removes when core implement it
502-
func extractConfigPath(ctx context.Context) string {
503-
homeDir := core.ExtractUserHomeDir(ctx)
504-
return path.Join(homeDir, ".config", "scw", "config.yaml")
505-
}
506-
551+
// getProfile return a config profile by its name.
552+
// Warning: This return the profile pointer directly so it can be modified by commands.
553+
// For this reason we cannot rely on config.GetProfileByName method as it create a copy.
507554
func getProfile(config *scw.Config, profileName string) (*scw.Profile, error) {
508-
if profileName == "" {
555+
if profileName == scw.DefaultProfileName {
509556
return &config.Profile, nil
510557
}
511558
profile, exist := config.Profiles[profileName]

internal/namespaces/config/testdata/test-config-dump-command-simple.golden

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,31 @@ profiles:
2727

2828
🟩🟩🟩 JSON STDOUT 🟩🟩🟩
2929
{
30-
"AccessKey": "SCWXXXXXXXXXXXXXXXXX",
31-
"SecretKey": "11111111-1111-1111-1111-111111111111",
32-
"APIURL": null,
33-
"Insecure": true,
34-
"DefaultOrganizationID": "11111111-1111-1111-1111-111111111111",
35-
"DefaultProjectID": null,
36-
"DefaultRegion": "fr-par",
37-
"DefaultZone": "fr-par-1",
38-
"SendTelemetry": true,
39-
"ActiveProfile": null,
40-
"Profiles": {
30+
"access_key": "SCWXXXXXXXXXXXXXXXXX",
31+
"secret_key": "11111111-1111-1111-1111-111111111111",
32+
"insecure": true,
33+
"default_organization_id": "11111111-1111-1111-1111-111111111111",
34+
"default_region": "fr-par",
35+
"default_zone": "fr-par-1",
36+
"send_telemetry": true,
37+
"profiles": {
4138
"p1": {
42-
"AccessKey": "SCWP1XXXXXXXXXXXXXXX",
43-
"SecretKey": "11111111-1111-1111-1111-111111111111",
44-
"APIURL": "https://p1-mock-api-url.com",
45-
"Insecure": true,
46-
"DefaultOrganizationID": "11111111-1111-1111-1111-111111111111",
47-
"DefaultProjectID": null,
48-
"DefaultRegion": "fr-par",
49-
"DefaultZone": "fr-par-1",
50-
"SendTelemetry": null
39+
"access_key": "SCWP1XXXXXXXXXXXXXXX",
40+
"secret_key": "11111111-1111-1111-1111-111111111111",
41+
"api_url": "https://p1-mock-api-url.com",
42+
"insecure": true,
43+
"default_organization_id": "11111111-1111-1111-1111-111111111111",
44+
"default_region": "fr-par",
45+
"default_zone": "fr-par-1"
5146
},
5247
"p2": {
53-
"AccessKey": "SCWP2XXXXXXXXXXXXXXX",
54-
"SecretKey": "11111111-1111-1111-1111-111111111111",
55-
"APIURL": "https://p2-mock-api-url.com",
56-
"Insecure": true,
57-
"DefaultOrganizationID": "11111111-1111-1111-1111-111111111111",
58-
"DefaultProjectID": null,
59-
"DefaultRegion": "fr-par",
60-
"DefaultZone": "fr-par-1",
61-
"SendTelemetry": null
48+
"access_key": "SCWP2XXXXXXXXXXXXXXX",
49+
"secret_key": "11111111-1111-1111-1111-111111111111",
50+
"api_url": "https://p2-mock-api-url.com",
51+
"insecure": true,
52+
"default_organization_id": "11111111-1111-1111-1111-111111111111",
53+
"default_region": "fr-par",
54+
"default_zone": "fr-par-1"
6255
}
6356
}
6457
}

internal/namespaces/info/custom.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func infosRoot() *core.Command {
7575
BuildInfo: core.ExtractBuildInfo(ctx),
7676
Settings: []*setting{
7777
configPath(ctx),
78-
profile(ctx),
78+
profile(ctx, config),
7979
defaultRegion(ctx, config, profileName),
8080
defaultZone(ctx, config, profileName),
8181
defaultOrganizationID(ctx, config, profileName),
@@ -105,18 +105,22 @@ func configPath(ctx context.Context) *setting {
105105
return setting
106106
}
107107

108-
func profile(ctx context.Context) *setting {
108+
func profile(ctx context.Context, config *scw.Config) *setting {
109109
setting := &setting{
110110
Key: "profile",
111111
Value: core.ExtractProfileName(ctx),
112112
}
113+
113114
switch {
114115
case core.ExtractProfileFlag(ctx) != "":
115116
setting.Origin = "flag --profile/-p"
116117
setting.Value = core.ExtractProfileFlag(ctx)
117118
case core.ExtractEnv(ctx, scw.ScwActiveProfileEnv) != "":
118119
setting.Origin = fmt.Sprintf("env (%s)", scw.ScwActiveProfileEnv)
119120
setting.Value = core.ExtractEnv(ctx, scw.ScwActiveProfileEnv)
121+
case config != nil && config.ActiveProfile != nil:
122+
setting.Origin = "active_profile in config file"
123+
setting.Value = *config.ActiveProfile
120124
default:
121125
setting.Origin = ""
122126
}

0 commit comments

Comments
 (0)