Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ profile.out
.DS_STORE
.vendor
/.vscode

# Vim Swapfiles
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ Options:
--sensitive=false Show sensitive data in outputs, i.e. API Token/Organization
-v, --version=false Print version information and quit
--region=par1 Change the default region (e.g. ams1)
-c, --config=<config path> Option config file path (default to ~/.scwrc)

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

### v1.17+dev (unreleased)

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

View full [commits list](https://github.com/scaleway/scaleway-cli/compare/v1.17...master)
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/cmd_help.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Options:
--sensitive=false Show sensitive data in outputs, i.e. API Token/Organization
-v, --version=false Print version information and quit
--region=par1 Change the default region (e.g. ams1)
-c, --config=<config path> Option config file path (default to ~/.scwrc)

Commands:
{{range .}}{{if not .Hidden}} {{.Name | printf "%-9s"}} {{.Description}}
Expand Down
10 changes: 7 additions & 3 deletions pkg/cli/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,19 @@ type Command struct {
// API is the interface used to communicate with Scaleway's API
API *api.ScalewayAPI

// ConfigPath for -c, --config parameter
ConfigPath string

streams *commands.Streams
}

// GetContext returns a standard context, with real stdin, stdout, stderr, a configured API and raw arguments
func (c *Command) GetContext(rawArgs []string) commands.CommandContext {
ctx := commands.CommandContext{
Env: os.Environ(),
RawArgs: rawArgs,
API: c.API,
Env: os.Environ(),
RawArgs: rawArgs,
API: c.API,
ConfigPath: c.ConfigPath,
}

if c.streams != nil {
Expand Down
14 changes: 8 additions & 6 deletions pkg/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
flQuiet = flag.Bool([]string{"q", "-quiet"}, false, "Enable quiet mode")
flSensitive = flag.Bool([]string{"-sensitive"}, false, "Show sensitive data in outputs, i.e. API Token/Organization")
flRegion = flag.String([]string{"-region"}, "par1", "Change the default region (e.g. ams1)")
flConfig = flag.String([]string{"c", "-config"}, "", "Optional config file path")
)

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

config, cfgErr := config.GetConfig()
config, cfgErr := config.GetConfig(*flConfig)
if cfgErr != nil && !os.IsNotExist(cfgErr) {
return 1, fmt.Errorf("unable to open .scwrc config file: %v", cfgErr)
}
Expand Down Expand Up @@ -93,12 +94,13 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
if err != nil {
return 1, fmt.Errorf("usage: scw %s", cmd.UsageLine)
}
cmd.ConfigPath = *flConfig
switch cmd.Name() {
case "login", "help", "version":
// commands that don't need API
case "_userdata":
// commands that may need API
api, _ := getScalewayAPI(*flRegion)
api, _ := getScalewayAPI(*flRegion, *flConfig)
cmd.API = api
default:
// commands that do need API
Expand All @@ -109,7 +111,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
return 1, nil
}
}
api, errGet := getScalewayAPI(*flRegion)
api, errGet := getScalewayAPI(*flRegion, *flConfig)
if errGet != nil {
return 1, fmt.Errorf("unable to initialize scw api: %v", errGet)
}
Expand All @@ -118,7 +120,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
// clean cache between versions
if cmd.API != nil && config.Version != scwversion.VERSION {
cmd.API.ClearCache()
config.Save()
config.Save(*flConfig)
}
err = cmd.Exec(cmd, cmd.Flag.Args())
switch err {
Expand All @@ -140,9 +142,9 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) {
}

// getScalewayAPI returns a ScalewayAPI using the user config file
func getScalewayAPI(region string) (*api.ScalewayAPI, error) {
func getScalewayAPI(region string, configPath string) (*api.ScalewayAPI, error) {
// We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object
config, err := config.GetConfig()
config, err := config.GetConfig(configPath)
if err != nil {
return nil, err
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/commands/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ type Streams struct {
type CommandContext struct {
Streams

Env []string
RawArgs []string
API *api.ScalewayAPI
Env []string
RawArgs []string
API *api.ScalewayAPI
ConfigPath string
}

// Getenv returns the equivalent of os.Getenv for the CommandContext.Env
Expand Down
4 changes: 2 additions & 2 deletions pkg/commands/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func uploadSSHKeys(apiConnection *api.ScalewayAPI, newKey string) {

// RunLogin is the handler for 'scw login'
func RunLogin(ctx CommandContext, args LoginArgs) error {
if config, cfgErr := config.GetConfig(); cfgErr == nil {
if config, cfgErr := config.GetConfig(ctx.ConfigPath); cfgErr == nil {
if TestConnection, err := api.NewScalewayAPI(config.Organization, config.Token, scwversion.UserAgent(), "", clilogger.SetupLogger); err == nil {
if user, err := TestConnection.GetUser(); err == nil {
fmt.Println("You are already logged as", user.Fullname)
Expand Down Expand Up @@ -317,7 +317,7 @@ func RunLogin(ctx CommandContext, args LoginArgs) error {
fmt.Println("You can list your existing servers using `scw ps` or create a new one using `scw run ubuntu-xenial`.")
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.")
fmt.Println("Happy cloud riding.")
return cfg.Save()
return cfg.Save(ctx.ConfigPath)
}

func promptUser(prompt string, output *string, echo bool) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func RealAPIContext() *CommandContext {
if os.Getenv("TEST_WITH_REAL_API") == "0" {
return nil
}
config, err := config.GetConfig()
config, err := config.GetConfig("")
if err != nil {
logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err)
return nil
Expand Down
42 changes: 33 additions & 9 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"

"github.com/scaleway/scaleway-cli/pkg/scwversion"
)
Expand All @@ -30,10 +31,14 @@ type Config struct {
}

// Save write the config file
func (c *Config) Save() error {
scwrcPath, err := GetConfigFilePath()
if err != nil {
return fmt.Errorf("Unable to get scwrc config file path: %s", err)
func (c *Config) Save(configPath string) error {
scwrcPath := configPath
var err error
if configPath == "" {
scwrcPath, err = GetConfigFilePath()
if err != nil {
return fmt.Errorf("Unable to get scwrc config file path: %s", err)
}
}
scwrc, err := os.OpenFile(scwrcPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600)
if err != nil {
Expand All @@ -49,10 +54,25 @@ func (c *Config) Save() error {
}

// GetConfig returns the Scaleway CLI config file for the current user
func GetConfig() (*Config, error) {
scwrcPath, err := GetConfigFilePath()
if err != nil {
return nil, err
func GetConfig(scwrcPath string) (*Config, error) {
var err error

orgid := os.Getenv("SCW_ORGANIZATION")
token := os.Getenv("SCW_TOKEN")
if token != "" && orgid != "" {
cfg := Config{
Organization: strings.Trim(orgid, "\n"),
Token: strings.Trim(token, "\n"),
Version: scwversion.VERSION,
}
return &cfg, nil
}

if scwrcPath == "" {
scwrcPath, err = GetConfigFilePath()
if err != nil {
return nil, err
}
}

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

// GetConfigFilePath returns the path to the Scaleway CLI config file
func GetConfigFilePath() (string, error) {
path := os.Getenv("SCW_CONFIG_PATH")
if path != "" {
return path, nil
}
path, err := GetHomeDir()
if err != nil {
return "", err
Expand Down
82 changes: 82 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
package config

import (
"math/rand"
"os"
"strconv"
"strings"
"testing"
"time"

"github.com/scaleway/scaleway-cli/pkg/scwversion"
. "github.com/smartystreets/goconvey/convey"
)

Expand All @@ -23,6 +28,83 @@ func TestGetConfigFilePath(t *testing.T) {
})
}

func TestGetConfigFilePathEnv(t *testing.T) {
Convey("Testing GetConfigFilePath() with env variable", t, func() {
os.Setenv("SCW_CONFIG_PATH", "./config_testdata1")
configPath, err := GetConfigFilePath()
So(err, ShouldBeNil)
So(configPath, ShouldEqual, "./config_testdata1")
os.Unsetenv("SCW_CONFIG_PATH")
})
}

func TestGetConfig(t *testing.T) {
Convey("Testing GetConfig() with and without env variable", t, func() {
rand.Seed(time.Now().UTC().UnixNano())
randOrg := strconv.FormatInt(rand.Int63(), 16)
randToken := strconv.FormatInt(rand.Int63(), 16)
cfg := &Config{
Organization: strings.Trim(randOrg, "\n"),
Token: strings.Trim(randToken, "\n"),
}
os.Setenv("SCW_CONFIG_PATH", "./config_testdata1")
err := cfg.Save("")
So(err, ShouldBeNil)
cfg, err = GetConfig("./config_testdata1")
So(cfg.Organization, ShouldEqual, randOrg)
So(cfg.Token, ShouldEqual, randToken)
os.Unsetenv("SCW_CONFIG_PATH")
cfg, err = GetConfig("./config_testdata1")
So(err, ShouldBeNil)
So(cfg.Organization, ShouldEqual, randOrg)
So(cfg.Token, ShouldEqual, randToken)
os.Setenv("SCW_ORGANIZATION", randOrg)
os.Setenv("SCW_TOKEN", randToken)
cfg, err = GetConfig("")
So(err, ShouldBeNil)
So(cfg.Organization, ShouldEqual, randOrg)
So(cfg.Token, ShouldEqual, randToken)
os.Unsetenv("SCW_ORGANIZATION")
os.Unsetenv("SCW_TOKEN")
})
}

func TestSave(t *testing.T) {
Convey("Testing SaveConfig() with and without env variable", t, func() {
os.Setenv("SCW_CONFIG_PATH", "./config_testdata2")
rand.Seed(time.Now().UTC().UnixNano())
randOrg := strconv.FormatInt(rand.Int63(), 16)
randToken := strconv.FormatInt(rand.Int63(), 16)
cfg := &Config{
Organization: strings.Trim(randOrg, "\n"),
Token: strings.Trim(randToken, "\n"),
}
err := cfg.Save("")
So(err, ShouldBeNil)
cfg, err = GetConfig("")
So(err, ShouldBeNil)
So(cfg.Version, ShouldEqual, scwversion.VERSION)
So(cfg.Organization, ShouldEqual, randOrg)
So(cfg.Token, ShouldEqual, randToken)
os.Unsetenv("SCW_CONFIG_PATH")

randOrg = strconv.FormatInt(rand.Int63(), 16)
randToken = strconv.FormatInt(rand.Int63(), 16)
cfg = &Config{
Organization: strings.Trim(randOrg, "\n"),
Token: strings.Trim(randToken, "\n"),
}
err = cfg.Save("./config_testdata2")
So(err, ShouldBeNil)
cfg, err = GetConfig("./config_testdata2")
So(err, ShouldBeNil)
So(cfg.Version, ShouldEqual, scwversion.VERSION)
So(cfg.Organization, ShouldEqual, randOrg)
So(cfg.Token, ShouldEqual, randToken)

})
}

func TestGetHomeDir(t *testing.T) {
Convey("Testing GetHomeDir()", t, func() {
homedir, err := GetHomeDir()
Expand Down