github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/firewaller/firewaller_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package firewaller_test 5 6 import ( 7 "fmt" 8 "reflect" 9 "sync/atomic" 10 "time" 11 12 "github.com/juju/clock" 13 "github.com/juju/clock/testclock" 14 "github.com/juju/errors" 15 "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/charm.v6" 20 "gopkg.in/juju/names.v2" 21 "gopkg.in/juju/worker.v1" 22 23 "github.com/juju/juju/api" 24 basetesting "github.com/juju/juju/api/base/testing" 25 "github.com/juju/juju/api/credentialvalidator" 26 "github.com/juju/juju/api/crossmodelrelations" 27 apifirewaller "github.com/juju/juju/api/firewaller" 28 "github.com/juju/juju/api/remoterelations" 29 apitesting "github.com/juju/juju/api/testing" 30 "github.com/juju/juju/apiserver/params" 31 "github.com/juju/juju/core/crossmodel" 32 "github.com/juju/juju/core/status" 33 "github.com/juju/juju/environs" 34 "github.com/juju/juju/environs/config" 35 "github.com/juju/juju/environs/context" 36 "github.com/juju/juju/environs/instances" 37 jujutesting "github.com/juju/juju/juju/testing" 38 "github.com/juju/juju/network" 39 "github.com/juju/juju/provider/dummy" 40 "github.com/juju/juju/state" 41 statetesting "github.com/juju/juju/state/testing" 42 coretesting "github.com/juju/juju/testing" 43 "github.com/juju/juju/testing/factory" 44 "github.com/juju/juju/worker/firewaller" 45 ) 46 47 // firewallerBaseSuite implements common functionality for embedding 48 // into each of the other per-mode suites. 49 type firewallerBaseSuite struct { 50 jujutesting.JujuConnSuite 51 testing.OsEnvSuite 52 op <-chan dummy.Operation 53 charm *state.Charm 54 controllerMachine *state.Machine 55 controllerPassword string 56 57 st api.Connection 58 firewaller *apifirewaller.Client 59 remoteRelations *remoterelations.Client 60 crossmodelFirewaller *crossmodelrelations.Client 61 clock clock.Clock 62 63 callCtx context.ProviderCallContext 64 credentialsFacade *credentialvalidator.Facade 65 } 66 67 func (s *firewallerBaseSuite) SetUpSuite(c *gc.C) { 68 s.OsEnvSuite.SetUpSuite(c) 69 s.JujuConnSuite.SetUpSuite(c) 70 } 71 72 func (s *firewallerBaseSuite) TearDownSuite(c *gc.C) { 73 s.JujuConnSuite.TearDownSuite(c) 74 s.OsEnvSuite.TearDownSuite(c) 75 } 76 77 func (s *firewallerBaseSuite) SetUpTest(c *gc.C) { 78 s.OsEnvSuite.SetUpTest(c) 79 s.JujuConnSuite.SetUpTest(c) 80 81 s.callCtx = context.NewCloudCallContext() 82 } 83 84 func (s *firewallerBaseSuite) TearDownTest(c *gc.C) { 85 s.JujuConnSuite.TearDownTest(c) 86 s.OsEnvSuite.TearDownTest(c) 87 } 88 89 var _ worker.Worker = (*firewaller.Firewaller)(nil) 90 91 func (s *firewallerBaseSuite) setUpTest(c *gc.C, firewallMode string) { 92 add := map[string]interface{}{"firewall-mode": firewallMode} 93 s.DummyConfig = dummy.SampleConfig().Merge(add).Delete("admin-secret") 94 95 s.JujuConnSuite.SetUpTest(c) 96 s.charm = s.AddTestingCharm(c, "dummy") 97 98 // Create a manager machine and login to the API. 99 var err error 100 s.controllerMachine, err = s.State.AddMachine("quantal", state.JobManageModel) 101 c.Assert(err, jc.ErrorIsNil) 102 s.controllerPassword, err = utils.RandomPassword() 103 c.Assert(err, jc.ErrorIsNil) 104 err = s.controllerMachine.SetPassword(s.controllerPassword) 105 c.Assert(err, jc.ErrorIsNil) 106 err = s.controllerMachine.SetProvisioned("i-manager", "", "fake_nonce", nil) 107 c.Assert(err, jc.ErrorIsNil) 108 s.st = s.OpenAPIAsMachine(c, s.controllerMachine.Tag(), s.controllerPassword, "fake_nonce") 109 c.Assert(s.st, gc.NotNil) 110 111 // Create the API facades. 112 firewallerClient, err := apifirewaller.NewClient(s.st) 113 c.Assert(err, jc.ErrorIsNil) 114 s.firewaller = firewallerClient 115 s.remoteRelations = remoterelations.NewClient(s.st) 116 c.Assert(s.remoteRelations, gc.NotNil) 117 118 s.credentialsFacade = credentialvalidator.NewFacade(s.st) 119 } 120 121 // assertPorts retrieves the open ports of the instance and compares them 122 // to the expected. 123 func (s *firewallerBaseSuite) assertPorts(c *gc.C, inst instances.Instance, machineId string, expected []network.IngressRule) { 124 fwInst, ok := inst.(instances.InstanceFirewaller) 125 c.Assert(ok, gc.Equals, true) 126 127 start := time.Now() 128 for { 129 s.BackingState.StartSync() 130 got, err := fwInst.IngressRules(s.callCtx, machineId) 131 if err != nil { 132 c.Fatal(err) 133 return 134 } 135 network.SortIngressRules(got) 136 network.SortIngressRules(expected) 137 if reflect.DeepEqual(got, expected) { 138 c.Succeed() 139 return 140 } 141 if time.Since(start) > coretesting.LongWait { 142 c.Fatalf("timed out: expected %q; got %q", expected, got) 143 return 144 } 145 time.Sleep(coretesting.ShortWait) 146 } 147 } 148 149 // assertEnvironPorts retrieves the open ports of environment and compares them 150 // to the expected. 151 func (s *firewallerBaseSuite) assertEnvironPorts(c *gc.C, expected []network.IngressRule) { 152 fwEnv, ok := s.Environ.(environs.Firewaller) 153 c.Assert(ok, gc.Equals, true) 154 155 start := time.Now() 156 for { 157 s.BackingState.StartSync() 158 got, err := fwEnv.IngressRules(s.callCtx) 159 if err != nil { 160 c.Fatal(err) 161 return 162 } 163 network.SortIngressRules(got) 164 network.SortIngressRules(expected) 165 if reflect.DeepEqual(got, expected) { 166 c.Succeed() 167 return 168 } 169 if time.Since(start) > coretesting.LongWait { 170 c.Fatalf("timed out: expected %q; got %q", expected, got) 171 return 172 } 173 time.Sleep(coretesting.ShortWait) 174 } 175 } 176 177 func (s *firewallerBaseSuite) addUnit(c *gc.C, app *state.Application) (*state.Unit, *state.Machine) { 178 u, err := app.AddUnit(state.AddUnitParams{}) 179 c.Assert(err, jc.ErrorIsNil) 180 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 181 c.Assert(err, jc.ErrorIsNil) 182 id, err := u.AssignedMachineId() 183 c.Assert(err, jc.ErrorIsNil) 184 m, err := s.State.Machine(id) 185 c.Assert(err, jc.ErrorIsNil) 186 return u, m 187 } 188 189 // startInstance starts a new instance for the given machine. 190 func (s *firewallerBaseSuite) startInstance(c *gc.C, m *state.Machine) instances.Instance { 191 inst, hc := jujutesting.AssertStartInstance(c, s.Environ, s.callCtx, s.ControllerConfig.ControllerUUID(), m.Id()) 192 err := m.SetProvisioned(inst.Id(), "", "fake_nonce", hc) 193 c.Assert(err, jc.ErrorIsNil) 194 return inst 195 } 196 197 type InstanceModeSuite struct { 198 firewallerBaseSuite 199 } 200 201 var _ = gc.Suite(&InstanceModeSuite{}) 202 203 func (s *InstanceModeSuite) SetUpTest(c *gc.C) { 204 s.firewallerBaseSuite.setUpTest(c, config.FwInstance) 205 } 206 207 func (s *InstanceModeSuite) TearDownTest(c *gc.C) { 208 s.firewallerBaseSuite.JujuConnSuite.TearDownTest(c) 209 } 210 211 // mockClock will panic if anything but After is called 212 type mockClock struct { 213 clock.Clock 214 wait time.Duration 215 c *gc.C 216 } 217 218 func (m *mockClock) Now() time.Time { 219 return time.Now() 220 } 221 222 func (m *mockClock) After(duration time.Duration) <-chan time.Time { 223 m.wait = duration 224 return time.After(time.Millisecond) 225 } 226 227 func (s *InstanceModeSuite) newFirewaller(c *gc.C) worker.Worker { 228 return s.newFirewallerWithClock(c, &mockClock{c: c}) 229 } 230 231 func (s *InstanceModeSuite) newFirewallerWithClock(c *gc.C, clock clock.Clock) worker.Worker { 232 s.clock = clock 233 fwEnv, ok := s.Environ.(environs.Firewaller) 234 c.Assert(ok, gc.Equals, true) 235 236 cfg := firewaller.Config{ 237 ModelUUID: s.State.ModelUUID(), 238 Mode: config.FwInstance, 239 EnvironFirewaller: fwEnv, 240 EnvironInstances: s.Environ, 241 FirewallerAPI: s.firewaller, 242 RemoteRelationsApi: s.remoteRelations, 243 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 244 return s.crossmodelFirewaller, nil 245 }, 246 Clock: s.clock, 247 CredentialAPI: s.credentialsFacade, 248 } 249 fw, err := firewaller.NewFirewaller(cfg) 250 c.Assert(err, jc.ErrorIsNil) 251 return fw 252 } 253 254 func (s *InstanceModeSuite) TestStartStop(c *gc.C) { 255 fw := s.newFirewaller(c) 256 statetesting.AssertKillAndWait(c, fw) 257 } 258 259 func (s *InstanceModeSuite) TestNotExposedApplication(c *gc.C) { 260 fw := s.newFirewaller(c) 261 defer statetesting.AssertKillAndWait(c, fw) 262 263 app := s.AddTestingApplication(c, "wordpress", s.charm) 264 u, m := s.addUnit(c, app) 265 inst := s.startInstance(c, m) 266 267 err := u.OpenPort("tcp", 80) 268 c.Assert(err, jc.ErrorIsNil) 269 err = u.OpenPort("tcp", 8080) 270 c.Assert(err, jc.ErrorIsNil) 271 272 s.assertPorts(c, inst, m.Id(), nil) 273 274 err = u.ClosePort("tcp", 80) 275 c.Assert(err, jc.ErrorIsNil) 276 277 s.assertPorts(c, inst, m.Id(), nil) 278 } 279 280 func (s *InstanceModeSuite) TestExposedApplication(c *gc.C) { 281 fw := s.newFirewaller(c) 282 defer statetesting.AssertKillAndWait(c, fw) 283 284 app := s.AddTestingApplication(c, "wordpress", s.charm) 285 286 err := app.SetExposed() 287 c.Assert(err, jc.ErrorIsNil) 288 u, m := s.addUnit(c, app) 289 inst := s.startInstance(c, m) 290 291 err = u.OpenPorts("tcp", 80, 90) 292 c.Assert(err, jc.ErrorIsNil) 293 err = u.OpenPort("tcp", 8080) 294 c.Assert(err, jc.ErrorIsNil) 295 296 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 297 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 298 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 299 }) 300 301 err = u.ClosePorts("tcp", 80, 90) 302 c.Assert(err, jc.ErrorIsNil) 303 304 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 305 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 306 }) 307 } 308 309 func (s *InstanceModeSuite) TestMultipleExposedApplications(c *gc.C) { 310 fw := s.newFirewaller(c) 311 defer statetesting.AssertKillAndWait(c, fw) 312 313 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 314 err := app1.SetExposed() 315 c.Assert(err, jc.ErrorIsNil) 316 317 u1, m1 := s.addUnit(c, app1) 318 inst1 := s.startInstance(c, m1) 319 err = u1.OpenPort("tcp", 80) 320 c.Assert(err, jc.ErrorIsNil) 321 err = u1.OpenPort("tcp", 8080) 322 c.Assert(err, jc.ErrorIsNil) 323 324 app2 := s.AddTestingApplication(c, "mysql", s.charm) 325 c.Assert(err, jc.ErrorIsNil) 326 err = app2.SetExposed() 327 c.Assert(err, jc.ErrorIsNil) 328 329 u2, m2 := s.addUnit(c, app2) 330 inst2 := s.startInstance(c, m2) 331 err = u2.OpenPort("tcp", 3306) 332 c.Assert(err, jc.ErrorIsNil) 333 334 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 335 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 336 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 337 }) 338 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 339 network.MustNewIngressRule("tcp", 3306, 3306, "0.0.0.0/0"), 340 }) 341 342 err = u1.ClosePort("tcp", 80) 343 c.Assert(err, jc.ErrorIsNil) 344 err = u2.ClosePort("tcp", 3306) 345 c.Assert(err, jc.ErrorIsNil) 346 347 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 348 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 349 }) 350 s.assertPorts(c, inst2, m2.Id(), nil) 351 } 352 353 func (s *InstanceModeSuite) TestMachineWithoutInstanceId(c *gc.C) { 354 fw := s.newFirewaller(c) 355 defer statetesting.AssertKillAndWait(c, fw) 356 357 app := s.AddTestingApplication(c, "wordpress", s.charm) 358 err := app.SetExposed() 359 c.Assert(err, jc.ErrorIsNil) 360 // add a unit but don't start its instance yet. 361 u1, m1 := s.addUnit(c, app) 362 363 // add another unit and start its instance, so that 364 // we're sure the firewaller has seen the first instance. 365 u2, m2 := s.addUnit(c, app) 366 inst2 := s.startInstance(c, m2) 367 err = u2.OpenPort("tcp", 80) 368 c.Assert(err, jc.ErrorIsNil) 369 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 370 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 371 }) 372 373 inst1 := s.startInstance(c, m1) 374 err = u1.OpenPort("tcp", 8080) 375 c.Assert(err, jc.ErrorIsNil) 376 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 377 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 378 }) 379 } 380 381 func (s *InstanceModeSuite) TestMultipleUnits(c *gc.C) { 382 fw := s.newFirewaller(c) 383 defer statetesting.AssertKillAndWait(c, fw) 384 385 app := s.AddTestingApplication(c, "wordpress", s.charm) 386 err := app.SetExposed() 387 c.Assert(err, jc.ErrorIsNil) 388 389 u1, m1 := s.addUnit(c, app) 390 inst1 := s.startInstance(c, m1) 391 err = u1.OpenPort("tcp", 80) 392 c.Assert(err, jc.ErrorIsNil) 393 394 u2, m2 := s.addUnit(c, app) 395 inst2 := s.startInstance(c, m2) 396 err = u2.OpenPort("tcp", 80) 397 c.Assert(err, jc.ErrorIsNil) 398 399 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 400 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 401 }) 402 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 403 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 404 }) 405 406 err = u1.ClosePort("tcp", 80) 407 c.Assert(err, jc.ErrorIsNil) 408 err = u2.ClosePort("tcp", 80) 409 c.Assert(err, jc.ErrorIsNil) 410 411 s.assertPorts(c, inst1, m1.Id(), nil) 412 s.assertPorts(c, inst2, m2.Id(), nil) 413 } 414 415 func (s *InstanceModeSuite) TestStartWithState(c *gc.C) { 416 app := s.AddTestingApplication(c, "wordpress", s.charm) 417 err := app.SetExposed() 418 c.Assert(err, jc.ErrorIsNil) 419 u, m := s.addUnit(c, app) 420 inst := s.startInstance(c, m) 421 422 err = u.OpenPort("tcp", 80) 423 c.Assert(err, jc.ErrorIsNil) 424 err = u.OpenPort("tcp", 8080) 425 c.Assert(err, jc.ErrorIsNil) 426 427 // Nothing open without firewaller. 428 s.assertPorts(c, inst, m.Id(), nil) 429 430 // Starting the firewaller opens the ports. 431 fw := s.newFirewaller(c) 432 defer statetesting.AssertKillAndWait(c, fw) 433 434 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 435 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 436 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 437 }) 438 439 err = app.SetExposed() 440 c.Assert(err, jc.ErrorIsNil) 441 } 442 443 func (s *InstanceModeSuite) TestStartWithPartialState(c *gc.C) { 444 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 445 c.Assert(err, jc.ErrorIsNil) 446 inst := s.startInstance(c, m) 447 448 app := s.AddTestingApplication(c, "wordpress", s.charm) 449 err = app.SetExposed() 450 c.Assert(err, jc.ErrorIsNil) 451 452 // Starting the firewaller, no open ports. 453 fw := s.newFirewaller(c) 454 defer statetesting.AssertKillAndWait(c, fw) 455 456 s.assertPorts(c, inst, m.Id(), nil) 457 458 // Complete steps to open port. 459 u, err := app.AddUnit(state.AddUnitParams{}) 460 c.Assert(err, jc.ErrorIsNil) 461 err = u.AssignToMachine(m) 462 c.Assert(err, jc.ErrorIsNil) 463 err = u.OpenPort("tcp", 80) 464 c.Assert(err, jc.ErrorIsNil) 465 466 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 467 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 468 }) 469 } 470 471 func (s *InstanceModeSuite) TestStartWithUnexposedApplication(c *gc.C) { 472 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 473 c.Assert(err, jc.ErrorIsNil) 474 inst := s.startInstance(c, m) 475 476 app := s.AddTestingApplication(c, "wordpress", s.charm) 477 u, err := app.AddUnit(state.AddUnitParams{}) 478 c.Assert(err, jc.ErrorIsNil) 479 err = u.AssignToMachine(m) 480 c.Assert(err, jc.ErrorIsNil) 481 err = u.OpenPort("tcp", 80) 482 c.Assert(err, jc.ErrorIsNil) 483 484 // Starting the firewaller, no open ports. 485 fw := s.newFirewaller(c) 486 defer statetesting.AssertKillAndWait(c, fw) 487 488 s.assertPorts(c, inst, m.Id(), nil) 489 490 // Expose service. 491 err = app.SetExposed() 492 c.Assert(err, jc.ErrorIsNil) 493 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 494 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 495 }) 496 } 497 498 func assertMachineInMachineds(c *gc.C, fw *firewaller.Firewaller, tag names.MachineTag, find bool) { 499 machineds := firewaller.GetMachineds(fw) 500 _, found := machineds[tag] 501 c.Assert(found, gc.Equals, find) 502 } 503 504 func (s *InstanceModeSuite) TestStartMachineWithManualMachine(c *gc.C) { 505 fw := s.newFirewaller(c) 506 defer statetesting.AssertKillAndWait(c, fw) 507 508 m, err := s.State.AddOneMachine(state.MachineTemplate{ 509 Series: "quantal", 510 Jobs: []state.MachineJob{state.JobHostUnits}, 511 InstanceId: "2", 512 Nonce: "manual:", 513 }) 514 c.Assert(err, jc.ErrorIsNil) 515 err = firewaller.StartMachine(fw.(*firewaller.Firewaller), m.MachineTag()) 516 c.Assert(err, jc.ErrorIsNil) 517 assertMachineInMachineds(c, fw.(*firewaller.Firewaller), m.MachineTag(), false) 518 519 m2, err := s.State.AddOneMachine(state.MachineTemplate{ 520 Series: "quantal", 521 Jobs: []state.MachineJob{state.JobHostUnits}, 522 }) 523 c.Assert(err, jc.ErrorIsNil) 524 err = firewaller.StartMachine(fw.(*firewaller.Firewaller), m2.MachineTag()) 525 c.Assert(err, jc.ErrorIsNil) 526 assertMachineInMachineds(c, fw.(*firewaller.Firewaller), m2.MachineTag(), true) 527 } 528 529 func (s *InstanceModeSuite) TestSetClearExposedApplication(c *gc.C) { 530 fw := s.newFirewaller(c) 531 defer statetesting.AssertKillAndWait(c, fw) 532 533 app := s.AddTestingApplication(c, "wordpress", s.charm) 534 535 u, m := s.addUnit(c, app) 536 inst := s.startInstance(c, m) 537 err := u.OpenPort("tcp", 80) 538 c.Assert(err, jc.ErrorIsNil) 539 err = u.OpenPort("tcp", 8080) 540 c.Assert(err, jc.ErrorIsNil) 541 542 // Not exposed service, so no open port. 543 s.assertPorts(c, inst, m.Id(), nil) 544 545 // SeExposed opens the ports. 546 err = app.SetExposed() 547 c.Assert(err, jc.ErrorIsNil) 548 549 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 550 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 551 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 552 }) 553 554 // ClearExposed closes the ports again. 555 err = app.ClearExposed() 556 c.Assert(err, jc.ErrorIsNil) 557 558 s.assertPorts(c, inst, m.Id(), nil) 559 } 560 561 func (s *InstanceModeSuite) TestRemoveUnit(c *gc.C) { 562 fw := s.newFirewaller(c) 563 defer statetesting.AssertKillAndWait(c, fw) 564 565 app := s.AddTestingApplication(c, "wordpress", s.charm) 566 err := app.SetExposed() 567 c.Assert(err, jc.ErrorIsNil) 568 569 u1, m1 := s.addUnit(c, app) 570 inst1 := s.startInstance(c, m1) 571 err = u1.OpenPort("tcp", 80) 572 c.Assert(err, jc.ErrorIsNil) 573 574 u2, m2 := s.addUnit(c, app) 575 inst2 := s.startInstance(c, m2) 576 err = u2.OpenPort("tcp", 80) 577 c.Assert(err, jc.ErrorIsNil) 578 579 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 580 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 581 }) 582 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 583 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 584 }) 585 586 // Remove unit. 587 err = u1.EnsureDead() 588 c.Assert(err, jc.ErrorIsNil) 589 err = u1.Remove() 590 c.Assert(err, jc.ErrorIsNil) 591 592 s.assertPorts(c, inst1, m1.Id(), nil) 593 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 594 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 595 }) 596 } 597 598 func (s *InstanceModeSuite) TestRemoveApplication(c *gc.C) { 599 fw := s.newFirewaller(c) 600 defer statetesting.AssertKillAndWait(c, fw) 601 602 app := s.AddTestingApplication(c, "wordpress", s.charm) 603 err := app.SetExposed() 604 c.Assert(err, jc.ErrorIsNil) 605 606 u, m := s.addUnit(c, app) 607 inst := s.startInstance(c, m) 608 err = u.OpenPort("tcp", 80) 609 c.Assert(err, jc.ErrorIsNil) 610 611 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 612 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 613 }) 614 615 // Remove application. 616 err = u.EnsureDead() 617 c.Assert(err, jc.ErrorIsNil) 618 err = u.Remove() 619 c.Assert(err, jc.ErrorIsNil) 620 err = app.Destroy() 621 c.Assert(err, jc.ErrorIsNil) 622 s.assertPorts(c, inst, m.Id(), nil) 623 } 624 625 func (s *InstanceModeSuite) TestRemoveMultipleApplications(c *gc.C) { 626 fw := s.newFirewaller(c) 627 defer statetesting.AssertKillAndWait(c, fw) 628 629 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 630 err := app1.SetExposed() 631 c.Assert(err, jc.ErrorIsNil) 632 633 u1, m1 := s.addUnit(c, app1) 634 inst1 := s.startInstance(c, m1) 635 err = u1.OpenPort("tcp", 80) 636 c.Assert(err, jc.ErrorIsNil) 637 638 app2 := s.AddTestingApplication(c, "mysql", s.charm) 639 err = app2.SetExposed() 640 c.Assert(err, jc.ErrorIsNil) 641 642 u2, m2 := s.addUnit(c, app2) 643 inst2 := s.startInstance(c, m2) 644 err = u2.OpenPort("tcp", 3306) 645 c.Assert(err, jc.ErrorIsNil) 646 647 s.assertPorts(c, inst1, m1.Id(), []network.IngressRule{ 648 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 649 }) 650 s.assertPorts(c, inst2, m2.Id(), []network.IngressRule{ 651 network.MustNewIngressRule("tcp", 3306, 3306, "0.0.0.0/0"), 652 }) 653 654 // Remove applications. 655 err = u2.EnsureDead() 656 c.Assert(err, jc.ErrorIsNil) 657 err = u2.Remove() 658 c.Assert(err, jc.ErrorIsNil) 659 err = app2.Destroy() 660 c.Assert(err, jc.ErrorIsNil) 661 662 err = u1.EnsureDead() 663 c.Assert(err, jc.ErrorIsNil) 664 err = u1.Remove() 665 c.Assert(err, jc.ErrorIsNil) 666 err = app1.Destroy() 667 c.Assert(err, jc.ErrorIsNil) 668 669 s.assertPorts(c, inst1, m1.Id(), nil) 670 s.assertPorts(c, inst2, m2.Id(), nil) 671 } 672 673 func (s *InstanceModeSuite) TestDeadMachine(c *gc.C) { 674 fw := s.newFirewaller(c) 675 defer statetesting.AssertKillAndWait(c, fw) 676 677 app := s.AddTestingApplication(c, "wordpress", s.charm) 678 err := app.SetExposed() 679 c.Assert(err, jc.ErrorIsNil) 680 681 u, m := s.addUnit(c, app) 682 inst := s.startInstance(c, m) 683 err = u.OpenPort("tcp", 80) 684 c.Assert(err, jc.ErrorIsNil) 685 686 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 687 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 688 }) 689 690 // Remove unit and application, also tested without. Has no effect. 691 err = u.EnsureDead() 692 c.Assert(err, jc.ErrorIsNil) 693 err = u.Remove() 694 c.Assert(err, jc.ErrorIsNil) 695 err = app.Destroy() 696 c.Assert(err, jc.ErrorIsNil) 697 698 // Kill machine. 699 err = m.Refresh() 700 c.Assert(err, jc.ErrorIsNil) 701 err = m.EnsureDead() 702 c.Assert(err, jc.ErrorIsNil) 703 704 s.assertPorts(c, inst, m.Id(), nil) 705 } 706 707 func (s *InstanceModeSuite) TestRemoveMachine(c *gc.C) { 708 fw := s.newFirewaller(c) 709 710 app := s.AddTestingApplication(c, "wordpress", s.charm) 711 err := app.SetExposed() 712 c.Assert(err, jc.ErrorIsNil) 713 714 u, m := s.addUnit(c, app) 715 inst := s.startInstance(c, m) 716 err = u.OpenPort("tcp", 80) 717 c.Assert(err, jc.ErrorIsNil) 718 719 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 720 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 721 }) 722 723 // Remove unit. 724 err = u.EnsureDead() 725 c.Assert(err, jc.ErrorIsNil) 726 err = u.Remove() 727 c.Assert(err, jc.ErrorIsNil) 728 729 // Remove machine. Nothing bad should happen, but can't 730 // assert port state since the machine must have been 731 // destroyed and we lost its reference. 732 err = m.Refresh() 733 c.Assert(err, jc.ErrorIsNil) 734 err = m.EnsureDead() 735 c.Assert(err, jc.ErrorIsNil) 736 err = m.Remove() 737 c.Assert(err, jc.ErrorIsNil) 738 739 // TODO (manadart 2019-02-01): This fails intermittently with a "not found" 740 // error for the machine. This is not a huge problem in production, as the 741 // worker will restart and proceed happily thereafter. 742 // That error is detected here for expediency, but the ideal mitigation is 743 // a refactoring of the worker logic as per LP:1814277. 744 fw.Kill() 745 err = fw.Wait() 746 c.Assert(err == nil || params.IsCodeNotFound(err), jc.IsTrue) 747 } 748 749 func (s *InstanceModeSuite) TestStartWithStateOpenPortsBroken(c *gc.C) { 750 app := s.AddTestingApplication(c, "wordpress", s.charm) 751 err := app.SetExposed() 752 c.Assert(err, jc.ErrorIsNil) 753 u, m := s.addUnit(c, app) 754 inst := s.startInstance(c, m) 755 756 err = u.OpenPort("tcp", 80) 757 c.Assert(err, jc.ErrorIsNil) 758 759 // Nothing open without firewaller. 760 s.assertPorts(c, inst, m.Id(), nil) 761 dummy.SetInstanceBroken(inst, "OpenPorts") 762 763 // Starting the firewaller should attempt to open the ports, 764 // and fail due to the method being broken. 765 fw := s.newFirewaller(c) 766 767 errc := make(chan error, 1) 768 go func() { errc <- fw.Wait() }() 769 s.BackingState.StartSync() 770 select { 771 case err := <-errc: 772 c.Assert(err, gc.ErrorMatches, 773 `cannot respond to units changes for "machine-1": dummyInstance.OpenPorts is broken`) 774 case <-time.After(coretesting.LongWait): 775 fw.Kill() 776 fw.Wait() 777 c.Fatal("timed out waiting for firewaller to stop") 778 } 779 } 780 781 func (s *InstanceModeSuite) setupRemoteRelationRequirerRoleConsumingSide( 782 c *gc.C, published chan bool, apiErr *bool, ingressRequired *bool, clock clock.Clock, 783 ) (worker.Worker, *state.RelationUnit) { 784 // Set up the consuming model - create the local app. 785 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 786 // Set up the consuming model - create the remote app. 787 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 788 appToken := utils.MustNewUUID().String() 789 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 790 Name: "mysql", SourceModel: offeringModelTag, 791 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}}, 792 }) 793 c.Assert(err, jc.ErrorIsNil) 794 // Create the external controller info. 795 ec := state.NewExternalControllers(s.State) 796 _, err = ec.Save(crossmodel.ControllerInfo{ 797 ControllerTag: coretesting.ControllerTag, 798 Addrs: []string{"1.2.3.4:1234"}, 799 CACert: coretesting.CACert}, offeringModelTag.Id()) 800 c.Assert(err, jc.ErrorIsNil) 801 802 mac, err := apitesting.NewMacaroon("apimac") 803 c.Assert(err, jc.ErrorIsNil) 804 var relToken string 805 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 806 c.Check(objType, gc.Equals, "CrossModelRelations") 807 c.Check(version, gc.Equals, 0) 808 c.Check(id, gc.Equals, "") 809 c.Check(request, gc.Equals, "PublishIngressNetworkChanges") 810 expected := params.IngressNetworksChanges{ 811 Changes: []params.IngressNetworksChangeEvent{{ 812 RelationToken: relToken, 813 ApplicationToken: appToken, 814 }}, 815 } 816 817 // Extract macaroons so we can compare them separately 818 // (as they can't be compared using DeepEquals due to 'UnmarshaledAs') 819 expectedMacs := arg.(params.IngressNetworksChanges).Changes[0].Macaroons 820 arg.(params.IngressNetworksChanges).Changes[0].Macaroons = nil 821 c.Assert(len(expectedMacs), gc.Equals, 1) 822 apitesting.MacaroonEquals(c, expectedMacs[0], mac) 823 824 // Networks may be empty or not, depending on coalescing of watcher events. 825 // We may get an initial empty event followed by an event with a network. 826 // Or we may get just the event with a network. 827 // Set the arg networks to empty and compare below. 828 changes := arg.(params.IngressNetworksChanges) 829 argNetworks := changes.Changes[0].Networks 830 argIngressRequired := changes.Changes[0].IngressRequired 831 832 changes.Changes[0].Networks = nil 833 expected.Changes[0].IngressRequired = argIngressRequired 834 c.Check(arg, gc.DeepEquals, expected) 835 836 if !*ingressRequired { 837 c.Assert(changes.Changes[0].Networks, gc.HasLen, 0) 838 } 839 if *ingressRequired && len(argNetworks) > 0 { 840 c.Assert(argIngressRequired, jc.IsTrue) 841 c.Assert(argNetworks, jc.DeepEquals, []string{"10.0.0.4/32"}) 842 } 843 changes.Changes[0].Macaroons = expectedMacs 844 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 845 *(result.(*params.ErrorResults)) = params.ErrorResults{ 846 Results: []params.ErrorResult{{}}, 847 } 848 if *apiErr { 849 return errors.New("fail") 850 } 851 if !*ingressRequired || len(argNetworks) > 0 { 852 published <- true 853 } 854 return nil 855 }) 856 857 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 858 c.Assert(s.crossmodelFirewaller, gc.NotNil) 859 860 // Create the firewaller facade on the consuming model. 861 fw := s.newFirewallerWithClock(c, clock) 862 863 eps, err := s.State.InferEndpoints("wordpress", "mysql") 864 c.Assert(err, jc.ErrorIsNil) 865 rel, err := s.State.AddRelation(eps...) 866 c.Assert(err, jc.ErrorIsNil) 867 868 // Export the relation details so the firewaller knows it's ready to be processed. 869 re := s.State.RemoteEntities() 870 relToken, err = re.ExportLocalEntity(rel.Tag()) 871 c.Assert(err, jc.ErrorIsNil) 872 err = re.SaveMacaroon(rel.Tag(), mac) 873 c.Assert(err, jc.ErrorIsNil) 874 err = re.ImportRemoteEntity(app.Tag(), appToken) 875 c.Assert(err, jc.ErrorIsNil) 876 877 // We should not have published any ingress events yet - no unit has entered scope. 878 select { 879 case <-time.After(coretesting.ShortWait): 880 case <-published: 881 c.Fatal("unexpected ingress change to be published") 882 } 883 884 // Add a public address to the consuming unit so the firewaller can use it. 885 wpm := s.Factory.MakeMachine(c, &factory.MachineParams{ 886 Addresses: []network.Address{network.NewAddress("10.0.0.4")}, 887 }) 888 u, err := wordpress.AddUnit(state.AddUnitParams{}) 889 c.Assert(err, jc.ErrorIsNil) 890 err = u.AssignToMachine(wpm) 891 c.Assert(err, jc.ErrorIsNil) 892 ru, err := rel.Unit(u) 893 c.Assert(err, jc.ErrorIsNil) 894 return fw, ru 895 } 896 897 func (s *InstanceModeSuite) TestRemoteRelationRequirerRoleConsumingSide(c *gc.C) { 898 published := make(chan bool) 899 ingressRequired := true 900 apiErr := false 901 fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, &apiErr, &ingressRequired, &mockClock{c: c}) 902 defer statetesting.AssertKillAndWait(c, fw) 903 904 // Add a unit on the consuming app and have it enter the relation scope. 905 // This will trigger the firewaller to publish the changes. 906 err := ru.EnterScope(map[string]interface{}{}) 907 c.Assert(err, jc.ErrorIsNil) 908 s.BackingState.StartSync() 909 select { 910 case <-time.After(coretesting.LongWait): 911 c.Fatal("time out waiting for ingress change to be published on enter scope") 912 case <-published: 913 } 914 915 // Check the relation ready poll time is as expected. 916 c.Assert(s.clock.(*mockClock).wait, gc.Equals, 3*time.Second) 917 918 // Change should be sent when unit leaves scope. 919 ingressRequired = false 920 err = ru.LeaveScope() 921 c.Assert(err, jc.ErrorIsNil) 922 s.BackingState.StartSync() 923 select { 924 case <-time.After(coretesting.LongWait): 925 c.Fatal("time out waiting for ingress change to be published on leave scope") 926 case <-published: 927 } 928 } 929 930 func (s *InstanceModeSuite) TestRemoteRelationWorkerError(c *gc.C) { 931 published := make(chan bool) 932 ingressRequired := true 933 apiErr := true 934 fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, &apiErr, &ingressRequired, testclock.NewClock(time.Time{})) 935 defer statetesting.AssertKillAndWait(c, fw) 936 937 // Add a unit on the consuming app and have it enter the relation scope. 938 // This will trigger the firewaller to try and publish the changes. 939 err := ru.EnterScope(map[string]interface{}{}) 940 c.Assert(err, jc.ErrorIsNil) 941 s.BackingState.StartSync() 942 943 // We should not have published any ingress events yet - no changed published. 944 select { 945 case <-time.After(coretesting.ShortWait): 946 case <-published: 947 c.Fatal("unexpected ingress change to be published") 948 } 949 950 // Give the worker time to restart and try again. 951 apiErr = false 952 s.clock.(*testclock.Clock).WaitAdvance(60*time.Second, coretesting.LongWait, 1) 953 select { 954 case <-time.After(coretesting.LongWait): 955 c.Fatal("time out waiting for ingress change to be published on enter scope") 956 case <-published: 957 } 958 } 959 960 func (s *InstanceModeSuite) TestRemoteRelationProviderRoleConsumingSide(c *gc.C) { 961 // Set up the consuming model - create the local app. 962 s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 963 // Set up the consuming model - create the remote app. 964 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 965 appToken := utils.MustNewUUID().String() 966 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 967 Name: "wordpress", SourceModel: offeringModelTag, 968 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "requirer", Scope: "global"}}, 969 }) 970 c.Assert(err, jc.ErrorIsNil) 971 // Create the external controller info. 972 ec := state.NewExternalControllers(s.State) 973 _, err = ec.Save(crossmodel.ControllerInfo{ 974 ControllerTag: coretesting.ControllerTag, 975 Addrs: []string{"1.2.3.4:1234"}, 976 CACert: coretesting.CACert}, offeringModelTag.Id()) 977 c.Assert(err, jc.ErrorIsNil) 978 979 mac, err := apitesting.NewMacaroon("apimac") 980 c.Assert(err, jc.ErrorIsNil) 981 watched := make(chan bool) 982 var relToken string 983 callCount := int32(0) 984 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 985 switch atomic.LoadInt32(&callCount) { 986 case 0: 987 c.Check(objType, gc.Equals, "CrossModelRelations") 988 c.Check(version, gc.Equals, 0) 989 c.Check(id, gc.Equals, "") 990 c.Check(request, gc.Equals, "WatchEgressAddressesForRelations") 991 expected := params.RemoteEntityArgs{ 992 Args: []params.RemoteEntityArg{{ 993 Token: relToken, 994 }}, 995 } 996 // Extract macaroons so we can compare them separately 997 // (as they can't be compared using DeepEquals due to 'UnmarshaledAs') 998 rArgs := arg.(params.RemoteEntityArgs) 999 newMacs := rArgs.Args[0].Macaroons 1000 rArgs.Args[0].Macaroons = nil 1001 apitesting.MacaroonEquals(c, newMacs[0], mac) 1002 c.Check(arg, gc.DeepEquals, expected) 1003 c.Assert(result, gc.FitsTypeOf, ¶ms.StringsWatchResults{}) 1004 *(result.(*params.StringsWatchResults)) = params.StringsWatchResults{ 1005 Results: []params.StringsWatchResult{{StringsWatcherId: "1"}}, 1006 } 1007 watched <- true 1008 default: 1009 c.Check(objType, gc.Equals, "StringsWatcher") 1010 } 1011 atomic.AddInt32(&callCount, 1) 1012 return nil 1013 }) 1014 1015 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 1016 c.Assert(s.crossmodelFirewaller, gc.NotNil) 1017 1018 // Create the firewaller facade on the consuming model. 1019 fw := s.newFirewaller(c) 1020 defer statetesting.AssertKillAndWait(c, fw) 1021 1022 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1023 c.Assert(err, jc.ErrorIsNil) 1024 rel, err := s.State.AddRelation(eps...) 1025 c.Assert(err, jc.ErrorIsNil) 1026 1027 // Export the relation details so the firewaller knows it's ready to be processed. 1028 re := s.State.RemoteEntities() 1029 relToken, err = re.ExportLocalEntity(rel.Tag()) 1030 c.Assert(err, jc.ErrorIsNil) 1031 err = re.SaveMacaroon(rel.Tag(), mac) 1032 c.Assert(err, jc.ErrorIsNil) 1033 err = re.ImportRemoteEntity(app.Tag(), appToken) 1034 c.Assert(err, jc.ErrorIsNil) 1035 1036 select { 1037 case <-time.After(coretesting.LongWait): 1038 c.Fatal("time out waiting for watcher call") 1039 case <-watched: 1040 } 1041 1042 // Check the relation ready poll time is as expected. 1043 c.Assert(s.clock.(*mockClock).wait, gc.Equals, 3*time.Second) 1044 } 1045 1046 func (s *InstanceModeSuite) TestRemoteRelationIngressRejected(c *gc.C) { 1047 // Set up the consuming model - create the local app. 1048 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1049 // Set up the consuming model - create the remote app. 1050 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 1051 appToken := utils.MustNewUUID().String() 1052 1053 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1054 Name: "mysql", SourceModel: offeringModelTag, 1055 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}}, 1056 }) 1057 c.Assert(err, jc.ErrorIsNil) 1058 // Create the external controller info. 1059 ec := state.NewExternalControllers(s.State) 1060 _, err = ec.Save(crossmodel.ControllerInfo{ 1061 ControllerTag: coretesting.ControllerTag, 1062 Addrs: []string{"1.2.3.4:1234"}, 1063 CACert: coretesting.CACert}, offeringModelTag.Id()) 1064 c.Assert(err, jc.ErrorIsNil) 1065 1066 mac, err := apitesting.NewMacaroon("apimac") 1067 c.Assert(err, jc.ErrorIsNil) 1068 1069 published := make(chan bool) 1070 done := false 1071 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 1072 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 1073 *(result.(*params.ErrorResults)) = params.ErrorResults{ 1074 Results: []params.ErrorResult{{Error: ¶ms.Error{Code: params.CodeForbidden, Message: "error"}}}, 1075 } 1076 // We can get more than one api call depending on the 1077 // granularity of watcher events. 1078 if !done { 1079 done = true 1080 published <- true 1081 } 1082 return nil 1083 }) 1084 1085 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 1086 c.Assert(s.crossmodelFirewaller, gc.NotNil) 1087 1088 // Create the firewaller facade on the consuming model. 1089 fw := s.newFirewaller(c) 1090 defer statetesting.AssertKillAndWait(c, fw) 1091 1092 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1093 c.Assert(err, jc.ErrorIsNil) 1094 rel, err := s.State.AddRelation(eps...) 1095 c.Assert(err, jc.ErrorIsNil) 1096 1097 // Export the relation details so the firewaller knows it's ready to be processed. 1098 re := s.State.RemoteEntities() 1099 _, err = re.ExportLocalEntity(rel.Tag()) 1100 c.Assert(err, jc.ErrorIsNil) 1101 err = re.SaveMacaroon(rel.Tag(), mac) 1102 c.Assert(err, jc.ErrorIsNil) 1103 err = re.ImportRemoteEntity(app.Tag(), appToken) 1104 c.Assert(err, jc.ErrorIsNil) 1105 1106 // Add a public address to the consuming unit so the firewaller can use it. 1107 wpm := s.Factory.MakeMachine(c, &factory.MachineParams{ 1108 Addresses: []network.Address{network.NewAddress("10.0.0.4")}, 1109 }) 1110 u, err := wordpress.AddUnit(state.AddUnitParams{}) 1111 c.Assert(err, jc.ErrorIsNil) 1112 err = u.AssignToMachine(wpm) 1113 c.Assert(err, jc.ErrorIsNil) 1114 ru, err := rel.Unit(u) 1115 c.Assert(err, jc.ErrorIsNil) 1116 1117 // Add a unit on the consuming app and have it enter the relation scope. 1118 // This will trigger the firewaller to publish the changes. 1119 err = ru.EnterScope(map[string]interface{}{}) 1120 c.Assert(err, jc.ErrorIsNil) 1121 s.BackingState.StartSync() 1122 select { 1123 case <-time.After(coretesting.LongWait): 1124 c.Fatal("time out waiting for ingress change to be published on enter scope") 1125 case <-published: 1126 } 1127 1128 // Check that the relation status is set to error. 1129 s.BackingState.StartSync() 1130 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 1131 relStatus, err := rel.Status() 1132 c.Check(err, jc.ErrorIsNil) 1133 if relStatus.Status != status.Error { 1134 continue 1135 } 1136 c.Check(relStatus.Message, gc.Equals, "error") 1137 return 1138 } 1139 c.Fatal("time out waiting for relation status to be updated") 1140 } 1141 1142 func (s *InstanceModeSuite) assertIngressCidrs(c *gc.C, ingress []string, expected []string) { 1143 // Set up the offering model - create the local app. 1144 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1145 u, m := s.addUnit(c, mysql) 1146 inst := s.startInstance(c, m) 1147 err := u.OpenPort("tcp", 3306) 1148 c.Assert(err, jc.ErrorIsNil) 1149 1150 // Set up the offering model - create the remote app. 1151 consumingModelTag := names.NewModelTag(utils.MustNewUUID().String()) 1152 relToken := utils.MustNewUUID().String() 1153 appToken := utils.MustNewUUID().String() 1154 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1155 Name: "wordpress", SourceModel: consumingModelTag, IsConsumerProxy: true, 1156 Endpoints: []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer", Scope: "global"}}, 1157 }) 1158 c.Assert(err, jc.ErrorIsNil) 1159 1160 // Create the firewaller facade on the offering model. 1161 fw := s.newFirewaller(c) 1162 defer statetesting.AssertKillAndWait(c, fw) 1163 1164 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1165 c.Assert(err, jc.ErrorIsNil) 1166 rel, err := s.State.AddRelation(eps...) 1167 c.Assert(err, jc.ErrorIsNil) 1168 1169 // Export the relation details so the firewaller knows it's ready to be processed. 1170 re := s.State.RemoteEntities() 1171 err = re.ImportRemoteEntity(rel.Tag(), relToken) 1172 c.Assert(err, jc.ErrorIsNil) 1173 err = re.ImportRemoteEntity(app.Tag(), appToken) 1174 c.Assert(err, jc.ErrorIsNil) 1175 1176 // No port changes yet. 1177 s.assertPorts(c, inst, m.Id(), nil) 1178 1179 // Save a new ingress network against the relation. 1180 rin := state.NewRelationIngressNetworks(s.State) 1181 _, err = rin.Save(rel.Tag().Id(), false, ingress) 1182 c.Assert(err, jc.ErrorIsNil) 1183 1184 //Ports opened. 1185 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 1186 network.MustNewIngressRule("tcp", 3306, 3306, expected...), 1187 }) 1188 1189 // Check the relation ready poll time is as expected. 1190 c.Assert(s.clock.(*mockClock).wait, gc.Equals, 3*time.Second) 1191 1192 // Change should be sent when ingress networks disappear. 1193 _, err = rin.Save(rel.Tag().Id(), false, nil) 1194 c.Assert(err, jc.ErrorIsNil) 1195 s.assertPorts(c, inst, m.Id(), nil) 1196 1197 _, err = rin.Save(rel.Tag().Id(), false, ingress) 1198 c.Assert(err, jc.ErrorIsNil) 1199 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 1200 network.MustNewIngressRule("tcp", 3306, 3306, expected...), 1201 }) 1202 1203 // And again when relation is suspended. 1204 err = rel.SetSuspended(true, "") 1205 c.Assert(err, jc.ErrorIsNil) 1206 s.assertPorts(c, inst, m.Id(), nil) 1207 1208 // And again when relation is resumed. 1209 err = rel.SetSuspended(false, "") 1210 c.Assert(err, jc.ErrorIsNil) 1211 s.assertPorts(c, inst, m.Id(), []network.IngressRule{ 1212 network.MustNewIngressRule("tcp", 3306, 3306, expected...), 1213 }) 1214 1215 // And again when relation is destroyed. 1216 err = rel.Destroy() 1217 c.Assert(err, jc.ErrorIsNil) 1218 s.assertPorts(c, inst, m.Id(), nil) 1219 } 1220 1221 func (s *InstanceModeSuite) TestRemoteRelationProviderRoleOffering(c *gc.C) { 1222 s.assertIngressCidrs(c, []string{"10.0.0.4/16"}, []string{"10.0.0.4/16"}) 1223 } 1224 1225 func (s *InstanceModeSuite) TestRemoteRelationIngressFallbackToPublic(c *gc.C) { 1226 var ingress []string 1227 for i := 1; i < 30; i++ { 1228 ingress = append(ingress, fmt.Sprintf("10.%d.0.1/32", i)) 1229 } 1230 s.assertIngressCidrs(c, ingress, []string{"0.0.0.0/0"}) 1231 } 1232 1233 func (s *InstanceModeSuite) TestRemoteRelationIngressFallbackToWhitelist(c *gc.C) { 1234 fwRules := state.NewFirewallRules(s.State) 1235 err := fwRules.Save(state.FirewallRule{ 1236 WellKnownService: state.JujuApplicationOfferRule, 1237 WhitelistCIDRs: []string{"192.168.1.0/16"}, 1238 }) 1239 c.Assert(err, jc.ErrorIsNil) 1240 var ingress []string 1241 for i := 1; i < 30; i++ { 1242 ingress = append(ingress, fmt.Sprintf("10.%d.0.1/32", i)) 1243 } 1244 s.assertIngressCidrs(c, ingress, []string{"192.168.1.0/16"}) 1245 } 1246 1247 func (s *InstanceModeSuite) TestRemoteRelationIngressMergesCIDRS(c *gc.C) { 1248 ingress := []string{ 1249 "192.0.1.254/31", 1250 "192.0.2.0/28", 1251 "192.0.2.16/28", 1252 "192.0.2.32/28", 1253 "192.0.2.48/28", 1254 "192.0.2.64/28", 1255 "192.0.2.80/28", 1256 "192.0.2.96/28", 1257 "192.0.2.112/28", 1258 "192.0.2.128/28", 1259 "192.0.2.144/28", 1260 "192.0.2.160/28", 1261 "192.0.2.176/28", 1262 "192.0.2.192/28", 1263 "192.0.2.208/28", 1264 "192.0.2.224/28", 1265 "192.0.2.240/28", 1266 "192.0.3.0/28", 1267 "192.0.4.0/28", 1268 "192.0.5.0/28", 1269 "192.0.6.0/28", 1270 } 1271 expected := []string{ 1272 "192.0.1.254/31", 1273 "192.0.2.0/24", 1274 "192.0.3.0/28", 1275 "192.0.4.0/28", 1276 "192.0.5.0/28", 1277 "192.0.6.0/28", 1278 } 1279 s.assertIngressCidrs(c, ingress, expected) 1280 } 1281 1282 type GlobalModeSuite struct { 1283 firewallerBaseSuite 1284 } 1285 1286 var _ = gc.Suite(&GlobalModeSuite{}) 1287 1288 func (s *GlobalModeSuite) SetUpTest(c *gc.C) { 1289 s.firewallerBaseSuite.setUpTest(c, config.FwGlobal) 1290 } 1291 1292 func (s *GlobalModeSuite) TearDownTest(c *gc.C) { 1293 s.firewallerBaseSuite.JujuConnSuite.TearDownTest(c) 1294 } 1295 1296 func (s *GlobalModeSuite) newFirewaller(c *gc.C) worker.Worker { 1297 fwEnv, ok := s.Environ.(environs.Firewaller) 1298 c.Assert(ok, gc.Equals, true) 1299 1300 cfg := firewaller.Config{ 1301 ModelUUID: s.State.ModelUUID(), 1302 Mode: config.FwGlobal, 1303 EnvironFirewaller: fwEnv, 1304 EnvironInstances: s.Environ, 1305 FirewallerAPI: s.firewaller, 1306 RemoteRelationsApi: s.remoteRelations, 1307 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 1308 return s.crossmodelFirewaller, nil 1309 }, 1310 CredentialAPI: s.credentialsFacade, 1311 } 1312 fw, err := firewaller.NewFirewaller(cfg) 1313 c.Assert(err, jc.ErrorIsNil) 1314 return fw 1315 } 1316 1317 func (s *GlobalModeSuite) TestStartStop(c *gc.C) { 1318 fw := s.newFirewaller(c) 1319 statetesting.AssertKillAndWait(c, fw) 1320 } 1321 1322 func (s *GlobalModeSuite) TestGlobalMode(c *gc.C) { 1323 // Start firewaller and open ports. 1324 fw := s.newFirewaller(c) 1325 defer statetesting.AssertKillAndWait(c, fw) 1326 1327 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 1328 err := app1.SetExposed() 1329 c.Assert(err, jc.ErrorIsNil) 1330 1331 u1, m1 := s.addUnit(c, app1) 1332 s.startInstance(c, m1) 1333 err = u1.OpenPorts("tcp", 80, 90) 1334 c.Assert(err, jc.ErrorIsNil) 1335 err = u1.OpenPort("tcp", 8080) 1336 c.Assert(err, jc.ErrorIsNil) 1337 1338 app2 := s.AddTestingApplication(c, "moinmoin", s.charm) 1339 c.Assert(err, jc.ErrorIsNil) 1340 err = app2.SetExposed() 1341 c.Assert(err, jc.ErrorIsNil) 1342 1343 u2, m2 := s.addUnit(c, app2) 1344 s.startInstance(c, m2) 1345 err = u2.OpenPorts("tcp", 80, 90) 1346 c.Assert(err, jc.ErrorIsNil) 1347 1348 s.assertEnvironPorts(c, []network.IngressRule{ 1349 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 1350 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1351 }) 1352 1353 // Closing a port opened by a different unit won't touch the environment. 1354 err = u1.ClosePorts("tcp", 80, 90) 1355 c.Assert(err, jc.ErrorIsNil) 1356 s.assertEnvironPorts(c, []network.IngressRule{ 1357 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 1358 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1359 }) 1360 1361 // Closing a port used just once changes the environment. 1362 err = u1.ClosePort("tcp", 8080) 1363 c.Assert(err, jc.ErrorIsNil) 1364 s.assertEnvironPorts(c, []network.IngressRule{ 1365 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 1366 }) 1367 1368 // Closing the last port also modifies the environment. 1369 err = u2.ClosePorts("tcp", 80, 90) 1370 c.Assert(err, jc.ErrorIsNil) 1371 s.assertEnvironPorts(c, nil) 1372 } 1373 1374 func (s *GlobalModeSuite) TestStartWithUnexposedApplication(c *gc.C) { 1375 m, err := s.State.AddMachine("quantal", state.JobHostUnits) 1376 c.Assert(err, jc.ErrorIsNil) 1377 s.startInstance(c, m) 1378 1379 app := s.AddTestingApplication(c, "wordpress", s.charm) 1380 u, err := app.AddUnit(state.AddUnitParams{}) 1381 c.Assert(err, jc.ErrorIsNil) 1382 err = u.AssignToMachine(m) 1383 c.Assert(err, jc.ErrorIsNil) 1384 err = u.OpenPort("tcp", 80) 1385 c.Assert(err, jc.ErrorIsNil) 1386 1387 // Starting the firewaller, no open ports. 1388 fw := s.newFirewaller(c) 1389 defer statetesting.AssertKillAndWait(c, fw) 1390 1391 s.assertEnvironPorts(c, nil) 1392 1393 // Expose application. 1394 err = app.SetExposed() 1395 c.Assert(err, jc.ErrorIsNil) 1396 s.assertEnvironPorts(c, []network.IngressRule{ 1397 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1398 }) 1399 } 1400 1401 func (s *GlobalModeSuite) TestRestart(c *gc.C) { 1402 // Start firewaller and open ports. 1403 fw := s.newFirewaller(c) 1404 1405 app := s.AddTestingApplication(c, "wordpress", s.charm) 1406 err := app.SetExposed() 1407 c.Assert(err, jc.ErrorIsNil) 1408 1409 u, m := s.addUnit(c, app) 1410 s.startInstance(c, m) 1411 err = u.OpenPorts("tcp", 80, 90) 1412 c.Assert(err, jc.ErrorIsNil) 1413 err = u.OpenPort("tcp", 8080) 1414 c.Assert(err, jc.ErrorIsNil) 1415 1416 s.assertEnvironPorts(c, []network.IngressRule{ 1417 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 1418 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1419 }) 1420 1421 // Stop firewaller and close one and open a different port. 1422 err = worker.Stop(fw) 1423 c.Assert(err, jc.ErrorIsNil) 1424 1425 err = u.ClosePort("tcp", 8080) 1426 c.Assert(err, jc.ErrorIsNil) 1427 err = u.OpenPort("tcp", 8888) 1428 c.Assert(err, jc.ErrorIsNil) 1429 1430 // Start firewaller and check port. 1431 fw = s.newFirewaller(c) 1432 defer statetesting.AssertKillAndWait(c, fw) 1433 1434 s.assertEnvironPorts(c, []network.IngressRule{ 1435 network.MustNewIngressRule("tcp", 80, 90, "0.0.0.0/0"), 1436 network.MustNewIngressRule("tcp", 8888, 8888, "0.0.0.0/0"), 1437 }) 1438 } 1439 1440 func (s *GlobalModeSuite) TestRestartUnexposedApplication(c *gc.C) { 1441 // Start firewaller and open ports. 1442 fw := s.newFirewaller(c) 1443 1444 app := s.AddTestingApplication(c, "wordpress", s.charm) 1445 err := app.SetExposed() 1446 c.Assert(err, jc.ErrorIsNil) 1447 1448 u, m := s.addUnit(c, app) 1449 s.startInstance(c, m) 1450 err = u.OpenPort("tcp", 80) 1451 c.Assert(err, jc.ErrorIsNil) 1452 err = u.OpenPort("tcp", 8080) 1453 c.Assert(err, jc.ErrorIsNil) 1454 1455 s.assertEnvironPorts(c, []network.IngressRule{ 1456 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1457 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1458 }) 1459 1460 // Stop firewaller and clear exposed flag on application. 1461 err = worker.Stop(fw) 1462 c.Assert(err, jc.ErrorIsNil) 1463 1464 err = app.ClearExposed() 1465 c.Assert(err, jc.ErrorIsNil) 1466 1467 // Start firewaller and check port. 1468 fw = s.newFirewaller(c) 1469 defer statetesting.AssertKillAndWait(c, fw) 1470 1471 s.assertEnvironPorts(c, nil) 1472 } 1473 1474 func (s *GlobalModeSuite) TestRestartPortCount(c *gc.C) { 1475 // Start firewaller and open ports. 1476 fw := s.newFirewaller(c) 1477 1478 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 1479 err := app1.SetExposed() 1480 c.Assert(err, jc.ErrorIsNil) 1481 1482 u1, m1 := s.addUnit(c, app1) 1483 s.startInstance(c, m1) 1484 err = u1.OpenPort("tcp", 80) 1485 c.Assert(err, jc.ErrorIsNil) 1486 err = u1.OpenPort("tcp", 8080) 1487 c.Assert(err, jc.ErrorIsNil) 1488 1489 s.assertEnvironPorts(c, []network.IngressRule{ 1490 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1491 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1492 }) 1493 1494 // Stop firewaller and add another application using the port. 1495 err = worker.Stop(fw) 1496 c.Assert(err, jc.ErrorIsNil) 1497 1498 app2 := s.AddTestingApplication(c, "moinmoin", s.charm) 1499 err = app2.SetExposed() 1500 c.Assert(err, jc.ErrorIsNil) 1501 1502 u2, m2 := s.addUnit(c, app2) 1503 s.startInstance(c, m2) 1504 err = u2.OpenPort("tcp", 80) 1505 c.Assert(err, jc.ErrorIsNil) 1506 1507 // Start firewaller and check port. 1508 fw = s.newFirewaller(c) 1509 defer statetesting.AssertKillAndWait(c, fw) 1510 1511 s.assertEnvironPorts(c, []network.IngressRule{ 1512 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1513 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1514 }) 1515 1516 // Closing a port opened by a different unit won't touch the environment. 1517 err = u1.ClosePort("tcp", 80) 1518 c.Assert(err, jc.ErrorIsNil) 1519 s.assertEnvironPorts(c, []network.IngressRule{ 1520 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1521 network.MustNewIngressRule("tcp", 8080, 8080, "0.0.0.0/0"), 1522 }) 1523 1524 // Closing a port used just once changes the environment. 1525 err = u1.ClosePort("tcp", 8080) 1526 c.Assert(err, jc.ErrorIsNil) 1527 s.assertEnvironPorts(c, []network.IngressRule{ 1528 network.MustNewIngressRule("tcp", 80, 80, "0.0.0.0/0"), 1529 }) 1530 1531 // Closing the last port also modifies the environment. 1532 err = u2.ClosePort("tcp", 80) 1533 c.Assert(err, jc.ErrorIsNil) 1534 s.assertEnvironPorts(c, nil) 1535 } 1536 1537 type NoneModeSuite struct { 1538 firewallerBaseSuite 1539 } 1540 1541 var _ = gc.Suite(&NoneModeSuite{}) 1542 1543 func (s *NoneModeSuite) SetUpTest(c *gc.C) { 1544 s.firewallerBaseSuite.setUpTest(c, config.FwNone) 1545 } 1546 1547 func (s *NoneModeSuite) TestStopImmediately(c *gc.C) { 1548 fwEnv, ok := s.Environ.(environs.Firewaller) 1549 c.Assert(ok, gc.Equals, true) 1550 1551 cfg := firewaller.Config{ 1552 ModelUUID: s.State.ModelUUID(), 1553 Mode: config.FwNone, 1554 EnvironFirewaller: fwEnv, 1555 EnvironInstances: s.Environ, 1556 FirewallerAPI: s.firewaller, 1557 RemoteRelationsApi: s.remoteRelations, 1558 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 1559 return s.crossmodelFirewaller, nil 1560 }, 1561 CredentialAPI: s.credentialsFacade, 1562 } 1563 _, err := firewaller.NewFirewaller(cfg) 1564 c.Assert(err, gc.ErrorMatches, `invalid firewall-mode "none"`) 1565 }