github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/migrationtarget/client_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationtarget_test
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/textproto"
    13  	"net/url"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/juju/errors"
    18  	"github.com/juju/httprequest"
    19  	jujutesting "github.com/juju/testing"
    20  	jc "github.com/juju/testing/checkers"
    21  	"github.com/juju/version"
    22  	gc "gopkg.in/check.v1"
    23  	"gopkg.in/juju/charm.v6"
    24  	"gopkg.in/juju/names.v2"
    25  
    26  	"github.com/juju/juju/api/base"
    27  	apitesting "github.com/juju/juju/api/base/testing"
    28  	"github.com/juju/juju/api/migrationtarget"
    29  	"github.com/juju/juju/apiserver/params"
    30  	coremigration "github.com/juju/juju/core/migration"
    31  	"github.com/juju/juju/resource/resourcetesting"
    32  	"github.com/juju/juju/tools"
    33  	jujuversion "github.com/juju/juju/version"
    34  )
    35  
    36  type ClientSuite struct {
    37  	jujutesting.IsolationSuite
    38  }
    39  
    40  var _ = gc.Suite(&ClientSuite{})
    41  
    42  func (s *ClientSuite) getClientAndStub(c *gc.C) (*migrationtarget.Client, *jujutesting.Stub) {
    43  	var stub jujutesting.Stub
    44  	apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
    45  		stub.AddCall(objType+"."+request, id, arg)
    46  		return errors.New("boom")
    47  	})
    48  	client := migrationtarget.NewClient(apiCaller)
    49  	return client, &stub
    50  }
    51  
    52  func (s *ClientSuite) TestPrechecks(c *gc.C) {
    53  	client, stub := s.getClientAndStub(c)
    54  
    55  	ownerTag := names.NewUserTag("owner")
    56  	vers := version.MustParse("1.2.3")
    57  	controllerVers := version.MustParse("1.2.5")
    58  
    59  	err := client.Prechecks(coremigration.ModelInfo{
    60  		UUID:                   "uuid",
    61  		Owner:                  ownerTag,
    62  		Name:                   "name",
    63  		AgentVersion:           vers,
    64  		ControllerAgentVersion: controllerVers,
    65  	})
    66  	c.Assert(err, gc.ErrorMatches, "boom")
    67  
    68  	expectedArg := params.MigrationModelInfo{
    69  		UUID:                   "uuid",
    70  		Name:                   "name",
    71  		OwnerTag:               ownerTag.String(),
    72  		AgentVersion:           vers,
    73  		ControllerAgentVersion: controllerVers,
    74  	}
    75  	stub.CheckCalls(c, []jujutesting.StubCall{
    76  		{"MigrationTarget.Prechecks", []interface{}{"", expectedArg}},
    77  	})
    78  }
    79  
    80  func (s *ClientSuite) TestImport(c *gc.C) {
    81  	client, stub := s.getClientAndStub(c)
    82  
    83  	err := client.Import([]byte("foo"))
    84  
    85  	expectedArg := params.SerializedModel{Bytes: []byte("foo")}
    86  	stub.CheckCalls(c, []jujutesting.StubCall{
    87  		{"MigrationTarget.Import", []interface{}{"", expectedArg}},
    88  	})
    89  	c.Assert(err, gc.ErrorMatches, "boom")
    90  }
    91  
    92  func (s *ClientSuite) TestAbort(c *gc.C) {
    93  	client, stub := s.getClientAndStub(c)
    94  
    95  	uuid := "fake"
    96  	err := client.Abort(uuid)
    97  	s.AssertModelCall(c, stub, names.NewModelTag(uuid), "Abort", err, true)
    98  }
    99  
   100  func (s *ClientSuite) TestActivate(c *gc.C) {
   101  	client, stub := s.getClientAndStub(c)
   102  
   103  	uuid := "fake"
   104  	err := client.Activate(uuid)
   105  	s.AssertModelCall(c, stub, names.NewModelTag(uuid), "Activate", err, true)
   106  }
   107  
   108  func (s *ClientSuite) TestOpenLogTransferStream(c *gc.C) {
   109  	caller := fakeConnector{Stub: &jujutesting.Stub{}}
   110  	client := migrationtarget.NewClient(caller)
   111  	stream, err := client.OpenLogTransferStream("bad-dad")
   112  	c.Assert(stream, gc.IsNil)
   113  	c.Assert(err, gc.ErrorMatches, "sound hound")
   114  
   115  	caller.Stub.CheckCall(c, 0, "ConnectControllerStream", "/migrate/logtransfer",
   116  		url.Values{"jujuclientversion": {jujuversion.Current.String()}},
   117  		http.Header{textproto.CanonicalMIMEHeaderKey(params.MigrationModelHTTPHeader): {"bad-dad"}},
   118  	)
   119  }
   120  
   121  func (s *ClientSuite) TestLatestLogTime(c *gc.C) {
   122  	var stub jujutesting.Stub
   123  	t1 := time.Date(2016, 12, 1, 10, 31, 0, 0, time.UTC)
   124  
   125  	apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   126  		target, ok := result.(*time.Time)
   127  		c.Assert(ok, jc.IsTrue)
   128  		*target = t1
   129  		stub.AddCall(objType+"."+request, id, arg)
   130  		return nil
   131  	})
   132  	client := migrationtarget.NewClient(apiCaller)
   133  	result, err := client.LatestLogTime("fake")
   134  
   135  	c.Assert(result, gc.Equals, t1)
   136  	s.AssertModelCall(c, &stub, names.NewModelTag("fake"), "LatestLogTime", err, false)
   137  }
   138  
   139  func (s *ClientSuite) TestLatestLogTimeError(c *gc.C) {
   140  	client, stub := s.getClientAndStub(c)
   141  	result, err := client.LatestLogTime("fake")
   142  
   143  	c.Assert(result, gc.Equals, time.Time{})
   144  	s.AssertModelCall(c, stub, names.NewModelTag("fake"), "LatestLogTime", err, true)
   145  }
   146  
   147  func (s *ClientSuite) TestAdoptResources(c *gc.C) {
   148  	client, stub := s.getClientAndStub(c)
   149  	err := client.AdoptResources("the-model")
   150  	c.Assert(err, gc.ErrorMatches, "boom")
   151  	stub.CheckCall(c, 0, "MigrationTarget.AdoptResources", "", params.AdoptResourcesArgs{
   152  		ModelTag:                "model-the-model",
   153  		SourceControllerVersion: jujuversion.Current,
   154  	})
   155  }
   156  
   157  func (s *ClientSuite) TestCheckMachines(c *gc.C) {
   158  	var stub jujutesting.Stub
   159  	apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   160  		target, ok := result.(*params.ErrorResults)
   161  		c.Assert(ok, jc.IsTrue)
   162  		*target = params.ErrorResults{Results: []params.ErrorResult{
   163  			{Error: &params.Error{Message: "oops"}},
   164  			{Error: &params.Error{Message: "oh no"}},
   165  		}}
   166  		stub.AddCall(objType+"."+request, id, arg)
   167  		return nil
   168  	})
   169  	client := migrationtarget.NewClient(apiCaller)
   170  	results, err := client.CheckMachines("django")
   171  	c.Assert(results, gc.HasLen, 2)
   172  	c.Assert(results[0], gc.ErrorMatches, "oops")
   173  	c.Assert(results[1], gc.ErrorMatches, "oh no")
   174  	s.AssertModelCall(c, &stub, names.NewModelTag("django"), "CheckMachines", err, false)
   175  }
   176  
   177  func (s *ClientSuite) TestUploadCharm(c *gc.C) {
   178  	const charmBody = "charming"
   179  	curl := charm.MustParseURL("cs:~user/foo-2")
   180  	doer := newFakeDoer(c, params.CharmsResponse{
   181  		CharmURL: curl.String(),
   182  	})
   183  	caller := &fakeHTTPCaller{
   184  		httpClient: &httprequest.Client{Doer: doer},
   185  	}
   186  	client := migrationtarget.NewClient(caller)
   187  	outCurl, err := client.UploadCharm("uuid", curl, strings.NewReader(charmBody))
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Assert(outCurl, gc.DeepEquals, curl)
   190  	c.Assert(doer.method, gc.Equals, "POST")
   191  	c.Assert(doer.url, gc.Equals, "/migrate/charms?revision=2&schema=cs&series=&user=user")
   192  	c.Assert(doer.body, gc.Equals, charmBody)
   193  }
   194  
   195  func (s *ClientSuite) TestUploadTools(c *gc.C) {
   196  	const toolsBody = "toolie"
   197  	vers := version.MustParseBinary("2.0.0-xenial-amd64")
   198  	someTools := &tools.Tools{Version: vers}
   199  	doer := newFakeDoer(c, params.ToolsResult{
   200  		ToolsList: []*tools.Tools{someTools},
   201  	})
   202  	caller := &fakeHTTPCaller{
   203  		httpClient: &httprequest.Client{Doer: doer},
   204  	}
   205  	client := migrationtarget.NewClient(caller)
   206  	toolsList, err := client.UploadTools(
   207  		"uuid",
   208  		strings.NewReader(toolsBody),
   209  		vers,
   210  		"trusty", "warty",
   211  	)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	c.Assert(toolsList, gc.HasLen, 1)
   214  	c.Assert(toolsList[0], gc.DeepEquals, someTools)
   215  	c.Assert(doer.method, gc.Equals, "POST")
   216  	c.Assert(doer.url, gc.Equals, "/migrate/tools?binaryVersion=2.0.0-xenial-amd64&series=trusty,warty")
   217  	c.Assert(doer.body, gc.Equals, toolsBody)
   218  }
   219  
   220  func (s *ClientSuite) TestUploadResource(c *gc.C) {
   221  	const resourceBody = "resourceful"
   222  	doer := newFakeDoer(c, "")
   223  	caller := &fakeHTTPCaller{
   224  		httpClient: &httprequest.Client{Doer: doer},
   225  	}
   226  	client := migrationtarget.NewClient(caller)
   227  
   228  	res := resourcetesting.NewResource(c, nil, "blob", "app", resourceBody).Resource
   229  	res.Revision = 1
   230  
   231  	err := client.UploadResource("uuid", res, strings.NewReader(resourceBody))
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	c.Assert(doer.method, gc.Equals, "POST")
   234  	expectedURL := fmt.Sprintf("/migrate/resources?application=app&description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=1&size=11&timestamp=%d&type=file&user=a-user", res.Fingerprint.Hex(), res.Timestamp.UnixNano())
   235  	c.Assert(doer.url, gc.Equals, expectedURL)
   236  	c.Assert(doer.body, gc.Equals, resourceBody)
   237  }
   238  
   239  func (s *ClientSuite) TestSetUnitResource(c *gc.C) {
   240  	const resourceBody = "resourceful"
   241  	doer := newFakeDoer(c, "")
   242  	caller := &fakeHTTPCaller{
   243  		httpClient: &httprequest.Client{Doer: doer},
   244  	}
   245  	client := migrationtarget.NewClient(caller)
   246  
   247  	res := resourcetesting.NewResource(c, nil, "blob", "app", resourceBody).Resource
   248  	res.Revision = 2
   249  
   250  	err := client.SetUnitResource("uuid", "app/0", res)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	c.Assert(doer.method, gc.Equals, "POST")
   253  	expectedURL := fmt.Sprintf("/migrate/resources?description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=2&size=11&timestamp=%d&type=file&unit=app%%2F0&user=a-user", res.Fingerprint.Hex(), res.Timestamp.UnixNano())
   254  	c.Assert(doer.url, gc.Equals, expectedURL)
   255  	c.Assert(doer.body, gc.Equals, "")
   256  }
   257  
   258  func (s *ClientSuite) TestPlaceholderResource(c *gc.C) {
   259  	doer := newFakeDoer(c, "")
   260  	caller := &fakeHTTPCaller{
   261  		httpClient: &httprequest.Client{Doer: doer},
   262  	}
   263  	client := migrationtarget.NewClient(caller)
   264  
   265  	res := resourcetesting.NewPlaceholderResource(c, "blob", "app")
   266  	res.Revision = 3
   267  	res.Size = 123
   268  
   269  	err := client.SetPlaceholderResource("uuid", res)
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	c.Assert(doer.method, gc.Equals, "POST")
   272  	expectedURL := fmt.Sprintf("/migrate/resources?application=app&description=blob+description&fingerprint=%s&name=blob&origin=upload&path=blob.tgz&revision=3&size=123&type=file", res.Fingerprint.Hex())
   273  	c.Assert(doer.url, gc.Equals, expectedURL)
   274  	c.Assert(doer.body, gc.Equals, "")
   275  }
   276  
   277  func (s *ClientSuite) TestCACert(c *gc.C) {
   278  	call := func(objType string, version int, id, request string, args, response interface{}) error {
   279  		c.Check(objType, gc.Equals, "MigrationTarget")
   280  		c.Check(request, gc.Equals, "CACert")
   281  		c.Check(args, gc.Equals, nil)
   282  		c.Check(response, gc.FitsTypeOf, (*params.BytesResult)(nil))
   283  		response.(*params.BytesResult).Result = []byte("foo cert")
   284  		return nil
   285  	}
   286  	client := migrationtarget.NewClient(apitesting.APICallerFunc(call))
   287  	r, err := client.CACert()
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(r, gc.Equals, "foo cert")
   290  }
   291  
   292  func (s *ClientSuite) AssertModelCall(c *gc.C, stub *jujutesting.Stub, tag names.ModelTag, call string, err error, expectError bool) {
   293  	expectedArg := params.ModelArgs{ModelTag: tag.String()}
   294  	stub.CheckCalls(c, []jujutesting.StubCall{
   295  		{"MigrationTarget." + call, []interface{}{"", expectedArg}},
   296  	})
   297  	if expectError {
   298  		c.Assert(err, gc.ErrorMatches, "boom")
   299  	} else {
   300  		c.Assert(err, jc.ErrorIsNil)
   301  	}
   302  }
   303  
   304  type fakeConnector struct {
   305  	base.APICaller
   306  
   307  	*jujutesting.Stub
   308  }
   309  
   310  func (fakeConnector) BestFacadeVersion(string) int {
   311  	return 0
   312  }
   313  
   314  func (c fakeConnector) ConnectControllerStream(path string, attrs url.Values, headers http.Header) (base.Stream, error) {
   315  	c.Stub.AddCall("ConnectControllerStream", path, attrs, headers)
   316  	return nil, errors.New("sound hound")
   317  }
   318  
   319  type fakeHTTPCaller struct {
   320  	base.APICaller
   321  	httpClient *httprequest.Client
   322  	err        error
   323  }
   324  
   325  func (fakeHTTPCaller) BestFacadeVersion(string) int {
   326  	return 0
   327  }
   328  
   329  func (c fakeHTTPCaller) HTTPClient() (*httprequest.Client, error) {
   330  	return c.httpClient, c.err
   331  }
   332  
   333  func newFakeDoer(c *gc.C, respBody interface{}) *fakeDoer {
   334  	body, err := json.Marshal(respBody)
   335  	c.Assert(err, jc.ErrorIsNil)
   336  	resp := &http.Response{
   337  		StatusCode: 200,
   338  		Body:       ioutil.NopCloser(bytes.NewReader(body)),
   339  	}
   340  	resp.Header = make(http.Header)
   341  	resp.Header.Add("Content-Type", "application/json")
   342  	return &fakeDoer{
   343  		response: resp,
   344  	}
   345  }
   346  
   347  type fakeDoer struct {
   348  	response *http.Response
   349  
   350  	method string
   351  	url    string
   352  	body   string
   353  }
   354  
   355  func (d *fakeDoer) Do(req *http.Request) (*http.Response, error) {
   356  	d.method = req.Method
   357  	d.url = req.URL.String()
   358  	body, err := ioutil.ReadAll(req.Body)
   359  	if err != nil {
   360  		panic(err)
   361  	}
   362  	d.body = string(body)
   363  	return d.response, nil
   364  }