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