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 }