LEFT | RIGHT |
| 1 // launchpad.net/juju/go/state |
| 2 // |
| 3 // Copyright (c) 2011-2012 Canonical Ltd. |
| 4 package state |
| 5 |
| 6 import ( |
| 7 . "launchpad.net/gocheck" |
| 8 "launchpad.net/goyaml" |
| 9 "launchpad.net/gozk/zookeeper" |
| 10 ) |
| 11 |
| 12 type TopologySuite struct { |
| 13 zkServer *zookeeper.Server |
| 14 zkTestRoot string |
| 15 zkTestPort int |
| 16 zkAddr string |
| 17 zkConn *zookeeper.Conn |
1 t *topology | 18 t *topology |
2 } | 19 } |
3 | 20 |
4 var _ = Suite(&TopologySuite{}) | 21 var _ = Suite(&TopologySuite{}) |
| 22 var TestingZkAddr string |
5 | 23 |
6 func (s *TopologySuite) SetUpTest(c *C) { | 24 func (s *TopologySuite) SetUpTest(c *C) { |
7 // Connect the server. | 25 // Connect the server. |
| 26 st, err := Open(&Info{ |
| 27 Addrs: []string{TestingZkAddr}, |
| 28 }) |
| 29 c.Assert(err, IsNil) |
| 30 s.zkConn = ZkConn(st) |
| 31 // Read the toplogy. |
| 32 s.t, err = readTopology(s.zkConn) |
| 33 c.Assert(err, IsNil) |
| 34 } |
| 35 |
| 36 func (s *TopologySuite) TearDownTest(c *C) { |
| 37 // Delete possible nodes, ignore errors. |
| 38 zkRemoveTree(s.zkConn, "/topology") |
| 39 s.zkConn.Close() |
| 40 } |
| 41 |
| 42 func (s TopologySuite) TestAddMachine(c *C) { |
| 43 // Check that adding machines works correctly. |
| 44 err := s.t.AddMachine("m-0") |
| 45 c.Assert(err, IsNil) |
| 46 err = s.t.AddMachine("m-1") |
| 47 c.Assert(err, IsNil) |
| 48 keys := s.t.MachineKeys() |
| 49 c.Assert(keys, DeepEquals, []string{"m-0", "m-1"}) |
| 50 } |
| 51 |
| 52 func (s TopologySuite) TestAddDuplicatedMachine(c *C) { |
| 53 // Check that adding a duplicated machine by key fails. |
| 54 err := s.t.AddMachine("m-0") |
| 55 c.Assert(err, IsNil) |
| 56 err = s.t.AddMachine("m-0") |
| 57 c.Assert(err, ErrorMatches, `attempted to add duplicated machine "m-0"`) |
| 58 } |
| 59 |
| 60 func (s TopologySuite) TestRemoveMachine(c *C) { |
| 61 // Check that removing machines works correctly. |
| 62 err := s.t.AddMachine("m-0") |
| 63 c.Assert(err, IsNil) |
| 64 err = s.t.AddMachine("m-1") |
| 65 c.Assert(err, IsNil) |
| 66 // Add non-assigned unit. This tests that the logic of |
| 67 // checking for assigned units works correctly too. |
| 68 err = s.t.AddService("s-0", "wordpress") |
| 69 c.Assert(err, IsNil) |
| 70 _, err = s.t.AddUnit("s-0", "u-0") |
| 71 c.Assert(err, IsNil) |
| 72 |
| 73 err = s.t.RemoveMachine("m-0") |
| 74 c.Assert(err, IsNil) |
| 75 |
| 76 found := s.t.HasMachine("m-0") |
| 77 c.Assert(found, Equals, false) |
| 78 found = s.t.HasMachine("m-1") |
| 79 c.Assert(found, Equals, true) |
| 80 } |
| 81 |
| 82 func (s TopologySuite) TestRemoveNonExistentMachine(c *C) { |
| 83 // Check that the removing of a non-existent machine fails. |
| 84 err := s.t.RemoveMachine("m-0") |
| 85 c.Assert(err, ErrorMatches, `machine with key "m-0" not found`) |
| 86 } |
| 87 |
| 88 func (s TopologySuite) TestRemoveMachineWithAssignedUnits(c *C) { |
| 89 // Check that a machine can't be removed when it has assigned units. |
| 90 err := s.t.AddMachine("m-0") |
| 91 c.Assert(err, IsNil) |
| 92 err = s.t.AddService("s-0", "wordpress") |
| 93 c.Assert(err, IsNil) |
| 94 _, err = s.t.AddUnit("s-0", "u-0") |
| 95 c.Assert(err, IsNil) |
| 96 _, err = s.t.AddUnit("s-0", "u-1") |
| 97 c.Assert(err, IsNil) |
| 98 err = s.t.AssignUnitToMachine("s-0", "u-1", "m-0") |
| 99 c.Assert(err, IsNil) |
| 100 err = s.t.RemoveMachine("m-0") |
| 101 c.Assert(err, ErrorMatches, `can't remove machine "m-0" while units ared
assigned`) |
| 102 } |
| 103 |
| 104 func (s TopologySuite) TestMachineHasUnits(c *C) { |
| 105 // Check various ways a machine might or might not be assigned |
| 106 // to a unit. |
| 107 err := s.t.AddMachine("m-0") |
| 108 c.Assert(err, IsNil) |
| 109 err = s.t.AddMachine("m-1") |
| 110 c.Assert(err, IsNil) |
| 111 err = s.t.AddService("s-0", "wordpress") |
| 112 c.Assert(err, IsNil) |
| 113 _, err = s.t.AddUnit("s-0", "u-0") |
| 114 c.Assert(err, IsNil) |
| 115 _, err = s.t.AddUnit("s-0", "u-1") |
| 116 c.Assert(err, IsNil) |
| 117 err = s.t.AssignUnitToMachine("s-0", "u-1", "m-0") |
| 118 c.Assert(err, IsNil) |
| 119 ok, err := s.t.MachineHasUnits("m-0") |
| 120 c.Assert(err, IsNil) |
| 121 c.Assert(ok, Equals, true) |
| 122 ok, err = s.t.MachineHasUnits("m-1") |
| 123 c.Assert(err, IsNil) |
| 124 c.Assert(ok, Equals, false) |
| 125 ok, err = s.t.MachineHasUnits("m-99") |
| 126 c.Assert(err, ErrorMatches, `machine with key "m-99" not found`) |
| 127 } |
| 128 |
| 129 func (s TopologySuite) TestHasMachine(c *C) { |
| 130 // Check that the test for a machine works correctly. |
| 131 found := s.t.HasMachine("m-0") |
| 132 c.Assert(found, Equals, false) |
| 133 err := s.t.AddMachine("m-0") |
| 134 c.Assert(err, IsNil) |
| 135 found = s.t.HasMachine("m-0") |
| 136 c.Assert(found, Equals, true) |
| 137 found = s.t.HasMachine("m-1") |
| 138 c.Assert(found, Equals, false) |
| 139 } |
| 140 |
| 141 func (s TopologySuite) TestMachineKeys(c *C) { |
| 142 // Check that the retrieval of all services keys works correctly. |
| 143 keys := s.t.MachineKeys() |
| 144 c.Assert(keys, DeepEquals, []string{}) |
| 145 err := s.t.AddMachine("m-0") |
| 146 c.Assert(err, IsNil) |
| 147 err = s.t.AddMachine("m-1") |
| 148 c.Assert(err, IsNil) |
| 149 keys = s.t.MachineKeys() |
| 150 c.Assert(keys, DeepEquals, []string{"m-0", "m-1"}) |
| 151 } |
| 152 |
| 153 func (s TopologySuite) TestAddService(c *C) { |
| 154 // Check that adding services works correctly. |
| 155 c.Assert(s.t.HasService("s-0"), Equals, false) |
| 156 err := s.t.AddService("s-0", "wordpress") |
| 157 c.Assert(err, IsNil) |
| 158 err = s.t.AddService("s-1", "mysql") |
| 159 c.Assert(err, IsNil) |
| 160 c.Assert(s.t.HasService("s-0"), Equals, true) |
| 161 c.Assert(s.t.HasService("s-1"), Equals, true) |
| 162 } |
| 163 |
| 164 func (s TopologySuite) TestAddDuplicatedService(c *C) { |
| 165 // Check that adding a duplicated service by key or name fails. |
| 166 err := s.t.AddService("s-0", "wordpress") |
| 167 c.Assert(err, IsNil) |
| 168 err = s.t.AddService("s-0", "mysql") |
| 169 c.Assert(err, ErrorMatches, `attempted to add duplicated service "s-0"`) |
| 170 err = s.t.AddService("s-1", "wordpress") |
| 171 c.Assert(err, ErrorMatches, `service name "wordpress" already in use`) |
| 172 } |
| 173 |
| 174 func (s TopologySuite) TestHasService(c *C) { |
| 175 // Check that the test for a service works correctly. |
| 176 found := s.t.HasService("s-0") |
| 177 c.Assert(found, Equals, false) |
| 178 err := s.t.AddService("s-0", "wordpress") |
| 179 c.Assert(err, IsNil) |
| 180 found = s.t.HasService("s-0") |
| 181 c.Assert(found, Equals, true) |
| 182 found = s.t.HasService("s-1") |
| 183 c.Assert(found, Equals, false) |
| 184 } |
| 185 |
| 186 func (s TopologySuite) TestServiceKey(c *C) { |
| 187 // Check that the key retrieval for a service name works correctly. |
| 188 key, err := s.t.ServiceKey("wordpress") |
| 189 c.Assert(err, ErrorMatches, `service with name "wordpress" not found`) |
| 190 err = s.t.AddService("s-0", "wordpress") |
| 191 c.Assert(err, IsNil) |
| 192 key, err = s.t.ServiceKey("wordpress") |
| 193 c.Assert(err, IsNil) |
| 194 c.Assert(key, Equals, "s-0") |
| 195 } |
| 196 |
| 197 func (s TopologySuite) TestServiceKeys(c *C) { |
| 198 // Check that the retrieval of all services keys works correctly. |
| 199 keys := s.t.ServiceKeys() |
| 200 c.Assert(keys, DeepEquals, []string{}) |
| 201 err := s.t.AddService("s-0", "wordpress") |
| 202 c.Assert(err, IsNil) |
| 203 err = s.t.AddService("s-1", "mysql") |
| 204 c.Assert(err, IsNil) |
| 205 keys = s.t.ServiceKeys() |
| 206 c.Assert(keys, DeepEquals, []string{"s-0", "s-1"}) |
| 207 } |
| 208 |
| 209 func (s TopologySuite) TestServiceName(c *C) { |
| 210 // Check that the name retrieval for a service name works correctly. |
| 211 name, err := s.t.ServiceName("s-0") |
| 212 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 213 err = s.t.AddService("s-0", "wordpress") |
| 214 c.Assert(err, IsNil) |
| 215 name, err = s.t.ServiceName("s-0") |
| 216 c.Assert(err, IsNil) |
| 217 c.Assert(name, Equals, "wordpress") |
| 218 } |
| 219 |
| 220 func (s TopologySuite) TestRemoveService(c *C) { |
| 221 // Check that the removing of a service works correctly. |
| 222 err := s.t.AddService("s-0", "wordpress") |
| 223 c.Assert(err, IsNil) |
| 224 err = s.t.AddService("s-1", "mysql") |
| 225 c.Assert(err, IsNil) |
| 226 err = s.t.RemoveService("s-0") |
| 227 c.Assert(err, IsNil) |
| 228 c.Assert(s.t.HasService("s-0"), Equals, false) |
| 229 c.Assert(s.t.HasService("s-1"), Equals, true) |
| 230 } |
| 231 |
| 232 func (s TopologySuite) TestRemoveNonExistentService(c *C) { |
| 233 // Check that the removing of a non-existent service fails. |
| 234 err := s.t.RemoveService("s-99") |
| 235 c.Assert(err, ErrorMatches, `service with key "s-99" not found`) |
| 236 } |
| 237 |
| 238 func (s TopologySuite) TestAddUnit(c *C) { |
| 239 // Check that the adding of a unit works correctly. |
| 240 err := s.t.AddService("s-0", "wordpress") |
| 241 c.Assert(err, IsNil) |
| 242 err = s.t.AddService("s-1", "mysql") |
| 243 c.Assert(err, IsNil) |
| 244 seq, err := s.t.AddUnit("s-0", "u-05") |
| 245 c.Assert(err, IsNil) |
| 246 c.Assert(seq, Equals, 0) |
| 247 seq, err = s.t.AddUnit("s-0", "u-12") |
| 248 c.Assert(err, IsNil) |
| 249 c.Assert(seq, Equals, 1) |
| 250 seq, err = s.t.AddUnit("s-1", "u-07") |
| 251 c.Assert(err, IsNil) |
| 252 c.Assert(seq, Equals, 0) |
| 253 keys, err := s.t.UnitKeys("s-0") |
| 254 c.Assert(err, IsNil) |
| 255 c.Assert(keys, DeepEquals, []string{"u-05", "u-12"}) |
| 256 keys, err = s.t.UnitKeys("s-1") |
| 257 c.Assert(err, IsNil) |
| 258 c.Assert(keys, DeepEquals, []string{"u-07"}) |
| 259 } |
| 260 |
| 261 func (s TopologySuite) TestGlobalUniqueUnitNames(c *C) { |
| 262 // Check that even if the underlying service is destroyed |
| 263 // and a new one with the same name is created we'll never |
| 264 // get a duplicate unit name. |
| 265 err := s.t.AddService("s-0", "wordpress") |
| 266 c.Assert(err, IsNil) |
| 267 seq, err := s.t.AddUnit("s-0", "u-0") |
| 268 c.Assert(err, IsNil) |
| 269 c.Assert(seq, Equals, 0) |
| 270 seq, err = s.t.AddUnit("s-0", "u-1") |
| 271 c.Assert(err, IsNil) |
| 272 c.Assert(seq, Equals, 1) |
| 273 err = s.t.RemoveService("s-0") |
| 274 c.Assert(err, IsNil) |
| 275 err = s.t.AddService("s-0", "wordpress") |
| 276 c.Assert(err, IsNil) |
| 277 seq, err = s.t.AddUnit("s-0", "u-1") |
| 278 c.Assert(err, IsNil) |
| 279 c.Assert(seq, Equals, 2) |
| 280 name, err := s.t.UnitName("s-0", "u-1") |
| 281 c.Assert(err, IsNil) |
| 282 c.Assert(name, Equals, "wordpress/2") |
| 283 } |
| 284 |
| 285 func (s TopologySuite) TestAddDuplicatedUnit(c *C) { |
| 286 // Check that it's not possible to add a unit twice. |
| 287 err := s.t.AddService("s-0", "wordpress") |
| 288 c.Assert(err, IsNil) |
| 289 _, err = s.t.AddUnit("s-0", "u-0") |
| 290 c.Assert(err, IsNil) |
| 291 _, err = s.t.AddUnit("s-0", "u-0") |
| 292 c.Assert(err, ErrorMatches, `unit "u-0" already in use in service "s-0"`
) |
| 293 } |
| 294 |
| 295 func (s TopologySuite) TestAddUnitToNonExistingService(c *C) { |
| 296 // Check that the adding of a unit to a non-existing services |
| 297 // fails correctly. |
| 298 _, err := s.t.AddUnit("s-0", "u-0") |
| 299 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 300 } |
| 301 |
| 302 func (s TopologySuite) TestAddUnitToDifferentService(c *C) { |
| 303 // Check that the adding of the same unit to two different |
| 304 // services fails correctly. |
| 305 err := s.t.AddService("s-0", "wordpress") |
| 306 c.Assert(err, IsNil) |
| 307 err = s.t.AddService("s-1", "mysql") |
| 308 c.Assert(err, IsNil) |
| 309 _, err = s.t.AddUnit("s-0", "u-0") |
| 310 c.Assert(err, IsNil) |
| 311 _, err = s.t.AddUnit("s-1", "u-0") |
| 312 c.Assert(err, ErrorMatches, `unit "u-0" already in use in service "s-0"`
) |
| 313 } |
| 314 |
| 315 func (s TopologySuite) TestUnitKeys(c *C) { |
| 316 // Check if registered units from a service are returned correctly. |
| 317 err := s.t.AddService("s-0", "wordpress") |
| 318 c.Assert(err, IsNil) |
| 319 err = s.t.AddService("s-1", "mysql") |
| 320 c.Assert(err, IsNil) |
| 321 units, err := s.t.UnitKeys("s-0") |
| 322 c.Assert(err, IsNil) |
| 323 c.Assert(units, DeepEquals, []string{}) |
| 324 _, err = s.t.AddUnit("s-0", "u-0") |
| 325 c.Assert(err, IsNil) |
| 326 _, err = s.t.AddUnit("s-0", "u-1") |
| 327 c.Assert(err, IsNil) |
| 328 _, err = s.t.AddUnit("s-1", "u-2") |
| 329 c.Assert(err, IsNil) |
| 330 units, err = s.t.UnitKeys("s-0") |
| 331 c.Assert(err, IsNil) |
| 332 c.Assert(units, DeepEquals, []string{"u-0", "u-1"}) |
| 333 units, err = s.t.UnitKeys("s-1") |
| 334 c.Assert(err, IsNil) |
| 335 c.Assert(units, DeepEquals, []string{"u-2"}) |
| 336 } |
| 337 |
| 338 func (s TopologySuite) TestUnitKeysWithNonExistingService(c *C) { |
| 339 // Check if the retrieving of unit keys from a non-existing |
| 340 // service fails correctly. |
| 341 _, err := s.t.UnitKeys("s-0") |
| 342 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 343 } |
| 344 |
| 345 func (s TopologySuite) TestHasUnit(c *C) { |
| 346 // Check that the test for a unit in a service works correctly. |
| 347 err := s.t.AddService("s-0", "wordpress") |
| 348 c.Assert(err, IsNil) |
| 349 found := s.t.HasUnit("s-0", "u-0") |
| 350 c.Assert(found, Equals, false) |
| 351 _, err = s.t.AddUnit("s-0", "u-0") |
| 352 c.Assert(err, IsNil) |
| 353 found = s.t.HasUnit("s-0", "u-0") |
| 354 c.Assert(found, Equals, true) |
| 355 found = s.t.HasUnit("s-0", "u-1") |
| 356 c.Assert(found, Equals, false) |
| 357 } |
| 358 |
| 359 func (s TopologySuite) TestUnitName(c *C) { |
| 360 // Check that the human readable names are returned correctly. |
| 361 err := s.t.AddService("s-0", "wordpress") |
| 362 c.Assert(err, IsNil) |
| 363 err = s.t.AddService("s-1", "mysql") |
| 364 c.Assert(err, IsNil) |
| 365 _, err = s.t.AddUnit("s-0", "u-0") |
| 366 c.Assert(err, IsNil) |
| 367 _, err = s.t.AddUnit("s-0", "u-1") |
| 368 c.Assert(err, IsNil) |
| 369 _, err = s.t.AddUnit("s-1", "u-2") |
| 370 c.Assert(err, IsNil) |
| 371 name, err := s.t.UnitName("s-0", "u-0") |
| 372 c.Assert(err, IsNil) |
| 373 c.Assert(name, Equals, "wordpress/0") |
| 374 name, err = s.t.UnitName("s-0", "u-1") |
| 375 c.Assert(err, IsNil) |
| 376 c.Assert(name, Equals, "wordpress/1") |
| 377 name, err = s.t.UnitName("s-1", "u-2") |
| 378 c.Assert(err, IsNil) |
| 379 c.Assert(name, Equals, "mysql/0") |
| 380 } |
| 381 |
| 382 func (s TopologySuite) TestUnitNameWithNonExistingServiceOrUnit(c *C) { |
| 383 // Check if the retrieval of unit names fails if the service |
| 384 // or the unit doesn't exist. |
| 385 _, err := s.t.UnitName("s-0", "u-1") |
| 386 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 387 err = s.t.AddService("s-0", "wordpress") |
| 388 c.Assert(err, IsNil) |
| 389 _, err = s.t.UnitName("s-0", "u-1") |
| 390 c.Assert(err, ErrorMatches, `unit with key "u-1" not found`) |
| 391 _, err = s.t.AddUnit("s-0", "u-0") |
| 392 c.Assert(err, IsNil) |
| 393 _, err = s.t.UnitName("s-0", "u-1") |
| 394 c.Assert(err, ErrorMatches, `unit with key "u-1" not found`) |
| 395 } |
| 396 |
| 397 func (s TopologySuite) TestRemoveUnit(c *C) { |
| 398 // Check that the removing of a unit works correctly. |
| 399 err := s.t.AddService("s-0", "wordpress") |
| 400 c.Assert(err, IsNil) |
| 401 _, err = s.t.AddUnit("s-0", "u-0") |
| 402 c.Assert(err, IsNil) |
| 403 _, err = s.t.AddUnit("s-0", "u-1") |
| 404 c.Assert(err, IsNil) |
| 405 err = s.t.RemoveUnit("s-0", "u-0") |
| 406 c.Assert(err, IsNil) |
| 407 found := s.t.HasUnit("s-0", "u-0") |
| 408 c.Assert(found, Equals, false) |
| 409 found = s.t.HasUnit("s-0", "u-1") |
| 410 c.Assert(found, Equals, true) |
| 411 } |
| 412 |
| 413 func (s TopologySuite) TestRemoveNonExistingUnit(c *C) { |
| 414 // Check that the removing of non-existing units fails. |
| 415 err := s.t.RemoveUnit("s-0", "u-0") |
| 416 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 417 err = s.t.AddService("s-0", "wordpress") |
| 418 c.Assert(err, IsNil) |
| 419 err = s.t.RemoveUnit("s-0", "u-0") |
| 420 c.Assert(err, ErrorMatches, `unit with key "u-0" not found`) |
| 421 } |
| 422 |
| 423 func (s TopologySuite) TestUnitKeyFromSequence(c *C) { |
| 424 // Check that the retrieving of a unit key by service key |
| 425 // and sequence number works correctly. |
| 426 err := s.t.AddService("s-0", "wordpress") |
| 427 c.Assert(err, IsNil) |
| 428 _, err = s.t.AddUnit("s-0", "u-0") |
| 429 c.Assert(err, IsNil) |
| 430 _, err = s.t.AddUnit("s-0", "u-1") |
| 431 c.Assert(err, IsNil) |
| 432 key, err := s.t.UnitKeyFromSequence("s-0", 0) |
| 433 c.Assert(err, IsNil) |
| 434 c.Assert(key, Equals, "u-0") |
| 435 key, err = s.t.UnitKeyFromSequence("s-0", 1) |
| 436 c.Assert(err, IsNil) |
| 437 c.Assert(key, Equals, "u-1") |
| 438 key, err = s.t.UnitKeyFromSequence("s-0", 2) |
| 439 c.Assert(err, ErrorMatches, `unit with sequence number 2 not found`) |
| 440 } |
| 441 |
| 442 func (s TopologySuite) TestUnitKeyFromNonExistingService(c *C) { |
| 443 _, err := s.t.UnitKeyFromSequence("s-0", 0) |
| 444 c.Assert(err, ErrorMatches, `service with key "s-0" not found`) |
| 445 } |
| 446 |
| 447 type ConfigNodeSuite struct { |
| 448 zkServer *zookeeper.Server |
| 449 zkTestRoot string |
| 450 zkTestPort int |
| 451 zkAddr string |
| 452 zkConn *zookeeper.Conn |
| 453 path string |
| 454 } |
| 455 |
| 456 var _ = Suite(&ConfigNodeSuite{}) |
| 457 |
| 458 func (s *ConfigNodeSuite) SetUpSuite(c *C) { |
| 459 s.path = "/config" |
| 460 } |
| 461 |
| 462 func (s *ConfigNodeSuite) SetUpTest(c *C) { |
| 463 // Connect the server. |
| 464 st, err := Open(&Info{ |
| 465 Addrs: []string{TestingZkAddr}, |
| 466 }) |
| 467 c.Assert(err, IsNil) |
| 468 s.zkConn = ZkConn(st) |
| 469 } |
| 470 |
| 471 func (s *ConfigNodeSuite) TearDownTest(c *C) { |
| 472 // Delete possible nodes, ignore errors. |
| 473 zkRemoveTree(s.zkConn, s.path) |
| 474 s.zkConn.Close() |
| 475 } |
| 476 |
| 477 func (s ConfigNodeSuite) TestCreateEmptyConfigNode(c *C) { |
| 478 // Check that creating an empty node works correctly. |
| 479 node, err := createConfigNode(s.zkConn, s.path, nil) |
| 480 c.Assert(err, IsNil) |
| 481 c.Assert(node.Keys(), DeepEquals, []string{}) |
| 482 } |
| 483 |
| 484 func (s ConfigNodeSuite) TestReadWithoutWrite(c *C) { |
| 485 // Check reading without writing. |
| 486 node, err := readConfigNode(s.zkConn, s.path) |
| 487 c.Assert(err, IsNil) |
| 488 c.Assert(node, Not(IsNil)) |
| 489 } |
| 490 |
| 491 func (s ConfigNodeSuite) TestSetWithoutWrite(c *C) { |
| 492 // Check that config values can be set. |
| 493 _, err := s.zkConn.Create(s.path, "", 0, zkPermAll) |
| 494 c.Assert(err, IsNil) |
| 495 node, err := readConfigNode(s.zkConn, s.path) |
| 496 c.Assert(err, IsNil) |
| 497 options := map[string]interface{}{"alpha": "beta", "one": 1} |
| 498 node.Update(options) |
| 499 c.Assert(node.Map(), DeepEquals, options) |
| 500 // Node data has to be empty. |
| 501 yaml, _, err := s.zkConn.Get("/config") |
| 502 c.Assert(err, IsNil) |
| 503 c.Assert(yaml, Equals, "") |
| 504 } |
| 505 |
| 506 func (s ConfigNodeSuite) TestSetWithWrite(c *C) { |
| 507 // Check that write updates the local and the ZooKeeper state. |
| 508 node, err := readConfigNode(s.zkConn, s.path) |
| 509 c.Assert(err, IsNil) |
| 510 options := map[string]interface{}{"alpha": "beta", "one": 1} |
| 511 node.Update(options) |
| 512 changes, err := node.Write() |
| 513 c.Assert(err, IsNil) |
| 514 c.Assert(changes, DeepEquals, []ItemChange{ |
| 515 {ItemAdded, "alpha", nil, "beta"}, |
| 516 {ItemAdded, "one", nil, 1}, |
| 517 }) |
| 518 // Check local state. |
| 519 c.Assert(node.Map(), DeepEquals, options) |
| 520 // Check ZooKeeper state. |
| 521 yaml, _, err := s.zkConn.Get(s.path) |
| 522 c.Assert(err, IsNil) |
| 523 zkData := make(map[string]interface{}) |
| 524 err = goyaml.Unmarshal([]byte(yaml), zkData) |
| 525 c.Assert(zkData, DeepEquals, options) |
| 526 } |
| 527 |
| 528 func (s ConfigNodeSuite) TestConflictOnSet(c *C) { |
| 529 // Check version conflict errors. |
| 530 nodeOne, err := readConfigNode(s.zkConn, s.path) |
| 531 c.Assert(err, IsNil) |
| 532 nodeTwo, err := readConfigNode(s.zkConn, s.path) |
| 533 c.Assert(err, IsNil) |
| 534 |
| 535 optionsOld := map[string]interface{}{"alpha": "beta", "one": 1} |
| 536 nodeOne.Update(optionsOld) |
| 537 nodeOne.Write() |
| 538 |
| 539 nodeTwo.Update(optionsOld) |
| 540 changes, err := nodeTwo.Write() |
| 541 c.Assert(err, IsNil) |
| 542 c.Assert(changes, DeepEquals, []ItemChange{ |
| 543 {ItemAdded, "alpha", nil, "beta"}, |
| 544 {ItemAdded, "one", nil, 1}, |
| 545 }) |
| 546 |
| 547 // First test node one. |
| 548 c.Assert(nodeOne.Map(), DeepEquals, optionsOld) |
| 549 |
| 550 // Write on node one. |
| 551 optionsNew := map[string]interface{}{"alpha": "gamma", "one": "two"} |
| 552 nodeOne.Update(optionsNew) |
| 553 changes, err = nodeOne.Write() |
| 554 c.Assert(err, IsNil) |
| 555 c.Assert(changes, DeepEquals, []ItemChange{ |
| 556 {ItemModified, "alpha", "beta", "gamma"}, |
| 557 {ItemModified, "one", 1, "two"}, |
| 558 }) |
| 559 |
| 560 // Verify that node one reports as expected. |
| 561 c.Assert(nodeOne.Map(), DeepEquals, optionsNew) |
| 562 |
| 563 // Verify that node two has still the old data. |
| 564 c.Assert(nodeTwo.Map(), DeepEquals, optionsOld) |
| 565 |
| 566 // Now issue a Set/Write from node two. This will |
| 567 // merge the data deleting 'one' and updating |
| 568 // other values. |
| 569 optionsMerge := map[string]interface{}{"alpha": "cappa", "new": "next"} |
| 570 nodeTwo.Update(optionsMerge) |
| 571 nodeTwo.Delete("one") |
| 572 |
| 573 expected := map[string]interface{}{"alpha": "cappa", "new": "next"} |
| 574 changes, err = nodeTwo.Write() |
| 575 c.Assert(err, IsNil) |
| 576 c.Assert(changes, DeepEquals, []ItemChange{ |
| 577 {ItemModified, "alpha", "beta", "cappa"}, |
| 578 {ItemAdded, "new", nil, "next"}, |
| 579 {ItemDeleted, "one", 1, nil}, |
| 580 }) |
| 581 c.Assert(expected, DeepEquals, nodeTwo.Map()) |
| 582 |
| 583 // But node one still reflects the former data. |
| 584 c.Assert(nodeOne.Map(), DeepEquals, optionsNew) |
| 585 } |
| 586 |
| 587 func (s ConfigNodeSuite) TestSetItem(c *C) { |
| 588 // Check that Set works as expected. |
| 589 node, err := readConfigNode(s.zkConn, s.path) |
| 590 c.Assert(err, IsNil) |
| 591 options := map[string]interface{}{"alpha": "beta", "one": 1} |
| 592 node.Set("alpha", "beta") |
| 593 node.Set("one", 1) |
| 594 changes, err := node.Write() |
| 595 c.Assert(err, IsNil) |
| 596 c.Assert(changes, DeepEquals, []ItemChange{ |
| 597 {ItemAdded, "alpha", nil, "beta"}, |
| 598 {ItemAdded, "one", nil, 1}, |
| 599 }) |
| 600 // Check local state. |
| 601 c.Assert(node.Map(), DeepEquals, options) |
| 602 // Check ZooKeeper state. |
| 603 yaml, _, err := s.zkConn.Get(s.path) |
| 604 c.Assert(err, IsNil) |
| 605 zkData := make(map[string]interface{}) |
| 606 err = goyaml.Unmarshal([]byte(yaml), zkData) |
| 607 c.Assert(zkData, DeepEquals, options) |
| 608 } |
| 609 |
| 610 func (s ConfigNodeSuite) TestMultipleReads(c *C) { |
| 611 // Check that reads without writes always resets the data. |
| 612 nodeOne, err := readConfigNode(s.zkConn, s.path) |
| 613 c.Assert(err, IsNil) |
| 614 nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"}) |
| 615 value, ok := nodeOne.Get("alpha") |
| 616 c.Assert(ok, Equals, true) |
| 617 c.Assert(value, Equals, "beta") |
| 618 value, ok = nodeOne.Get("foo") |
| 619 c.Assert(ok, Equals, true) |
| 620 c.Assert(value, Equals, "bar") |
| 621 value, ok = nodeOne.Get("baz") |
| 622 c.Assert(ok, Equals, false) |
| 623 |
| 624 // A read resets the data to the empty state. |
| 625 err = nodeOne.Read() |
| 626 c.Assert(err, IsNil) |
| 627 c.Assert(nodeOne.Map(), DeepEquals, map[string]interface{}{}) |
| 628 nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"}) |
| 629 changes, err := nodeOne.Write() |
| 630 c.Assert(err, IsNil) |
| 631 c.Assert(changes, DeepEquals, []ItemChange{ |
| 632 {ItemAdded, "alpha", nil, "beta"}, |
| 633 {ItemAdded, "foo", nil, "bar"}, |
| 634 }) |
| 635 |
| 636 // A write retains the newly set values. |
| 637 value, ok = nodeOne.Get("alpha") |
| 638 c.Assert(ok, Equals, true) |
| 639 c.Assert(value, Equals, "beta") |
| 640 value, ok = nodeOne.Get("foo") |
| 641 c.Assert(ok, Equals, true) |
| 642 c.Assert(value, Equals, "bar") |
| 643 |
| 644 // Now get another state instance and change ZooKeeper state. |
| 645 nodeTwo, err := readConfigNode(s.zkConn, s.path) |
| 646 c.Assert(err, IsNil) |
| 647 nodeTwo.Update(map[string]interface{}{"foo": "different"}) |
| 648 changes, err = nodeTwo.Write() |
| 649 c.Assert(err, IsNil) |
| 650 c.Assert(changes, DeepEquals, []ItemChange{ |
| 651 {ItemModified, "foo", "bar", "different"}, |
| 652 }) |
| 653 |
| 654 // This should pull in the new state into node one. |
| 655 err = nodeOne.Read() |
| 656 c.Assert(err, IsNil) |
| 657 value, ok = nodeOne.Get("alpha") |
| 658 c.Assert(ok, Equals, true) |
| 659 c.Assert(value, Equals, "beta") |
| 660 value, ok = nodeOne.Get("foo") |
| 661 c.Assert(ok, Equals, true) |
| 662 c.Assert(value, Equals, "different") |
| 663 } |
| 664 |
| 665 func (s ConfigNodeSuite) TestDeleteEmptiesState(c *C) { |
| 666 // Check that delete creates an empty state. |
| 667 node, err := readConfigNode(s.zkConn, s.path) |
| 668 c.Assert(err, IsNil) |
| 669 node.Set("a", "foo") |
| 670 changes, err := node.Write() |
| 671 c.Assert(err, IsNil) |
| 672 c.Assert(changes, DeepEquals, []ItemChange{ |
| 673 {ItemAdded, "a", nil, "foo"}, |
| 674 }) |
| 675 node.Delete("a") |
| 676 changes, err = node.Write() |
| 677 c.Assert(err, IsNil) |
| 678 c.Assert(changes, DeepEquals, []ItemChange{ |
| 679 {ItemDeleted, "a", "foo", nil}, |
| 680 }) |
| 681 c.Assert(node.Map(), DeepEquals, map[string]interface{}{}) |
| 682 } |
| 683 |
| 684 func (s ConfigNodeSuite) TestReadResync(c *C) { |
| 685 // Check that read pulls the data into the node. |
| 686 nodeOne, err := readConfigNode(s.zkConn, s.path) |
| 687 c.Assert(err, IsNil) |
| 688 nodeOne.Set("a", "foo") |
| 689 changes, err := nodeOne.Write() |
| 690 c.Assert(err, IsNil) |
| 691 c.Assert(changes, DeepEquals, []ItemChange{ |
| 692 {ItemAdded, "a", nil, "foo"}, |
| 693 }) |
| 694 nodeTwo, err := readConfigNode(s.zkConn, s.path) |
| 695 c.Assert(err, IsNil) |
| 696 nodeTwo.Delete("a") |
| 697 changes, err = nodeTwo.Write() |
| 698 c.Assert(err, IsNil) |
| 699 c.Assert(changes, DeepEquals, []ItemChange{ |
| 700 {ItemDeleted, "a", "foo", nil}, |
| 701 }) |
| 702 nodeTwo.Set("a", "bar") |
| 703 changes, err = nodeTwo.Write() |
| 704 c.Assert(err, IsNil) |
| 705 c.Assert(changes, DeepEquals, []ItemChange{ |
| 706 {ItemAdded, "a", nil, "bar"}, |
| 707 }) |
| 708 // Read of node one should pick up the new value. |
| 709 err = nodeOne.Read() |
| 710 c.Assert(err, IsNil) |
| 711 value, ok := nodeOne.Get("a") |
| 712 c.Assert(ok, Equals, true) |
| 713 c.Assert(value, Equals, "bar") |
| 714 } |
| 715 |
| 716 func (s ConfigNodeSuite) TestMultipleWrites(c *C) { |
| 717 // Check that multiple writes only do the right changes. |
| 718 node, err := readConfigNode(s.zkConn, s.path) |
| 719 c.Assert(err, IsNil) |
| 720 node.Update(map[string]interface{}{"foo": "bar", "this": "that"}) |
| 721 changes, err := node.Write() |
| 722 c.Assert(err, IsNil) |
| 723 c.Assert(changes, DeepEquals, []ItemChange{ |
| 724 {ItemAdded, "foo", nil, "bar"}, |
| 725 {ItemAdded, "this", nil, "that"}, |
| 726 }) |
| 727 node.Delete("this") |
| 728 node.Set("another", "value") |
| 729 changes, err = node.Write() |
| 730 c.Assert(err, IsNil) |
| 731 c.Assert(changes, DeepEquals, []ItemChange{ |
| 732 {ItemAdded, "another", nil, "value"}, |
| 733 {ItemDeleted, "this", "that", nil}, |
| 734 }) |
| 735 |
| 736 expected := map[string]interface{}{"foo": "bar", "another": "value"} |
| 737 c.Assert(expected, DeepEquals, node.Map()) |
| 738 |
| 739 changes, err = node.Write() |
| 740 c.Assert(err, IsNil) |
| 741 c.Assert(changes, DeepEquals, []ItemChange{}) |
| 742 |
| 743 err = node.Read() |
| 744 c.Assert(err, IsNil) |
| 745 c.Assert(expected, DeepEquals, node.Map()) |
| 746 |
| 747 changes, err = node.Write() |
| 748 c.Assert(err, IsNil) |
| 749 c.Assert(changes, DeepEquals, []ItemChange{}) |
| 750 } |
| 751 |
| 752 func (s ConfigNodeSuite) TestWriteTwice(c *C) { |
| 753 // Check the correct writing into a node by two config nodes. |
| 754 nodeOne, err := readConfigNode(s.zkConn, s.path) |
| 755 c.Assert(err, IsNil) |
| 756 nodeOne.Set("a", "foo") |
| 757 changes, err := nodeOne.Write() |
| 758 c.Assert(err, IsNil) |
| 759 c.Assert(changes, DeepEquals, []ItemChange{ |
| 760 {ItemAdded, "a", nil, "foo"}, |
| 761 }) |
| 762 |
| 763 nodeTwo, err := readConfigNode(s.zkConn, s.path) |
| 764 c.Assert(err, IsNil) |
| 765 nodeTwo.Set("a", "bar") |
| 766 changes, err = nodeTwo.Write() |
| 767 c.Assert(err, IsNil) |
| 768 c.Assert(changes, DeepEquals, []ItemChange{ |
| 769 {ItemModified, "a", "foo", "bar"}, |
| 770 }) |
| 771 |
| 772 // Shouldn't write again. Changes were already |
| 773 // flushed and acted upon by other parties. |
| 774 changes, err = nodeOne.Write() |
| 775 c.Assert(err, IsNil) |
| 776 c.Assert(changes, DeepEquals, []ItemChange{}) |
| 777 |
| 778 err = nodeOne.Read() |
| 779 c.Assert(err, IsNil) |
| 780 c.Assert(nodeOne, DeepEquals, nodeTwo) |
| 781 } |
| 782 |
| 783 type QuoteSuite struct{} |
| 784 |
| 785 var _ = Suite(&QuoteSuite{}) |
| 786 |
| 787 func (s QuoteSuite) TestUnmodified(c *C) { |
| 788 // Check that a string containig only valid |
| 789 // chars stays unmodified. |
| 790 in := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-" |
| 791 out := Quote(in) |
| 792 c.Assert(out, Equals, in) |
| 793 } |
| 794 |
| 795 func (s QuoteSuite) TestQuote(c *C) { |
| 796 // Check that invalid chars are translated correctly. |
| 797 in := "hello_there/how'are~you-today.sir" |
| 798 out := Quote(in) |
| 799 c.Assert(out, Equals, "hello_5f_there_2f_how_27_are_7e_you-today.sir") |
| 800 } |
LEFT | RIGHT |