github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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  	c.Assert(apistate.AuthTag(), gc.Equals, tag)
   111  }
   112  
   113  func (s *stateSuite) TestLoginMacaroonInvalidId(c *gc.C) {
   114  	apistate, tag, _ := s.OpenAPIWithoutLogin(c)
   115  	defer apistate.Close()
   116  	mac, err := macaroon.New([]byte("root-key"), "id", "juju")
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	err = apistate.Login(tag, "", "", []macaroon.Slice{{mac}})
   119  	c.Assert(err, gc.ErrorMatches, "invalid entity name or password \\(unauthorized access\\)")
   120  }
   121  
   122  func (s *stateSuite) TestLoginMacaroonInvalidUser(c *gc.C) {
   123  	apistate, tag, _ := s.OpenAPIWithoutLogin(c)
   124  	defer apistate.Close()
   125  	// Use s.APIState, because we can't get at UserManager without logging in.
   126  	mac, err := usermanager.NewClient(s.APIState).CreateLocalLoginMacaroon(tag.(names.UserTag))
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	err = apistate.Login(names.NewUserTag("bob@local"), "", "", []macaroon.Slice{{mac}})
   129  	c.Assert(err, gc.ErrorMatches, "invalid entity name or password \\(unauthorized access\\)")
   130  }
   131  
   132  func (s *stateSuite) TestLoginTracksFacadeVersions(c *gc.C) {
   133  	apistate, tag, password := s.OpenAPIWithoutLogin(c)
   134  	defer apistate.Close()
   135  	// We haven't called Login yet, so the Facade Versions should be empty
   136  	c.Check(apistate.AllFacadeVersions(), gc.HasLen, 0)
   137  	err := apistate.Login(tag, password, "", nil)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	// Now that we've logged in, AllFacadeVersions should be updated.
   140  	allVersions := apistate.AllFacadeVersions()
   141  	c.Check(allVersions, gc.Not(gc.HasLen), 0)
   142  	// For sanity checking, ensure that we have a v2 of the Client facade
   143  	c.Assert(allVersions["Client"], gc.Not(gc.HasLen), 0)
   144  	c.Check(allVersions["Client"][0], gc.Equals, 1)
   145  }
   146  
   147  func (s *stateSuite) TestAllFacadeVersionsSafeFromMutation(c *gc.C) {
   148  	allVersions := s.APIState.AllFacadeVersions()
   149  	clients := allVersions["Client"]
   150  	origClients := make([]int, len(clients))
   151  	copy(origClients, clients)
   152  	// Mutating the dict should not affect the cached versions
   153  	allVersions["Client"] = append(allVersions["Client"], 2597)
   154  	newVersions := s.APIState.AllFacadeVersions()
   155  	newClientVers := newVersions["Client"]
   156  	c.Check(newClientVers, gc.DeepEquals, origClients)
   157  	c.Check(newClientVers[len(newClientVers)-1], gc.Not(gc.Equals), 2597)
   158  }
   159  
   160  func (s *stateSuite) TestBestFacadeVersion(c *gc.C) {
   161  	c.Check(s.APIState.BestFacadeVersion("Client"), gc.Equals, 1)
   162  }
   163  
   164  func (s *stateSuite) TestAPIHostPortsMovesConnectedValueFirst(c *gc.C) {
   165  	hostportslist := s.APIState.APIHostPorts()
   166  	c.Check(hostportslist, gc.HasLen, 1)
   167  	serverhostports := hostportslist[0]
   168  	c.Check(serverhostports, gc.HasLen, 1)
   169  	goodAddress := serverhostports[0]
   170  	// the other addresses, but always see this one as well.
   171  	info := s.APIInfo(c)
   172  	// We intentionally set this to invalid values
   173  	badValue := network.HostPort{
   174  		Address: network.Address{
   175  			Value: "0.1.2.3",
   176  			Type:  network.IPv4Address,
   177  			Scope: network.ScopeMachineLocal,
   178  		},
   179  		Port: 1234,
   180  	}
   181  	badServer := []network.HostPort{badValue}
   182  	extraAddress := network.HostPort{
   183  		Address: network.Address{
   184  			Value: "0.1.2.4",
   185  			Type:  network.IPv4Address,
   186  			Scope: network.ScopeMachineLocal,
   187  		},
   188  		Port: 5678,
   189  	}
   190  	extraAddress2 := network.HostPort{
   191  		Address: network.Address{
   192  			Value: "0.1.2.1",
   193  			Type:  network.IPv4Address,
   194  			Scope: network.ScopeMachineLocal,
   195  		},
   196  		Port: 9012,
   197  	}
   198  	serverExtra := []network.HostPort{
   199  		extraAddress, goodAddress, extraAddress2,
   200  	}
   201  	current := [][]network.HostPort{badServer, serverExtra}
   202  	s.State.SetAPIHostPorts(current)
   203  	apistate, err := api.Open(info, api.DialOpts{})
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	defer apistate.Close()
   206  	hostports := apistate.APIHostPorts()
   207  	// We should have rotate the server we connected to as the first item,
   208  	// and the address of that server as the first address
   209  	sortedServer := []network.HostPort{
   210  		goodAddress, extraAddress, extraAddress2,
   211  	}
   212  	expected := [][]network.HostPort{sortedServer, badServer}
   213  	c.Check(hostports, gc.DeepEquals, expected)
   214  }
   215  
   216  var exampleHostPorts = []network.HostPort{{
   217  	Address: network.NewAddress("0.1.2.3"),
   218  	Port:    1234,
   219  }, {
   220  	Address: network.NewAddress("0.1.2.4"),
   221  	Port:    5678,
   222  }, {
   223  	Address: network.NewAddress("0.1.2.1"),
   224  	Port:    9012,
   225  }, {
   226  	Address: network.NewAddress("0.1.9.1"),
   227  	Port:    8888,
   228  }}
   229  
   230  func (s *slideSuite) TestSlideToFrontNoOp(c *gc.C) {
   231  	servers := [][]network.HostPort{
   232  		{exampleHostPorts[0]},
   233  		{exampleHostPorts[1]},
   234  	}
   235  	// order should not have changed
   236  	expected := [][]network.HostPort{
   237  		{exampleHostPorts[0]},
   238  		{exampleHostPorts[1]},
   239  	}
   240  	api.SlideAddressToFront(servers, 0, 0)
   241  	c.Check(servers, gc.DeepEquals, expected)
   242  }
   243  
   244  func (s *slideSuite) TestSlideToFrontAddress(c *gc.C) {
   245  	servers := [][]network.HostPort{
   246  		{exampleHostPorts[0], exampleHostPorts[1], exampleHostPorts[2]},
   247  		{exampleHostPorts[3]},
   248  	}
   249  	// server order should not change, but ports should be switched
   250  	expected := [][]network.HostPort{
   251  		{exampleHostPorts[1], exampleHostPorts[0], exampleHostPorts[2]},
   252  		{exampleHostPorts[3]},
   253  	}
   254  	api.SlideAddressToFront(servers, 0, 1)
   255  	c.Check(servers, gc.DeepEquals, expected)
   256  }
   257  
   258  func (s *slideSuite) TestSlideToFrontServer(c *gc.C) {
   259  	servers := [][]network.HostPort{
   260  		{exampleHostPorts[0], exampleHostPorts[1]},
   261  		{exampleHostPorts[2]},
   262  		{exampleHostPorts[3]},
   263  	}
   264  	// server 1 should be slid to the front
   265  	expected := [][]network.HostPort{
   266  		{exampleHostPorts[2]},
   267  		{exampleHostPorts[0], exampleHostPorts[1]},
   268  		{exampleHostPorts[3]},
   269  	}
   270  	api.SlideAddressToFront(servers, 1, 0)
   271  	c.Check(servers, gc.DeepEquals, expected)
   272  }
   273  
   274  func (s *slideSuite) TestSlideToFrontBoth(c *gc.C) {
   275  	servers := [][]network.HostPort{
   276  		{exampleHostPorts[0]},
   277  		{exampleHostPorts[1], exampleHostPorts[2]},
   278  		{exampleHostPorts[3]},
   279  	}
   280  	// server 1 should be slid to the front
   281  	expected := [][]network.HostPort{
   282  		{exampleHostPorts[2], exampleHostPorts[1]},
   283  		{exampleHostPorts[0]},
   284  		{exampleHostPorts[3]},
   285  	}
   286  	api.SlideAddressToFront(servers, 1, 1)
   287  	c.Check(servers, gc.DeepEquals, expected)
   288  }