github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/api/state_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package api_test
     5  
     6  import (
     7  	stdtesting "testing"
     8  
     9  	"github.com/juju/names"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/macaroon.v1"
    13  
    14  	"github.com/juju/juju/api"
    15  	"github.com/juju/juju/api/usermanager"
    16  	jujutesting "github.com/juju/juju/juju/testing"
    17  	"github.com/juju/juju/network"
    18  	coretesting "github.com/juju/juju/testing"
    19  )
    20  
    21  func TestAll(t *stdtesting.T) {
    22  	coretesting.MgoTestPackage(t)
    23  }
    24  
    25  type stateSuite struct {
    26  	jujutesting.JujuConnSuite
    27  }
    28  
    29  var _ = gc.Suite(&stateSuite{})
    30  
    31  type slideSuite struct {
    32  	coretesting.BaseSuite
    33  }
    34  
    35  var _ = gc.Suite(&slideSuite{})
    36  
    37  func (s *stateSuite) TestCloseMultipleOk(c *gc.C) {
    38  	c.Assert(s.APIState.Close(), gc.IsNil)
    39  	c.Assert(s.APIState.Close(), gc.IsNil)
    40  	c.Assert(s.APIState.Close(), gc.IsNil)
    41  }
    42  
    43  // OpenAPIWithoutLogin connects to the API and returns an api.State without
    44  // actually calling st.Login already. The returned strings are the "tag" and
    45  // "password" that we would have used to login.
    46  func (s *stateSuite) OpenAPIWithoutLogin(c *gc.C) (api.Connection, names.Tag, string) {
    47  	info := s.APIInfo(c)
    48  	tag := info.Tag
    49  	password := info.Password
    50  	info.Tag = nil
    51  	info.Password = ""
    52  	info.Macaroons = nil
    53  	info.SkipLogin = true
    54  	apistate, err := api.Open(info, api.DialOpts{})
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	return apistate, tag, password
    57  }
    58  
    59  func (s *stateSuite) TestAPIHostPortsAlwaysIncludesTheConnection(c *gc.C) {
    60  	hostportslist := s.APIState.APIHostPorts()
    61  	c.Check(hostportslist, gc.HasLen, 1)
    62  	serverhostports := hostportslist[0]
    63  	c.Check(serverhostports, gc.HasLen, 1)
    64  	// the other addresses, but always see this one as well.
    65  	info := s.APIInfo(c)
    66  	// We intentionally set this to invalid values
    67  	badServer := network.NewHostPorts(1234, "0.1.2.3")
    68  	badServer[0].Scope = network.ScopeMachineLocal
    69  	s.State.SetAPIHostPorts([][]network.HostPort{badServer})
    70  	apistate, err := api.Open(info, api.DialOpts{})
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	defer apistate.Close()
    73  	hostports := apistate.APIHostPorts()
    74  	c.Check(hostports, gc.DeepEquals, [][]network.HostPort{
    75  		serverhostports,
    76  		badServer,
    77  	})
    78  }
    79  
    80  func (s *stateSuite) TestLoginSetsModelTag(c *gc.C) {
    81  	env, err := s.State.Model()
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	apistate, tag, password := s.OpenAPIWithoutLogin(c)
    84  	defer apistate.Close()
    85  	// We haven't called Login yet, so the ModelTag shouldn't be set.
    86  	modelTag, err := apistate.ModelTag()
    87  	c.Check(err, gc.ErrorMatches, `"" is not a valid tag`)
    88  	c.Check(modelTag, gc.Equals, names.ModelTag{})
    89  	err = apistate.Login(tag, password, "", nil)
    90  	c.Assert(err, jc.ErrorIsNil)
    91  	// Now that we've logged in, ModelTag should be updated correctly.
    92  	modelTag, err = apistate.ModelTag()
    93  	c.Check(err, jc.ErrorIsNil)
    94  	c.Check(modelTag, gc.Equals, env.ModelTag())
    95  	// The controller tag is also set, and since the model is the
    96  	// controller model, the uuid is the same.
    97  	controllerTag, err := apistate.ControllerTag()
    98  	c.Check(err, jc.ErrorIsNil)
    99  	c.Check(controllerTag, gc.Equals, env.ModelTag())
   100  }
   101  
   102  func (s *stateSuite) TestLoginMacaroon(c *gc.C) {
   103  	apistate, tag, _ := s.OpenAPIWithoutLogin(c)
   104  	defer apistate.Close()
   105  	// Use s.APIState, because we can't get at UserManager without logging in.
   106  	mac, err := usermanager.NewClient(s.APIState).CreateLocalLoginMacaroon(tag.(names.UserTag))
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	err = apistate.Login(tag, "", "", []macaroon.Slice{{mac}})
   109  	c.Assert(err, jc.ErrorIsNil)
   110  }
   111  
   112  func (s *stateSuite) TestLoginMacaroonInvalidId(c *gc.C) {
   113  	apistate, tag, _ := s.OpenAPIWithoutLogin(c)
   114  	defer apistate.Close()
   115  	mac, err := macaroon.New([]byte("root-key"), "id", "juju")
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	err = apistate.Login(tag, "", "", []macaroon.Slice{{mac}})
   118  	c.Assert(err, gc.ErrorMatches, "invalid entity name or password \\(unauthorized access\\)")
   119  }
   120  
   121  func (s *stateSuite) TestLoginMacaroonInvalidUser(c *gc.C) {
   122  	apistate, tag, _ := s.OpenAPIWithoutLogin(c)
   123  	defer apistate.Close()
   124  	// Use s.APIState, because we can't get at UserManager without logging in.
   125  	mac, err := usermanager.NewClient(s.APIState).CreateLocalLoginMacaroon(tag.(names.UserTag))
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	err = apistate.Login(names.NewUserTag("bob@local"), "", "", []macaroon.Slice{{mac}})
   128  	c.Assert(err, gc.ErrorMatches, "invalid entity name or password \\(unauthorized access\\)")
   129  }
   130  
   131  func (s *stateSuite) TestLoginTracksFacadeVersions(c *gc.C) {
   132  	apistate, tag, password := s.OpenAPIWithoutLogin(c)
   133  	defer apistate.Close()
   134  	// We haven't called Login yet, so the Facade Versions should be empty
   135  	c.Check(apistate.AllFacadeVersions(), gc.HasLen, 0)
   136  	err := apistate.Login(tag, password, "", nil)
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	// Now that we've logged in, AllFacadeVersions should be updated.
   139  	allVersions := apistate.AllFacadeVersions()
   140  	c.Check(allVersions, gc.Not(gc.HasLen), 0)
   141  	// For sanity checking, ensure that we have a v2 of the Client facade
   142  	c.Assert(allVersions["Client"], gc.Not(gc.HasLen), 0)
   143  	c.Check(allVersions["Client"][0], gc.Equals, 1)
   144  }
   145  
   146  func (s *stateSuite) TestAllFacadeVersionsSafeFromMutation(c *gc.C) {
   147  	allVersions := s.APIState.AllFacadeVersions()
   148  	clients := allVersions["Client"]
   149  	origClients := make([]int, len(clients))
   150  	copy(origClients, clients)
   151  	// Mutating the dict should not affect the cached versions
   152  	allVersions["Client"] = append(allVersions["Client"], 2597)
   153  	newVersions := s.APIState.AllFacadeVersions()
   154  	newClientVers := newVersions["Client"]
   155  	c.Check(newClientVers, gc.DeepEquals, origClients)
   156  	c.Check(newClientVers[len(newClientVers)-1], gc.Not(gc.Equals), 2597)
   157  }
   158  
   159  func (s *stateSuite) TestBestFacadeVersion(c *gc.C) {
   160  	c.Check(s.APIState.BestFacadeVersion("Client"), gc.Equals, 1)
   161  }
   162  
   163  func (s *stateSuite) TestAPIHostPortsMovesConnectedValueFirst(c *gc.C) {
   164  	hostportslist := s.APIState.APIHostPorts()
   165  	c.Check(hostportslist, gc.HasLen, 1)
   166  	serverhostports := hostportslist[0]
   167  	c.Check(serverhostports, gc.HasLen, 1)
   168  	goodAddress := serverhostports[0]
   169  	// the other addresses, but always see this one as well.
   170  	info := s.APIInfo(c)
   171  	// We intentionally set this to invalid values
   172  	badValue := network.HostPort{
   173  		Address: network.Address{
   174  			Value: "0.1.2.3",
   175  			Type:  network.IPv4Address,
   176  			Scope: network.ScopeMachineLocal,
   177  		},
   178  		Port: 1234,
   179  	}
   180  	badServer := []network.HostPort{badValue}
   181  	extraAddress := network.HostPort{
   182  		Address: network.Address{
   183  			Value: "0.1.2.4",
   184  			Type:  network.IPv4Address,
   185  			Scope: network.ScopeMachineLocal,
   186  		},
   187  		Port: 5678,
   188  	}
   189  	extraAddress2 := network.HostPort{
   190  		Address: network.Address{
   191  			Value: "0.1.2.1",
   192  			Type:  network.IPv4Address,
   193  			Scope: network.ScopeMachineLocal,
   194  		},
   195  		Port: 9012,
   196  	}
   197  	serverExtra := []network.HostPort{
   198  		extraAddress, goodAddress, extraAddress2,
   199  	}
   200  	current := [][]network.HostPort{badServer, serverExtra}
   201  	s.State.SetAPIHostPorts(current)
   202  	apistate, err := api.Open(info, api.DialOpts{})
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	defer apistate.Close()
   205  	hostports := apistate.APIHostPorts()
   206  	// We should have rotate the server we connected to as the first item,
   207  	// and the address of that server as the first address
   208  	sortedServer := []network.HostPort{
   209  		goodAddress, extraAddress, extraAddress2,
   210  	}
   211  	expected := [][]network.HostPort{sortedServer, badServer}
   212  	c.Check(hostports, gc.DeepEquals, expected)
   213  }
   214  
   215  var exampleHostPorts = []network.HostPort{{
   216  	Address: network.NewAddress("0.1.2.3"),
   217  	Port:    1234,
   218  }, {
   219  	Address: network.NewAddress("0.1.2.4"),
   220  	Port:    5678,
   221  }, {
   222  	Address: network.NewAddress("0.1.2.1"),
   223  	Port:    9012,
   224  }, {
   225  	Address: network.NewAddress("0.1.9.1"),
   226  	Port:    8888,
   227  }}
   228  
   229  func (s *slideSuite) TestSlideToFrontNoOp(c *gc.C) {
   230  	servers := [][]network.HostPort{
   231  		{exampleHostPorts[0]},
   232  		{exampleHostPorts[1]},
   233  	}
   234  	// order should not have changed
   235  	expected := [][]network.HostPort{
   236  		{exampleHostPorts[0]},
   237  		{exampleHostPorts[1]},
   238  	}
   239  	api.SlideAddressToFront(servers, 0, 0)
   240  	c.Check(servers, gc.DeepEquals, expected)
   241  }
   242  
   243  func (s *slideSuite) TestSlideToFrontAddress(c *gc.C) {
   244  	servers := [][]network.HostPort{
   245  		{exampleHostPorts[0], exampleHostPorts[1], exampleHostPorts[2]},
   246  		{exampleHostPorts[3]},
   247  	}
   248  	// server order should not change, but ports should be switched
   249  	expected := [][]network.HostPort{
   250  		{exampleHostPorts[1], exampleHostPorts[0], exampleHostPorts[2]},
   251  		{exampleHostPorts[3]},
   252  	}
   253  	api.SlideAddressToFront(servers, 0, 1)
   254  	c.Check(servers, gc.DeepEquals, expected)
   255  }
   256  
   257  func (s *slideSuite) TestSlideToFrontServer(c *gc.C) {
   258  	servers := [][]network.HostPort{
   259  		{exampleHostPorts[0], exampleHostPorts[1]},
   260  		{exampleHostPorts[2]},
   261  		{exampleHostPorts[3]},
   262  	}
   263  	// server 1 should be slid to the front
   264  	expected := [][]network.HostPort{
   265  		{exampleHostPorts[2]},
   266  		{exampleHostPorts[0], exampleHostPorts[1]},
   267  		{exampleHostPorts[3]},
   268  	}
   269  	api.SlideAddressToFront(servers, 1, 0)
   270  	c.Check(servers, gc.DeepEquals, expected)
   271  }
   272  
   273  func (s *slideSuite) TestSlideToFrontBoth(c *gc.C) {
   274  	servers := [][]network.HostPort{
   275  		{exampleHostPorts[0]},
   276  		{exampleHostPorts[1], exampleHostPorts[2]},
   277  		{exampleHostPorts[3]},
   278  	}
   279  	// server 1 should be slid to the front
   280  	expected := [][]network.HostPort{
   281  		{exampleHostPorts[2], exampleHostPorts[1]},
   282  		{exampleHostPorts[0]},
   283  		{exampleHostPorts[3]},
   284  	}
   285  	api.SlideAddressToFront(servers, 1, 1)
   286  	c.Check(servers, gc.DeepEquals, expected)
   287  }