pulumi/cmd/login.go
Matt Ellis 5fc226a952 Change configuration verbs for getting and setting values
A handful of UX improvments for config:

 - `pulumi config ls` has been removed. Now, `pulumi config` with no
   arguments prints the table of configuration values for a stack and
   a new command `pulumi config get <key>` prints the value for a
   single configuration key (useful for scripting).
 - `pulumi config text` and `pulumi config secret` have been merged
   into a single command `pulumi config set`. The flag `--secret` can
   be used to encrypt the value we store (like `pulumi config secret`
   used to do).
 - To make it obvious that setting a value with `pulumi config set` is
   in plan text, we now echo a message back to the user saying we
   added the configuration value in plaintext.

Fixes #552
2017-11-16 11:39:28 -08:00

84 lines
2.4 KiB
Go

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/pulumi/pulumi/pkg/apitype"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
)
// PulumiAccessTokenEnvVar is the environment variable used to bypass a prompt on login.
const PulumiAccessTokenEnvVar = "PULUMI_ACCESS_TOKEN"
func newLoginCmd() *cobra.Command {
return &cobra.Command{
Use: "login",
Short: "Log into the Pulumi Cloud Console",
Long: "Log into the Pulumi Cloud Console. You can script by using PULUMI_ACCESS_TOKEN environment variable.",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
return loginCmd()
}),
}
}
func newLogoutCmd() *cobra.Command {
return &cobra.Command{
Use: "logout",
Short: "Log out of the Pulumi CLI",
Long: "Log out of the Pulumi CLI. Deletes stored credentials on the local machine.",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
return deleteStoredCredentials()
}),
}
}
// loginCmd is the implementation of the login command.
func loginCmd() error {
// We intentionally don't accept command-line args for the user's access token. Having it in
// .bash_history is not great, and specifying it via flag isn't of much use.
accessToken := os.Getenv(PulumiAccessTokenEnvVar)
if accessToken != "" {
fmt.Printf("Using access token from %s.\n", PulumiAccessTokenEnvVar)
} else {
token, err := readConsole("Enter Pulumi access token")
if err != nil {
return err
}
accessToken = token
}
// Try and use the credentials to see if they are valid.
valid, err := isValidAccessToken(accessToken)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("invalid access token")
}
// Save them.
creds := accountCredentials{
AccessToken: accessToken,
}
return storeCredentials(creds)
}
// isValidAccessToken tries to use the provided Pulumi access token and returns if it is accepted
// or not. Returns error on any unexpected error.
func isValidAccessToken(accessToken string) (bool, error) {
// Make a request to get the authenticated user. If it returns a successful result, the token
// checks out.
if err := pulumiRESTCallWithAccessToken("GET", "/user", nil, nil, accessToken); err != nil {
if errResp, ok := err.(*apitype.ErrorResponse); ok && errResp.Code == 401 {
return false, nil
}
return false, fmt.Errorf("testing access token: %v", err)
}
return true, nil
}