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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloud_test
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/juju/cmd"
    14  	"github.com/juju/errors"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	jujucloud "github.com/juju/juju/cloud"
    19  	"github.com/juju/juju/cmd/juju/cloud"
    20  	"github.com/juju/juju/environs"
    21  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    22  	_ "github.com/juju/juju/provider/all"
    23  	"github.com/juju/juju/testing"
    24  )
    25  
    26  type addCredentialSuite struct {
    27  	testing.BaseSuite
    28  
    29  	store           *jujuclienttesting.MemStore
    30  	schema          map[jujucloud.AuthType]jujucloud.CredentialSchema
    31  	authTypes       []jujucloud.AuthType
    32  	cloudByNameFunc func(string) (*jujucloud.Cloud, error)
    33  }
    34  
    35  var _ = gc.Suite(&addCredentialSuite{
    36  	store: jujuclienttesting.NewMemStore(),
    37  })
    38  
    39  func (s *addCredentialSuite) SetUpSuite(c *gc.C) {
    40  	s.BaseSuite.SetUpSuite(c)
    41  	environs.RegisterProvider("mock-addcredential-provider", &mockProvider{credSchemas: &s.schema})
    42  	s.cloudByNameFunc = func(cloud string) (*jujucloud.Cloud, error) {
    43  		if cloud != "somecloud" && cloud != "anothercloud" {
    44  			return nil, errors.NotFoundf("cloud %v", cloud)
    45  		}
    46  		return &jujucloud.Cloud{
    47  			Type:      "mock-addcredential-provider",
    48  			AuthTypes: s.authTypes,
    49  		}, nil
    50  	}
    51  }
    52  
    53  func (s *addCredentialSuite) SetUpTest(c *gc.C) {
    54  	s.BaseSuite.SetUpTest(c)
    55  	s.store.Credentials = make(map[string]jujucloud.CloudCredential)
    56  }
    57  
    58  func (s *addCredentialSuite) run(c *gc.C, stdin io.Reader, args ...string) (*cmd.Context, error) {
    59  	addCmd := cloud.NewAddCredentialCommandForTest(s.store, s.cloudByNameFunc)
    60  	err := testing.InitCommand(addCmd, args)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	ctx := testing.Context(c)
    65  	ctx.Stdin = stdin
    66  	return ctx, addCmd.Run(ctx)
    67  }
    68  
    69  func (s *addCredentialSuite) TestBadArgs(c *gc.C) {
    70  	_, err := s.run(c, nil)
    71  	c.Assert(err, gc.ErrorMatches, `Usage: juju add-credential <cloud-name> \[-f <credentials.yaml>\]`)
    72  	_, err = s.run(c, nil, "somecloud", "-f", "credential.yaml", "extra")
    73  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["extra"\]`)
    74  }
    75  
    76  func (s *addCredentialSuite) TestBadCloudName(c *gc.C) {
    77  	_, err := s.run(c, nil, "badcloud")
    78  	c.Assert(err, gc.ErrorMatches, "cloud badcloud not valid")
    79  }
    80  
    81  func (s *addCredentialSuite) TestAddFromFileBadFilename(c *gc.C) {
    82  	_, err := s.run(c, nil, "somecloud", "-f", "somefile.yaml")
    83  	c.Assert(err, gc.ErrorMatches, ".*open somefile.yaml: .*")
    84  }
    85  
    86  func (s *addCredentialSuite) TestNoCredentialsRequired(c *gc.C) {
    87  	_, err := s.run(c, nil, "manual")
    88  	c.Assert(err, gc.ErrorMatches, `cloud "manual" does not require credentials`)
    89  }
    90  
    91  func (s *addCredentialSuite) createTestCredentialData(c *gc.C) string {
    92  	dir := c.MkDir()
    93  	credsFile := filepath.Join(dir, "cred.yaml")
    94  	data := `
    95  credentials:
    96    somecloud:
    97      me:
    98        auth-type: access-key
    99        access-key: <key>
   100        secret-key: <secret>
   101  `[1:]
   102  	err := ioutil.WriteFile(credsFile, []byte(data), 0600)
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	return credsFile
   105  }
   106  
   107  func (s *addCredentialSuite) TestAddFromFileNoCredentialsFound(c *gc.C) {
   108  	sourceFile := s.createTestCredentialData(c)
   109  	_, err := s.run(c, nil, "anothercloud", "-f", sourceFile)
   110  	c.Assert(err, gc.ErrorMatches, `no credentials for cloud anothercloud exist in file.*`)
   111  }
   112  
   113  func (s *addCredentialSuite) TestAddFromFileExisting(c *gc.C) {
   114  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   115  		"somecloud": {
   116  			AuthCredentials: map[string]jujucloud.Credential{"cred": {}},
   117  		},
   118  	}
   119  	sourceFile := s.createTestCredentialData(c)
   120  	_, err := s.run(c, nil, "somecloud", "-f", sourceFile)
   121  	c.Assert(err, gc.ErrorMatches, `credentials for cloud somecloud already exist; use --replace to overwrite / merge`)
   122  }
   123  
   124  func (s *addCredentialSuite) TestAddFromFileExistingReplace(c *gc.C) {
   125  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   126  		"somecloud": {
   127  			AuthCredentials: map[string]jujucloud.Credential{
   128  				"cred": jujucloud.NewCredential(jujucloud.UserPassAuthType, nil)},
   129  		},
   130  	}
   131  	sourceFile := s.createTestCredentialData(c)
   132  	_, err := s.run(c, nil, "somecloud", "-f", sourceFile, "--replace")
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   135  		"somecloud": {
   136  			AuthCredentials: map[string]jujucloud.Credential{
   137  				"cred": jujucloud.NewCredential(jujucloud.UserPassAuthType, nil),
   138  				"me": jujucloud.NewCredential(jujucloud.AccessKeyAuthType, map[string]string{
   139  					"access-key": "<key>",
   140  					"secret-key": "<secret>",
   141  				})},
   142  		},
   143  	})
   144  }
   145  
   146  func (s *addCredentialSuite) TestAddNewFromFile(c *gc.C) {
   147  	sourceFile := s.createTestCredentialData(c)
   148  	_, err := s.run(c, nil, "somecloud", "-f", sourceFile)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   151  		"somecloud": {
   152  			AuthCredentials: map[string]jujucloud.Credential{
   153  				"me": jujucloud.NewCredential(jujucloud.AccessKeyAuthType, map[string]string{
   154  					"access-key": "<key>",
   155  					"secret-key": "<secret>",
   156  				})},
   157  		},
   158  	})
   159  }
   160  
   161  // TODO(wallyworld) - these tests should also validate that the prompts and messages are as expected.
   162  
   163  func (s *addCredentialSuite) assertAddUserpassCredential(c *gc.C, input string, expected *jujucloud.Credential) {
   164  	s.schema = map[jujucloud.AuthType]jujucloud.CredentialSchema{
   165  		jujucloud.UserPassAuthType: {
   166  			{
   167  				"username", jujucloud.CredentialAttr{Optional: false},
   168  			}, {
   169  				"password", jujucloud.CredentialAttr{Hidden: true},
   170  			},
   171  		},
   172  	}
   173  	stdin := strings.NewReader(input)
   174  	_, err := s.run(c, stdin, "somecloud")
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	var cred jujucloud.Credential
   177  	if expected == nil {
   178  		cred = jujucloud.NewCredential(jujucloud.UserPassAuthType, map[string]string{
   179  			"username": "user",
   180  			"password": "password",
   181  		})
   182  	} else {
   183  		cred = *expected
   184  	}
   185  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   186  		"somecloud": {
   187  			AuthCredentials: map[string]jujucloud.Credential{
   188  				"fred": cred,
   189  			},
   190  		},
   191  	})
   192  }
   193  
   194  func (s *addCredentialSuite) TestAddCredentialSingleAuthType(c *gc.C) {
   195  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   196  	s.assertAddUserpassCredential(c, "fred\nuser\npassword\n", nil)
   197  }
   198  
   199  func (s *addCredentialSuite) TestAddCredentialRetryOnMissingMandatoryAttribute(c *gc.C) {
   200  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   201  	s.assertAddUserpassCredential(c, "fred\n\nuser\npassword\n", nil)
   202  }
   203  
   204  func (s *addCredentialSuite) TestAddCredentialMultipleAuthType(c *gc.C) {
   205  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType, jujucloud.AccessKeyAuthType}
   206  	s.assertAddUserpassCredential(c, "fred\nuserpass\nuser\npassword\n", nil)
   207  }
   208  
   209  func (s *addCredentialSuite) TestAddCredentialReplace(c *gc.C) {
   210  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   211  		"somecloud": {
   212  			AuthCredentials: map[string]jujucloud.Credential{
   213  				"fred": jujucloud.NewCredential(jujucloud.UserPassAuthType, nil)},
   214  		},
   215  	}
   216  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   217  	s.assertAddUserpassCredential(c, "fred\ny\nuser\npassword\n", nil)
   218  }
   219  
   220  func (s *addCredentialSuite) TestAddCredentialReplaceDecline(c *gc.C) {
   221  	cred := jujucloud.NewCredential(jujucloud.UserPassAuthType, nil)
   222  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   223  		"somecloud": {
   224  			AuthCredentials: map[string]jujucloud.Credential{
   225  				"fred": cred},
   226  		},
   227  	}
   228  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   229  	s.assertAddUserpassCredential(c, "fred\nn\n", &cred)
   230  }
   231  
   232  func (s *addCredentialSuite) assertAddFileCredential(c *gc.C, input, fileKey string) {
   233  	dir := c.MkDir()
   234  	filename := filepath.Join(dir, "jsonfile")
   235  	err := ioutil.WriteFile(filename, []byte{}, 0600)
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	stdin := strings.NewReader(fmt.Sprintf(input, filename))
   239  	addCmd := cloud.NewAddCredentialCommandForTest(s.store, s.cloudByNameFunc)
   240  	err = testing.InitCommand(addCmd, []string{"somecloud"})
   241  	c.Assert(err, jc.ErrorIsNil)
   242  	ctx := testing.ContextForDir(c, dir)
   243  	ctx.Stdin = stdin
   244  	err = addCmd.Run(ctx)
   245  	c.Assert(err, jc.ErrorIsNil)
   246  
   247  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   248  		"somecloud": {
   249  			AuthCredentials: map[string]jujucloud.Credential{
   250  				"fred": jujucloud.NewCredential(s.authTypes[0], map[string]string{
   251  					fileKey: filename,
   252  				}),
   253  			},
   254  		},
   255  	})
   256  }
   257  
   258  func (s *addCredentialSuite) TestAddJsonFileCredential(c *gc.C) {
   259  	s.authTypes = []jujucloud.AuthType{jujucloud.JSONFileAuthType}
   260  	s.schema = map[jujucloud.AuthType]jujucloud.CredentialSchema{
   261  		jujucloud.JSONFileAuthType: {
   262  			{
   263  				"file",
   264  				jujucloud.CredentialAttr{
   265  					Optional: false,
   266  					FilePath: true,
   267  				},
   268  			},
   269  		},
   270  	}
   271  	// Input includes invalid file info.
   272  	s.assertAddFileCredential(c, "fred\nbadfile\n.\n%s\n", "file")
   273  }
   274  
   275  func (s *addCredentialSuite) TestAddCredentialWithFileAttr(c *gc.C) {
   276  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   277  	s.schema = map[jujucloud.AuthType]jujucloud.CredentialSchema{
   278  		jujucloud.UserPassAuthType: {
   279  			{
   280  				"key",
   281  				jujucloud.CredentialAttr{
   282  					FileAttr: "key-file",
   283  				},
   284  			},
   285  		},
   286  	}
   287  	// Input includes invalid file info.
   288  	s.assertAddFileCredential(c, "fred\nbadfile\n.\n%s\n", "key-file")
   289  }
   290  
   291  func (s *addCredentialSuite) assertAddCredentialWithOptions(c *gc.C, input string) {
   292  	s.authTypes = []jujucloud.AuthType{jujucloud.UserPassAuthType}
   293  	s.schema = map[jujucloud.AuthType]jujucloud.CredentialSchema{
   294  		jujucloud.UserPassAuthType: {
   295  			{
   296  				"username", jujucloud.CredentialAttr{Optional: false},
   297  			}, {
   298  				"algorithm", jujucloud.CredentialAttr{Options: []interface{}{"optionA", "optionB"}},
   299  			},
   300  		},
   301  	}
   302  	// Input includes a bad option
   303  	stdin := strings.NewReader(input)
   304  	_, err := s.run(c, stdin, "somecloud")
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   307  		"somecloud": {
   308  			AuthCredentials: map[string]jujucloud.Credential{
   309  				"fred": jujucloud.NewCredential(jujucloud.UserPassAuthType, map[string]string{
   310  					"username":  "user",
   311  					"algorithm": "optionA",
   312  				}),
   313  			},
   314  		},
   315  	})
   316  }
   317  
   318  func (s *addCredentialSuite) TestAddCredentialWithOptions(c *gc.C) {
   319  	s.assertAddCredentialWithOptions(c, "fred\nuser\nbadoption\noptionA\n")
   320  }
   321  
   322  func (s *addCredentialSuite) TestAddCredentialWithOptionsAutofill(c *gc.C) {
   323  	s.assertAddCredentialWithOptions(c, "fred\nuser\n\n")
   324  }
   325  
   326  func (s *addCredentialSuite) TestAddMAASCredential(c *gc.C) {
   327  	s.authTypes = []jujucloud.AuthType{jujucloud.OAuth1AuthType}
   328  	s.schema = map[jujucloud.AuthType]jujucloud.CredentialSchema{
   329  		jujucloud.OAuth1AuthType: {
   330  			{
   331  				"maas-oauth", jujucloud.CredentialAttr{},
   332  			},
   333  		},
   334  	}
   335  	stdin := strings.NewReader("fred\nauth:token\n")
   336  	_, err := s.run(c, stdin, "maas")
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	c.Assert(s.store.Credentials, jc.DeepEquals, map[string]jujucloud.CloudCredential{
   339  		"maas": {
   340  			AuthCredentials: map[string]jujucloud.Credential{
   341  				"fred": jujucloud.NewCredential(jujucloud.OAuth1AuthType, map[string]string{
   342  					"maas-oauth": "auth:token",
   343  				}),
   344  			},
   345  		},
   346  	})
   347  }