diff --git a/cmd/scw/testdata/test-all-usage-account-ssh-key-init-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-account-ssh-key-init-usage.stderr.golden new file mode 100644 index 0000000000..68995b3a96 --- /dev/null +++ b/cmd/scw/testdata/test-all-usage-account-ssh-key-init-usage.stderr.golden @@ -0,0 +1,12 @@ +Initiliaze SSH key. + +USAGE: + scw account ssh-key init + +FLAGS: + -h, --help help for init + +GLOBAL FLAGS: + -D, --debug Enable debug mode + -o, --output string Output format: json or human + -p, --profile string The config profile to use diff --git a/cmd/scw/testdata/test-all-usage-account-ssh-key-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-account-ssh-key-usage.stderr.golden new file mode 100644 index 0000000000..03897dacd0 --- /dev/null +++ b/cmd/scw/testdata/test-all-usage-account-ssh-key-usage.stderr.golden @@ -0,0 +1,17 @@ +Manage SSH key. + +USAGE: + scw account ssh-key + +AVAILABLE COMMANDS: + init Initiliaze SSH key + +FLAGS: + -h, --help help for ssh-key + +GLOBAL FLAGS: + -D, --debug Enable debug mode + -o, --output string Output format: json or human + -p, --profile string The config profile to use + +Use "scw account ssh-key [command] --help" for more information about a command. diff --git a/cmd/scw/testdata/test-main-usage-usage.stderr.golden b/cmd/scw/testdata/test-main-usage-usage.stderr.golden index 4c4a008f39..1edf651557 100644 --- a/cmd/scw/testdata/test-main-usage-usage.stderr.golden +++ b/cmd/scw/testdata/test-main-usage-usage.stderr.golden @@ -8,6 +8,7 @@ AVAILABLE COMMANDS: marketplace Marketplace API init Initialize the config config Config file management + account Manage SSH key autocomplete Install autocompletion script version Display cli version help Help about any command diff --git a/go.sum b/go.sum index 2abdc01500..fe7ad4bf0f 100644 --- a/go.sum +++ b/go.sum @@ -8,9 +8,11 @@ github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1p github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/certifi/gocertifi v0.0.0-20200104152315-a6d78f326758 h1:epkmRmrjzR0Km6DrVMk/ZQwDdMY2w31I3kQT91DbxS0= github.com/certifi/gocertifi v0.0.0-20200104152315-a6d78f326758/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -35,9 +37,12 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= @@ -53,14 +58,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200226081452-7bdc7806b035 h1:PGuOvUWTkkNFh/cN4IIR45YaFWfbLgijvarr7mBqUFg= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200226081452-7bdc7806b035/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200316165553-4378e6801116 h1:vHShZ6sgpWTTN9xnrBMOwyjVbA8D7xcGOGyCfgkXJTU= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200316165553-4378e6801116/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200317080505-8dd22a160c48 h1:LX+2i/gWH+X013HCisZ3/dB8cF6h2wdn60iemwe92D4= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200317080505-8dd22a160c48/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200317142453-2a0af47d63be h1:FX+wAaLvpWFqgQIROo0f2iTefe+phJWFUBuEG2HPDW8= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200317142453-2a0af47d63be/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200319151040-8db41486782b h1:MHFYWCAxh6gqGEzdht6Zx8r917FDXyNDo5mhN40Q9X4= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200319151040-8db41486782b/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.6.0.20200324130151-e0b5fd35d2c8 h1:/dunZoNLCRQF6DCpSiyK7vXPsR7aWTJVICUXxkakGc8= @@ -95,6 +92,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= diff --git a/internal/core/client.go b/internal/core/client.go index a6a6fe64a8..3cf1efab2e 100644 --- a/internal/core/client.go +++ b/internal/core/client.go @@ -24,7 +24,7 @@ func createClient(meta *meta) (*scw.Client, error) { return nil, err } - if meta.ProfileFlag != "" { + if meta != nil && meta.ProfileFlag != "" { config.ActiveProfile = scw.StringPtr(meta.ProfileFlag) } @@ -53,8 +53,12 @@ func createClient(meta *meta) (*scw.Client, error) { } } + userAgent := "scaleway-cli/" + if meta != nil { + userAgent += meta.BuildInfo.Version.String() + } opts := []scw.ClientOption{ - scw.WithUserAgent("scaleway-cli/" + meta.BuildInfo.Version.String()), + scw.WithUserAgent(userAgent), scw.WithProfile(profile), } diff --git a/internal/core/context.go b/internal/core/context.go index bec8fc3c8a..999ad742f8 100644 --- a/internal/core/context.go +++ b/internal/core/context.go @@ -60,6 +60,18 @@ func ExtractClient(ctx context.Context) *scw.Client { return extractMeta(ctx).Client } +func ExtractClientOrCreate(ctx context.Context) (*scw.Client, error) { + client := ExtractClient(ctx) + if client == nil { + var err error + client, err = createClient(nil) + if err != nil { + return nil, err + } + } + return client, nil +} + func ExtractBuildInfo(ctx context.Context) *BuildInfo { return extractMeta(ctx).BuildInfo } diff --git a/internal/namespaces/account/custom.go b/internal/namespaces/account/custom.go new file mode 100644 index 0000000000..e871b410b8 --- /dev/null +++ b/internal/namespaces/account/custom.go @@ -0,0 +1,112 @@ +package account + +import ( + "context" + "io/ioutil" + "os" + "path" + "reflect" + "strings" + + "github.com/scaleway/scaleway-cli/internal/args" + "github.com/scaleway/scaleway-cli/internal/core" + "github.com/scaleway/scaleway-cli/internal/interactive" + account "github.com/scaleway/scaleway-sdk-go/api/account/v2alpha1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +func GetCommands() *core.Commands { + return core.NewCommands( + sshKeyCommand(), + initCommand(), + ) +} + +func sshKeyCommand() *core.Command { + return &core.Command{ + Short: `Manage SSH key`, + Long: `Manage SSH key.`, + Namespace: "account", + Resource: "ssh-key", + NoClient: true, + ArgsType: reflect.TypeOf(args.RawArgs{}), + } +} + +func initCommand() *core.Command { + return &core.Command{ + Short: `Initiliaze SSH key`, + Long: `Initiliaze SSH key.`, + Namespace: "account", + Resource: "ssh-key", + Verb: "init", + ArgsType: reflect.TypeOf(args.RawArgs{}), + ArgSpecs: core.ArgSpecs{}, + Run: InitRun, + } +} + +func InitRun(ctx context.Context, argsI interface{}) (i interface{}, e error) { + // Explanation + _, _ = interactive.Println("An SSH key is required if you want to connect to a server. More info at https://www.scaleway.com/en/docs/configure-new-ssh-key/") + + // Get default SSH key locally + relativePath := path.Join(".ssh", "id_rsa.pub") + filename := path.Join(core.ExtractEnv(ctx, "HOME"), relativePath) + shortenedFilename := "~/" + relativePath + localSSHKeyContent, err := ioutil.ReadFile(filename) + + addKeyInstructions := `scw account ssh-key add name=my-key key="$(cat path/to/my/key.pub)"` + + // Early exit if key is not present locally + if os.IsNotExist(err) { + return nil, sshKeyNotFound(filename, addKeyInstructions) + } else if err != nil { + return nil, err + } + + // Get all SSH keys from Scaleway + client, err := core.ExtractClientOrCreate(ctx) + if err != nil { + return nil, err + } + + api := account.NewAPI(client) + listSSHKeysResponse, err := api.ListSSHKeys(&account.ListSSHKeysRequest{}, scw.WithAllPages()) + if err != nil { + return nil, err + } + + // Early exit if the SSH key is present locally and on Scaleway + for _, SSHKey := range listSSHKeysResponse.SSHKeys { + if strings.TrimSpace(SSHKey.PublicKey) == strings.TrimSpace(string(localSSHKeyContent)) { + return nil, sshKeyAlreadyPresent(shortenedFilename) + } + } + + // Ask user + addSSHKey, err := interactive.PromptBoolWithConfig(&interactive.PromptBoolConfig{ + Prompt: "We found an SSH key in " + shortenedFilename + ". Do you want to add it to your Scaleway account ?", + DefaultValue: true, + }) + if err != nil { + return nil, err + } + + // Early exit if user doesn't want to add the key + if !addSSHKey { + return nil, installationCanceled(addKeyInstructions) + } + + // Add key + _, err = api.CreateSSHKey(&account.CreateSSHKeyRequest{ + PublicKey: string(localSSHKeyContent), + }) + if err != nil { + return nil, err + } + + return core.SuccessResult{ + Message: "Key " + shortenedFilename + " successfully added", + }, nil +} diff --git a/internal/namespaces/account/custom_test.go b/internal/namespaces/account/custom_test.go new file mode 100644 index 0000000000..a405976d49 --- /dev/null +++ b/internal/namespaces/account/custom_test.go @@ -0,0 +1,39 @@ +package account + +import ( + "io/ioutil" + "os" + "path" + "testing" + + "github.com/scaleway/scaleway-cli/internal/core" +) + +func Test_initCommand(t *testing.T) { + t.Run("simple", core.Test(&core.TestConfig{ + Commands: GetCommands(), + BeforeFunc: func(ctx *core.BeforeFuncCtx) error { + tmpDir := os.TempDir() + pathToPublicKey := path.Join(tmpDir, ".ssh", "id_rsa.pub") + _, err := os.Stat(pathToPublicKey) + if err != nil { + content := "ssh-rsa AAAAB3NzaC1yc2EAAAEDAQABAAABAQC/wF8Q8LjEexuWDc8TfKmWVZ1CiiHK6KvO0E/Rk9+d6ssqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb email" + err := os.MkdirAll(path.Join(tmpDir, ".ssh"), 0755) + if err != nil { + return err + } + err = ioutil.WriteFile(pathToPublicKey, []byte(content), 0644) + return err + } + return err + }, + Cmd: `scw account ssh-key init with-ssh-key=true`, + Check: core.TestCheckCombine( + core.TestCheckGolden(), + core.TestCheckExitCode(0), + ), + OverrideEnv: map[string]string{ + "HOME": os.TempDir(), + }, + })) +} diff --git a/internal/namespaces/account/error.go b/internal/namespaces/account/error.go new file mode 100644 index 0000000000..5641753b18 --- /dev/null +++ b/internal/namespaces/account/error.go @@ -0,0 +1,27 @@ +package account + +import ( + "fmt" + + "github.com/scaleway/scaleway-cli/internal/core" +) + +func installationCanceled(addKeyInstructions string) *core.CliError { + return &core.CliError{ + Err: fmt.Errorf("installation of SSH key canceled"), + Hint: "Add it later using " + addKeyInstructions, + } +} + +func sshKeyNotFound(filename string, addKeyInstructions string) *core.CliError { + return &core.CliError{ + Err: fmt.Errorf("could not find an ssh key at " + filename), + Hint: "Add one later using " + addKeyInstructions, + } +} + +func sshKeyAlreadyPresent(shortenedFilename string) *core.CliError { + return &core.CliError{ + Err: fmt.Errorf("key " + shortenedFilename + " is already present on your scaleway account"), + } +} diff --git a/internal/namespaces/account/testdata/test-init-command-simple.cassette.yaml b/internal/namespaces/account/testdata/test-init-command-simple.cassette.yaml new file mode 100644 index 0000000000..b3c2f33ab6 --- /dev/null +++ b/internal/namespaces/account/testdata/test-init-command-simple.cassette.yaml @@ -0,0 +1,86 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.13.1; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/account/v2alpha1/ssh-keys?order_by=created_at_asc&page=1 + method: GET + response: + body: '{"ssh_keys":[{"id":"1ef19b65-53c9-4789-a760-0e0d2e26cf35","name":"scw-test","public_key":"ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAACAQDfEI5h63XLQTu7sQKJjjBXe1S4//qsYPhWv09P0n5dLyae5oaAuS15p4o7TTx2tEi5/cixiNbkvnR5TCoxU0VWJN0X09t+LILLHefEFhGocyQ94pw79QZVKAcw2gwE+jq8/PDCwdyi4bVLi4AcvLbx9z2kn182MBkz4pwkS0rpWN2v8Lcjk9/neoxxlxyHJsGZPPql67N/XcNJtUUoagJZEwkf+/etuLqJqB8P5YKyrORrPJFNiiBqQQCkVEX40uR4dBVmRPe+oWsJY+NfGgOqjTjaaRXmfhrQnu2pM1fIDt7Zh4BbabHms7qGQuDqQTbeJ/0AVE+HhVXZ+dNiMm4/70vfX3Cuoia2swEJCbVoLSV7I1I7ojlPGYEAt0Ka6jW3Jdr2D/BratRpeNRMdMRuVYpwx8uXakAizpG8mfn/k56LL9PziKxDG6Q6GccdPszMWsVcSOaA9Kt03ZS8+T+r4VBVy89PSs3Vwh541eVjuVQ8QxB0PjQ2usY8xN6mIbLtb/Ezlp7uHlJccfjPNvZ+01YMqVugAj+oNKfVUIj3HBIh+VvAsQDXN04cl4YsOpNmhc6MFG5EhF119BK2mpu3Df/it7rf/RJNLM1NOlW3ZBMvfsV63CARMXaDncAQNZqC4HpX0rr+l58F2MY/H/Vrq8wr2UJKecZoQYPsTD2Znw== + lbourgois@scw-loic-mac.local","fingerprint":"4096 MD5:a6:97:64:f1:92:5b:2c:68:b6:b8:bb:cc:4c:30:f2:1e + lbourgois@scw-loic-mac.local (ssh-rsa)","created_at":"2020-03-06T13:36:01.678719Z","updated_at":"2020-03-06T13:36:01.678719Z","creation_info":{"address":"195.154.229.35","user_agent":"Mozilla/5.0 + (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0","country_code":"FR"},"organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"},{"id":"4719404d-3056-41c5-b5e4-1db2d4d0238f","name":"","public_key":"ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAABAQC/wF8Q8LjEexuWDc8YfKmWVZ1CiiHK6KvO0E/Rk9+d6szqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb + lbourgois@MacBook-Pro-de-Loic.local\n","fingerprint":"2048 MD5:6c:4b:6c:7f:ca:16:46:7a:61:3d:e5:bd:5b:2f:02:9e + lbourgois@MacBook-Pro-de-Loic.local (ssh-rsa)","created_at":"2020-03-10T10:31:12.749497Z","updated_at":"2020-03-10T10:31:12.749497Z","creation_info":{"address":"","user_agent":"","country_code":""},"organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"},{"id":"6ff5bb48-add1-458a-95bf-a3f5eda3767c","name":"","public_key":"ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAABAQC/wF8Q8LjEexuWDc8TfKmWVZ1CiiHK6KvO0E/Rk9+d6szqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb + lbourgois@MacBook-Pro-de-Loic.local\n","fingerprint":"2048 MD5:de:57:c6:c4:a5:4a:cc:cd:6f:00:33:99:94:7a:8b:08 + lbourgois@MacBook-Pro-de-Loic.local (ssh-rsa)","created_at":"2020-03-20T14:00:34.537763Z","updated_at":"2020-03-20T14:00:34.537763Z","creation_info":{"address":"","user_agent":"","country_code":""},"organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"},{"id":"695899a9-2b74-4dd1-b806-4d56bd5828bd","name":"","public_key":"ssh-rsa + AAAAB3NzaC1yc2EAAAADAQABAAABAQC/wF8Q8LjEexuWDc8TfKmWVZ1CiiHK6KvO0E/Rk9+d6ssqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb + email","fingerprint":"2048 MD5:b4:9f:bd:7b:fc:66:66:cc:b3:33:41:72:fc:be:05:c8 + email (ssh-rsa)","created_at":"2020-03-20T16:30:55.236831Z","updated_at":"2020-03-20T16:30:55.236831Z","creation_info":{"address":"","user_agent":"","country_code":""},"organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"}],"total_count":4}' + headers: + Content-Length: + - "3670" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 23 Mar 2020 18:00:13 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 4115f527-fef6-4baf-97c7-ab5dc93c5b15 + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"name":"","public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAEDAQABAAABAQC/wF8Q8LjEexuWDc8TfKmWVZ1CiiHK6KvO0E/Rk9+d6ssqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb + email","organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.13.1; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/account/v2alpha1/ssh-keys + method: POST + response: + body: '{"id":"901636bc-37e5-4678-bcbf-e4606cf41238","name":"","public_key":"ssh-rsa + AAAAB3NzaC1yc2EAAAEDAQABAAABAQC/wF8Q8LjEexuWDc8TfKmWVZ1CiiHK6KvO0E/Rk9+d6ssqbrvtbJWRsJXZFC8+DGWVM0UFFicmOfTwEjDWzuQPFkYhmpXrD1UiLx9Viku1g1qEJgcsyH2uAwwW3OnsH1W44D6Ni/zOzMButFeKZgPeD8H9YNkpbZBZ9QrKFiAhvEyJDYSY0bsbH1/qR5DE+dLNuGlJ/g3kUMVaXSI6dHNcBHTbK0Mse23Uopk2U3BSpvX9JdbcLaYtHDOytwd16rNYui7el3uOmlR8oUpAXkeKQxBPoxgy3qI/P8/l44L9RFpbklkmdiw2ph2ymiSkRSYCWdvEVIK/A+0D8VFjGXOb + email","fingerprint":"None MD5:9e:19:2e:19:40:88:a9:58:7e:ca:ec:00:1b:36:96:57 + email (ssh-rsa)","created_at":"2020-03-23T18:00:14.135950Z","updated_at":"2020-03-23T18:00:14.135950Z","creation_info":{"address":"","user_agent":"","country_code":""},"organization_id":"aba2d0d0-b01d-4d88-b322-935edc96d0fd"}' + headers: + Content-Length: + - "754" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Mon, 23 Mar 2020 18:00:14 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 284c68f3-2c62-4592-9b57-c5b15e50df04 + status: 200 OK + code: 200 + duration: "" diff --git a/internal/namespaces/account/testdata/test-init-command-simple.stdout.golden b/internal/namespaces/account/testdata/test-init-command-simple.stdout.golden new file mode 100644 index 0000000000..49e2e04822 --- /dev/null +++ b/internal/namespaces/account/testdata/test-init-command-simple.stdout.golden @@ -0,0 +1 @@ +message Key ~/.ssh/id_rsa.pub successfully added diff --git a/internal/namespaces/get_commands.go b/internal/namespaces/get_commands.go index 72c2a873c8..72fc271e8d 100644 --- a/internal/namespaces/get_commands.go +++ b/internal/namespaces/get_commands.go @@ -2,6 +2,7 @@ package namespaces import ( "github.com/scaleway/scaleway-cli/internal/core" + "github.com/scaleway/scaleway-cli/internal/namespaces/account" autocompleteNamespace "github.com/scaleway/scaleway-cli/internal/namespaces/autocomplete" baremetal "github.com/scaleway/scaleway-cli/internal/namespaces/baremetal/v1alpha1" configNamespace "github.com/scaleway/scaleway-cli/internal/namespaces/config" @@ -25,6 +26,7 @@ func GetCommands() *core.Commands { commands.Merge(marketplace.GetCommands()) commands.Merge(initNamespace.GetCommands()) commands.Merge(configNamespace.GetCommands()) + commands.Merge(account.GetCommands()) commands.Merge(autocompleteNamespace.GetCommands()) commands.Merge(versionNamespace.GetCommands()) return commands diff --git a/internal/namespaces/init/init.go b/internal/namespaces/init/init.go index f99a838aa8..f0629bfe8b 100644 --- a/internal/namespaces/init/init.go +++ b/internal/namespaces/init/init.go @@ -10,6 +10,7 @@ import ( "github.com/scaleway/scaleway-cli/internal/account" "github.com/scaleway/scaleway-cli/internal/core" "github.com/scaleway/scaleway-cli/internal/interactive" + accountcommands "github.com/scaleway/scaleway-cli/internal/namespaces/account" "github.com/scaleway/scaleway-cli/internal/namespaces/autocomplete" "github.com/scaleway/scaleway-cli/internal/terminal" "github.com/scaleway/scaleway-sdk-go/logger" @@ -62,6 +63,7 @@ type initArgs struct { Zone scw.Zone OrganizationID string SendTelemetry *bool + WithSSHKey *bool InstallAutocomplete *bool } @@ -93,6 +95,11 @@ func initCommand() *core.Command { { Name: "send-usage", }, + { + Name: "with-ssh-key", + Short: "Whether the ssh key for managing instances should be uploaded automatically", + Default: core.DefaultValueSetter("true"), + }, { Name: "install-autocomplete", Short: "Whether the autocomplete script should be installed during initialisation", @@ -272,13 +279,26 @@ func initCommand() *core.Command { successMessage := "Initialization completed with success" + // Install autocomplete if *args.InstallAutocomplete { + _, _ = interactive.Println() _, err := autocomplete.InstallCommandRun(ctx, &autocomplete.InstallArgs{}) if err != nil { - successMessage += " except for autocomplete:\n" + err.Error() + successMessage += "\n except for autocomplete: " + err.Error() } } + // Init SSH Key + if *args.WithSSHKey { + _, _ = interactive.Println() + result, err := accountcommands.InitRun(ctx, nil) + if err != nil { + successMessage += "\n except for ssh-key: " + err.Error() + } + _, _ = interactive.Println(result) + _, _ = interactive.Println() + } + return &core.SuccessResult{ Message: successMessage, }, nil