Skip to content

fix(init): rely on token organization #1146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 1, 2020
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
67 changes: 11 additions & 56 deletions internal/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (

// Token represents a Token
type Token struct {
UserID string `json:"user_id"`
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
ID string `json:"id"`
ID string `json:"id"`
UserID string `json:"user_id"`
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
OrganizationID string `json:"organization_id"`
ProjectID string `json:"project_id"`
}

type LoginResponse struct {
Expand All @@ -31,14 +33,6 @@ type LoginRequest struct {
Expires bool `json:"expires"`
}

type organizationsResponse struct {
Organizations []organization `json:"organizations"`
}

type organization struct {
ID string `json:"id"`
}

var (
accountURL = "https://account.scaleway.com"
)
Expand Down Expand Up @@ -79,66 +73,27 @@ func Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) {
return loginResponse, err
}

func GetAccessKey(ctx context.Context, secretKey string) (string, error) {
func GetAPIKey(ctx context.Context, secretKey string) (*Token, error) {
resp, err := extractHTTPClient(ctx).Get(accountURL + "/tokens/" + secretKey)
if err != nil {
return "", err
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("could not get token")
return nil, fmt.Errorf("could not get token")
}

token := &LoginResponse{}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}

err = json.Unmarshal(b, token)
if err != nil {
return "", err
}

return token.Token.AccessKey, err
}

func getOrganizations(ctx context.Context, secretKey string) ([]organization, error) {
req, err := http.NewRequest("GET", accountURL+"/organizations", nil)
if err != nil {
return nil, err
}
req.Header.Add("X-Auth-Token", secretKey)
resp, err := extractHTTPClient(ctx).Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not get organizations from %s", accountURL)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
organizationsResponse := &organizationsResponse{}
err = json.Unmarshal(body, organizationsResponse)
err = json.Unmarshal(b, token)
if err != nil {
return nil, err
}
return organizationsResponse.Organizations, nil
}

func GetOrganizationsIds(ctx context.Context, secretKey string) ([]string, error) {
organizations, err := getOrganizations(ctx, secretKey)
if err != nil {
return nil, err
}
ids := []string(nil)
for _, organization := range organizations {
ids = append(ids, organization.ID)
}
return ids, nil
return token.Token, err
}
2 changes: 0 additions & 2 deletions internal/namespaces/init/custom_init_autocomplete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
func baseBeforeFunc() core.BeforeFunc {
return func(ctx *core.BeforeFuncCtx) error {
ctx.Meta["SecretKey"], _ = ctx.Client.GetSecretKey()
ctx.Meta["OrganizationID"], _ = ctx.Client.GetDefaultOrganizationID()
return nil
}
}
Expand All @@ -29,7 +28,6 @@ const (
func Test_InitAutocomplete(t *testing.T) {
defaultSettings := map[string]string{
"secret-key": "{{ .SecretKey }}",
"organization-id": "{{ .OrganizationID }}",
"send-telemetry": "false",
"remove-v1-config": "false",
"with-ssh-key": "false",
Expand Down
1 change: 0 additions & 1 deletion internal/namespaces/init/custom_init_ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func addSSHKeyToAccount(metaKey string, name string, key string) core.BeforeFunc
func Test_InitSSH(t *testing.T) {
defaultSettings := map[string]string{
"secret-key": "{{ .SecretKey }}",
"organization-id": "{{ .OrganizationID }}",
"send-telemetry": "false",
"remove-v1-config": "false",
"install-autocomplete": "false",
Expand Down
64 changes: 6 additions & 58 deletions internal/namespaces/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ type initArgs struct {
SecretKey string
Region scw.Region
Zone scw.Zone
OrganizationID string
SendTelemetry *bool
WithSSHKey *bool
InstallAutocomplete *bool
Expand Down Expand Up @@ -100,19 +99,8 @@ func initCommand() *core.Command {
Name: "remove-v1-config",
Short: "Whether to remove the v1 configuration file if it exists",
},

// `organization-id` is not required before `PreValidateFunc()`, but is required after `PreValidateFunc()`.
// See workflow in cobra_utils.go/cobraRun().
// It is not required in the command line: the user is not obliged to type it.
// But it is required to make the request: this is why we use `ValidateOrganizationIDRequired().
// If `organization-id` is not typed by the user, we set it in `PreValidateFunc()`.
{
Name: "organization-id",
Short: "Organization ID to use. If none is passed will use default organization ID from the config",
ValidateFunc: core.ValidateOrganizationIDRequired(),
},
core.RegionArgSpec(scw.RegionFrPar, scw.RegionNlAms),
core.ZoneArgSpec(),
core.ZoneArgSpec(scw.ZoneFrPar1, scw.ZoneFrPar2, scw.ZoneNlAms1),
},
SeeAlsos: []*core.SeeAlso{
{
Expand Down Expand Up @@ -195,15 +183,6 @@ func initCommand() *core.Command {
}
}

// Set OrganizationID if not done previously
// As OrganizationID depends on args.SecretKey, we can't use a DefaultFunc or ArgPromptFunc.
if args.OrganizationID == "" {
args.OrganizationID, err = getOrganizationID(ctx, args.SecretKey)
if err != nil {
return err
}
}

// Ask for send usage permission
if args.SendTelemetry == nil {
_, _ = interactive.Println()
Expand Down Expand Up @@ -281,21 +260,22 @@ func initCommand() *core.Command {
}

// Get access key
accessKey, err := account.GetAccessKey(ctx, args.SecretKey)
apiKey, err := account.GetAPIKey(ctx, args.SecretKey)
if err != nil {
return "", &core.CliError{
Err: err,
Details: "Failed to retrieve Access Key for the given Secret Key.",
Details: "Failed to retrieve Access Key from the given Secret Key.",
}
}

profile := &scw.Profile{
AccessKey: &accessKey,
AccessKey: &apiKey.AccessKey,
SecretKey: &args.SecretKey,
DefaultZone: scw.StringPtr(args.Zone.String()),
DefaultRegion: scw.StringPtr(args.Region.String()),
DefaultOrganizationID: &args.OrganizationID,
DefaultOrganizationID: &apiKey.OrganizationID, // An api key is always bound to an organization.
}

// Save the profile as default or as a named profile
profileName := core.ExtractProfileName(ctx)
_, err = config.GetProfile(profileName)
Expand Down Expand Up @@ -438,38 +418,6 @@ func promptCredentials(ctx context.Context) (string, error) {
}
}

// getOrganizationId handles prompting for the argument organization-id
// If we have only 1 id : we use it, and don't prompt
// If we have more than 1 id, we prompt, with id[0] as default value.
func getOrganizationID(ctx context.Context, secretKey string) (string, error) {
IDs, err := account.GetOrganizationsIds(ctx, secretKey)
if err != nil {
logger.Warningf("%v", err)
return promptOrganizationID(ctx, IDs)
}
if len(IDs) != 1 {
return promptOrganizationID(ctx, IDs)
}
return IDs[0], nil
}

func promptOrganizationID(ctx context.Context, IDs []string) (string, error) {
config := &interactive.PromptStringConfig{
Prompt: "Enter your Organization ID",
ValidateFunc: interactive.ValidateOrganizationID(),
Ctx: ctx,
}
if len(IDs) > 0 {
config.DefaultValue = IDs[0]
config.DefaultValueDoc = IDs[0]
}
ID, err := interactive.PromptStringWithConfig(config)
if err != nil {
return "", err
}
return ID, nil
}

const logo = `
@@@@@@@@@@@@@@@.
@@@@@@@@@@@@@@@@@@@@ __ __ _
Expand Down
3 changes: 0 additions & 3 deletions internal/namespaces/init/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ func beforeFuncSaveConfig(config *scw.Config) core.BeforeFunc {
func TestInit(t *testing.T) {
defaultArgs := map[string]string{
"secret-key": "{{ .SecretKey }}",
"organization-id": "{{ .OrganizationID }}",
"send-telemetry": "true",
"install-autocomplete": "false",
"remove-v1-config": "false",
Expand Down Expand Up @@ -142,9 +141,7 @@ func TestInit(t *testing.T) {
core.TestCheckGolden(),
checkConfig(func(t *testing.T, ctx *core.CheckFuncCtx, config *scw.Config) {
secretKey, _ := ctx.Client.GetSecretKey()
organizationID, _ := ctx.Client.GetDefaultOrganizationID()
assert.Equal(t, secretKey, *config.SecretKey)
assert.Equal(t, organizationID, *config.DefaultOrganizationID)
}),
),
TmpHomeDir: true,
Expand Down