github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/cloud/listcredentials.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloud
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"sort"
    10  	"strings"
    11  	"text/tabwriter"
    12  
    13  	"github.com/juju/cmd"
    14  	"github.com/juju/errors"
    15  	"launchpad.net/gnuflag"
    16  
    17  	jujucloud "github.com/juju/juju/cloud"
    18  	"github.com/juju/juju/cmd/juju/common"
    19  	"github.com/juju/juju/environs"
    20  	"github.com/juju/juju/jujuclient"
    21  )
    22  
    23  var usageListCredentialsSummary = `
    24  Lists credentials for a cloud.`[1:]
    25  
    26  var usageListCredentialsDetails = `
    27  Credentials are used with `[1:] + "`juju bootstrap`" + `  and ` + "`juju create-model`" + `.
    28  An arbitrary "credential name" is used to represent credentials, which are 
    29  added either via ` + "`juju add-credential` or `juju autoload-credentials`" + `.
    30  Note that there can be multiple sets of credentials and thus multiple 
    31  names.
    32  Actual authentication material is exposed with the '--show-secrets' 
    33  option.
    34  A controller and subsequently created models can be created with a 
    35  different set of credentials but any action taken within the model (e.g.:
    36  ` + "`juju deploy`; `juju add-unit`" + `) applies the set used to create the model. 
    37  Recall that when a controller is created a 'default' model is also 
    38  created.
    39  Credentials denoted with an asterisk '*' are currently set as the default
    40  for the given cloud.
    41  
    42  Examples:
    43      juju list-credentials
    44      juju list-credentials aws
    45      juju list-credentials --format yaml --show-secrets
    46  
    47  See also: 
    48      add-credential
    49      remove-credential
    50      set-default-credential
    51      autoload-credentials`
    52  
    53  type listCredentialsCommand struct {
    54  	cmd.CommandBase
    55  	out         cmd.Output
    56  	cloudName   string
    57  	showSecrets bool
    58  
    59  	store              jujuclient.CredentialGetter
    60  	personalCloudsFunc func() (map[string]jujucloud.Cloud, error)
    61  	cloudByNameFunc    func(string) (*jujucloud.Cloud, error)
    62  }
    63  
    64  type credentialsMap struct {
    65  	Credentials map[string]jujucloud.CloudCredential `yaml:"credentials" json:"credentials"`
    66  }
    67  
    68  // NewListCredentialsCommand returns a command to list cloud credentials.
    69  func NewListCredentialsCommand() cmd.Command {
    70  	return &listCredentialsCommand{
    71  		store:           jujuclient.NewFileCredentialStore(),
    72  		cloudByNameFunc: jujucloud.CloudByName,
    73  	}
    74  }
    75  
    76  func (c *listCredentialsCommand) Info() *cmd.Info {
    77  	return &cmd.Info{
    78  		Name:    "list-credentials",
    79  		Args:    "[<cloud name>]",
    80  		Purpose: usageListCredentialsSummary,
    81  		Doc:     usageListCredentialsDetails,
    82  	}
    83  }
    84  
    85  func (c *listCredentialsCommand) SetFlags(f *gnuflag.FlagSet) {
    86  	f.BoolVar(&c.showSecrets, "show-secrets", false, "Show secrets")
    87  	c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{
    88  		"yaml":    cmd.FormatYaml,
    89  		"json":    cmd.FormatJson,
    90  		"tabular": formatCredentialsTabular,
    91  	})
    92  }
    93  
    94  func (c *listCredentialsCommand) Init(args []string) error {
    95  	cloudName, err := cmd.ZeroOrOneArgs(args)
    96  	if err != nil {
    97  		return errors.Trace(err)
    98  	}
    99  	c.cloudName = cloudName
   100  	return nil
   101  }
   102  
   103  func (c *listCredentialsCommand) personalClouds() (map[string]jujucloud.Cloud, error) {
   104  	if c.personalCloudsFunc == nil {
   105  		return jujucloud.PersonalCloudMetadata()
   106  	}
   107  	return c.personalCloudsFunc()
   108  }
   109  
   110  // displayCloudName returns the provided cloud name prefixed
   111  // with "local:" if it is a local cloud.
   112  func displayCloudName(cloudName string, personalCloudNames []string) string {
   113  	for _, name := range personalCloudNames {
   114  		if cloudName == name {
   115  			return localPrefix + cloudName
   116  		}
   117  	}
   118  	return cloudName
   119  }
   120  
   121  func (c *listCredentialsCommand) Run(ctxt *cmd.Context) error {
   122  	var credentials map[string]jujucloud.CloudCredential
   123  	credentials, err := c.store.AllCredentials()
   124  	if err != nil && !errors.IsNotFound(err) {
   125  		return err
   126  	}
   127  	if c.cloudName != "" {
   128  		for cloudName := range credentials {
   129  			if cloudName != c.cloudName {
   130  				delete(credentials, cloudName)
   131  			}
   132  		}
   133  	}
   134  
   135  	// Find local cloud names.
   136  	personalClouds, err := c.personalClouds()
   137  	if err != nil {
   138  		return err
   139  	}
   140  	var personalCloudNames []string
   141  	for name := range personalClouds {
   142  		personalCloudNames = append(personalCloudNames, name)
   143  	}
   144  
   145  	displayCredentials := make(map[string]jujucloud.CloudCredential)
   146  	for cloudName, cred := range credentials {
   147  		if !c.showSecrets {
   148  			if err := c.removeSecrets(cloudName, &cred); err != nil {
   149  				return errors.Annotatef(err, "removing secrets from credentials for cloud %v", cloudName)
   150  			}
   151  		}
   152  		displayCredentials[displayCloudName(cloudName, personalCloudNames)] = cred
   153  	}
   154  	return c.out.Write(ctxt, credentialsMap{displayCredentials})
   155  }
   156  
   157  func (c *listCredentialsCommand) removeSecrets(cloudName string, cloudCred *jujucloud.CloudCredential) error {
   158  	cloud, err := common.CloudOrProvider(cloudName, c.cloudByNameFunc)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	provider, err := environs.Provider(cloud.Type)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	schemas := provider.CredentialSchemas()
   167  	for name, cred := range cloudCred.AuthCredentials {
   168  		sanitisedCred, err := jujucloud.RemoveSecrets(cred, schemas)
   169  		if err != nil {
   170  			return err
   171  		}
   172  		cloudCred.AuthCredentials[name] = *sanitisedCred
   173  	}
   174  	return nil
   175  }
   176  
   177  // formatCredentialsTabular returns a tabular summary of cloud information.
   178  func formatCredentialsTabular(value interface{}) ([]byte, error) {
   179  	credentials, ok := value.(credentialsMap)
   180  	if !ok {
   181  		return nil, errors.Errorf("expected value of type %T, got %T", credentials, value)
   182  	}
   183  
   184  	// For tabular we'll sort alphabetically by cloud, and then by credential name.
   185  	var cloudNames []string
   186  	for name := range credentials.Credentials {
   187  		cloudNames = append(cloudNames, name)
   188  	}
   189  	sort.Strings(cloudNames)
   190  
   191  	var out bytes.Buffer
   192  	const (
   193  		// To format things into columns.
   194  		minwidth = 0
   195  		tabwidth = 1
   196  		padding  = 2
   197  		padchar  = ' '
   198  		flags    = 0
   199  	)
   200  	tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags)
   201  	p := func(values ...string) {
   202  		text := strings.Join(values, "\t")
   203  		fmt.Fprintln(tw, text)
   204  	}
   205  	p("CLOUD\tCREDENTIALS")
   206  	for _, cloudName := range cloudNames {
   207  		var haveDefault bool
   208  		var credentialNames []string
   209  		credentials := credentials.Credentials[cloudName]
   210  		for credentialName := range credentials.AuthCredentials {
   211  			if credentialName == credentials.DefaultCredential {
   212  				credentialNames = append([]string{credentialName + "*"}, credentialNames...)
   213  				haveDefault = true
   214  			} else {
   215  				credentialNames = append(credentialNames, credentialName)
   216  			}
   217  		}
   218  		if haveDefault {
   219  			sort.Strings(credentialNames[1:])
   220  		} else {
   221  			sort.Strings(credentialNames)
   222  		}
   223  		p(cloudName, strings.Join(credentialNames, ", "))
   224  	}
   225  	tw.Flush()
   226  
   227  	return out.Bytes(), nil
   228  }