github.com/saucelabs/saucectl@v0.175.1/internal/cmd/configure/cmd.go (about)

     1  package configure
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/AlecAivazis/survey/v2"
    10  	"github.com/fatih/color"
    11  	"github.com/rs/zerolog/log"
    12  	"github.com/saucelabs/saucectl/internal/credentials"
    13  	"github.com/saucelabs/saucectl/internal/iam"
    14  	"github.com/saucelabs/saucectl/internal/msg"
    15  	"github.com/saucelabs/saucectl/internal/segment"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  var (
    20  	configureUse     = "configure"
    21  	configureShort   = "Configure your Sauce Labs credentials"
    22  	configureLong    = `Persist locally your Sauce Labs credentials`
    23  	configureExample = "saucectl configure"
    24  	cliUsername      = ""
    25  	cliAccessKey     = ""
    26  )
    27  
    28  // Command creates the `configure` command
    29  func Command() *cobra.Command {
    30  	cmd := &cobra.Command{
    31  		Use:          configureUse,
    32  		Short:        configureShort,
    33  		Long:         configureLong,
    34  		Example:      configureExample,
    35  		SilenceUsage: true,
    36  		Run: func(cmd *cobra.Command, args []string) {
    37  			tracker := segment.DefaultTracker
    38  
    39  			go func() {
    40  				tracker.Collect("Configure", nil)
    41  				_ = tracker.Close()
    42  			}()
    43  
    44  			if err := Run(); err != nil {
    45  				log.Err(err).Msg("failed to execute configure command")
    46  				os.Exit(1)
    47  			}
    48  		},
    49  	}
    50  	cmd.Flags().StringVarP(&cliUsername, "username", "u", "", "username, available on your sauce labs account")
    51  	cmd.Flags().StringVarP(&cliAccessKey, "accessKey", "a", "", "accessKey, available on your sauce labs account")
    52  
    53  	cmd.AddCommand(ListCommand())
    54  	return cmd
    55  }
    56  
    57  func printCreds(creds iam.Credentials) {
    58  	fmt.Println()
    59  
    60  	labelStyle := color.New(color.Bold)
    61  	valueStyle := color.New(color.FgBlue)
    62  
    63  	fmt.Println("Currently configured credentials:")
    64  	fmt.Println(labelStyle.Sprint("\t  Username:"), valueStyle.Sprint(creds.Username))
    65  	fmt.Println(labelStyle.Sprint("\tAccess key:"), valueStyle.Sprint(mask(creds.AccessKey)))
    66  
    67  	fmt.Println()
    68  	fmt.Printf("Collected from: %s", creds.Source)
    69  
    70  	fmt.Println()
    71  	fmt.Println()
    72  }
    73  
    74  // interactiveConfiguration expect user to manually type-in its credentials
    75  func interactiveConfiguration() (iam.Credentials, error) {
    76  	overwrite := true
    77  	var err error
    78  
    79  	creds := credentials.Get()
    80  
    81  	if creds.IsSet() {
    82  		printCreds(creds)
    83  
    84  		qs := &survey.Confirm{
    85  			Message: "Overwrite existing credentials?",
    86  		}
    87  		err = survey.AskOne(qs, &overwrite)
    88  		if err != nil {
    89  			return creds, err
    90  		}
    91  	}
    92  
    93  	if overwrite {
    94  		if !creds.IsSet() {
    95  			fmt.Println(msg.SignupMessage)
    96  		}
    97  
    98  		qs := []*survey.Question{
    99  			{
   100  				Name: "username",
   101  				Prompt: &survey.Input{
   102  					Message: "SauceLabs username",
   103  				},
   104  				Validate: func(val interface{}) error {
   105  					str, ok := val.(string)
   106  					if !ok {
   107  						return errors.New(msg.InvalidUsername)
   108  					}
   109  					str = strings.TrimSpace(str)
   110  					if str == "" {
   111  						return errors.New(msg.EmptyUsername)
   112  
   113  					}
   114  					return nil
   115  				},
   116  			},
   117  			{
   118  				Name: "accessKey",
   119  				Prompt: &survey.Password{
   120  					Message: "SauceLabs access key",
   121  				},
   122  				Validate: func(val interface{}) error {
   123  					str, ok := val.(string)
   124  					if !ok {
   125  						return errors.New(msg.InvalidAccessKey)
   126  					}
   127  					str = strings.TrimSpace(str)
   128  					if str == "" {
   129  						return errors.New(msg.EmptyAccessKey)
   130  
   131  					}
   132  					return nil
   133  				},
   134  			},
   135  		}
   136  
   137  		fmt.Println() // visual paragraph break
   138  		if err = survey.Ask(qs, &creds); err != nil {
   139  			return creds, err
   140  		}
   141  		fmt.Println() // visual paragraph break
   142  	}
   143  
   144  	return creds, nil
   145  }
   146  
   147  // Run starts the configure command
   148  func Run() error {
   149  	var creds iam.Credentials
   150  	var err error
   151  
   152  	if cliUsername == "" && cliAccessKey == "" {
   153  		creds, err = interactiveConfiguration()
   154  	} else {
   155  		creds = iam.Credentials{
   156  			Username:  cliUsername,
   157  			AccessKey: cliAccessKey,
   158  		}
   159  	}
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	if !creds.IsSet() {
   165  		log.Error().Msg("The provided credentials appear to be invalid and will NOT be saved.")
   166  		return fmt.Errorf(msg.InvalidCredentials)
   167  	}
   168  	if err := credentials.ToFile(creds); err != nil {
   169  		return fmt.Errorf("unable to save credentials: %s", err)
   170  	}
   171  	fmt.Println("You're all set!")
   172  	return nil
   173  }
   174  
   175  func mask(str string) string {
   176  	n := len(str)
   177  	if n == 0 {
   178  		return ""
   179  	}
   180  	res := []byte{}
   181  	for i := 0; i < n; i++ {
   182  		if str[i] == '-' {
   183  			res = append(res, str[i])
   184  		} else if i >= n-4 {
   185  			res = append(res, str[i])
   186  		} else {
   187  			res = append(res, '*')
   188  		}
   189  	}
   190  	return string(res)
   191  }