Skip to content

Commit 8c7c75e

Browse files
authored
Merge pull request #521 from fflorens/fork/add_config
Add --config option for arbitrary config file
2 parents 7b12c96 + 9a205fb commit 8c7c75e

File tree

10 files changed

+148
-24
lines changed

10 files changed

+148
-24
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@ profile.out
1212
.DS_STORE
1313
.vendor
1414
/.vscode
15+
16+
# Vim Swapfiles
17+
[._]*.s[a-v][a-z]
18+
[._]*.sw[a-p]
19+
[._]s[a-rt-v][a-z]
20+
[._]ss[a-gi-z]
21+
[._]sw[a-p]

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ Options:
181181
--sensitive=false Show sensitive data in outputs, i.e. API Token/Organization
182182
-v, --version=false Print version information and quit
183183
--region=par1 Change the default region (e.g. ams1)
184+
-c, --config=<config path> Option config file path (default to ~/.scwrc)
184185

185186
Commands:
186187
help help of the scw command line
@@ -1214,6 +1215,8 @@ $ scw inspect myserver | jq '.[0].public_ip.address'
12141215

12151216
### v1.17+dev (unreleased)
12161217

1218+
* Add the ability to use an arbitrary config file using `-c`,`--config` option.
1219+
* Add the ability to specify a token and organiationId from the environnement using `SCW_TOKEN` and `SCW_ORGANIZATION` variables.
12171220
* This is the current development version. Update below with your changes. Remove this line when releasing the package.
12181221

12191222
View full [commits list](https://github.com/scaleway/scaleway-cli/compare/v1.17...master)

pkg/cli/cmd_help.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Options:
4444
--sensitive=false Show sensitive data in outputs, i.e. API Token/Organization
4545
-v, --version=false Print version information and quit
4646
--region=par1 Change the default region (e.g. ams1)
47+
-c, --config=<config path> Option config file path (default to ~/.scwrc)
4748
4849
Commands:
4950
{{range .}}{{if not .Hidden}} {{.Name | printf "%-9s"}} {{.Description}}

pkg/cli/command.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,19 @@ type Command struct {
5151
// API is the interface used to communicate with Scaleway's API
5252
API *api.ScalewayAPI
5353

54+
// ConfigPath for -c, --config parameter
55+
ConfigPath string
56+
5457
streams *commands.Streams
5558
}
5659

5760
// GetContext returns a standard context, with real stdin, stdout, stderr, a configured API and raw arguments
5861
func (c *Command) GetContext(rawArgs []string) commands.CommandContext {
5962
ctx := commands.CommandContext{
60-
Env: os.Environ(),
61-
RawArgs: rawArgs,
62-
API: c.API,
63+
Env: os.Environ(),
64+
RawArgs: rawArgs,
65+
API: c.API,
66+
ConfigPath: c.ConfigPath,
6367
}
6468

6569
if c.streams != nil {

pkg/cli/main.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var (
3333
flQuiet = flag.Bool([]string{"q", "-quiet"}, false, "Enable quiet mode")
3434
flSensitive = flag.Bool([]string{"-sensitive"}, false, "Show sensitive data in outputs, i.e. API Token/Organization")
3535
flRegion = flag.String([]string{"-region"}, "par1", "Change the default region (e.g. ams1)")
36+
flConfig = flag.String([]string{"c", "-config"}, "", "Optional config file path")
3637
)
3738

3839
// Start is the entrypoint
@@ -47,7 +48,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
4748
}
4849
flag.CommandLine.Parse(rawArgs)
4950

50-
config, cfgErr := config.GetConfig()
51+
config, cfgErr := config.GetConfig(*flConfig)
5152
if cfgErr != nil && !os.IsNotExist(cfgErr) {
5253
return 1, fmt.Errorf("unable to open .scwrc config file: %v", cfgErr)
5354
}
@@ -93,12 +94,13 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
9394
if err != nil {
9495
return 1, fmt.Errorf("usage: scw %s", cmd.UsageLine)
9596
}
97+
cmd.ConfigPath = *flConfig
9698
switch cmd.Name() {
9799
case "login", "help", "version":
98100
// commands that don't need API
99101
case "_userdata":
100102
// commands that may need API
101-
api, _ := getScalewayAPI(*flRegion)
103+
api, _ := getScalewayAPI(*flRegion, *flConfig)
102104
cmd.API = api
103105
default:
104106
// commands that do need API
@@ -109,7 +111,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
109111
return 1, nil
110112
}
111113
}
112-
api, errGet := getScalewayAPI(*flRegion)
114+
api, errGet := getScalewayAPI(*flRegion, *flConfig)
113115
if errGet != nil {
114116
return 1, fmt.Errorf("unable to initialize scw api: %v", errGet)
115117
}
@@ -118,7 +120,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
118120
// clean cache between versions
119121
if cmd.API != nil && config.Version != scwversion.VERSION {
120122
cmd.API.ClearCache()
121-
config.Save()
123+
config.Save(*flConfig)
122124
}
123125
err = cmd.Exec(cmd, cmd.Flag.Args())
124126
switch err {
@@ -140,9 +142,9 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
140142
}
141143

142144
// getScalewayAPI returns a ScalewayAPI using the user config file
143-
func getScalewayAPI(region string) (*api.ScalewayAPI, error) {
145+
func getScalewayAPI(region string, configPath string) (*api.ScalewayAPI, error) {
144146
// We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object
145-
config, err := config.GetConfig()
147+
config, err := config.GetConfig(configPath)
146148
if err != nil {
147149
return nil, err
148150
}

pkg/commands/command.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ type Streams struct {
2323
type CommandContext struct {
2424
Streams
2525

26-
Env []string
27-
RawArgs []string
28-
API *api.ScalewayAPI
26+
Env []string
27+
RawArgs []string
28+
API *api.ScalewayAPI
29+
ConfigPath string
2930
}
3031

3132
// Getenv returns the equivalent of os.Getenv for the CommandContext.Env

pkg/commands/login.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func uploadSSHKeys(apiConnection *api.ScalewayAPI, newKey string) {
268268

269269
// RunLogin is the handler for 'scw login'
270270
func RunLogin(ctx CommandContext, args LoginArgs) error {
271-
if config, cfgErr := config.GetConfig(); cfgErr == nil {
271+
if config, cfgErr := config.GetConfig(ctx.ConfigPath); cfgErr == nil {
272272
if TestConnection, err := api.NewScalewayAPI(config.Organization, config.Token, scwversion.UserAgent(), "", clilogger.SetupLogger); err == nil {
273273
if user, err := TestConnection.GetUser(); err == nil {
274274
fmt.Println("You are already logged as", user.Fullname)
@@ -317,7 +317,7 @@ func RunLogin(ctx CommandContext, args LoginArgs) error {
317317
fmt.Println("You can list your existing servers using `scw ps` or create a new one using `scw run ubuntu-xenial`.")
318318
fmt.Println("You can get a list of all available commands using `scw -h` and get more usage examples on github.com/scaleway/scaleway-cli.")
319319
fmt.Println("Happy cloud riding.")
320-
return cfg.Save()
320+
return cfg.Save(ctx.ConfigPath)
321321
}
322322

323323
func promptUser(prompt string, output *string, echo bool) error {

pkg/commands/test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func RealAPIContext() *CommandContext {
3939
if os.Getenv("TEST_WITH_REAL_API") == "0" {
4040
return nil
4141
}
42-
config, err := config.GetConfig()
42+
config, err := config.GetConfig("")
4343
if err != nil {
4444
logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err)
4545
return nil

pkg/config/config.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os"
1414
"path/filepath"
1515
"runtime"
16+
"strings"
1617

1718
"github.com/scaleway/scaleway-cli/pkg/scwversion"
1819
)
@@ -30,10 +31,14 @@ type Config struct {
3031
}
3132

3233
// Save write the config file
33-
func (c *Config) Save() error {
34-
scwrcPath, err := GetConfigFilePath()
35-
if err != nil {
36-
return fmt.Errorf("Unable to get scwrc config file path: %s", err)
34+
func (c *Config) Save(configPath string) error {
35+
scwrcPath := configPath
36+
var err error
37+
if configPath == "" {
38+
scwrcPath, err = GetConfigFilePath()
39+
if err != nil {
40+
return fmt.Errorf("Unable to get scwrc config file path: %s", err)
41+
}
3742
}
3843
scwrc, err := os.OpenFile(scwrcPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600)
3944
if err != nil {
@@ -49,10 +54,25 @@ func (c *Config) Save() error {
4954
}
5055

5156
// GetConfig returns the Scaleway CLI config file for the current user
52-
func GetConfig() (*Config, error) {
53-
scwrcPath, err := GetConfigFilePath()
54-
if err != nil {
55-
return nil, err
57+
func GetConfig(scwrcPath string) (*Config, error) {
58+
var err error
59+
60+
orgid := os.Getenv("SCW_ORGANIZATION")
61+
token := os.Getenv("SCW_TOKEN")
62+
if token != "" && orgid != "" {
63+
cfg := Config{
64+
Organization: strings.Trim(orgid, "\n"),
65+
Token: strings.Trim(token, "\n"),
66+
Version: scwversion.VERSION,
67+
}
68+
return &cfg, nil
69+
}
70+
71+
if scwrcPath == "" {
72+
scwrcPath, err = GetConfigFilePath()
73+
if err != nil {
74+
return nil, err
75+
}
5676
}
5777

5878
// Don't check permissions on Windows, Go knows nothing about them on this platform
@@ -63,7 +83,7 @@ func GetConfig() (*Config, error) {
6383
if errStat == nil {
6484
perm := stat.Mode().Perm()
6585
if perm&0066 != 0 {
66-
return nil, fmt.Errorf("permissions %#o for .scwrc are too open", perm)
86+
return nil, fmt.Errorf("permissions %#o for %s are too open", perm, scwrcPath)
6787
}
6888
}
6989
}
@@ -83,6 +103,10 @@ func GetConfig() (*Config, error) {
83103

84104
// GetConfigFilePath returns the path to the Scaleway CLI config file
85105
func GetConfigFilePath() (string, error) {
106+
path := os.Getenv("SCW_CONFIG_PATH")
107+
if path != "" {
108+
return path, nil
109+
}
86110
path, err := GetHomeDir()
87111
if err != nil {
88112
return "", err

pkg/config/config_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55
package config
66

77
import (
8+
"math/rand"
9+
"os"
10+
"strconv"
811
"strings"
912
"testing"
13+
"time"
1014

15+
"github.com/scaleway/scaleway-cli/pkg/scwversion"
1116
. "github.com/smartystreets/goconvey/convey"
1217
)
1318

@@ -23,6 +28,83 @@ func TestGetConfigFilePath(t *testing.T) {
2328
})
2429
}
2530

31+
func TestGetConfigFilePathEnv(t *testing.T) {
32+
Convey("Testing GetConfigFilePath() with env variable", t, func() {
33+
os.Setenv("SCW_CONFIG_PATH", "./config_testdata1")
34+
configPath, err := GetConfigFilePath()
35+
So(err, ShouldBeNil)
36+
So(configPath, ShouldEqual, "./config_testdata1")
37+
os.Unsetenv("SCW_CONFIG_PATH")
38+
})
39+
}
40+
41+
func TestGetConfig(t *testing.T) {
42+
Convey("Testing GetConfig() with and without env variable", t, func() {
43+
rand.Seed(time.Now().UTC().UnixNano())
44+
randOrg := strconv.FormatInt(rand.Int63(), 16)
45+
randToken := strconv.FormatInt(rand.Int63(), 16)
46+
cfg := &Config{
47+
Organization: strings.Trim(randOrg, "\n"),
48+
Token: strings.Trim(randToken, "\n"),
49+
}
50+
os.Setenv("SCW_CONFIG_PATH", "./config_testdata1")
51+
err := cfg.Save("")
52+
So(err, ShouldBeNil)
53+
cfg, err = GetConfig("./config_testdata1")
54+
So(cfg.Organization, ShouldEqual, randOrg)
55+
So(cfg.Token, ShouldEqual, randToken)
56+
os.Unsetenv("SCW_CONFIG_PATH")
57+
cfg, err = GetConfig("./config_testdata1")
58+
So(err, ShouldBeNil)
59+
So(cfg.Organization, ShouldEqual, randOrg)
60+
So(cfg.Token, ShouldEqual, randToken)
61+
os.Setenv("SCW_ORGANIZATION", randOrg)
62+
os.Setenv("SCW_TOKEN", randToken)
63+
cfg, err = GetConfig("")
64+
So(err, ShouldBeNil)
65+
So(cfg.Organization, ShouldEqual, randOrg)
66+
So(cfg.Token, ShouldEqual, randToken)
67+
os.Unsetenv("SCW_ORGANIZATION")
68+
os.Unsetenv("SCW_TOKEN")
69+
})
70+
}
71+
72+
func TestSave(t *testing.T) {
73+
Convey("Testing SaveConfig() with and without env variable", t, func() {
74+
os.Setenv("SCW_CONFIG_PATH", "./config_testdata2")
75+
rand.Seed(time.Now().UTC().UnixNano())
76+
randOrg := strconv.FormatInt(rand.Int63(), 16)
77+
randToken := strconv.FormatInt(rand.Int63(), 16)
78+
cfg := &Config{
79+
Organization: strings.Trim(randOrg, "\n"),
80+
Token: strings.Trim(randToken, "\n"),
81+
}
82+
err := cfg.Save("")
83+
So(err, ShouldBeNil)
84+
cfg, err = GetConfig("")
85+
So(err, ShouldBeNil)
86+
So(cfg.Version, ShouldEqual, scwversion.VERSION)
87+
So(cfg.Organization, ShouldEqual, randOrg)
88+
So(cfg.Token, ShouldEqual, randToken)
89+
os.Unsetenv("SCW_CONFIG_PATH")
90+
91+
randOrg = strconv.FormatInt(rand.Int63(), 16)
92+
randToken = strconv.FormatInt(rand.Int63(), 16)
93+
cfg = &Config{
94+
Organization: strings.Trim(randOrg, "\n"),
95+
Token: strings.Trim(randToken, "\n"),
96+
}
97+
err = cfg.Save("./config_testdata2")
98+
So(err, ShouldBeNil)
99+
cfg, err = GetConfig("./config_testdata2")
100+
So(err, ShouldBeNil)
101+
So(cfg.Version, ShouldEqual, scwversion.VERSION)
102+
So(cfg.Organization, ShouldEqual, randOrg)
103+
So(cfg.Token, ShouldEqual, randToken)
104+
105+
})
106+
}
107+
26108
func TestGetHomeDir(t *testing.T) {
27109
Convey("Testing GetHomeDir()", t, func() {
28110
homedir, err := GetHomeDir()

0 commit comments

Comments
 (0)