github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/cloud/detectcredentials_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  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/juju/cmd"
    13  	"github.com/juju/cmd/cmdtesting"
    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"
    22  	"github.com/juju/juju/testing"
    23  )
    24  
    25  type detectCredentialsSuite struct {
    26  	testing.BaseSuite
    27  
    28  	store       *jujuclient.MemStore
    29  	aCredential jujucloud.CloudCredential
    30  }
    31  
    32  var _ = gc.Suite(&detectCredentialsSuite{})
    33  
    34  type mockProvider struct {
    35  	environs.CloudEnvironProvider
    36  	detectedCreds *jujucloud.CloudCredential
    37  	credSchemas   *map[jujucloud.AuthType]jujucloud.CredentialSchema
    38  }
    39  
    40  func (p *mockProvider) DetectCredentials() (*jujucloud.CloudCredential, error) {
    41  	if len(p.detectedCreds.AuthCredentials) == 0 {
    42  		return nil, errors.NotFoundf("credentials")
    43  	}
    44  	return p.detectedCreds, nil
    45  }
    46  
    47  func (p *mockProvider) CredentialSchemas() map[jujucloud.AuthType]jujucloud.CredentialSchema {
    48  	if p.credSchemas == nil {
    49  		return map[jujucloud.AuthType]jujucloud.CredentialSchema{
    50  			jujucloud.AccessKeyAuthType: {
    51  				{
    52  					"access-key", jujucloud.CredentialAttr{},
    53  				}, {
    54  					"secret-key", jujucloud.CredentialAttr{Hidden: true},
    55  				},
    56  			},
    57  			jujucloud.UserPassAuthType: {
    58  				{
    59  					"username", jujucloud.CredentialAttr{},
    60  				}, {
    61  					"password", jujucloud.CredentialAttr{Hidden: true},
    62  				}, {
    63  					"application-password", jujucloud.CredentialAttr{Hidden: true},
    64  				},
    65  			},
    66  			jujucloud.OAuth2AuthType: {
    67  				{
    68  					"client-id", jujucloud.CredentialAttr{},
    69  				}, {
    70  					"client-email", jujucloud.CredentialAttr{},
    71  				}, {
    72  					"private-key", jujucloud.CredentialAttr{Hidden: true},
    73  				}, {
    74  					"project-id", jujucloud.CredentialAttr{},
    75  				},
    76  			},
    77  		}
    78  	}
    79  	return *p.credSchemas
    80  }
    81  
    82  func (p *mockProvider) FinalizeCredential(
    83  	ctx environs.FinalizeCredentialContext,
    84  	args environs.FinalizeCredentialParams,
    85  ) (*jujucloud.Credential, error) {
    86  	if args.Credential.AuthType() == "interactive" {
    87  		fmt.Fprintln(ctx.GetStderr(), "generating userpass credential")
    88  		out := jujucloud.NewCredential(jujucloud.UserPassAuthType, map[string]string{
    89  			"username":             args.Credential.Attributes()["username"],
    90  			"password":             args.CloudEndpoint,
    91  			"application-password": args.CloudIdentityEndpoint,
    92  		})
    93  		return &out, nil
    94  	}
    95  	return &args.Credential, nil
    96  }
    97  
    98  func (s *detectCredentialsSuite) SetUpSuite(c *gc.C) {
    99  	s.BaseSuite.SetUpSuite(c)
   100  	unreg := environs.RegisterProvider("mock-provider", &mockProvider{detectedCreds: &s.aCredential})
   101  	s.AddCleanup(func(_ *gc.C) {
   102  		unreg()
   103  	})
   104  }
   105  
   106  func (s *detectCredentialsSuite) SetUpTest(c *gc.C) {
   107  	s.BaseSuite.SetUpTest(c)
   108  	s.store = jujuclient.NewMemStore()
   109  	s.aCredential = jujucloud.CloudCredential{}
   110  }
   111  
   112  func (s *detectCredentialsSuite) run(c *gc.C, stdin io.Reader, clouds map[string]jujucloud.Cloud) (*cmd.Context, error) {
   113  	registeredProvidersFunc := func() []string {
   114  		return []string{"mock-provider"}
   115  	}
   116  	allCloudsFunc := func() (map[string]jujucloud.Cloud, error) {
   117  		return clouds, nil
   118  	}
   119  	cloudByNameFunc := func(cloudName string) (*jujucloud.Cloud, error) {
   120  		if cloud, ok := clouds[cloudName]; ok {
   121  			return &cloud, nil
   122  		}
   123  		return nil, errors.NotFoundf("cloud %s", cloudName)
   124  	}
   125  	command := cloud.NewDetectCredentialsCommandForTest(s.store, registeredProvidersFunc, allCloudsFunc, cloudByNameFunc)
   126  	err := cmdtesting.InitCommand(command, nil)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	ctx := cmdtesting.Context(c)
   129  	ctx.Stdin = stdin
   130  	return ctx, command.Run(ctx)
   131  }
   132  
   133  func (s *detectCredentialsSuite) credentialWithLabel(authType jujucloud.AuthType, label string) jujucloud.Credential {
   134  	cred := jujucloud.NewCredential(authType, nil)
   135  	cred.Label = label
   136  	return cred
   137  }
   138  
   139  func (s *detectCredentialsSuite) assertDetectCredential(c *gc.C, cloudName, expectedRegion, errText string) {
   140  	s.aCredential = jujucloud.CloudCredential{
   141  		DefaultRegion: "default region",
   142  		AuthCredentials: map[string]jujucloud.Credential{
   143  			"test": s.credentialWithLabel(jujucloud.AccessKeyAuthType, "credential")},
   144  	}
   145  	clouds := map[string]jujucloud.Cloud{
   146  		"test-cloud": {
   147  			Type: "mock-provider",
   148  		},
   149  		"another-cloud": {
   150  			Type: "another-provider",
   151  		},
   152  	}
   153  
   154  	stdin := strings.NewReader(fmt.Sprintf("1\n%s\nQ\n", cloudName))
   155  	ctx, err := s.run(c, stdin, clouds)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	if errText == "" {
   158  		if expectedRegion != "" {
   159  			s.aCredential.DefaultRegion = expectedRegion
   160  		}
   161  		c.Assert(s.store.Credentials["test-cloud"], jc.DeepEquals, s.aCredential)
   162  	} else {
   163  		output := strings.Replace(cmdtesting.Stderr(ctx), "\n", "", -1)
   164  		c.Assert(output, gc.Matches, ".*"+regexp.QuoteMeta(errText)+".*")
   165  	}
   166  }
   167  
   168  func (s *detectCredentialsSuite) TestDetectNewCredential(c *gc.C) {
   169  	s.assertDetectCredential(c, "test-cloud", "", "")
   170  }
   171  
   172  func (s *detectCredentialsSuite) TestDetectCredentialOverwrites(c *gc.C) {
   173  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   174  		"test-cloud": {
   175  			AuthCredentials: map[string]jujucloud.Credential{
   176  				"test": jujucloud.NewCredential(jujucloud.AccessKeyAuthType, nil),
   177  			},
   178  		},
   179  	}
   180  	s.assertDetectCredential(c, "test-cloud", "", "")
   181  }
   182  
   183  func (s *detectCredentialsSuite) TestDetectCredentialKeepsExistingRegion(c *gc.C) {
   184  	s.store.Credentials = map[string]jujucloud.CloudCredential{
   185  		"test-cloud": {
   186  			DefaultRegion: "west",
   187  			AuthCredentials: map[string]jujucloud.Credential{
   188  				"test": jujucloud.NewCredential(jujucloud.AccessKeyAuthType, nil),
   189  			},
   190  		},
   191  	}
   192  	s.assertDetectCredential(c, "test-cloud", "west", "")
   193  }
   194  
   195  func (s *detectCredentialsSuite) TestDetectCredentialDefaultCloud(c *gc.C) {
   196  	s.assertDetectCredential(c, "", "", "")
   197  }
   198  
   199  func (s *detectCredentialsSuite) TestDetectCredentialUnknownCloud(c *gc.C) {
   200  	s.assertDetectCredential(c, "foo", "", "cloud foo not valid")
   201  }
   202  
   203  func (s *detectCredentialsSuite) TestDetectCredentialInvalidCloud(c *gc.C) {
   204  	s.assertDetectCredential(c, "another-cloud", "", "chosen credentials not compatible with a another-provider cloud")
   205  }
   206  
   207  func (s *detectCredentialsSuite) TestNewDetectCredentialNoneFound(c *gc.C) {
   208  	stdin := strings.NewReader("")
   209  	ctx, err := s.run(c, stdin, nil)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	output := strings.Replace(cmdtesting.Stderr(ctx), "\n", "", -1)
   212  	c.Assert(output, gc.Matches, ".*No cloud credentials found.*")
   213  	c.Assert(s.store.Credentials, gc.HasLen, 0)
   214  }
   215  
   216  func (s *detectCredentialsSuite) TestDetectCredentialInvalidChoice(c *gc.C) {
   217  	s.aCredential = jujucloud.CloudCredential{
   218  		DefaultRegion: "detected region",
   219  		AuthCredentials: map[string]jujucloud.Credential{
   220  			"test":    s.credentialWithLabel(jujucloud.AccessKeyAuthType, "credential 1"),
   221  			"another": s.credentialWithLabel(jujucloud.AccessKeyAuthType, "credential 2")},
   222  	}
   223  
   224  	stdin := strings.NewReader("3\nQ\n")
   225  	ctx, err := s.run(c, stdin, nil)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	output := strings.Replace(cmdtesting.Stderr(ctx), "\n", "", -1)
   228  	c.Assert(output, gc.Matches, ".*Invalid choice, enter a number between 1 and 2.*")
   229  	c.Assert(s.store.Credentials, gc.HasLen, 0)
   230  }