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 }