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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller_test
     5  
     6  import (
     7  	"encoding/asn1"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"net/url"
    15  	"strings"
    16  
    17  	"github.com/juju/cmd"
    18  	"github.com/juju/cmd/cmdtesting"
    19  	"github.com/juju/errors"
    20  	jc "github.com/juju/testing/checkers"
    21  	"golang.org/x/crypto/nacl/secretbox"
    22  	gc "gopkg.in/check.v1"
    23  	"gopkg.in/juju/names.v2"
    24  
    25  	"github.com/juju/juju/api"
    26  	"github.com/juju/juju/api/base"
    27  	"github.com/juju/juju/apiserver/params"
    28  	"github.com/juju/juju/cmd/juju/controller"
    29  	"github.com/juju/juju/core/model"
    30  	"github.com/juju/juju/jujuclient"
    31  	"github.com/juju/juju/testing"
    32  )
    33  
    34  type RegisterSuite struct {
    35  	testing.FakeJujuXDGDataHomeSuite
    36  	apiConnection            *mockAPIConnection
    37  	store                    *jujuclient.MemStore
    38  	apiOpenError             error
    39  	listModels               func(jujuclient.ClientStore, string, string) ([]base.UserModel, error)
    40  	listModelsControllerName string
    41  	listModelsUserName       string
    42  	server                   *httptest.Server
    43  	httpHandler              http.Handler
    44  }
    45  
    46  const noModelsText = `
    47  There are no models available. You can add models with
    48  "juju add-model", or you can ask an administrator or owner
    49  of a model to grant access to that model with "juju grant".
    50  `
    51  
    52  var _ = gc.Suite(&RegisterSuite{})
    53  
    54  func (s *RegisterSuite) SetUpTest(c *gc.C) {
    55  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
    56  
    57  	s.apiOpenError = nil
    58  	s.httpHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    59  	s.server = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    60  		s.httpHandler.ServeHTTP(w, r)
    61  	}))
    62  
    63  	serverURL, err := url.Parse(s.server.URL)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	s.apiConnection = &mockAPIConnection{
    66  		controllerTag: names.NewControllerTag(mockControllerUUID),
    67  		addr:          serverURL.Host,
    68  	}
    69  	s.listModelsControllerName = ""
    70  	s.listModelsUserName = ""
    71  	s.listModels = func(_ jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
    72  		s.listModelsControllerName = controllerName
    73  		s.listModelsUserName = userName
    74  		return nil, nil
    75  	}
    76  
    77  	s.store = jujuclient.NewMemStore()
    78  }
    79  
    80  func (s *RegisterSuite) TearDownTest(c *gc.C) {
    81  	s.server.Close()
    82  	s.FakeJujuXDGDataHomeSuite.TearDownTest(c)
    83  }
    84  
    85  func (s *RegisterSuite) TestInit(c *gc.C) {
    86  	registerCommand := controller.NewRegisterCommandForTest(nil, nil, nil)
    87  
    88  	err := cmdtesting.InitCommand(registerCommand, []string{})
    89  	c.Assert(err, gc.ErrorMatches, "registration data missing")
    90  
    91  	err = cmdtesting.InitCommand(registerCommand, []string{"foo", "bar"})
    92  	c.Assert(err, gc.ErrorMatches, `unrecognized args: \["bar"\]`)
    93  }
    94  
    95  func (s *RegisterSuite) TestRegister(c *gc.C) {
    96  	s.testRegisterSuccess(c, nil, "")
    97  	c.Assert(s.listModelsControllerName, gc.Equals, "controller-name")
    98  	c.Assert(s.listModelsUserName, gc.Equals, "bob")
    99  }
   100  
   101  func (s *RegisterSuite) TestRegisterOneModel(c *gc.C) {
   102  	s.listModels = func(_ jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
   103  		return []base.UserModel{{
   104  			Name:  "theoneandonly",
   105  			Owner: "carol",
   106  			UUID:  mockControllerUUID,
   107  			Type:  model.IAAS,
   108  		}}, nil
   109  	}
   110  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   111  Enter a new password: »hunter2
   112  
   113  Confirm password: »hunter2
   114  
   115  Enter a name for this controller \[controller-name\]: »
   116  Initial password successfully set for bob.
   117  
   118  Welcome, bob. You are now logged into "controller-name".
   119  
   120  Current model set to "carol/theoneandonly".
   121  `[1:])
   122  	s.testRegisterSuccess(c, prompter, "")
   123  	c.Assert(
   124  		s.store.Models["controller-name"].CurrentModel,
   125  		gc.Equals, "carol/theoneandonly",
   126  	)
   127  	prompter.CheckDone()
   128  }
   129  
   130  func (s *RegisterSuite) TestRegisterMultipleModels(c *gc.C) {
   131  	s.listModels = func(_ jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
   132  		return []base.UserModel{{
   133  			Name:  "model1",
   134  			Owner: "bob",
   135  			UUID:  mockControllerUUID,
   136  			Type:  model.IAAS,
   137  		}, {
   138  			Name:  "model2",
   139  			Owner: "bob",
   140  			UUID:  "eeeeeeee-12e9-11e4-8a70-b2227cce2b55",
   141  			Type:  model.IAAS,
   142  		}}, nil
   143  	}
   144  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   145  Enter a new password: »hunter2
   146  
   147  Confirm password: »hunter2
   148  
   149  Enter a name for this controller \[controller-name\]: »
   150  Initial password successfully set for bob.
   151  
   152  Welcome, bob. You are now logged into "controller-name".
   153  
   154  There are 2 models available. Use "juju switch" to select
   155  one of them:
   156    - juju switch model1
   157    - juju switch model2
   158  `[1:])
   159  	defer prompter.CheckDone()
   160  	s.testRegisterSuccess(c, prompter, "")
   161  
   162  	// When there are multiple models, no current model will be set.
   163  	// Instead, the command will output the list of models and inform
   164  	// the user how to set the current model.
   165  	_, err := s.store.CurrentModel("controller-name")
   166  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   167  }
   168  
   169  // testRegisterSuccess tests that the register command when the given
   170  // stdio instance is used for input and output. If stdio is nil, a
   171  // default prompter will be used.
   172  // If controllerName is non-empty, that name will be expected
   173  // to be the name of the registered controller.
   174  func (s *RegisterSuite) testRegisterSuccess(c *gc.C, stdio io.ReadWriter, controllerName string) {
   175  	srv := s.mockServer(c)
   176  	s.httpHandler = srv
   177  
   178  	if controllerName == "" {
   179  		controllerName = "controller-name"
   180  	}
   181  
   182  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   183  		User:           "bob",
   184  		SecretKey:      mockSecretKey,
   185  		ControllerName: "controller-name",
   186  	})
   187  	c.Logf("registration data: %q", registrationData)
   188  	if stdio == nil {
   189  		prompter := cmdtesting.NewSeqPrompter(c, "»", `
   190  Enter a new password: »hunter2
   191  
   192  Confirm password: »hunter2
   193  
   194  Enter a name for this controller \[controller-name\]: »
   195  Initial password successfully set for bob.
   196  
   197  Welcome, bob. You are now logged into "controller-name".
   198  `[1:]+noModelsText)
   199  		defer prompter.CheckDone()
   200  		stdio = prompter
   201  	}
   202  	err := s.run(c, stdio, registrationData)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	// There should have been one POST command to "/register".
   206  	c.Assert(srv.requests, gc.HasLen, 1)
   207  	c.Assert(srv.requests[0].Method, gc.Equals, "POST")
   208  	c.Assert(srv.requests[0].URL.Path, gc.Equals, "/register")
   209  	var request params.SecretKeyLoginRequest
   210  	err = json.Unmarshal(srv.requestBodies[0], &request)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(request.User, jc.DeepEquals, "user-bob")
   213  	c.Assert(request.Nonce, gc.HasLen, 24)
   214  	requestPayloadPlaintext, err := json.Marshal(params.SecretKeyLoginRequestPayload{
   215  		"hunter2",
   216  	})
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	expectedCiphertext := s.seal(c, requestPayloadPlaintext, mockSecretKey, request.Nonce)
   219  	c.Assert(request.PayloadCiphertext, jc.DeepEquals, expectedCiphertext)
   220  
   221  	// The controller and account details should be recorded with
   222  	// the specified controller name and user
   223  	// name from the registration string.
   224  
   225  	controller, err := s.store.ControllerByName(controllerName)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	c.Assert(controller, jc.DeepEquals, &jujuclient.ControllerDetails{
   228  		ControllerUUID: mockControllerUUID,
   229  		APIEndpoints:   []string{s.apiConnection.addr},
   230  		CACert:         testing.CACert,
   231  	})
   232  	account, err := s.store.AccountDetails(controllerName)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(account, jc.DeepEquals, &jujuclient.AccountDetails{
   235  		User:            "bob",
   236  		LastKnownAccess: "login",
   237  	})
   238  }
   239  
   240  func (s *RegisterSuite) TestRegisterInvalidRegistrationData(c *gc.C) {
   241  	err := s.run(c, nil, "not base64")
   242  	c.Assert(err, gc.ErrorMatches, "illegal base64 data at input byte 3")
   243  
   244  	err = s.run(c, nil, "YXNuLjEK")
   245  	c.Assert(err, gc.ErrorMatches, "asn1: structure error: .*")
   246  }
   247  
   248  func (s *RegisterSuite) TestRegisterEmptyControllerName(c *gc.C) {
   249  	srv := s.mockServer(c)
   250  	s.httpHandler = srv
   251  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   252  		User:      "bob",
   253  		SecretKey: mockSecretKey,
   254  	})
   255  	// We check that it loops when an empty controller name
   256  	// is entered and that the loop terminates when the user
   257  	// types ^D.
   258  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   259  Enter a new password: »hunter2
   260  
   261  Confirm password: »hunter2
   262  
   263  Enter a name for this controller: »
   264  You must specify a non-empty controller name.
   265  Enter a name for this controller: »
   266  You must specify a non-empty controller name.
   267  Enter a name for this controller: »»
   268  `[1:])
   269  	err := s.run(c, prompter, registrationData)
   270  	c.Assert(err, gc.ErrorMatches, "EOF")
   271  	prompter.AssertDone()
   272  }
   273  
   274  func (s *RegisterSuite) TestRegisterControllerNameExists(c *gc.C) {
   275  	err := s.store.AddController("controller-name", jujuclient.ControllerDetails{
   276  		ControllerUUID: "0d75314a-5266-4f4f-8523-415be76f92dc",
   277  		CACert:         testing.CACert,
   278  	})
   279  	c.Assert(err, jc.ErrorIsNil)
   280  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   281  Enter a new password: »hunter2
   282  
   283  Confirm password: »hunter2
   284  
   285  Enter a name for this controller: »controller-name
   286  Controller "controller-name" already exists.
   287  Enter a name for this controller: »other-name
   288  Initial password successfully set for bob.
   289  
   290  Welcome, bob. You are now logged into "other-name".
   291  `[1:]+noModelsText)
   292  	s.testRegisterSuccess(c, prompter, "other-name")
   293  	prompter.AssertDone()
   294  }
   295  
   296  func (s *RegisterSuite) TestControllerUUIDExists(c *gc.C) {
   297  	// Controller has the UUID from s.testRegister to mimic a user with
   298  	// this controller already registered (regardless of its name).
   299  	err := s.store.AddController("controller-name", jujuclient.ControllerDetails{
   300  		ControllerUUID: mockControllerUUID,
   301  		CACert:         testing.CACert,
   302  	})
   303  
   304  	s.listModels = func(_ jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
   305  		return []base.UserModel{{
   306  			Name:  "model-name",
   307  			Owner: "bob",
   308  			UUID:  mockControllerUUID,
   309  			Type:  model.IAAS,
   310  		}}, nil
   311  	}
   312  
   313  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   314  		User:           "bob",
   315  		SecretKey:      mockSecretKey,
   316  		ControllerName: "controller-name",
   317  	})
   318  
   319  	srv := s.mockServer(c)
   320  	s.httpHandler = srv
   321  
   322  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   323  Enter a new password: »hunter2
   324  
   325  Confirm password: »hunter2
   326  
   327  Enter a name for this controller: »foo
   328  Initial password successfully set for bob.
   329  `[1:])
   330  	err = s.run(c, prompter, registrationData)
   331  	c.Assert(err, gc.ErrorMatches, `controller is already registered as "controller-name"`, gc.Commentf("details: %v", errors.Details(err)))
   332  	prompter.CheckDone()
   333  }
   334  
   335  func (s *RegisterSuite) TestProposedControllerNameExists(c *gc.C) {
   336  	// Controller does not have the UUID from s.testRegister, thereby
   337  	// mimicing a user with an already registered 'foreign' controller.
   338  	err := s.store.AddController("controller-name", jujuclient.ControllerDetails{
   339  		ControllerUUID: "0d75314a-5266-4f4f-8523-415be76f92dc",
   340  		CACert:         testing.CACert,
   341  	})
   342  	c.Assert(err, jc.ErrorIsNil)
   343  
   344  	s.listModels = func(_ jujuclient.ClientStore, controllerName, userName string) ([]base.UserModel, error) {
   345  		return []base.UserModel{{
   346  			Name:  "model-name",
   347  			Owner: "bob",
   348  			UUID:  mockControllerUUID,
   349  			Type:  model.IAAS,
   350  		}}, nil
   351  	}
   352  
   353  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   354  Enter a new password: »hunter2
   355  
   356  Confirm password: »hunter2
   357  
   358  Enter a name for this controller: »controller-name
   359  Controller "controller-name" already exists.
   360  Enter a name for this controller: »other-name
   361  Initial password successfully set for bob.
   362  
   363  Welcome, bob. You are now logged into "other-name".
   364  
   365  Current model set to "bob/model-name".
   366  `[1:])
   367  	defer prompter.CheckDone()
   368  	s.testRegisterSuccess(c, prompter, "other-name")
   369  }
   370  
   371  func (s *RegisterSuite) TestRegisterEmptyPassword(c *gc.C) {
   372  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   373  		User:      "bob",
   374  		SecretKey: mockSecretKey,
   375  	})
   376  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   377  Enter a new password: »
   378  
   379  `[1:])
   380  	defer prompter.CheckDone()
   381  	err := s.run(c, prompter, registrationData)
   382  	c.Assert(err, gc.ErrorMatches, "you must specify a non-empty password")
   383  }
   384  
   385  func (s *RegisterSuite) TestRegisterPasswordMismatch(c *gc.C) {
   386  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   387  		User:      "bob",
   388  		SecretKey: mockSecretKey,
   389  	})
   390  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   391  Enter a new password: »hunter2
   392  
   393  Confirm password: »hunter3
   394  
   395  `[1:])
   396  	defer prompter.CheckDone()
   397  	err := s.run(c, prompter, registrationData)
   398  	c.Assert(err, gc.ErrorMatches, "passwords do not match")
   399  }
   400  
   401  func (s *RegisterSuite) TestAPIOpenError(c *gc.C) {
   402  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   403  		User:      "bob",
   404  		SecretKey: mockSecretKey,
   405  	})
   406  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   407  Enter a new password: »hunter2
   408  
   409  Confirm password: »hunter2
   410  
   411  Enter a name for this controller: »foo
   412  `[1:])
   413  	defer prompter.CheckDone()
   414  	s.apiOpenError = errors.New("open failed")
   415  	err := s.run(c, prompter, registrationData)
   416  	c.Assert(c.GetTestLog(), gc.Matches, "(.|\n)*open failed(.|\n)*")
   417  	c.Assert(err, gc.ErrorMatches, `
   418  Provided registration token may have been expired.
   419  A controller administrator must reset your user to issue a new token.
   420  See "juju help change-user-password" for more information.`[1:])
   421  }
   422  
   423  func (s *RegisterSuite) TestRegisterServerError(c *gc.C) {
   424  	response, err := json.Marshal(params.ErrorResult{
   425  		Error: &params.Error{Message: "xyz", Code: "123"},
   426  	})
   427  
   428  	s.httpHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   429  		w.WriteHeader(http.StatusInternalServerError)
   430  		_, err = w.Write(response)
   431  		c.Check(err, jc.ErrorIsNil)
   432  	})
   433  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   434  Enter a new password: »hunter2
   435  
   436  Confirm password: »hunter2
   437  
   438  Enter a name for this controller: »foo
   439  
   440  `[1:])
   441  
   442  	registrationData := s.encodeRegistrationData(c, jujuclient.RegistrationInfo{
   443  		User:      "bob",
   444  		SecretKey: mockSecretKey,
   445  	})
   446  	err = s.run(c, prompter, registrationData)
   447  	c.Assert(c.GetTestLog(), gc.Matches, "(.|\n)* xyz(.|\n)*")
   448  	c.Assert(err, gc.ErrorMatches, `
   449  Provided registration token may have been expired.
   450  A controller administrator must reset your user to issue a new token.
   451  See "juju help change-user-password" for more information.`[1:])
   452  
   453  	// Check that the controller hasn't been added.
   454  	_, err = s.store.ControllerByName("controller-name")
   455  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   456  }
   457  
   458  func (s *RegisterSuite) TestRegisterPublic(c *gc.C) {
   459  	s.apiConnection.authTag = names.NewUserTag("bob@external")
   460  	s.apiConnection.controllerAccess = "login"
   461  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   462  Enter a name for this controller: »public-controller-name
   463  
   464  Welcome, bob@external. You are now logged into "public-controller-name".
   465  `[1:]+noModelsText)
   466  	defer prompter.CheckDone()
   467  	err := s.run(c, prompter, "0.1.2.3")
   468  	c.Assert(err, jc.ErrorIsNil)
   469  
   470  	// The controller and account details should be recorded with
   471  	// the specified controller name and user
   472  	// name from the auth tag.
   473  
   474  	controller, err := s.store.ControllerByName("public-controller-name")
   475  	c.Assert(err, jc.ErrorIsNil)
   476  	c.Assert(controller, jc.DeepEquals, &jujuclient.ControllerDetails{
   477  		ControllerUUID: mockControllerUUID,
   478  		APIEndpoints:   []string{"0.1.2.3:443"},
   479  	})
   480  	account, err := s.store.AccountDetails("public-controller-name")
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	c.Assert(account, jc.DeepEquals, &jujuclient.AccountDetails{
   483  		User:            "bob@external",
   484  		LastKnownAccess: "login",
   485  	})
   486  }
   487  
   488  func (s *RegisterSuite) TestRegisterPublicAPIOpenError(c *gc.C) {
   489  	s.apiOpenError = errors.New("open failed")
   490  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   491  Enter a name for this controller: »public-controller-name
   492  `[1:])
   493  	defer prompter.CheckDone()
   494  	err := s.run(c, prompter, "0.1.2.3")
   495  	c.Assert(err, gc.ErrorMatches, `open failed`)
   496  }
   497  
   498  func (s *RegisterSuite) TestRegisterPublicWithPort(c *gc.C) {
   499  	s.apiConnection.authTag = names.NewUserTag("bob@external")
   500  	s.apiConnection.controllerAccess = "login"
   501  	prompter := cmdtesting.NewSeqPrompter(c, "»", `
   502  Enter a name for this controller: »public-controller-name
   503  
   504  Welcome, bob@external. You are now logged into "public-controller-name".
   505  `[1:]+noModelsText)
   506  	defer prompter.CheckDone()
   507  	err := s.run(c, prompter, "0.1.2.3:5678")
   508  	c.Assert(err, jc.ErrorIsNil)
   509  
   510  	// The controller and account details should be recorded with
   511  	// the specified controller name and user
   512  	// name from the auth tag.
   513  
   514  	controller, err := s.store.ControllerByName("public-controller-name")
   515  	c.Assert(err, jc.ErrorIsNil)
   516  	c.Assert(controller, jc.DeepEquals, &jujuclient.ControllerDetails{
   517  		ControllerUUID: mockControllerUUID,
   518  		APIEndpoints:   []string{"0.1.2.3:5678"},
   519  	})
   520  }
   521  
   522  type mockServer struct {
   523  	requests      []*http.Request
   524  	requestBodies [][]byte
   525  	response      []byte
   526  }
   527  
   528  const mockControllerUUID = "df136476-12e9-11e4-8a70-b2227cce2b54"
   529  
   530  var mockSecretKey = []byte(strings.Repeat("X", 32))
   531  
   532  // mockServer returns a mock HTTP server that will always respond with a
   533  // response encoded with mockSecretKey and a constant nonce, containing
   534  // testing.CACert and mockControllerUUID.
   535  //
   536  // Each time a call is made, the requests and requestBodies fields in
   537  // the returned mockServer instance are appended with the request details.
   538  func (s *RegisterSuite) mockServer(c *gc.C) *mockServer {
   539  	respNonce := []byte(strings.Repeat("X", 24))
   540  
   541  	responsePayloadPlaintext, err := json.Marshal(params.SecretKeyLoginResponsePayload{
   542  		CACert:         testing.CACert,
   543  		ControllerUUID: mockControllerUUID,
   544  	})
   545  	c.Assert(err, jc.ErrorIsNil)
   546  	response, err := json.Marshal(params.SecretKeyLoginResponse{
   547  		Nonce:             respNonce,
   548  		PayloadCiphertext: s.seal(c, responsePayloadPlaintext, mockSecretKey, respNonce),
   549  	})
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	return &mockServer{
   552  		response: response,
   553  	}
   554  }
   555  
   556  func (s *RegisterSuite) encodeRegistrationData(c *gc.C, info jujuclient.RegistrationInfo) string {
   557  	info.Addrs = []string{s.apiConnection.addr}
   558  	data, err := asn1.Marshal(info)
   559  	c.Assert(err, jc.ErrorIsNil)
   560  	// Append some junk to the end of the encoded data to
   561  	// ensure that, if we have to pad the data in add-user,
   562  	// register can still decode it.
   563  	data = append(data, 0, 0, 0)
   564  	return base64.URLEncoding.EncodeToString(data)
   565  }
   566  
   567  func (srv *mockServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   568  	srv.requests = append(srv.requests, r)
   569  	requestBody, err := ioutil.ReadAll(r.Body)
   570  	if err != nil {
   571  		panic(err)
   572  	}
   573  	srv.requestBodies = append(srv.requestBodies, requestBody)
   574  	_, err = w.Write(srv.response)
   575  	if err != nil {
   576  		panic(err)
   577  	}
   578  }
   579  
   580  func (s *RegisterSuite) apiOpen(info *api.Info, opts api.DialOpts) (api.Connection, error) {
   581  	if s.apiOpenError != nil {
   582  		return nil, s.apiOpenError
   583  	}
   584  	return s.apiConnection, nil
   585  }
   586  
   587  func (s *RegisterSuite) run(c *gc.C, stdio io.ReadWriter, args ...string) error {
   588  	if stdio == nil {
   589  		p := noPrompts(c)
   590  		stdio = p
   591  		defer p.CheckDone()
   592  	}
   593  
   594  	command := controller.NewRegisterCommandForTest(s.apiOpen, s.listModels, s.store)
   595  	err := cmdtesting.InitCommand(command, args)
   596  	c.Assert(err, jc.ErrorIsNil)
   597  	return command.Run(&cmd.Context{
   598  		Dir:    c.MkDir(),
   599  		Stdin:  stdio,
   600  		Stdout: stdio,
   601  		Stderr: stdio,
   602  	})
   603  }
   604  
   605  func noPrompts(c *gc.C) *cmdtesting.SeqPrompter {
   606  	return cmdtesting.NewSeqPrompter(c, "»", "")
   607  }
   608  
   609  func (s *RegisterSuite) seal(c *gc.C, message, key, nonce []byte) []byte {
   610  	var keyArray [32]byte
   611  	var nonceArray [24]byte
   612  	c.Assert(copy(keyArray[:], key), gc.Equals, len(keyArray))
   613  	c.Assert(copy(nonceArray[:], nonce), gc.Equals, len(nonceArray))
   614  	return secretbox.Seal(nil, message, &nonceArray, &keyArray)
   615  }