github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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 "strconv" 9 "sync/atomic" 10 "time" 11 12 "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery" 13 "github.com/juju/charm/v12" 14 "github.com/juju/clock" 15 "github.com/juju/clock/testclock" 16 "github.com/juju/errors" 17 "github.com/juju/loggo" 18 "github.com/juju/mgo/v3/txn" 19 "github.com/juju/names/v5" 20 jc "github.com/juju/testing/checkers" 21 "github.com/juju/utils/v3" 22 "github.com/juju/worker/v3" 23 gc "gopkg.in/check.v1" 24 25 "github.com/juju/juju/api" 26 "github.com/juju/juju/api/agent/credentialvalidator" 27 basetesting "github.com/juju/juju/api/base/testing" 28 "github.com/juju/juju/api/controller/crossmodelrelations" 29 apifirewaller "github.com/juju/juju/api/controller/firewaller" 30 "github.com/juju/juju/api/controller/remoterelations" 31 apitesting "github.com/juju/juju/api/testing" 32 "github.com/juju/juju/core/crossmodel" 33 "github.com/juju/juju/core/network" 34 "github.com/juju/juju/core/network/firewall" 35 "github.com/juju/juju/core/status" 36 "github.com/juju/juju/core/watcher" 37 "github.com/juju/juju/core/watcher/watchertest" 38 "github.com/juju/juju/environs" 39 "github.com/juju/juju/environs/config" 40 "github.com/juju/juju/environs/context" 41 "github.com/juju/juju/environs/instances" 42 "github.com/juju/juju/environs/models" 43 jujutesting "github.com/juju/juju/juju/testing" 44 "github.com/juju/juju/provider/dummy" 45 "github.com/juju/juju/rpc/params" 46 "github.com/juju/juju/state" 47 statetesting "github.com/juju/juju/state/testing" 48 "github.com/juju/juju/testing" 49 coretesting "github.com/juju/juju/testing" 50 "github.com/juju/juju/testing/factory" 51 "github.com/juju/juju/worker/firewaller" 52 ) 53 54 const allEndpoints = "" 55 56 // firewallerBaseSuite implements common functionality for embedding 57 // into each of the other per-mode suites. 58 type firewallerBaseSuite struct { 59 jujutesting.JujuConnSuite 60 charm *state.Charm 61 controllerMachine *state.Machine 62 controllerPassword string 63 64 st api.Connection 65 firewaller firewaller.FirewallerAPI 66 remoteRelations *remoterelations.Client 67 crossmodelFirewaller *crossmodelrelations.Client 68 clock testclock.AdvanceableClock 69 70 callCtx context.ProviderCallContext 71 credentialsFacade *credentialvalidator.Facade 72 } 73 74 func (s *firewallerBaseSuite) SetUpTest(c *gc.C) { 75 s.JujuConnSuite.SetUpTest(c) 76 77 s.callCtx = context.NewEmptyCloudCallContext() 78 } 79 80 var _ worker.Worker = (*firewaller.Firewaller)(nil) 81 82 func (s *firewallerBaseSuite) setUpTest(c *gc.C, firewallMode string) { 83 add := map[string]interface{}{"firewall-mode": firewallMode} 84 s.DummyConfig = dummy.SampleConfig().Merge(add).Delete("admin-secret") 85 86 s.JujuConnSuite.SetUpTest(c) 87 s.charm = s.AddTestingCharm(c, "wordpress") 88 89 // Create a manager machine and login to the API. 90 var err error 91 s.controllerMachine, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel) 92 c.Assert(err, jc.ErrorIsNil) 93 s.controllerPassword, err = utils.RandomPassword() 94 c.Assert(err, jc.ErrorIsNil) 95 err = s.controllerMachine.SetPassword(s.controllerPassword) 96 c.Assert(err, jc.ErrorIsNil) 97 err = s.controllerMachine.SetProvisioned("i-manager", "", "fake_nonce", nil) 98 c.Assert(err, jc.ErrorIsNil) 99 s.st = s.OpenAPIAsMachine(c, s.controllerMachine.Tag(), s.controllerPassword, "fake_nonce") 100 c.Assert(s.st, gc.NotNil) 101 102 // Create the API facades. 103 firewallerClient, err := apifirewaller.NewClient(s.st) 104 c.Assert(err, jc.ErrorIsNil) 105 s.firewaller = firewallerClient 106 s.remoteRelations = remoterelations.NewClient(s.st) 107 c.Assert(s.remoteRelations, gc.NotNil) 108 109 s.credentialsFacade = credentialvalidator.NewFacade(s.st) 110 } 111 112 // assertIngressRules retrieves the ingress rules from the provided instance 113 // and compares them to the expected value. 114 func (s *firewallerBaseSuite) assertIngressRules(c *gc.C, inst instances.Instance, machineId string, 115 expected firewall.IngressRules) { 116 fwInst, ok := inst.(instances.InstanceFirewaller) 117 c.Assert(ok, gc.Equals, true) 118 119 start := time.Now() 120 for { 121 // Make it more likely for the dust to have settled (there still may 122 // be rare cases where a test passes when it shouldn't if expected 123 // is nil, which is the initial value). 124 time.Sleep(coretesting.ShortWait) 125 126 got, err := fwInst.IngressRules(s.callCtx, machineId) 127 if err != nil { 128 c.Fatal(err) 129 } 130 if got.EqualTo(expected) { 131 c.Succeed() 132 return 133 } 134 if time.Since(start) > coretesting.LongWait { 135 c.Fatalf("timed out: expected %q; got %q", expected, got) 136 } 137 time.Sleep(coretesting.ShortWait) 138 } 139 } 140 141 // assertEnvironPorts retrieves the open ports of environment and compares them 142 // to the expected. 143 func (s *firewallerBaseSuite) assertEnvironPorts(c *gc.C, expected firewall.IngressRules) { 144 fwEnv, ok := s.Environ.(environs.Firewaller) 145 c.Assert(ok, gc.Equals, true) 146 147 start := time.Now() 148 for { 149 got, err := fwEnv.IngressRules(s.callCtx) 150 if err != nil { 151 c.Fatal(err) 152 } 153 if got.EqualTo(expected) { 154 c.Succeed() 155 return 156 } 157 if time.Since(start) > coretesting.LongWait { 158 c.Fatalf("timed out: expected %q; got %q", expected, got) 159 } 160 time.Sleep(coretesting.ShortWait) 161 } 162 } 163 164 // assertModelIngressRules retrieves the ingress rules from the model firewall 165 // and compares them to the expected value 166 func (s *firewallerBaseSuite) assertModelIngressRules(c *gc.C, expected firewall.IngressRules) { 167 fwEnv, ok := s.Environ.(models.ModelFirewaller) 168 c.Assert(ok, gc.Equals, true) 169 170 start := time.Now() 171 for { 172 got, err := fwEnv.ModelIngressRules(s.callCtx) 173 if err != nil && !errors.IsNotFound(err) { 174 c.Fatal(err) 175 } 176 if got.EqualTo(expected) { 177 c.Succeed() 178 return 179 } 180 if time.Since(start) > coretesting.LongWait { 181 c.Fatalf("timed out: expected %q; got %q", expected, got) 182 } 183 time.Sleep(coretesting.ShortWait) 184 } 185 } 186 187 func (s *firewallerBaseSuite) addUnit(c *gc.C, app *state.Application) (*state.Unit, *state.Machine) { 188 u, err := app.AddUnit(state.AddUnitParams{}) 189 c.Assert(err, jc.ErrorIsNil) 190 err = s.State.AssignUnit(u, state.AssignCleanEmpty) 191 c.Assert(err, jc.ErrorIsNil) 192 id, err := u.AssignedMachineId() 193 c.Assert(err, jc.ErrorIsNil) 194 m, err := s.State.Machine(id) 195 c.Assert(err, jc.ErrorIsNil) 196 return u, m 197 } 198 199 // startInstance starts a new instance for the given machine. 200 func (s *firewallerBaseSuite) startInstance(c *gc.C, m *state.Machine) instances.Instance { 201 inst, hc := jujutesting.AssertStartInstance(c, s.Environ, s.callCtx, s.ControllerConfig.ControllerUUID(), m.Id()) 202 err := m.SetProvisioned(inst.Id(), "", "fake_nonce", hc) 203 c.Assert(err, jc.ErrorIsNil) 204 return inst 205 } 206 207 type InstanceModeSuite struct { 208 firewallerBaseSuite 209 watchMachineNotify func(tag names.MachineTag) 210 flushModelNotify func() 211 } 212 213 var _ = gc.Suite(&InstanceModeSuite{}) 214 215 func (s *InstanceModeSuite) SetUpTest(c *gc.C) { 216 s.firewallerBaseSuite.setUpTest(c, config.FwInstance) 217 s.watchMachineNotify = nil 218 s.flushModelNotify = nil 219 s.clock = testclock.NewDilatedWallClock(testing.ShortWait) 220 } 221 222 func (s *InstanceModeSuite) newFirewaller(c *gc.C) worker.Worker { 223 return s.newFirewallerWithIPV6CIDRSupport(c, true) 224 } 225 226 func (s *InstanceModeSuite) newFirewallerWithIPV6CIDRSupport(c *gc.C, ipv6CIDRSupport bool) worker.Worker { 227 fwEnv, ok := s.Environ.(environs.Firewaller) 228 c.Assert(ok, gc.Equals, true) 229 230 modelFwEnv, ok := s.Environ.(models.ModelFirewaller) 231 c.Assert(ok, gc.Equals, true) 232 233 cfg := firewaller.Config{ 234 ModelUUID: s.State.ModelUUID(), 235 Mode: config.FwInstance, 236 EnvironFirewaller: fwEnv, 237 EnvironModelFirewaller: modelFwEnv, 238 EnvironInstances: s.Environ, 239 EnvironIPV6CIDRSupport: ipv6CIDRSupport, 240 FirewallerAPI: s.firewaller, 241 RemoteRelationsApi: s.remoteRelations, 242 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 243 return s.crossmodelFirewaller, nil 244 }, 245 Clock: s.clock, 246 Logger: loggo.GetLogger("test"), 247 CredentialAPI: s.credentialsFacade, 248 WatchMachineNotify: s.watchMachineNotify, 249 FlushModelNotify: s.flushModelNotify, 250 } 251 fw, err := firewaller.NewFirewaller(cfg) 252 c.Assert(err, jc.ErrorIsNil) 253 return fw 254 } 255 256 func (s *InstanceModeSuite) newFirewallerWithoutModelFirewaller(c *gc.C) worker.Worker { 257 fwEnv, ok := s.Environ.(environs.Firewaller) 258 c.Assert(ok, gc.Equals, true) 259 260 cfg := firewaller.Config{ 261 ModelUUID: s.State.ModelUUID(), 262 Mode: config.FwInstance, 263 EnvironFirewaller: fwEnv, 264 EnvironInstances: s.Environ, 265 EnvironIPV6CIDRSupport: true, 266 FirewallerAPI: s.firewaller, 267 RemoteRelationsApi: s.remoteRelations, 268 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 269 return s.crossmodelFirewaller, nil 270 }, 271 Clock: s.clock, 272 Logger: loggo.GetLogger("test"), 273 CredentialAPI: s.credentialsFacade, 274 WatchMachineNotify: s.watchMachineNotify, 275 FlushModelNotify: s.flushModelNotify, 276 } 277 fw, err := firewaller.NewFirewaller(cfg) 278 c.Assert(err, jc.ErrorIsNil) 279 return fw 280 } 281 282 func (s *InstanceModeSuite) TestStartStop(c *gc.C) { 283 fw := s.newFirewaller(c) 284 statetesting.AssertKillAndWait(c, fw) 285 } 286 287 func (s *InstanceModeSuite) TestStartStopWithoutModelFirewaller(c *gc.C) { 288 fw := s.newFirewallerWithoutModelFirewaller(c) 289 statetesting.AssertKillAndWait(c, fw) 290 } 291 292 func (s *InstanceModeSuite) testNotExposedApplication(c *gc.C, fw worker.Worker) { 293 app := s.AddTestingApplication(c, "wordpress", s.charm) 294 u, m := s.addUnit(c, app) 295 inst := s.startInstance(c, m) 296 297 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 298 network.MustParsePortRange("80/tcp"), 299 network.MustParsePortRange("8080/tcp"), 300 }) 301 302 s.assertIngressRules(c, inst, m.Id(), nil) 303 304 mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 305 network.MustParsePortRange("80/tcp"), 306 }) 307 308 s.assertIngressRules(c, inst, m.Id(), nil) 309 } 310 311 func (s *InstanceModeSuite) TestNotExposedApplication(c *gc.C) { 312 fw := s.newFirewaller(c) 313 defer statetesting.AssertKillAndWait(c, fw) 314 s.testNotExposedApplication(c, fw) 315 } 316 317 func (s *InstanceModeSuite) TestNotExposedApplicationWithoutModelFirewaller(c *gc.C) { 318 fw := s.newFirewallerWithoutModelFirewaller(c) 319 defer statetesting.AssertKillAndWait(c, fw) 320 s.testNotExposedApplication(c, fw) 321 } 322 323 func (s *InstanceModeSuite) TestExposedApplication(c *gc.C) { 324 fw := s.newFirewaller(c) 325 defer statetesting.AssertKillAndWait(c, fw) 326 327 app := s.AddTestingApplication(c, "wordpress", s.charm) 328 329 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 330 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 331 }) 332 c.Assert(err, jc.ErrorIsNil) 333 u, m := s.addUnit(c, app) 334 inst := s.startInstance(c, m) 335 336 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 337 network.MustParsePortRange("80-90/tcp"), 338 network.MustParsePortRange("8080/tcp"), 339 }) 340 341 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 342 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 343 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 344 }) 345 346 mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 347 network.MustParsePortRange("80-90/tcp"), 348 }) 349 350 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 351 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 352 }) 353 } 354 355 func (s *InstanceModeSuite) TestMultipleExposedApplications(c *gc.C) { 356 fw := s.newFirewaller(c) 357 defer statetesting.AssertKillAndWait(c, fw) 358 359 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 360 err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{ 361 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 362 }) 363 c.Assert(err, jc.ErrorIsNil) 364 365 u1, m1 := s.addUnit(c, app1) 366 inst1 := s.startInstance(c, m1) 367 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 368 network.MustParsePortRange("80/tcp"), 369 network.MustParsePortRange("8080/tcp"), 370 }) 371 372 app2 := s.AddTestingApplication(c, "mysql", s.charm) 373 c.Assert(err, jc.ErrorIsNil) 374 err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{ 375 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 376 }) 377 c.Assert(err, jc.ErrorIsNil) 378 379 u2, m2 := s.addUnit(c, app2) 380 inst2 := s.startInstance(c, m2) 381 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 382 network.MustParsePortRange("3306/tcp"), 383 }) 384 385 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 386 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 387 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 388 }) 389 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 390 firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), firewall.AllNetworksIPV4CIDR), 391 }) 392 393 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 394 network.MustParsePortRange("80/tcp"), 395 }) 396 mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 397 network.MustParsePortRange("3306/tcp"), 398 }) 399 400 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 401 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 402 }) 403 s.assertIngressRules(c, inst2, m2.Id(), nil) 404 } 405 406 func (s *InstanceModeSuite) TestMachineWithoutInstanceId(c *gc.C) { 407 fw := s.newFirewaller(c) 408 defer statetesting.AssertKillAndWait(c, fw) 409 410 app := s.AddTestingApplication(c, "wordpress", s.charm) 411 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 412 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 413 }) 414 c.Assert(err, jc.ErrorIsNil) 415 // add a unit but don't start its instance yet. 416 u1, m1 := s.addUnit(c, app) 417 418 // add another unit and start its instance, so that 419 // we're sure the firewaller has seen the first instance. 420 u2, m2 := s.addUnit(c, app) 421 inst2 := s.startInstance(c, m2) 422 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 423 network.MustParsePortRange("80/tcp"), 424 }) 425 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 426 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 427 }) 428 429 inst1 := s.startInstance(c, m1) 430 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 431 network.MustParsePortRange("8080/tcp"), 432 }) 433 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 434 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 435 }) 436 } 437 438 func (s *InstanceModeSuite) TestMultipleUnits(c *gc.C) { 439 fw := s.newFirewaller(c) 440 defer statetesting.AssertKillAndWait(c, fw) 441 442 app := s.AddTestingApplication(c, "wordpress", s.charm) 443 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 444 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 445 }) 446 c.Assert(err, jc.ErrorIsNil) 447 448 u1, m1 := s.addUnit(c, app) 449 inst1 := s.startInstance(c, m1) 450 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 451 network.MustParsePortRange("80/tcp"), 452 }) 453 454 u2, m2 := s.addUnit(c, app) 455 inst2 := s.startInstance(c, m2) 456 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 457 network.MustParsePortRange("80/tcp"), 458 }) 459 460 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 461 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 462 }) 463 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 464 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 465 }) 466 467 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 468 network.MustParsePortRange("80/tcp"), 469 }) 470 mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 471 network.MustParsePortRange("80/tcp"), 472 }) 473 474 s.assertIngressRules(c, inst1, m1.Id(), nil) 475 s.assertIngressRules(c, inst2, m2.Id(), nil) 476 } 477 478 func (s *InstanceModeSuite) TestStartWithState(c *gc.C) { 479 app := s.AddTestingApplication(c, "wordpress", s.charm) 480 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 481 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 482 }) 483 c.Assert(err, jc.ErrorIsNil) 484 u, m := s.addUnit(c, app) 485 inst := s.startInstance(c, m) 486 487 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 488 network.MustParsePortRange("80/tcp"), 489 network.MustParsePortRange("8080/tcp"), 490 }) 491 492 // Nothing open without firewaller. 493 s.assertIngressRules(c, inst, m.Id(), nil) 494 495 // Starting the firewaller opens the ports. 496 fw := s.newFirewaller(c) 497 defer statetesting.AssertKillAndWait(c, fw) 498 499 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 500 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 501 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 502 }) 503 504 err = app.MergeExposeSettings(nil) 505 c.Assert(err, jc.ErrorIsNil) 506 } 507 508 func (s *InstanceModeSuite) TestStartWithPartialState(c *gc.C) { 509 m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 510 c.Assert(err, jc.ErrorIsNil) 511 inst := s.startInstance(c, m) 512 513 app := s.AddTestingApplication(c, "wordpress", s.charm) 514 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 515 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 516 }) 517 c.Assert(err, jc.ErrorIsNil) 518 519 // Starting the firewaller, no open ports. 520 fw := s.newFirewaller(c) 521 defer statetesting.AssertKillAndWait(c, fw) 522 523 s.assertIngressRules(c, inst, m.Id(), nil) 524 525 // Complete steps to open port. 526 u, err := app.AddUnit(state.AddUnitParams{}) 527 c.Assert(err, jc.ErrorIsNil) 528 err = u.AssignToMachine(m) 529 c.Assert(err, jc.ErrorIsNil) 530 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 531 network.MustParsePortRange("80/tcp"), 532 }) 533 534 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 535 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 536 }) 537 } 538 539 func (s *InstanceModeSuite) TestStartWithUnexposedApplication(c *gc.C) { 540 m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 541 c.Assert(err, jc.ErrorIsNil) 542 inst := s.startInstance(c, m) 543 544 app := s.AddTestingApplication(c, "wordpress", s.charm) 545 u, err := app.AddUnit(state.AddUnitParams{}) 546 c.Assert(err, jc.ErrorIsNil) 547 err = u.AssignToMachine(m) 548 c.Assert(err, jc.ErrorIsNil) 549 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 550 network.MustParsePortRange("80/tcp"), 551 }) 552 553 // Starting the firewaller, no open ports. 554 fw := s.newFirewaller(c) 555 defer statetesting.AssertKillAndWait(c, fw) 556 557 s.assertIngressRules(c, inst, m.Id(), nil) 558 559 // Expose service. 560 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 561 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 562 }) 563 c.Assert(err, jc.ErrorIsNil) 564 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 565 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 566 }) 567 } 568 569 func (s *InstanceModeSuite) TestStartMachineWithManualMachine(c *gc.C) { 570 watching := make(chan names.MachineTag, 10) // buffer to ensure test never blocks 571 s.watchMachineNotify = func(tag names.MachineTag) { 572 watching <- tag 573 } 574 assertWatching := func(expected names.MachineTag) { 575 select { 576 case got := <-watching: 577 c.Assert(got, gc.Equals, expected) 578 case <-time.After(coretesting.LongWait): 579 c.Fatalf("timed out waiting to watch machine %v", expected.Id()) 580 } 581 } 582 583 fw := s.newFirewaller(c) 584 defer statetesting.AssertKillAndWait(c, fw) 585 586 // Wait for manager machine (started by setUpTest) 587 assertWatching(names.NewMachineTag("0")) 588 589 _, err := s.State.AddOneMachine(state.MachineTemplate{ 590 Base: state.UbuntuBase("12.10"), 591 Jobs: []state.MachineJob{state.JobHostUnits}, 592 InstanceId: "2", 593 Nonce: "manual:", 594 }) 595 c.Assert(err, jc.ErrorIsNil) 596 select { 597 case tag := <-watching: 598 c.Fatalf("shouldn't be watching manual machine %v", tag) 599 case <-time.After(coretesting.ShortWait): 600 } 601 602 m, err := s.State.AddOneMachine(state.MachineTemplate{ 603 Base: state.UbuntuBase("12.10"), 604 Jobs: []state.MachineJob{state.JobHostUnits}, 605 }) 606 c.Assert(err, jc.ErrorIsNil) 607 assertWatching(m.MachineTag()) 608 } 609 610 // TODO: remove once JujuConnSuite is gone. 611 type firewallerAPIShim struct { 612 firewaller.FirewallerAPI 613 newModelMachineWatcher func() (watcher.StringsWatcher, error) 614 } 615 616 func (a *firewallerAPIShim) WatchModelMachines() (watcher.StringsWatcher, error) { 617 return a.newModelMachineWatcher() 618 } 619 620 func (s *InstanceModeSuite) TestFlushModelAfterFirstMachineOnly(c *gc.C) { 621 machinesChan := make(chan []string, 1) 622 machinesChan <- []string{} 623 s.firewaller = &firewallerAPIShim{ 624 FirewallerAPI: s.firewaller, 625 newModelMachineWatcher: func() (watcher.StringsWatcher, error) { 626 return watchertest.NewMockStringsWatcher(machinesChan), nil 627 }, 628 } 629 630 flush := make(chan struct{}, 10) // buffer to ensure test never blocks 631 s.flushModelNotify = func() { 632 flush <- struct{}{} 633 } 634 635 fw := s.newFirewaller(c) 636 defer statetesting.AssertKillAndWait(c, fw) 637 638 // Initial event from model config watcher 639 select { 640 case <-flush: 641 case <-time.After(coretesting.LongWait): 642 c.Fatalf("timed out waiting for initial event") 643 } 644 645 m1, err := s.State.AddOneMachine(state.MachineTemplate{ 646 Base: state.UbuntuBase("12.10"), 647 Jobs: []state.MachineJob{state.JobHostUnits}, 648 }) 649 c.Assert(err, jc.ErrorIsNil) 650 machinesChan <- []string{m1.Id()} 651 652 select { 653 case <-flush: 654 case <-time.After(coretesting.LongWait): 655 c.Fatalf("timed out waiting for first machine event") 656 } 657 658 m2, err := s.State.AddOneMachine(state.MachineTemplate{ 659 Base: state.UbuntuBase("12.10"), 660 Jobs: []state.MachineJob{state.JobHostUnits}, 661 }) 662 c.Assert(err, jc.ErrorIsNil) 663 machinesChan <- []string{m2.Id()} 664 665 // Since the initial event successfully configured the model firewall 666 // the next machine shouldn't trigger a model flush 667 select { 668 case <-flush: 669 c.Fatalf("unexpected model flush creating machine") 670 case <-time.After(coretesting.ShortWait): 671 } 672 } 673 674 func (s *InstanceModeSuite) TestSetClearExposedApplication(c *gc.C) { 675 fw := s.newFirewaller(c) 676 defer statetesting.AssertKillAndWait(c, fw) 677 678 app := s.AddTestingApplication(c, "wordpress", s.charm) 679 680 u, m := s.addUnit(c, app) 681 inst := s.startInstance(c, m) 682 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 683 network.MustParsePortRange("80/tcp"), 684 network.MustParsePortRange("8080/tcp"), 685 }) 686 687 // Not exposed service, so no open port. 688 s.assertIngressRules(c, inst, m.Id(), nil) 689 690 // SeExposed opens the ports. 691 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 692 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 693 }) 694 c.Assert(err, jc.ErrorIsNil) 695 696 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 697 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 698 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 699 }) 700 701 // ClearExposed closes the ports again. 702 err = app.ClearExposed() 703 c.Assert(err, jc.ErrorIsNil) 704 705 s.assertIngressRules(c, inst, m.Id(), nil) 706 } 707 708 func (s *InstanceModeSuite) TestRemoveUnit(c *gc.C) { 709 fw := s.newFirewaller(c) 710 defer statetesting.AssertKillAndWait(c, fw) 711 712 app := s.AddTestingApplication(c, "wordpress", s.charm) 713 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 714 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 715 }) 716 c.Assert(err, jc.ErrorIsNil) 717 718 u1, m1 := s.addUnit(c, app) 719 inst1 := s.startInstance(c, m1) 720 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 721 network.MustParsePortRange("80/tcp"), 722 }) 723 724 u2, m2 := s.addUnit(c, app) 725 inst2 := s.startInstance(c, m2) 726 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 727 network.MustParsePortRange("80/tcp"), 728 }) 729 730 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 731 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 732 }) 733 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 734 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 735 }) 736 737 // Remove unit. 738 err = u1.EnsureDead() 739 c.Assert(err, jc.ErrorIsNil) 740 err = u1.Remove() 741 c.Assert(err, jc.ErrorIsNil) 742 743 s.assertIngressRules(c, inst1, m1.Id(), nil) 744 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 745 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 746 }) 747 } 748 749 func (s *InstanceModeSuite) TestRemoveApplication(c *gc.C) { 750 fw := s.newFirewaller(c) 751 defer statetesting.AssertKillAndWait(c, fw) 752 753 app := s.AddTestingApplication(c, "wordpress", s.charm) 754 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 755 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 756 }) 757 c.Assert(err, jc.ErrorIsNil) 758 759 u, m := s.addUnit(c, app) 760 inst := s.startInstance(c, m) 761 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 762 network.MustParsePortRange("80/tcp"), 763 }) 764 765 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 766 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 767 }) 768 769 // Remove application. 770 err = u.EnsureDead() 771 c.Assert(err, jc.ErrorIsNil) 772 err = u.Remove() 773 c.Assert(err, jc.ErrorIsNil) 774 err = app.Destroy() 775 c.Assert(err, jc.ErrorIsNil) 776 s.assertIngressRules(c, inst, m.Id(), nil) 777 } 778 779 func (s *InstanceModeSuite) TestRemoveMultipleApplications(c *gc.C) { 780 fw := s.newFirewaller(c) 781 defer statetesting.AssertKillAndWait(c, fw) 782 783 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 784 err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{ 785 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 786 }) 787 c.Assert(err, jc.ErrorIsNil) 788 789 u1, m1 := s.addUnit(c, app1) 790 inst1 := s.startInstance(c, m1) 791 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 792 network.MustParsePortRange("80/tcp"), 793 }) 794 795 app2 := s.AddTestingApplication(c, "mysql", s.charm) 796 err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{ 797 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 798 }) 799 c.Assert(err, jc.ErrorIsNil) 800 801 u2, m2 := s.addUnit(c, app2) 802 inst2 := s.startInstance(c, m2) 803 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 804 network.MustParsePortRange("3306/tcp"), 805 }) 806 807 s.assertIngressRules(c, inst1, m1.Id(), firewall.IngressRules{ 808 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 809 }) 810 s.assertIngressRules(c, inst2, m2.Id(), firewall.IngressRules{ 811 firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), firewall.AllNetworksIPV4CIDR), 812 }) 813 814 // Remove applications. 815 err = u2.EnsureDead() 816 c.Assert(err, jc.ErrorIsNil) 817 err = u2.Remove() 818 c.Assert(err, jc.ErrorIsNil) 819 err = app2.Destroy() 820 c.Assert(err, jc.ErrorIsNil) 821 822 err = u1.EnsureDead() 823 c.Assert(err, jc.ErrorIsNil) 824 err = u1.Remove() 825 c.Assert(err, jc.ErrorIsNil) 826 err = app1.Destroy() 827 c.Assert(err, jc.ErrorIsNil) 828 829 s.assertIngressRules(c, inst1, m1.Id(), nil) 830 s.assertIngressRules(c, inst2, m2.Id(), nil) 831 } 832 833 func (s *InstanceModeSuite) TestDeadMachine(c *gc.C) { 834 fw := s.newFirewaller(c) 835 defer statetesting.AssertKillAndWait(c, fw) 836 837 app := s.AddTestingApplication(c, "wordpress", s.charm) 838 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 839 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 840 }) 841 c.Assert(err, jc.ErrorIsNil) 842 843 u, m := s.addUnit(c, app) 844 inst := s.startInstance(c, m) 845 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 846 network.MustParsePortRange("80/tcp"), 847 }) 848 849 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 850 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 851 }) 852 853 // Remove unit and application, also tested without. Has no effect. 854 err = u.EnsureDead() 855 c.Assert(err, jc.ErrorIsNil) 856 err = u.Remove() 857 c.Assert(err, jc.ErrorIsNil) 858 err = app.Destroy() 859 c.Assert(err, jc.ErrorIsNil) 860 861 // Kill machine. 862 err = m.Refresh() 863 c.Assert(err, jc.ErrorIsNil) 864 err = m.EnsureDead() 865 c.Assert(err, jc.ErrorIsNil) 866 867 s.assertIngressRules(c, inst, m.Id(), nil) 868 } 869 870 func (s *InstanceModeSuite) TestRemoveMachine(c *gc.C) { 871 fw := s.newFirewaller(c) 872 873 app := s.AddTestingApplication(c, "wordpress", s.charm) 874 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 875 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 876 }) 877 c.Assert(err, jc.ErrorIsNil) 878 879 u, m := s.addUnit(c, app) 880 inst := s.startInstance(c, m) 881 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 882 network.MustParsePortRange("80/tcp"), 883 }) 884 885 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 886 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 887 }) 888 889 // Remove unit. 890 err = u.EnsureDead() 891 c.Assert(err, jc.ErrorIsNil) 892 err = u.Remove() 893 c.Assert(err, jc.ErrorIsNil) 894 895 // Remove machine. Nothing bad should happen, but can't 896 // assert port state since the machine must have been 897 // destroyed and we lost its reference. 898 err = m.Refresh() 899 c.Assert(err, jc.ErrorIsNil) 900 err = m.EnsureDead() 901 c.Assert(err, jc.ErrorIsNil) 902 err = m.Remove() 903 c.Assert(err, jc.ErrorIsNil) 904 905 // TODO (manadart 2019-02-01): This fails intermittently with a "not found" 906 // error for the machine. This is not a huge problem in production, as the 907 // worker will restart and proceed happily thereafter. 908 // That error is detected here for expediency, but the ideal mitigation is 909 // a refactoring of the worker logic as per LP:1814277. 910 fw.Kill() 911 err = fw.Wait() 912 c.Assert(err == nil || params.IsCodeNotFound(err), jc.IsTrue) 913 } 914 915 func (s *InstanceModeSuite) TestStartWithStateOpenPortsBroken(c *gc.C) { 916 app := s.AddTestingApplication(c, "wordpress", s.charm) 917 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 918 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 919 }) 920 c.Assert(err, jc.ErrorIsNil) 921 u, m := s.addUnit(c, app) 922 inst := s.startInstance(c, m) 923 924 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 925 network.MustParsePortRange("80/tcp"), 926 }) 927 928 // Nothing open without firewaller. 929 s.assertIngressRules(c, inst, m.Id(), nil) 930 dummy.SetInstanceBroken(inst, "OpenPorts") 931 932 // Starting the firewaller should attempt to open the ports, 933 // and fail due to the method being broken. 934 fw := s.newFirewaller(c) 935 936 errc := make(chan error, 1) 937 go func() { errc <- fw.Wait() }() 938 select { 939 case err := <-errc: 940 c.Assert(err, gc.ErrorMatches, 941 `cannot respond to units changes for "machine-1", \"deadbeef-0bad-400d-8000-4b1d0d06f00d\": dummyInstance.OpenPorts is broken`) 942 case <-time.After(coretesting.LongWait): 943 fw.Kill() 944 fw.Wait() 945 c.Fatal("timed out waiting for firewaller to stop") 946 } 947 } 948 949 func (s *InstanceModeSuite) TestDefaultModelFirewall(c *gc.C) { 950 fw := s.newFirewaller(c) 951 defer statetesting.AssertKillAndWait(c, fw) 952 953 ctrlCfg, err := s.State.ControllerConfig() 954 c.Assert(err, jc.ErrorIsNil) 955 apiPort := ctrlCfg.APIPort() 956 957 s.assertModelIngressRules(c, firewall.IngressRules{ 958 firewall.NewIngressRule(network.MustParsePortRange("22"), "0.0.0.0/0", "::/0"), 959 firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"), 960 }) 961 } 962 963 func (s *InstanceModeSuite) TestConfigureModelFirewall(c *gc.C) { 964 fw := s.newFirewaller(c) 965 defer statetesting.AssertKillAndWait(c, fw) 966 967 model, err := s.State.Model() 968 c.Assert(err, jc.ErrorIsNil) 969 970 ctrlCfg, err := s.State.ControllerConfig() 971 c.Assert(err, jc.ErrorIsNil) 972 apiPort := ctrlCfg.APIPort() 973 974 s.assertModelIngressRules(c, firewall.IngressRules{ 975 firewall.NewIngressRule(network.MustParsePortRange("22"), "0.0.0.0/0", "::/0"), 976 firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"), 977 }) 978 979 err = model.UpdateModelConfig(map[string]interface{}{ 980 config.SSHAllowKey: "192.168.0.0/24", 981 }, nil) 982 c.Assert(err, jc.ErrorIsNil) 983 984 s.assertModelIngressRules(c, firewall.IngressRules{ 985 firewall.NewIngressRule(network.MustParsePortRange("22"), "192.168.0.0/24"), 986 firewall.NewIngressRule(network.MustParsePortRange(strconv.Itoa(apiPort)), "0.0.0.0/0", "::/0"), 987 }) 988 } 989 990 func (s *InstanceModeSuite) setupRemoteRelationRequirerRoleConsumingSide( 991 c *gc.C, published chan bool, shouldErr func() bool, ingressRequired *bool, clock clock.Clock, 992 ) (worker.Worker, *state.RelationUnit) { 993 // Set up the consuming model - create the local app. 994 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 995 // Set up the consuming model - create the remote app. 996 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 997 // Create the external controller info. 998 ec := state.NewExternalControllers(s.State) 999 _, err := ec.Save(crossmodel.ControllerInfo{ 1000 ControllerTag: coretesting.ControllerTag, 1001 Addrs: []string{"1.2.3.4:1234"}, 1002 CACert: coretesting.CACert}, offeringModelTag.Id()) 1003 c.Assert(err, jc.ErrorIsNil) 1004 1005 mac, err := apitesting.NewMacaroon("apimac") 1006 c.Assert(err, jc.ErrorIsNil) 1007 var relToken string 1008 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, 1009 arg, result interface{}) error { 1010 c.Check(objType, gc.Equals, "CrossModelRelations") 1011 c.Check(version, gc.Equals, 0) 1012 c.Check(id, gc.Equals, "") 1013 c.Check(request, gc.Equals, "PublishIngressNetworkChanges") 1014 expected := params.IngressNetworksChanges{ 1015 Changes: []params.IngressNetworksChangeEvent{{ 1016 RelationToken: relToken, 1017 }}, 1018 } 1019 1020 // Extract macaroons so we can compare them separately 1021 // (as they can't be compared using DeepEquals due to 'UnmarshaledAs') 1022 expectedMacs := arg.(params.IngressNetworksChanges).Changes[0].Macaroons 1023 arg.(params.IngressNetworksChanges).Changes[0].Macaroons = nil 1024 c.Assert(len(expectedMacs), gc.Equals, 1) 1025 apitesting.MacaroonEquals(c, expectedMacs[0], mac) 1026 1027 // Networks may be empty or not, depending on coalescing of watcher events. 1028 // We may get an initial empty event followed by an event with a network. 1029 // Or we may get just the event with a network. 1030 // Set the arg networks to empty and compare below. 1031 changes := arg.(params.IngressNetworksChanges) 1032 argNetworks := changes.Changes[0].Networks 1033 argIngressRequired := changes.Changes[0].IngressRequired 1034 1035 changes.Changes[0].Networks = nil 1036 expected.Changes[0].IngressRequired = argIngressRequired 1037 expected.Changes[0].BakeryVersion = bakery.LatestVersion 1038 c.Check(arg, gc.DeepEquals, expected) 1039 1040 if !*ingressRequired { 1041 c.Assert(changes.Changes[0].Networks, gc.HasLen, 0) 1042 } 1043 if *ingressRequired && len(argNetworks) > 0 { 1044 c.Assert(argIngressRequired, jc.IsTrue) 1045 c.Assert(argNetworks, jc.DeepEquals, []string{"10.0.0.4/32"}) 1046 } 1047 changes.Changes[0].Macaroons = expectedMacs 1048 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 1049 *(result.(*params.ErrorResults)) = params.ErrorResults{ 1050 Results: []params.ErrorResult{{}}, 1051 } 1052 if shouldErr() { 1053 return errors.New("fail") 1054 } 1055 if !*ingressRequired || len(argNetworks) > 0 { 1056 published <- true 1057 } 1058 return nil 1059 }) 1060 1061 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 1062 c.Assert(s.crossmodelFirewaller, gc.NotNil) 1063 1064 // Create the firewaller facade on the consuming model. 1065 fw := s.newFirewaller(c) 1066 1067 _, err = s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1068 Name: "mysql", SourceModel: offeringModelTag, 1069 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}}, 1070 }) 1071 c.Assert(err, jc.ErrorIsNil) 1072 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1073 c.Assert(err, jc.ErrorIsNil) 1074 rel, err := s.State.AddRelation(eps...) 1075 c.Assert(err, jc.ErrorIsNil) 1076 1077 // Export the relation details so the firewaller knows it's ready to be processed. 1078 re := s.State.RemoteEntities() 1079 relToken, err = re.ExportLocalEntity(rel.Tag()) 1080 c.Assert(err, jc.ErrorIsNil) 1081 err = re.SaveMacaroon(rel.Tag(), mac) 1082 c.Assert(err, jc.ErrorIsNil) 1083 1084 // We should not have published any ingress events yet - no unit has entered scope. 1085 select { 1086 case <-time.After(coretesting.ShortWait): 1087 case <-published: 1088 c.Fatal("unexpected ingress change to be published") 1089 } 1090 1091 // Add a public address to the consuming unit so the firewaller can use it. 1092 wpm := s.Factory.MakeMachine(c, &factory.MachineParams{ 1093 Addresses: network.SpaceAddresses{network.NewSpaceAddress("10.0.0.4")}, 1094 }) 1095 u, err := wordpress.AddUnit(state.AddUnitParams{}) 1096 c.Assert(err, jc.ErrorIsNil) 1097 err = u.AssignToMachine(wpm) 1098 c.Assert(err, jc.ErrorIsNil) 1099 ru, err := rel.Unit(u) 1100 c.Assert(err, jc.ErrorIsNil) 1101 return fw, ru 1102 } 1103 1104 func (s *InstanceModeSuite) TestRemoteRelationRequirerRoleConsumingSide(c *gc.C) { 1105 published := make(chan bool) 1106 ingressRequired := true 1107 apiErr := func() bool { 1108 return false 1109 } 1110 fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, apiErr, &ingressRequired, s.clock) 1111 defer statetesting.AssertKillAndWait(c, fw) 1112 1113 // Add a unit on the consuming app and have it enter the relation scope. 1114 // This will trigger the firewaller to publish the changes. 1115 err := ru.EnterScope(map[string]interface{}{}) 1116 c.Assert(err, jc.ErrorIsNil) 1117 select { 1118 case <-time.After(coretesting.LongWait): 1119 c.Fatal("time out waiting for ingress change to be published on enter scope") 1120 case <-published: 1121 } 1122 1123 // Change should be sent when unit leaves scope. 1124 ingressRequired = false 1125 err = ru.LeaveScope() 1126 c.Assert(err, jc.ErrorIsNil) 1127 select { 1128 case <-time.After(coretesting.LongWait): 1129 c.Fatal("time out waiting for ingress change to be published on leave scope") 1130 case <-published: 1131 } 1132 } 1133 1134 func (s *InstanceModeSuite) TestRemoteRelationWorkerError(c *gc.C) { 1135 published := make(chan bool, 1) 1136 ingressRequired := true 1137 1138 apiCalled := make(chan struct{}, 1) 1139 callCount := 0 1140 apiErr := func() bool { 1141 select { 1142 case apiCalled <- struct{}{}: 1143 case <-time.After(coretesting.ShortWait): 1144 } 1145 callCount += 1 1146 return callCount == 1 1147 } 1148 fw, ru := s.setupRemoteRelationRequirerRoleConsumingSide(c, published, apiErr, &ingressRequired, s.clock) 1149 defer statetesting.AssertKillAndWait(c, fw) 1150 1151 // Add a unit on the consuming app and have it enter the relation scope. 1152 // This will trigger the firewaller to try and publish the changes. 1153 err := ru.EnterScope(map[string]interface{}{}) 1154 c.Assert(err, jc.ErrorIsNil) 1155 1156 select { 1157 case <-apiCalled: 1158 case <-time.After(coretesting.LongWait): 1159 c.Fatal("time out waiting for api to be called") 1160 } 1161 1162 // We should not have published any ingress events yet - no changed published. 1163 select { 1164 case <-time.After(coretesting.ShortWait): 1165 case <-published: 1166 c.Fatal("unexpected ingress change to be published") 1167 } 1168 1169 s.clock.Advance(time.Minute) 1170 1171 // Give the worker time to restart and try again. 1172 select { 1173 case <-time.After(coretesting.LongWait): 1174 c.Fatal("time out waiting for ingress change to be published on enter scope") 1175 case <-published: 1176 } 1177 } 1178 1179 func (s *InstanceModeSuite) TestRemoteRelationProviderRoleConsumingSide(c *gc.C) { 1180 // Set up the consuming model - create the local app. 1181 s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1182 // Set up the consuming model - create the remote app. 1183 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 1184 appToken := utils.MustNewUUID().String() 1185 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1186 Name: "wordpress", SourceModel: offeringModelTag, 1187 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "requirer", Scope: "global"}}, 1188 }) 1189 c.Assert(err, jc.ErrorIsNil) 1190 // Create the external controller info. 1191 ec := state.NewExternalControllers(s.State) 1192 _, err = ec.Save(crossmodel.ControllerInfo{ 1193 ControllerTag: coretesting.ControllerTag, 1194 Addrs: []string{"1.2.3.4:1234"}, 1195 CACert: coretesting.CACert}, offeringModelTag.Id()) 1196 c.Assert(err, jc.ErrorIsNil) 1197 1198 mac, err := apitesting.NewMacaroon("apimac") 1199 c.Assert(err, jc.ErrorIsNil) 1200 watched := make(chan bool) 1201 var relToken string 1202 callCount := int32(0) 1203 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, 1204 arg, result interface{}) error { 1205 switch atomic.LoadInt32(&callCount) { 1206 case 0: 1207 c.Check(objType, gc.Equals, "CrossModelRelations") 1208 c.Check(version, gc.Equals, 0) 1209 c.Check(id, gc.Equals, "") 1210 c.Check(request, gc.Equals, "WatchEgressAddressesForRelations") 1211 1212 rArgs := arg.(params.RemoteEntityArgs).Args 1213 c.Assert(rArgs, gc.HasLen, 1) 1214 c.Check(rArgs[0].Token, gc.Equals, relToken) 1215 1216 macs := rArgs[0].Macaroons 1217 c.Assert(macs, gc.HasLen, 1) 1218 c.Assert(macs[0], gc.NotNil) 1219 apitesting.MacaroonEquals(c, macs[0], mac) 1220 1221 c.Assert(result, gc.FitsTypeOf, ¶ms.StringsWatchResults{}) 1222 *(result.(*params.StringsWatchResults)) = params.StringsWatchResults{ 1223 Results: []params.StringsWatchResult{{StringsWatcherId: "1"}}, 1224 } 1225 watched <- true 1226 default: 1227 c.Check(objType, gc.Equals, "StringsWatcher") 1228 } 1229 atomic.AddInt32(&callCount, 1) 1230 return nil 1231 }) 1232 1233 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 1234 c.Assert(s.crossmodelFirewaller, gc.NotNil) 1235 1236 // Create the firewaller facade on the consuming model. 1237 fw := s.newFirewaller(c) 1238 defer statetesting.AssertKillAndWait(c, fw) 1239 1240 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1241 c.Assert(err, jc.ErrorIsNil) 1242 rel, err := s.State.AddRelation(eps...) 1243 c.Assert(err, jc.ErrorIsNil) 1244 1245 // Export the relation details so the firewaller knows it's ready to be processed. 1246 re := s.State.RemoteEntities() 1247 relToken, err = re.ExportLocalEntity(rel.Tag()) 1248 c.Assert(err, jc.ErrorIsNil) 1249 err = re.SaveMacaroon(rel.Tag(), mac) 1250 c.Assert(err, jc.ErrorIsNil) 1251 err = re.ImportRemoteEntity(app.Tag(), appToken) 1252 c.Assert(err, jc.ErrorIsNil) 1253 1254 select { 1255 case <-time.After(coretesting.LongWait): 1256 c.Fatal("time out waiting for watcher call") 1257 case <-watched: 1258 } 1259 } 1260 1261 func (s *InstanceModeSuite) TestRemoteRelationIngressRejected(c *gc.C) { 1262 // Set up the consuming model - create the local app. 1263 wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 1264 // Set up the consuming model - create the remote app. 1265 offeringModelTag := names.NewModelTag(utils.MustNewUUID().String()) 1266 appToken := utils.MustNewUUID().String() 1267 1268 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1269 Name: "mysql", SourceModel: offeringModelTag, 1270 Endpoints: []charm.Relation{{Name: "database", Interface: "mysql", Role: "provider", Scope: "global"}}, 1271 }) 1272 c.Assert(err, jc.ErrorIsNil) 1273 // Create the external controller info. 1274 ec := state.NewExternalControllers(s.State) 1275 _, err = ec.Save(crossmodel.ControllerInfo{ 1276 ControllerTag: coretesting.ControllerTag, 1277 Addrs: []string{"1.2.3.4:1234"}, 1278 CACert: coretesting.CACert}, offeringModelTag.Id()) 1279 c.Assert(err, jc.ErrorIsNil) 1280 1281 mac, err := apitesting.NewMacaroon("apimac") 1282 c.Assert(err, jc.ErrorIsNil) 1283 1284 published := make(chan bool) 1285 done := false 1286 apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, 1287 arg, result interface{}) error { 1288 c.Assert(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) 1289 *(result.(*params.ErrorResults)) = params.ErrorResults{ 1290 Results: []params.ErrorResult{{Error: ¶ms.Error{Code: params.CodeForbidden, Message: "error"}}}, 1291 } 1292 // We can get more than one api call depending on the 1293 // granularity of watcher events. 1294 if !done { 1295 done = true 1296 published <- true 1297 } 1298 return nil 1299 }) 1300 1301 s.crossmodelFirewaller = crossmodelrelations.NewClient(apiCaller) 1302 c.Assert(s.crossmodelFirewaller, gc.NotNil) 1303 1304 // Create the firewaller facade on the consuming model. 1305 fw := s.newFirewaller(c) 1306 defer statetesting.AssertKillAndWait(c, fw) 1307 1308 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1309 c.Assert(err, jc.ErrorIsNil) 1310 rel, err := s.State.AddRelation(eps...) 1311 c.Assert(err, jc.ErrorIsNil) 1312 1313 // Export the relation details so the firewaller knows it's ready to be processed. 1314 re := s.State.RemoteEntities() 1315 _, err = re.ExportLocalEntity(rel.Tag()) 1316 c.Assert(err, jc.ErrorIsNil) 1317 err = re.SaveMacaroon(rel.Tag(), mac) 1318 c.Assert(err, jc.ErrorIsNil) 1319 err = re.ImportRemoteEntity(app.Tag(), appToken) 1320 c.Assert(err, jc.ErrorIsNil) 1321 1322 // Add a public address to the consuming unit so the firewaller can use it. 1323 wpm := s.Factory.MakeMachine(c, &factory.MachineParams{ 1324 Addresses: network.SpaceAddresses{network.NewSpaceAddress("10.0.0.4")}, 1325 }) 1326 u, err := wordpress.AddUnit(state.AddUnitParams{}) 1327 c.Assert(err, jc.ErrorIsNil) 1328 err = u.AssignToMachine(wpm) 1329 c.Assert(err, jc.ErrorIsNil) 1330 ru, err := rel.Unit(u) 1331 c.Assert(err, jc.ErrorIsNil) 1332 1333 // Add a unit on the consuming app and have it enter the relation scope. 1334 // This will trigger the firewaller to publish the changes. 1335 err = ru.EnterScope(map[string]interface{}{}) 1336 c.Assert(err, jc.ErrorIsNil) 1337 select { 1338 case <-time.After(coretesting.LongWait): 1339 c.Fatal("time out waiting for ingress change to be published on enter scope") 1340 case <-published: 1341 } 1342 1343 // Check that the relation status is set to error 1344 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 1345 relStatus, err := rel.Status() 1346 c.Check(err, jc.ErrorIsNil) 1347 if relStatus.Status != status.Error { 1348 continue 1349 } 1350 c.Check(relStatus.Message, gc.Equals, "error") 1351 return 1352 } 1353 c.Fatal("time out waiting for relation status to be updated") 1354 } 1355 1356 func (s *InstanceModeSuite) assertIngressCidrs(c *gc.C, ingress []string, expected []string) { 1357 // Set up the offering model - create the local app. 1358 mysql := s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql")) 1359 u, m := s.addUnit(c, mysql) 1360 inst := s.startInstance(c, m) 1361 1362 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1363 network.MustParsePortRange("3306/tcp"), 1364 }) 1365 1366 // Set up the offering model - create the remote app. 1367 consumingModelTag := names.NewModelTag(utils.MustNewUUID().String()) 1368 relToken := utils.MustNewUUID().String() 1369 appToken := utils.MustNewUUID().String() 1370 app, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{ 1371 Name: "wordpress", SourceModel: consumingModelTag, IsConsumerProxy: true, 1372 Endpoints: []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer", Scope: "global"}}, 1373 }) 1374 c.Assert(err, jc.ErrorIsNil) 1375 s.WaitForModelWatchersIdle(c, s.State.ModelUUID()) 1376 1377 // Create the firewaller facade on the offering model. 1378 fw := s.newFirewaller(c) 1379 defer statetesting.AssertKillAndWait(c, fw) 1380 1381 eps, err := s.State.InferEndpoints("wordpress", "mysql") 1382 c.Assert(err, jc.ErrorIsNil) 1383 rel, err := s.State.AddRelation(eps...) 1384 c.Assert(err, jc.ErrorIsNil) 1385 1386 // Export the relation details so the firewaller knows it's ready to be processed. 1387 re := s.State.RemoteEntities() 1388 err = re.ImportRemoteEntity(rel.Tag(), relToken) 1389 c.Assert(err, jc.ErrorIsNil) 1390 err = re.ImportRemoteEntity(app.Tag(), appToken) 1391 c.Assert(err, jc.ErrorIsNil) 1392 1393 // No port changes yet. 1394 s.assertIngressRules(c, inst, m.Id(), nil) 1395 1396 // Save a new ingress network against the relation. 1397 rin := state.NewRelationIngressNetworks(s.State) 1398 _, err = rin.Save(rel.Tag().Id(), false, ingress) 1399 c.Assert(err, jc.ErrorIsNil) 1400 1401 // Ports opened. 1402 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1403 firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...), 1404 }) 1405 1406 // Change should be sent when ingress networks disappear. 1407 _, err = rin.Save(rel.Tag().Id(), false, nil) 1408 c.Assert(err, jc.ErrorIsNil) 1409 s.assertIngressRules(c, inst, m.Id(), nil) 1410 1411 _, err = rin.Save(rel.Tag().Id(), false, ingress) 1412 c.Assert(err, jc.ErrorIsNil) 1413 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1414 firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...), 1415 }) 1416 1417 // And again when relation is suspended. 1418 err = rel.SetSuspended(true, "") 1419 c.Assert(err, jc.ErrorIsNil) 1420 s.assertIngressRules(c, inst, m.Id(), nil) 1421 1422 // And again when relation is resumed. 1423 err = rel.SetSuspended(false, "") 1424 c.Assert(err, jc.ErrorIsNil) 1425 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1426 firewall.NewIngressRule(network.MustParsePortRange("3306/tcp"), expected...), 1427 }) 1428 1429 // And again when relation is destroyed. 1430 err = rel.Destroy() 1431 c.Assert(err, jc.ErrorIsNil) 1432 s.assertIngressRules(c, inst, m.Id(), nil) 1433 } 1434 1435 func (s *InstanceModeSuite) TestRemoteRelationProviderRoleOffering(c *gc.C) { 1436 s.assertIngressCidrs(c, []string{"10.0.0.4/16"}, []string{"10.0.0.4/16"}) 1437 } 1438 1439 func (s *InstanceModeSuite) TestRemoteRelationIngressFallbackToWhitelist(c *gc.C) { 1440 m, err := s.State.Model() 1441 c.Assert(err, jc.ErrorIsNil) 1442 m.UpdateModelConfig(map[string]interface{}{ 1443 config.SAASIngressAllowKey: "192.168.1.0/16", 1444 }, nil) 1445 var ingress []string 1446 for i := 1; i < 30; i++ { 1447 ingress = append(ingress, fmt.Sprintf("10.%d.0.1/32", i)) 1448 } 1449 s.assertIngressCidrs(c, ingress, []string{"192.168.1.0/16"}) 1450 } 1451 1452 func (s *InstanceModeSuite) TestRemoteRelationIngressMergesCIDRS(c *gc.C) { 1453 ingress := []string{ 1454 "192.0.1.254/31", 1455 "192.0.2.0/28", 1456 "192.0.2.16/28", 1457 "192.0.2.32/28", 1458 "192.0.2.48/28", 1459 "192.0.2.64/28", 1460 "192.0.2.80/28", 1461 "192.0.2.96/28", 1462 "192.0.2.112/28", 1463 "192.0.2.128/28", 1464 "192.0.2.144/28", 1465 "192.0.2.160/28", 1466 "192.0.2.176/28", 1467 "192.0.2.192/28", 1468 "192.0.2.208/28", 1469 "192.0.2.224/28", 1470 "192.0.2.240/28", 1471 "192.0.3.0/28", 1472 "192.0.4.0/28", 1473 "192.0.5.0/28", 1474 "192.0.6.0/28", 1475 } 1476 expected := []string{ 1477 "192.0.1.254/31", 1478 "192.0.2.0/24", 1479 "192.0.3.0/28", 1480 "192.0.4.0/28", 1481 "192.0.5.0/28", 1482 "192.0.6.0/28", 1483 } 1484 s.assertIngressCidrs(c, ingress, expected) 1485 } 1486 1487 func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpoints(c *gc.C) { 1488 // Create a spaces and add a subnet to it 1489 sp1, err := s.State.AddSpace("space1", network.Id("sp-1"), nil, false) 1490 c.Assert(err, jc.ErrorIsNil) 1491 _, err = s.State.AddSubnet(network.SubnetInfo{ 1492 ID: "subnet-1", 1493 CIDR: "42.42.0.0/16", 1494 SpaceID: sp1.Id(), 1495 SpaceName: sp1.Name(), 1496 IsPublic: false, 1497 }) 1498 c.Assert(err, jc.ErrorIsNil) 1499 1500 fw := s.newFirewaller(c) 1501 defer statetesting.AssertKillAndWait(c, fw) 1502 1503 app := s.AddTestingApplication(c, "wordpress", s.charm) 1504 1505 u, m := s.addUnit(c, app) 1506 inst := s.startInstance(c, m) 1507 1508 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1509 network.MustParsePortRange("80/tcp"), 1510 }) 1511 mustOpenPortRanges(c, s.State, u, "url", []network.PortRange{ 1512 network.MustParsePortRange("1337/tcp"), 1513 network.MustParsePortRange("1337/udp"), 1514 }) 1515 1516 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1517 allEndpoints: { 1518 ExposeToCIDRs: []string{"10.0.0.0/24"}, 1519 }, 1520 "url": { 1521 ExposeToCIDRs: []string{"192.168.0.0/24", "192.168.1.0/24"}, 1522 ExposeToSpaceIDs: []string{sp1.Id()}, 1523 }, 1524 }) 1525 c.Assert(err, jc.ErrorIsNil) 1526 1527 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1528 // We have opened port 80 for ALL endpoints (including "url"), 1529 // then exposed ALL endpoints to 10.0.0.0/24 and the "url" 1530 // endpoint to 192.168.{0,1}.0/24 and 42.42.0.0/16 (subnet 1531 // of space-1). 1532 // 1533 // We expect to see port 80 use all three CIDRs as valid input sources 1534 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24", "192.168.1.0/24", 1535 "42.42.0.0/16"), 1536 // 1537 // The 1337/{tcp,udp} ports have only been opened for the "url" 1538 // endpoint and the "url" endpoint has been exposed to 192.168.{0,1}.0/24 1539 // and 42.42.0.0/16 (the subnet for space-1). 1540 // 1541 // The ports should only be reachable from these CIDRs. Note 1542 // that the expose for the wildcard ("") endpoint is ignored 1543 // here as the expose settings for the "url" endpoint must 1544 // supersede it. 1545 firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24", "192.168.1.0/24", 1546 "42.42.0.0/16"), 1547 firewall.NewIngressRule(network.MustParsePortRange("1337/udp"), "192.168.0.0/24", "192.168.1.0/24", 1548 "42.42.0.0/16"), 1549 }) 1550 1551 // Change the expose settings and remove the entry for the wildcard endpoint 1552 err = app.UnsetExposeSettings([]string{allEndpoints}) 1553 c.Assert(err, jc.ErrorIsNil) 1554 1555 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1556 // We unexposed the wildcard endpoint so only the "url" endpoint 1557 // remains exposed. This endpoint has ports 1337/{tcp,udp} 1558 // explicitly open as well as port 80 which is opened for ALL 1559 // endpoints. These three ports should be exposed to the 1560 // CIDRs used when the "url" endpoint was exposed 1561 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "192.168.1.0/24", 1562 "42.42.0.0/16"), 1563 firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24", "192.168.1.0/24", 1564 "42.42.0.0/16"), 1565 firewall.NewIngressRule(network.MustParsePortRange("1337/udp"), "192.168.0.0/24", "192.168.1.0/24", 1566 "42.42.0.0/16"), 1567 }) 1568 } 1569 1570 func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceTopologyChanges(c *gc.C) { 1571 // Create two spaces and add a subnet to each one 1572 sp1, err := s.State.AddSpace("space1", network.Id("sp-1"), nil, false) 1573 c.Assert(err, jc.ErrorIsNil) 1574 _, err = s.State.AddSubnet(network.SubnetInfo{ 1575 ID: "subnet-1", 1576 CIDR: "192.168.0.0/24", 1577 SpaceID: sp1.Id(), 1578 SpaceName: sp1.Name(), 1579 IsPublic: false, 1580 }) 1581 c.Assert(err, jc.ErrorIsNil) 1582 1583 sp2, err := s.State.AddSpace("space2", network.Id("sp-2"), nil, true) 1584 c.Assert(err, jc.ErrorIsNil) 1585 sub2, err := s.State.AddSubnet(network.SubnetInfo{ 1586 ID: "subnet-2", 1587 CIDR: "192.168.1.0/24", 1588 SpaceID: sp2.Id(), 1589 SpaceName: sp2.Name(), 1590 IsPublic: false, 1591 }) 1592 c.Assert(err, jc.ErrorIsNil) 1593 1594 fw := s.newFirewaller(c) 1595 defer statetesting.AssertKillAndWait(c, fw) 1596 1597 app := s.AddTestingApplication(c, "wordpress", s.charm) 1598 1599 u, m := s.addUnit(c, app) 1600 inst := s.startInstance(c, m) 1601 // Open port 80 for all endpoints 1602 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1603 network.MustParsePortRange("80/tcp"), 1604 }) 1605 1606 // Expose app to space-1 1607 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1608 allEndpoints: { 1609 ExposeToSpaceIDs: []string{sp1.Id()}, 1610 }, 1611 }) 1612 c.Assert(err, jc.ErrorIsNil) 1613 1614 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1615 // We expect to see port 80 use the subnet-1 CIDR 1616 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"), 1617 }) 1618 1619 // Trigger a space topology change by moving subnet-2 into space 1 1620 moveOps := s.State.UpdateSubnetSpaceOps(sub2.ID(), sp1.Id()) 1621 c.Assert(s.State.ApplyOperation(modelOp{moveOps}), jc.ErrorIsNil) 1622 1623 // Check that worker picked up the change and updated the rules 1624 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1625 // We expect to see port 80 use subnet-{1,2} CIDRs 1626 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "192.168.1.0/24"), 1627 }) 1628 } 1629 1630 func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceDeleted(c *gc.C) { 1631 // Create two spaces and add a subnet to each one 1632 sp1, err := s.State.AddSpace("space1", "sp-1", nil, false) 1633 c.Assert(err, jc.ErrorIsNil) 1634 sub1, err := s.State.AddSubnet(network.SubnetInfo{ 1635 ID: "subnet-1", 1636 CIDR: "192.168.0.0/24", 1637 SpaceID: sp1.Id(), 1638 SpaceName: sp1.Name(), 1639 IsPublic: false, 1640 }) 1641 c.Assert(err, jc.ErrorIsNil) 1642 1643 sp2, err := s.State.AddSpace("space2", "sp-2", nil, true) 1644 c.Assert(err, jc.ErrorIsNil) 1645 _, err = s.State.AddSubnet(network.SubnetInfo{ 1646 ID: "subnet-2", 1647 CIDR: "192.168.1.0/24", 1648 SpaceID: sp2.Id(), 1649 SpaceName: sp2.Name(), 1650 IsPublic: false, 1651 }) 1652 c.Assert(err, jc.ErrorIsNil) 1653 1654 fw := s.newFirewaller(c) 1655 defer statetesting.AssertKillAndWait(c, fw) 1656 1657 app := s.AddTestingApplication(c, "wordpress", s.charm) 1658 1659 u, m := s.addUnit(c, app) 1660 inst := s.startInstance(c, m) 1661 // Open port 80 for all endpoints 1662 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1663 network.MustParsePortRange("80/tcp"), 1664 }) 1665 1666 // Expose app to space-1 1667 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1668 allEndpoints: { 1669 ExposeToSpaceIDs: []string{sp1.Id()}, 1670 }, 1671 }) 1672 c.Assert(err, jc.ErrorIsNil) 1673 1674 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1675 // We expect to see port 80 use the subnet-1 CIDR 1676 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"), 1677 }) 1678 1679 // Simulate the deletion of a space, with subnets moving back to alpha. 1680 moveOps := s.State.UpdateSubnetSpaceOps(sub1.ID(), network.AlphaSpaceId) 1681 deleteOps, err := sp1.RemoveSpaceOps() 1682 c.Assert(err, jc.ErrorIsNil) 1683 c.Assert(s.State.ApplyOperation(modelOp{append(moveOps, deleteOps...)}), jc.ErrorIsNil) 1684 1685 // We expect to see NO ingress rules as the referenced space does not exist. 1686 s.assertIngressRules(c, inst, m.Id(), nil) 1687 } 1688 1689 func (s *InstanceModeSuite) TestExposedApplicationWithExposedEndpointsWhenSpaceHasNoSubnets(c *gc.C) { 1690 // Create a space with a single subnet 1691 sp1, err := s.State.AddSpace("space1", "sp-1", nil, false) 1692 c.Assert(err, jc.ErrorIsNil) 1693 sub1, err := s.State.AddSubnet(network.SubnetInfo{ 1694 ID: "subnet-1", 1695 CIDR: "192.168.0.0/24", 1696 SpaceID: sp1.Id(), 1697 SpaceName: sp1.Name(), 1698 IsPublic: false, 1699 }) 1700 c.Assert(err, jc.ErrorIsNil) 1701 1702 fw := s.newFirewaller(c) 1703 defer statetesting.AssertKillAndWait(c, fw) 1704 1705 app := s.AddTestingApplication(c, "wordpress", s.charm) 1706 1707 u, m := s.addUnit(c, app) 1708 inst := s.startInstance(c, m) 1709 // Open port 80 for all endpoints and 1337 for the "url" endpoint 1710 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1711 network.MustParsePortRange("80/tcp"), 1712 }) 1713 mustOpenPortRanges(c, s.State, u, "url", []network.PortRange{ 1714 network.MustParsePortRange("1337/tcp"), 1715 }) 1716 1717 // Expose app to space-1 1718 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1719 allEndpoints: { 1720 ExposeToSpaceIDs: []string{sp1.Id()}, 1721 }, 1722 "url": { 1723 ExposeToSpaceIDs: []string{sp1.Id()}, 1724 }, 1725 }) 1726 c.Assert(err, jc.ErrorIsNil) 1727 1728 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1729 // We expect to see port 80 and 1337 use the subnet-1 CIDR 1730 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24"), 1731 firewall.NewIngressRule(network.MustParsePortRange("1337/tcp"), "192.168.0.0/24"), 1732 }) 1733 1734 // Move endpoint back to alpha space. This will leave space-1 with no 1735 // endpoints. 1736 moveOps := s.State.UpdateSubnetSpaceOps(sub1.ID(), network.AlphaSpaceId) 1737 c.Assert(s.State.ApplyOperation(modelOp{moveOps}), jc.ErrorIsNil) 1738 1739 // We expect to see NO ingress rules (and warnings in the logs) as 1740 // there are no CIDRs to access the exposed application. 1741 s.assertIngressRules(c, inst, m.Id(), nil) 1742 } 1743 1744 func (s *InstanceModeSuite) TestExposeToIPV6CIDRsOnIPV4OnlyProvider(c *gc.C) { 1745 supportsIPV6CIDRs := false 1746 fw := s.newFirewallerWithIPV6CIDRSupport(c, supportsIPV6CIDRs) 1747 defer statetesting.AssertKillAndWait(c, fw) 1748 1749 app := s.AddTestingApplication(c, "wordpress", s.charm) 1750 1751 u, m := s.addUnit(c, app) 1752 inst := s.startInstance(c, m) 1753 1754 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1755 network.MustParsePortRange("80/tcp"), 1756 }) 1757 1758 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1759 allEndpoints: { 1760 ExposeToCIDRs: []string{"10.0.0.0/24", "2002::1234:abcd:ffff:c0a8:101/64"}, 1761 }, 1762 }) 1763 c.Assert(err, jc.ErrorIsNil) 1764 1765 // Since the provider only supports IPV4 CIDRs, the firewall worker 1766 // will filter the IPV6 CIDRs when opening ports. 1767 s.assertIngressRules(c, inst, m.Id(), firewall.IngressRules{ 1768 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24"), 1769 }) 1770 } 1771 1772 type modelOp struct { 1773 ops []txn.Op 1774 } 1775 1776 func (m modelOp) Build(_ int) ([]txn.Op, error) { return m.ops, nil } 1777 func (m modelOp) Done(err error) error { return err } 1778 1779 type GlobalModeSuite struct { 1780 firewallerBaseSuite 1781 } 1782 1783 var _ = gc.Suite(&GlobalModeSuite{}) 1784 1785 func (s *GlobalModeSuite) SetUpTest(c *gc.C) { 1786 s.firewallerBaseSuite.setUpTest(c, config.FwGlobal) 1787 } 1788 1789 func (s *GlobalModeSuite) TearDownTest(c *gc.C) { 1790 s.firewallerBaseSuite.JujuConnSuite.TearDownTest(c) 1791 } 1792 1793 func (s *GlobalModeSuite) newFirewaller(c *gc.C) worker.Worker { 1794 return s.newFirewallerWithIPV6CIDRSupport(c, true) 1795 } 1796 1797 func (s *GlobalModeSuite) newFirewallerWithIPV6CIDRSupport(c *gc.C, supportIPV6CIDRs bool) worker.Worker { 1798 fwEnv, ok := s.Environ.(environs.Firewaller) 1799 c.Assert(ok, gc.Equals, true) 1800 1801 modelFwEnv, ok := s.Environ.(models.ModelFirewaller) 1802 c.Assert(ok, gc.Equals, true) 1803 1804 cfg := firewaller.Config{ 1805 ModelUUID: s.State.ModelUUID(), 1806 Mode: config.FwGlobal, 1807 EnvironFirewaller: fwEnv, 1808 EnvironModelFirewaller: modelFwEnv, 1809 EnvironInstances: s.Environ, 1810 EnvironIPV6CIDRSupport: supportIPV6CIDRs, 1811 FirewallerAPI: s.firewaller, 1812 RemoteRelationsApi: s.remoteRelations, 1813 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 1814 return s.crossmodelFirewaller, nil 1815 }, 1816 Logger: loggo.GetLogger("test"), 1817 CredentialAPI: s.credentialsFacade, 1818 } 1819 fw, err := firewaller.NewFirewaller(cfg) 1820 c.Assert(err, jc.ErrorIsNil) 1821 return fw 1822 } 1823 1824 func (s *GlobalModeSuite) TestStartStop(c *gc.C) { 1825 fw := s.newFirewaller(c) 1826 statetesting.AssertKillAndWait(c, fw) 1827 } 1828 1829 func (s *GlobalModeSuite) TestGlobalMode(c *gc.C) { 1830 // Start firewaller and open ports. 1831 fw := s.newFirewaller(c) 1832 defer statetesting.AssertKillAndWait(c, fw) 1833 1834 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 1835 err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1836 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 1837 }) 1838 c.Assert(err, jc.ErrorIsNil) 1839 1840 u1, m1 := s.addUnit(c, app1) 1841 s.startInstance(c, m1) 1842 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 1843 network.MustParsePortRange("80-90/tcp"), 1844 network.MustParsePortRange("8080/tcp"), 1845 }) 1846 1847 app2 := s.AddTestingApplication(c, "moinmoin", s.charm) 1848 c.Assert(err, jc.ErrorIsNil) 1849 err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1850 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 1851 }) 1852 c.Assert(err, jc.ErrorIsNil) 1853 1854 u2, m2 := s.addUnit(c, app2) 1855 s.startInstance(c, m2) 1856 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 1857 network.MustParsePortRange("80-90/tcp"), 1858 }) 1859 1860 s.assertEnvironPorts(c, firewall.IngressRules{ 1861 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 1862 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 1863 }) 1864 1865 // Closing a port opened by a different unit won't touch the environment. 1866 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 1867 network.MustParsePortRange("80-90/tcp"), 1868 }) 1869 s.assertEnvironPorts(c, firewall.IngressRules{ 1870 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 1871 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 1872 }) 1873 1874 // Closing a port used just once changes the environment. 1875 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 1876 network.MustParsePortRange("8080/tcp"), 1877 }) 1878 s.assertEnvironPorts(c, firewall.IngressRules{ 1879 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 1880 }) 1881 1882 // Closing the last port also modifies the environment. 1883 mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 1884 network.MustParsePortRange("80-90/tcp"), 1885 }) 1886 s.assertEnvironPorts(c, nil) 1887 } 1888 1889 func (s *GlobalModeSuite) TestStartWithUnexposedApplication(c *gc.C) { 1890 m, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits) 1891 c.Assert(err, jc.ErrorIsNil) 1892 s.startInstance(c, m) 1893 1894 app := s.AddTestingApplication(c, "wordpress", s.charm) 1895 u, err := app.AddUnit(state.AddUnitParams{}) 1896 c.Assert(err, jc.ErrorIsNil) 1897 err = u.AssignToMachine(m) 1898 c.Assert(err, jc.ErrorIsNil) 1899 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1900 network.MustParsePortRange("80/tcp"), 1901 }) 1902 1903 // Starting the firewaller, no open ports. 1904 fw := s.newFirewaller(c) 1905 defer statetesting.AssertKillAndWait(c, fw) 1906 1907 s.assertEnvironPorts(c, nil) 1908 1909 // Expose application. 1910 err = app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1911 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 1912 }) 1913 c.Assert(err, jc.ErrorIsNil) 1914 s.assertEnvironPorts(c, firewall.IngressRules{ 1915 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 1916 }) 1917 } 1918 1919 func (s *GlobalModeSuite) TestRestart(c *gc.C) { 1920 // Start firewaller and open ports. 1921 fw := s.newFirewaller(c) 1922 1923 app := s.AddTestingApplication(c, "wordpress", s.charm) 1924 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1925 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 1926 }) 1927 c.Assert(err, jc.ErrorIsNil) 1928 1929 u, m := s.addUnit(c, app) 1930 s.startInstance(c, m) 1931 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1932 network.MustParsePortRange("80-90/tcp"), 1933 network.MustParsePortRange("8080/tcp"), 1934 }) 1935 c.Assert(err, jc.ErrorIsNil) 1936 1937 s.assertEnvironPorts(c, firewall.IngressRules{ 1938 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 1939 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 1940 }) 1941 1942 // Stop firewaller and close one and open a different port. 1943 err = worker.Stop(fw) 1944 c.Assert(err, jc.ErrorIsNil) 1945 1946 mustClosePortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1947 network.MustParsePortRange("8080/tcp"), 1948 }) 1949 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1950 network.MustParsePortRange("8888/tcp"), 1951 }) 1952 1953 // Start firewaller and check port. 1954 fw = s.newFirewaller(c) 1955 defer statetesting.AssertKillAndWait(c, fw) 1956 1957 s.assertEnvironPorts(c, firewall.IngressRules{ 1958 firewall.NewIngressRule(network.MustParsePortRange("80-90/tcp"), firewall.AllNetworksIPV4CIDR), 1959 firewall.NewIngressRule(network.MustParsePortRange("8888/tcp"), firewall.AllNetworksIPV4CIDR), 1960 }) 1961 } 1962 1963 func (s *GlobalModeSuite) TestRestartUnexposedApplication(c *gc.C) { 1964 // Start firewaller and open ports. 1965 fw := s.newFirewaller(c) 1966 1967 app := s.AddTestingApplication(c, "wordpress", s.charm) 1968 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 1969 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 1970 }) 1971 c.Assert(err, jc.ErrorIsNil) 1972 1973 u, m := s.addUnit(c, app) 1974 s.startInstance(c, m) 1975 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 1976 network.MustParsePortRange("80/tcp"), 1977 network.MustParsePortRange("8080/tcp"), 1978 }) 1979 1980 s.assertEnvironPorts(c, firewall.IngressRules{ 1981 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 1982 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 1983 }) 1984 1985 // Stop firewaller and clear exposed flag on application. 1986 err = worker.Stop(fw) 1987 c.Assert(err, jc.ErrorIsNil) 1988 1989 err = app.ClearExposed() 1990 c.Assert(err, jc.ErrorIsNil) 1991 1992 // Start firewaller and check port. 1993 fw = s.newFirewaller(c) 1994 defer statetesting.AssertKillAndWait(c, fw) 1995 1996 s.assertEnvironPorts(c, nil) 1997 } 1998 1999 func (s *GlobalModeSuite) TestRestartPortCount(c *gc.C) { 2000 // Start firewaller and open ports. 2001 fw := s.newFirewaller(c) 2002 2003 app1 := s.AddTestingApplication(c, "wordpress", s.charm) 2004 err := app1.MergeExposeSettings(map[string]state.ExposedEndpoint{ 2005 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 2006 }) 2007 c.Assert(err, jc.ErrorIsNil) 2008 2009 u1, m1 := s.addUnit(c, app1) 2010 s.startInstance(c, m1) 2011 mustOpenPortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 2012 network.MustParsePortRange("80/tcp"), 2013 network.MustParsePortRange("8080/tcp"), 2014 }) 2015 2016 s.assertEnvironPorts(c, firewall.IngressRules{ 2017 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 2018 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 2019 }) 2020 2021 // Stop firewaller and add another application using the port. 2022 err = worker.Stop(fw) 2023 c.Assert(err, jc.ErrorIsNil) 2024 2025 app2 := s.AddTestingApplication(c, "moinmoin", s.charm) 2026 err = app2.MergeExposeSettings(map[string]state.ExposedEndpoint{ 2027 allEndpoints: {ExposeToCIDRs: []string{firewall.AllNetworksIPV4CIDR}}, 2028 }) 2029 c.Assert(err, jc.ErrorIsNil) 2030 2031 u2, m2 := s.addUnit(c, app2) 2032 s.startInstance(c, m2) 2033 mustOpenPortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 2034 network.MustParsePortRange("80/tcp"), 2035 }) 2036 2037 // Start firewaller and check port. 2038 fw = s.newFirewaller(c) 2039 defer statetesting.AssertKillAndWait(c, fw) 2040 2041 s.assertEnvironPorts(c, firewall.IngressRules{ 2042 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 2043 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 2044 }) 2045 2046 // Closing a port opened by a different unit won't touch the environment. 2047 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 2048 network.MustParsePortRange("80/tcp"), 2049 }) 2050 s.assertEnvironPorts(c, firewall.IngressRules{ 2051 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 2052 firewall.NewIngressRule(network.MustParsePortRange("8080/tcp"), firewall.AllNetworksIPV4CIDR), 2053 }) 2054 2055 // Closing a port used just once changes the environment. 2056 mustClosePortRanges(c, s.State, u1, allEndpoints, []network.PortRange{ 2057 network.MustParsePortRange("8080/tcp"), 2058 }) 2059 s.assertEnvironPorts(c, firewall.IngressRules{ 2060 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), firewall.AllNetworksIPV4CIDR), 2061 }) 2062 2063 // Closing the last port also modifies the environment. 2064 mustClosePortRanges(c, s.State, u2, allEndpoints, []network.PortRange{ 2065 network.MustParsePortRange("80/tcp"), 2066 }) 2067 s.assertEnvironPorts(c, nil) 2068 } 2069 2070 func (s *GlobalModeSuite) TestExposeToIPV6CIDRsOnIPV4OnlyProvider(c *gc.C) { 2071 supportsIPV6CIDRs := false 2072 fw := s.newFirewallerWithIPV6CIDRSupport(c, supportsIPV6CIDRs) 2073 defer statetesting.AssertKillAndWait(c, fw) 2074 2075 app := s.AddTestingApplication(c, "wordpress", s.charm) 2076 u, _ := s.addUnit(c, app) 2077 2078 mustOpenPortRanges(c, s.State, u, allEndpoints, []network.PortRange{ 2079 network.MustParsePortRange("80/tcp"), 2080 }) 2081 2082 err := app.MergeExposeSettings(map[string]state.ExposedEndpoint{ 2083 allEndpoints: { 2084 ExposeToCIDRs: []string{"10.0.0.0/24", "2002::1234:abcd:ffff:c0a8:101/64"}, 2085 }, 2086 }) 2087 c.Assert(err, jc.ErrorIsNil) 2088 2089 // Since the provider only supports IPV4 CIDRs, the firewall worker 2090 // will filter the IPV6 CIDRs when opening ports. 2091 s.assertEnvironPorts(c, firewall.IngressRules{ 2092 firewall.NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24"), 2093 }) 2094 } 2095 2096 type NoneModeSuite struct { 2097 firewallerBaseSuite 2098 } 2099 2100 var _ = gc.Suite(&NoneModeSuite{}) 2101 2102 func (s *NoneModeSuite) SetUpTest(c *gc.C) { 2103 s.firewallerBaseSuite.setUpTest(c, config.FwNone) 2104 } 2105 2106 func (s *NoneModeSuite) TestStopImmediately(c *gc.C) { 2107 fwEnv, ok := s.Environ.(environs.Firewaller) 2108 c.Assert(ok, gc.Equals, true) 2109 2110 cfg := firewaller.Config{ 2111 ModelUUID: s.State.ModelUUID(), 2112 Mode: config.FwNone, 2113 EnvironFirewaller: fwEnv, 2114 EnvironInstances: s.Environ, 2115 FirewallerAPI: s.firewaller, 2116 RemoteRelationsApi: s.remoteRelations, 2117 NewCrossModelFacadeFunc: func(*api.Info) (firewaller.CrossModelFirewallerFacadeCloser, error) { 2118 return s.crossmodelFirewaller, nil 2119 }, 2120 Logger: loggo.GetLogger("test"), 2121 CredentialAPI: s.credentialsFacade, 2122 } 2123 _, err := firewaller.NewFirewaller(cfg) 2124 c.Assert(err, gc.ErrorMatches, `invalid firewall-mode "none"`) 2125 } 2126 2127 func mustOpenPortRanges(c *gc.C, st *state.State, u *state.Unit, endpointName string, portRanges []network.PortRange) { 2128 unitPortRanges, err := u.OpenedPortRanges() 2129 c.Assert(err, jc.ErrorIsNil) 2130 2131 for _, pr := range portRanges { 2132 unitPortRanges.Open(endpointName, pr) 2133 } 2134 2135 c.Assert(st.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil) 2136 } 2137 2138 func mustClosePortRanges(c *gc.C, st *state.State, u *state.Unit, endpointName string, portRanges []network.PortRange) { 2139 unitPortRanges, err := u.OpenedPortRanges() 2140 c.Assert(err, jc.ErrorIsNil) 2141 2142 for _, pr := range portRanges { 2143 unitPortRanges.Close(endpointName, pr) 2144 } 2145 2146 c.Assert(st.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil) 2147 }