github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/caas/add_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caas_test
     5  
     6  import (
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/juju/cmd"
    13  	"github.com/juju/cmd/cmdtesting"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  	jujutesting "github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils/set"
    19  	gc "gopkg.in/check.v1"
    20  	"gopkg.in/juju/names.v2"
    21  	"gopkg.in/yaml.v2"
    22  
    23  	"github.com/juju/juju/apiserver/params"
    24  	"github.com/juju/juju/caas/kubernetes/clientconfig"
    25  	"github.com/juju/juju/cloud"
    26  	jujucloud "github.com/juju/juju/cloud"
    27  	"github.com/juju/juju/cmd/juju/caas"
    28  	jujucmdcloud "github.com/juju/juju/cmd/juju/cloud"
    29  	"github.com/juju/juju/jujuclient"
    30  )
    31  
    32  type addCAASSuite struct {
    33  	jujutesting.IsolationSuite
    34  	dir                       string
    35  	fakeCloudAPI              *fakeAddCloudAPI
    36  	fakeK8sBrokerRegionLister *fakeK8sBrokerRegionLister
    37  	store                     *fakeCloudMetadataStore
    38  	fileCredentialStore       *fakeCredentialStore
    39  	fakeK8SConfigFunc         *clientconfig.ClientConfigFunc
    40  	currentClusterRegionSet   *set.Strings
    41  }
    42  
    43  var _ = gc.Suite(&addCAASSuite{})
    44  
    45  var kubeConfigStr = `
    46  apiVersion: v1
    47  kind: Config
    48  clusters:
    49  - cluster:
    50      server: https://1.1.1.1:8888
    51      certificate-authority-data: QQ==
    52    name: the-cluster
    53  contexts:
    54  - context:
    55      cluster: the-cluster
    56      user: the-user
    57    name: the-context
    58  current-context: the-context
    59  preferences: {}
    60  users:
    61  - name: the-user
    62    user:
    63      password: thepassword
    64      username: theuser
    65  `
    66  
    67  type fakeCloudMetadataStore struct {
    68  	*jujutesting.CallMocker
    69  }
    70  
    71  func (f *fakeCloudMetadataStore) ParseCloudMetadataFile(path string) (map[string]cloud.Cloud, error) {
    72  	results := f.MethodCall(f, "ParseCloudMetadataFile", path)
    73  	return results[0].(map[string]cloud.Cloud), jujutesting.TypeAssertError(results[1])
    74  }
    75  
    76  func (f *fakeCloudMetadataStore) ParseOneCloud(data []byte) (cloud.Cloud, error) {
    77  	results := f.MethodCall(f, "ParseOneCloud", data)
    78  	return results[0].(cloud.Cloud), jujutesting.TypeAssertError(results[1])
    79  }
    80  
    81  func (f *fakeCloudMetadataStore) PublicCloudMetadata(searchPaths ...string) (result map[string]cloud.Cloud, fallbackUsed bool, _ error) {
    82  	results := f.MethodCall(f, "PublicCloudMetadata", searchPaths)
    83  	return results[0].(map[string]cloud.Cloud), results[1].(bool), jujutesting.TypeAssertError(results[2])
    84  }
    85  
    86  func (f *fakeCloudMetadataStore) PersonalCloudMetadata() (map[string]cloud.Cloud, error) {
    87  	results := f.MethodCall(f, "PersonalCloudMetadata")
    88  	return results[0].(map[string]cloud.Cloud), jujutesting.TypeAssertError(results[1])
    89  }
    90  
    91  func (f *fakeCloudMetadataStore) WritePersonalCloudMetadata(cloudsMap map[string]cloud.Cloud) error {
    92  	results := f.MethodCall(f, "WritePersonalCloudMetadata", cloudsMap)
    93  	return jujutesting.TypeAssertError(results[0])
    94  }
    95  
    96  type fakeAddCloudAPI struct {
    97  	*jujutesting.CallMocker
    98  	caas.AddCloudAPI
    99  	isCloudRegionRequired bool
   100  	authTypes             []cloud.AuthType
   101  	credentials           []names.CloudCredentialTag
   102  }
   103  
   104  func (api *fakeAddCloudAPI) Close() error {
   105  	return nil
   106  }
   107  
   108  func (api *fakeAddCloudAPI) AddCloud(kloud cloud.Cloud) error {
   109  	api.MethodCall(api, "AddCloud", kloud)
   110  	if kloud.HostCloudRegion == "" && api.isCloudRegionRequired {
   111  		return params.Error{Code: params.CodeCloudRegionRequired}
   112  	}
   113  	return nil
   114  }
   115  
   116  func (api *fakeAddCloudAPI) AddCredential(tag string, credential cloud.Credential) error {
   117  	return nil
   118  }
   119  
   120  type fakeK8sBrokerRegionLister struct {
   121  	*jujutesting.CallMocker
   122  	caas.K8sBrokerRegionLister
   123  }
   124  
   125  func (api *fakeK8sBrokerRegionLister) ListHostCloudRegions() (set.Strings, error) {
   126  	results := api.MethodCall(api, "ListHostCloudRegions")
   127  	return *results[0].(*set.Strings), jujutesting.TypeAssertError(results[1])
   128  }
   129  
   130  func fakeNewK8sClientConfig(_ io.Reader, contextName, clusterName string, _ clientconfig.K8sCredentialResolver) (*clientconfig.ClientConfig, error) {
   131  	cCfg := &clientconfig.ClientConfig{
   132  		CurrentContext: "key1",
   133  	}
   134  	contexts := map[string]clientconfig.Context{
   135  		"key1": {
   136  			CloudName:      "mrcloud1",
   137  			CredentialName: "credname1",
   138  		},
   139  		"key2": {
   140  			CloudName:      "mrcloud2",
   141  			CredentialName: "credname2",
   142  		},
   143  	}
   144  	clouds := map[string]clientconfig.CloudConfig{
   145  		"mrcloud1": {
   146  			Endpoint: "fakeendpoint1",
   147  			Attributes: map[string]interface{}{
   148  				"CAData": "fakecadata1",
   149  			},
   150  		},
   151  		"mrcloud2": {
   152  			Endpoint: "fakeendpoint2",
   153  			Attributes: map[string]interface{}{
   154  				"CAData": "fakecadata2",
   155  			},
   156  		},
   157  	}
   158  
   159  	var context clientconfig.Context
   160  	if contextName == "" {
   161  		contextName = cCfg.CurrentContext
   162  	}
   163  	if clusterName != "" {
   164  		var err error
   165  		context, contextName, err = func() (clientconfig.Context, string, error) {
   166  			for contextName, context := range contexts {
   167  				if clusterName == context.CloudName {
   168  					return context, contextName, nil
   169  				}
   170  			}
   171  			return clientconfig.Context{}, "", errors.NotFoundf("context for cluster name %q", clusterName)
   172  		}()
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  	} else {
   177  		context = contexts[contextName]
   178  	}
   179  	cCfg.Contexts = map[string]clientconfig.Context{contextName: context}
   180  	cCfg.Clouds = map[string]clientconfig.CloudConfig{context.CloudName: clouds[context.CloudName]}
   181  	return cCfg, nil
   182  }
   183  
   184  func fakeEmptyNewK8sClientConfig(io.Reader, string, string, clientconfig.K8sCredentialResolver) (*clientconfig.ClientConfig, error) {
   185  	return &clientconfig.ClientConfig{}, nil
   186  }
   187  
   188  type fakeCredentialStore struct {
   189  	jujutesting.Stub
   190  }
   191  
   192  func (fcs *fakeCredentialStore) CredentialForCloud(string) (*cloud.CloudCredential, error) {
   193  	panic("unexpected call to CredentialForCloud")
   194  }
   195  
   196  func (fcs *fakeCredentialStore) AllCredentials() (map[string]cloud.CloudCredential, error) {
   197  	fcs.AddCall("AllCredentials")
   198  	return map[string]cloud.CloudCredential{}, nil
   199  }
   200  
   201  func (fcs *fakeCredentialStore) UpdateCredential(cloudName string, details cloud.CloudCredential) error {
   202  	fcs.AddCall("UpdateCredential", cloudName, details)
   203  	return nil
   204  }
   205  
   206  func (s *addCAASSuite) SetUpTest(c *gc.C) {
   207  	s.IsolationSuite.SetUpTest(c)
   208  	s.dir = c.MkDir()
   209  
   210  	var logger loggo.Logger
   211  	s.fakeCloudAPI = &fakeAddCloudAPI{
   212  		CallMocker: jujutesting.NewCallMocker(logger),
   213  		authTypes: []cloud.AuthType{
   214  			cloud.EmptyAuthType,
   215  			cloud.AccessKeyAuthType,
   216  		},
   217  		credentials: []names.CloudCredentialTag{
   218  			names.NewCloudCredentialTag("cloud/admin/default"),
   219  			names.NewCloudCredentialTag("aws/other/secrets"),
   220  		},
   221  	}
   222  	s.currentClusterRegionSet = &set.Strings{}
   223  
   224  	s.store = &fakeCloudMetadataStore{CallMocker: jujutesting.NewCallMocker(logger)}
   225  
   226  	s.fakeK8sBrokerRegionLister = &fakeK8sBrokerRegionLister{CallMocker: jujutesting.NewCallMocker(logger)}
   227  	s.fakeK8sBrokerRegionLister.Call("ListHostCloudRegions").Returns(s.currentClusterRegionSet, nil)
   228  
   229  	initialCloudMap := map[string]cloud.Cloud{
   230  		"mrcloud1": {Name: "mrcloud1", Type: "kubernetes"},
   231  		"mrcloud2": {Name: "mrcloud2", Type: "kubernetes"},
   232  	}
   233  
   234  	s.store.Call("PersonalCloudMetadata").Returns(initialCloudMap, nil)
   235  
   236  	s.store.Call("PublicCloudMetadata", []string(nil)).Returns(initialCloudMap, false, nil)
   237  	s.store.Call("WritePersonalCloudMetadata", initialCloudMap).Returns(nil)
   238  }
   239  
   240  func (s *addCAASSuite) writeTempKubeConfig(c *gc.C) {
   241  	fullpath := filepath.Join(s.dir, "empty-config")
   242  	err := ioutil.WriteFile(fullpath, []byte(""), 0644)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	os.Setenv("KUBECONFIG", fullpath)
   245  }
   246  
   247  func NewMockClientStore() *jujuclient.MemStore {
   248  	store := jujuclient.NewMemStore()
   249  	store.CurrentControllerName = "foo"
   250  	store.Accounts["foo"] = jujuclient.AccountDetails{
   251  		User: "foouser",
   252  	}
   253  	store.Controllers["foo"] = jujuclient.ControllerDetails{
   254  		APIEndpoints: []string{"0.1.2.3:1234"},
   255  	}
   256  	store.Models["foo"] = &jujuclient.ControllerModels{
   257  		CurrentModel: "admin/bar",
   258  		Models:       map[string]jujuclient.ModelDetails{"admin/bar": {}},
   259  	}
   260  	return store
   261  }
   262  
   263  func (s *addCAASSuite) makeCommand(c *gc.C, cloudTypeExists, emptyClientConfig, shouldFakeNewK8sClientConfig bool) cmd.Command {
   264  	return caas.NewAddCAASCommandForTest(
   265  		s.store,
   266  		&fakeCredentialStore{},
   267  		NewMockClientStore(),
   268  		func() (caas.AddCloudAPI, error) {
   269  			return s.fakeCloudAPI, nil
   270  		},
   271  		func(cloud jujucloud.Cloud, credential jujucloud.Credential) (caas.K8sBrokerRegionLister, error) {
   272  			return s.fakeK8sBrokerRegionLister, nil
   273  		},
   274  		caas.FakeCluster(kubeConfigStr),
   275  		func(caasType string) (clientconfig.ClientConfigFunc, error) {
   276  			if !cloudTypeExists {
   277  				return nil, errors.Errorf("unsupported cloud type '%s'", caasType)
   278  			}
   279  			if !shouldFakeNewK8sClientConfig {
   280  				return clientconfig.NewClientConfigReader(caasType)
   281  			}
   282  			s.writeTempKubeConfig(c)
   283  			if emptyClientConfig {
   284  				return fakeEmptyNewK8sClientConfig, nil
   285  			} else {
   286  				return fakeNewK8sClientConfig, nil
   287  			}
   288  		},
   289  		func() (map[string]*jujucmdcloud.CloudDetails, error) {
   290  			return map[string]*jujucmdcloud.CloudDetails{
   291  				"google": {
   292  					Source:           "public",
   293  					CloudType:        "gce",
   294  					CloudDescription: "Google Cloud Platform",
   295  					AuthTypes:        []string{"jsonfile", "oauth2"},
   296  					Regions: yaml.MapSlice{
   297  						{Key: "us-east1", Value: map[string]string{"Name": "us-east1", "Endpoint": "https://www.googleapis.com"}},
   298  					},
   299  					RegionsMap: map[string]jujucmdcloud.RegionDetails{
   300  						"us-east1": {Name: "us-east1", Endpoint: "https://www.googleapis.com"},
   301  					},
   302  				},
   303  			}, nil
   304  		},
   305  	)
   306  }
   307  
   308  func (s *addCAASSuite) runCommand(c *gc.C, stdin io.Reader, com cmd.Command, args ...string) (*cmd.Context, error) {
   309  	ctx := cmdtesting.Context(c)
   310  	if err := cmdtesting.InitCommand(com, args); err != nil {
   311  		cmd.WriteError(ctx.Stderr, err)
   312  		return ctx, err
   313  	}
   314  	if stdin != nil {
   315  		ctx.Stdin = stdin
   316  	}
   317  	return ctx, com.Run(ctx)
   318  }
   319  
   320  func (s *addCAASSuite) TestAddExtraArg(c *gc.C) {
   321  	cmd := s.makeCommand(c, true, true, true)
   322  	_, err := s.runCommand(c, nil, cmd, "k8sname", "extra")
   323  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["extra"\]`)
   324  }
   325  
   326  func (s *addCAASSuite) TestEmptyKubeConfigFileWithoutStdin(c *gc.C) {
   327  	cmd := s.makeCommand(c, true, true, true)
   328  	_, err := s.runCommand(c, nil, cmd, "k8sname")
   329  	c.Assert(err, gc.ErrorMatches, `No k8s cluster definitions found in config`)
   330  }
   331  
   332  func (s *addCAASSuite) TestAddNameClash(c *gc.C) {
   333  	cmd := s.makeCommand(c, true, false, true)
   334  	_, err := s.runCommand(c, nil, cmd, "mrcloud1")
   335  	c.Assert(err, gc.ErrorMatches, `"mrcloud1" is the name of a public cloud`)
   336  }
   337  
   338  func (s *addCAASSuite) TestMissingName(c *gc.C) {
   339  	cmd := s.makeCommand(c, true, true, true)
   340  	_, err := s.runCommand(c, nil, cmd)
   341  	c.Assert(err, gc.ErrorMatches, `missing k8s name.`)
   342  }
   343  
   344  func (s *addCAASSuite) TestMissingArgs(c *gc.C) {
   345  	cmd := s.makeCommand(c, true, true, true)
   346  	_, err := s.runCommand(c, nil, cmd)
   347  	c.Assert(err, gc.ErrorMatches, `missing k8s name.`)
   348  }
   349  
   350  func (s *addCAASSuite) TestNonExistClusterName(c *gc.C) {
   351  	cmd := s.makeCommand(c, true, false, true)
   352  	_, err := s.runCommand(c, nil, cmd, "myk8s", "--cluster-name", "non existing cluster name")
   353  	c.Assert(err, gc.ErrorMatches, `context for cluster name \"non existing cluster name\" not found`)
   354  }
   355  
   356  type initTestsCase struct {
   357  	args           []string
   358  	expectedErrStr string
   359  }
   360  
   361  func (s *addCAASSuite) TestInit(c *gc.C) {
   362  	for _, ts := range []initTestsCase{
   363  		{
   364  			args:           []string{"--context-name", "a", "--cluster-name", "b"},
   365  			expectedErrStr: "only specify one of cluster-name or context-name, not both",
   366  		},
   367  		{
   368  			args:           []string{"--gke", "--context-name", "a"},
   369  			expectedErrStr: "do not specify context name when adding a GKE cluster",
   370  		},
   371  		{
   372  			args:           []string{"--project", "a"},
   373  			expectedErrStr: "do not specify project unless adding a GKE cluster",
   374  		},
   375  		{
   376  			args:           []string{"--credential", "a"},
   377  			expectedErrStr: "do not specify credential unless adding a GKE cluster",
   378  		},
   379  	} {
   380  		args := append([]string{"myk8s"}, ts.args...)
   381  		cmd := s.makeCommand(c, true, false, true)
   382  		_, err := s.runCommand(c, nil, cmd, args...)
   383  		c.Check(err, gc.ErrorMatches, ts.expectedErrStr)
   384  	}
   385  }
   386  
   387  type regionTestCase struct {
   388  	title          string
   389  	regionStr      string
   390  	expectedErrStr string
   391  }
   392  
   393  func (s *addCAASSuite) TestRegionFlag(c *gc.C) {
   394  	for _, ts := range []regionTestCase{
   395  		{
   396  			title:          "missing cloud",
   397  			regionStr:      "/region",
   398  			expectedErrStr: `validating cloud region "/region": parsing cloud region: cloud region /region not valid`,
   399  		},
   400  		{
   401  			title:          "missing region",
   402  			regionStr:      "cloud/",
   403  			expectedErrStr: `validating cloud region "cloud/": parsing cloud region: cloud region cloud/ not valid`,
   404  		},
   405  		{
   406  			title:          "invalid formnat, it should be <cloudType>/<region>",
   407  			regionStr:      "cloudRegion",
   408  			expectedErrStr: `validating cloud region "cloudRegion": parsing cloud region: cloud region cloudRegion not valid`,
   409  		},
   410  		{
   411  			title:          "not a known juju cloud region",
   412  			regionStr:      "cloud/region",
   413  			expectedErrStr: `validating cloud region "cloud/region": cloud region cloud/region not valid`,
   414  		},
   415  		{
   416  			title:          "all good",
   417  			regionStr:      "gce/us-east1",
   418  			expectedErrStr: "",
   419  		},
   420  	} {
   421  		cmd := s.makeCommand(c, true, false, true)
   422  		_, err := s.runCommand(c, nil, cmd, "myk8s", "--cluster-name", "mrcloud2", "--region", ts.regionStr)
   423  		if ts.expectedErrStr == "" {
   424  			c.Check(err, jc.ErrorIsNil)
   425  		} else {
   426  			c.Check(err, gc.ErrorMatches, ts.expectedErrStr)
   427  		}
   428  	}
   429  }
   430  
   431  func (s *addCAASSuite) TestGatherClusterRegionMetaRegionNoMatchesThenIgnored(c *gc.C) {
   432  	cmd := s.makeCommand(c, true, false, true)
   433  	_, err := s.runCommand(c, nil, cmd, "myk8s", "--cluster-name", "mrcloud2")
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	s.store.CheckCall(c, 2, "WritePersonalCloudMetadata",
   436  		map[string]cloud.Cloud{
   437  			"mrcloud1": {
   438  				Name:             "mrcloud1",
   439  				Type:             "kubernetes",
   440  				Description:      "",
   441  				AuthTypes:        cloud.AuthTypes(nil),
   442  				Endpoint:         "",
   443  				IdentityEndpoint: "",
   444  				StorageEndpoint:  "",
   445  				Regions:          []cloud.Region(nil),
   446  				Config:           map[string]interface{}(nil),
   447  				RegionConfig:     cloud.RegionConfig(nil),
   448  			},
   449  			"mrcloud2": {
   450  				Name:             "mrcloud2",
   451  				Type:             "kubernetes",
   452  				Description:      "",
   453  				AuthTypes:        cloud.AuthTypes(nil),
   454  				Endpoint:         "",
   455  				IdentityEndpoint: "",
   456  				StorageEndpoint:  "",
   457  				Regions:          []cloud.Region(nil),
   458  				Config:           map[string]interface{}(nil),
   459  				RegionConfig:     cloud.RegionConfig(nil),
   460  			},
   461  			"myk8s": {
   462  				Name:             "myk8s",
   463  				Type:             "kubernetes",
   464  				Description:      "",
   465  				AuthTypes:        cloud.AuthTypes{""},
   466  				Endpoint:         "fakeendpoint2",
   467  				IdentityEndpoint: "",
   468  				StorageEndpoint:  "",
   469  				Regions:          []cloud.Region(nil),
   470  				Config:           map[string]interface{}(nil),
   471  				RegionConfig:     cloud.RegionConfig(nil),
   472  				CACertificates:   []string{"fakecadata2"},
   473  			},
   474  		},
   475  	)
   476  }
   477  
   478  func (s *addCAASSuite) TestGatherClusterRegionMetaRegionMatchesAndPassThrough(c *gc.C) {
   479  	s.fakeCloudAPI.isCloudRegionRequired = true
   480  	cloudRegion := "gce/us-east1"
   481  	*s.currentClusterRegionSet = set.NewStrings(cloudRegion)
   482  
   483  	cmd := s.makeCommand(c, true, false, true)
   484  	_, err := s.runCommand(c, nil, cmd, "myk8s", "--cluster-name", "mrcloud2")
   485  	c.Assert(err, jc.ErrorIsNil)
   486  	// 1st try failed because region was missing.
   487  	s.fakeCloudAPI.CheckCall(c, 0, "AddCloud",
   488  		cloud.Cloud{
   489  			Name:             "myk8s",
   490  			HostCloudRegion:  "", // empty cloud region, but isCloudRegionRequired is true
   491  			Type:             "kubernetes",
   492  			Description:      "",
   493  			AuthTypes:        cloud.AuthTypes{""},
   494  			Endpoint:         "fakeendpoint2",
   495  			IdentityEndpoint: "",
   496  			StorageEndpoint:  "",
   497  			Regions:          []cloud.Region(nil),
   498  			Config:           map[string]interface{}(nil),
   499  			RegionConfig:     cloud.RegionConfig(nil),
   500  			CACertificates:   []string{"fakecadata2"},
   501  		},
   502  	)
   503  	s.fakeK8sBrokerRegionLister.CheckCall(c, 0, "ListHostCloudRegions")
   504  	// 2nd try with region fetched from ListHostCloudRegions.
   505  	s.fakeCloudAPI.CheckCall(c, 1, "AddCloud",
   506  		cloud.Cloud{
   507  			Name:             "myk8s",
   508  			HostCloudRegion:  cloudRegion,
   509  			Type:             "kubernetes",
   510  			Description:      "",
   511  			AuthTypes:        cloud.AuthTypes{""},
   512  			Endpoint:         "fakeendpoint2",
   513  			IdentityEndpoint: "",
   514  			StorageEndpoint:  "",
   515  			Regions:          []cloud.Region(nil),
   516  			Config:           map[string]interface{}(nil),
   517  			RegionConfig:     cloud.RegionConfig(nil),
   518  			CACertificates:   []string{"fakecadata2"},
   519  		},
   520  	)
   521  	s.store.CheckCall(c, 2, "WritePersonalCloudMetadata",
   522  		map[string]cloud.Cloud{
   523  			"mrcloud1": {
   524  				Name:             "mrcloud1",
   525  				Type:             "kubernetes",
   526  				Description:      "",
   527  				AuthTypes:        cloud.AuthTypes(nil),
   528  				Endpoint:         "",
   529  				IdentityEndpoint: "",
   530  				StorageEndpoint:  "",
   531  				Regions:          []cloud.Region(nil),
   532  				Config:           map[string]interface{}(nil),
   533  				RegionConfig:     cloud.RegionConfig(nil),
   534  			},
   535  			"mrcloud2": {
   536  				Name:             "mrcloud2",
   537  				Type:             "kubernetes",
   538  				Description:      "",
   539  				AuthTypes:        cloud.AuthTypes(nil),
   540  				Endpoint:         "",
   541  				IdentityEndpoint: "",
   542  				StorageEndpoint:  "",
   543  				Regions:          []cloud.Region(nil),
   544  				Config:           map[string]interface{}(nil),
   545  				RegionConfig:     cloud.RegionConfig(nil),
   546  			},
   547  			"myk8s": {
   548  				Name:             "myk8s",
   549  				HostCloudRegion:  cloudRegion,
   550  				Type:             "kubernetes",
   551  				Description:      "",
   552  				AuthTypes:        cloud.AuthTypes{""},
   553  				Endpoint:         "fakeendpoint2",
   554  				IdentityEndpoint: "",
   555  				StorageEndpoint:  "",
   556  				Regions:          []cloud.Region(nil),
   557  				Config:           map[string]interface{}(nil),
   558  				RegionConfig:     cloud.RegionConfig(nil),
   559  				CACertificates:   []string{"fakecadata2"},
   560  			},
   561  		},
   562  	)
   563  }
   564  
   565  func mockStdinPipe(content string) (*os.File, error) {
   566  	pr, pw, err := os.Pipe()
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	go func() {
   571  		defer pw.Close()
   572  		io.WriteString(pw, content)
   573  	}()
   574  	return pr, nil
   575  }
   576  
   577  func (s *addCAASSuite) TestCorrectParseFromStdIn(c *gc.C) {
   578  	cmd := s.makeCommand(c, true, true, false)
   579  	stdIn, err := mockStdinPipe(kubeConfigStr)
   580  	defer stdIn.Close()
   581  	c.Assert(err, jc.ErrorIsNil)
   582  	_, err = s.runCommand(c, stdIn, cmd, "myk8s")
   583  	c.Assert(err, jc.ErrorIsNil)
   584  	s.assertStoreClouds(c, "")
   585  }
   586  
   587  func (s *addCAASSuite) TestAddGkeCluster(c *gc.C) {
   588  	cmd := s.makeCommand(c, true, true, false)
   589  	_, err := s.runCommand(c, nil, cmd, "--gke", "myk8s", "--region", "us-east1")
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	s.assertStoreClouds(c, "gce/us-east1")
   592  }
   593  
   594  func (s *addCAASSuite) assertStoreClouds(c *gc.C, hostCloud string) {
   595  	s.store.CheckCall(c, 2, "WritePersonalCloudMetadata",
   596  		map[string]cloud.Cloud{
   597  			"myk8s": {
   598  				Name:             "myk8s",
   599  				Type:             "kubernetes",
   600  				Description:      "",
   601  				AuthTypes:        cloud.AuthTypes{"userpass"},
   602  				Endpoint:         "https://1.1.1.1:8888",
   603  				IdentityEndpoint: "",
   604  				StorageEndpoint:  "",
   605  				HostCloudRegion:  hostCloud,
   606  				Regions:          []cloud.Region(nil),
   607  				Config:           map[string]interface{}(nil),
   608  				RegionConfig:     cloud.RegionConfig(nil),
   609  				CACertificates:   []string{"A"},
   610  			},
   611  			"mrcloud1": {
   612  				Name:             "mrcloud1",
   613  				Type:             "kubernetes",
   614  				Description:      "",
   615  				AuthTypes:        cloud.AuthTypes(nil),
   616  				Endpoint:         "",
   617  				IdentityEndpoint: "",
   618  				StorageEndpoint:  "",
   619  				Regions:          []cloud.Region(nil),
   620  				Config:           map[string]interface{}(nil),
   621  				RegionConfig:     cloud.RegionConfig(nil),
   622  			},
   623  			"mrcloud2": {
   624  				Name:             "mrcloud2",
   625  				Type:             "kubernetes",
   626  				Description:      "",
   627  				AuthTypes:        cloud.AuthTypes(nil),
   628  				Endpoint:         "",
   629  				IdentityEndpoint: "",
   630  				StorageEndpoint:  "",
   631  				Regions:          []cloud.Region(nil),
   632  				Config:           map[string]interface{}(nil),
   633  				RegionConfig:     cloud.RegionConfig(nil),
   634  			},
   635  		},
   636  	)
   637  }
   638  
   639  func (s *addCAASSuite) TestCorrectUseCurrentContext(c *gc.C) {
   640  	cmd := s.makeCommand(c, true, false, true)
   641  	_, err := s.runCommand(c, nil, cmd, "myk8s")
   642  	c.Assert(err, jc.ErrorIsNil)
   643  	s.store.CheckCall(c, 2, "WritePersonalCloudMetadata",
   644  		map[string]cloud.Cloud{
   645  			"mrcloud1": {
   646  				Name:             "mrcloud1",
   647  				Type:             "kubernetes",
   648  				Description:      "",
   649  				AuthTypes:        cloud.AuthTypes(nil),
   650  				Endpoint:         "",
   651  				IdentityEndpoint: "",
   652  				StorageEndpoint:  "",
   653  				Regions:          []cloud.Region(nil),
   654  				Config:           map[string]interface{}(nil),
   655  				RegionConfig:     cloud.RegionConfig(nil),
   656  			},
   657  			"mrcloud2": {
   658  				Name:             "mrcloud2",
   659  				Type:             "kubernetes",
   660  				Description:      "",
   661  				AuthTypes:        cloud.AuthTypes(nil),
   662  				Endpoint:         "",
   663  				IdentityEndpoint: "",
   664  				StorageEndpoint:  "",
   665  				Regions:          []cloud.Region(nil),
   666  				Config:           map[string]interface{}(nil),
   667  				RegionConfig:     cloud.RegionConfig(nil),
   668  			},
   669  			"myk8s": {
   670  				Name:             "myk8s",
   671  				Type:             "kubernetes",
   672  				Description:      "",
   673  				AuthTypes:        cloud.AuthTypes{""},
   674  				Endpoint:         "fakeendpoint1",
   675  				IdentityEndpoint: "",
   676  				StorageEndpoint:  "",
   677  				Regions:          []cloud.Region(nil),
   678  				Config:           map[string]interface{}(nil),
   679  				RegionConfig:     cloud.RegionConfig(nil),
   680  				CACertificates:   []string{"fakecadata1"},
   681  			},
   682  		},
   683  	)
   684  }
   685  
   686  func (s *addCAASSuite) TestCorrectSelectContext(c *gc.C) {
   687  	cmd := s.makeCommand(c, true, false, true)
   688  	_, err := s.runCommand(c, nil, cmd, "myk8s", "--cluster-name", "mrcloud2")
   689  	c.Assert(err, jc.ErrorIsNil)
   690  	s.store.CheckCall(c, 2, "WritePersonalCloudMetadata",
   691  		map[string]cloud.Cloud{
   692  			"mrcloud1": {
   693  				Name:             "mrcloud1",
   694  				Type:             "kubernetes",
   695  				Description:      "",
   696  				AuthTypes:        cloud.AuthTypes(nil),
   697  				Endpoint:         "",
   698  				IdentityEndpoint: "",
   699  				StorageEndpoint:  "",
   700  				Regions:          []cloud.Region(nil),
   701  				Config:           map[string]interface{}(nil),
   702  				RegionConfig:     cloud.RegionConfig(nil),
   703  			},
   704  			"mrcloud2": {
   705  				Name:             "mrcloud2",
   706  				Type:             "kubernetes",
   707  				Description:      "",
   708  				AuthTypes:        cloud.AuthTypes(nil),
   709  				Endpoint:         "",
   710  				IdentityEndpoint: "",
   711  				StorageEndpoint:  "",
   712  				Regions:          []cloud.Region(nil),
   713  				Config:           map[string]interface{}(nil),
   714  				RegionConfig:     cloud.RegionConfig(nil),
   715  			},
   716  			"myk8s": {
   717  				Name:             "myk8s",
   718  				Type:             "kubernetes",
   719  				Description:      "",
   720  				AuthTypes:        cloud.AuthTypes{""},
   721  				Endpoint:         "fakeendpoint2",
   722  				IdentityEndpoint: "",
   723  				StorageEndpoint:  "",
   724  				Regions:          []cloud.Region(nil),
   725  				Config:           map[string]interface{}(nil),
   726  				RegionConfig:     cloud.RegionConfig(nil),
   727  				CACertificates:   []string{"fakecadata2"},
   728  			},
   729  		},
   730  	)
   731  }