Merge lp:~themue/juju-core/go-firewaller-added-global-mode into lp:~juju/juju-core/trunk

Proposed by Frank Mueller
Status: Merged
Approved by: Gustavo Niemeyer
Approved revision: no longer in the source branch.
Merged at revision: 691
Proposed branch: lp:~themue/juju-core/go-firewaller-added-global-mode
Merge into: lp:~juju/juju-core/trunk
Diff against target: 467 lines (+361/-17)
2 files modified
worker/firewaller/firewaller.go (+127/-17)
worker/firewaller/firewaller_test.go (+234/-0)
To merge this branch: bzr merge lp:~themue/juju-core/go-firewaller-added-global-mode
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+130157@code.launchpad.net

Description of the change

firewaller: integrated global mode

Firewaller now recognizes the global mode. It dies a
port usage counting usage counting and opens each
used port only once and closes it after the last using unit
has gone. The handling of ports in case of a restart has
been removed from this CL to a later one.

https://codereview.appspot.com/6713054/

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Needs some love.

https://codereview.appspot.com/6713054/diff/1/worker/firewaller/firewaller.go
File worker/firewaller/firewaller.go (right):

https://codereview.appspot.com/6713054/diff/1/worker/firewaller/firewaller.go#newcode195
worker/firewaller/firewaller.go:195: m, err := machined.machine()
Why do we need the machine, and the instance? It's feeling somewhat
hackish. If it's in global mode, it should be handed off to a method
that deals with global mode *only* in some sensible point, rather than
interleaving unnecessary logic like that.

https://codereview.appspot.com/6713054/diff/1/worker/firewaller/firewaller.go#newcode252
worker/firewaller/firewaller.go:252: if fw.initialPorts[port] {
It's not clear to me how initialPorts is working. What if there are open
ports that should not be? What if there are initialPorts that were
closed and need to be re-opened?

The overall approach is asking for some tasteful consideration.

https://codereview.appspot.com/6713054/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Much better thanks.

Still, what about ports previously opened?

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller.go
File worker/firewaller/firewaller.go (right):

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller.go#newcode177
worker/firewaller/firewaller.go:177: // flushGlobalPorts opens and
closes ports global in the environment.
// flushGlobalPorts opens and closes global ports in the environment.
// It keeps a reference count for ports so that only 0-to-1 and 1-to-0
events
// modify the environment.

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller.go#newcode183
worker/firewaller/firewaller.go:183: // The port is not already open.
Please drop. Code is shorter and obvious.

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller.go#newcode192
worker/firewaller/firewaller.go:192: // so close the port globally.
Same.

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller_test.go
File worker/firewaller/firewaller_test.go (right):

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller_test.go#newcode563
worker/firewaller/firewaller_test.go:563: // Check that all expected
ports are open in environment.
Please drop comment ("assertEnvironPorts" is clear).

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller_test.go#newcode566
worker/firewaller/firewaller_test.go:566: // Check that closing a
multiple used port on one machine lets it untouched.
// Closing a port opened by a different unit won't touch the
environment.

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller_test.go#newcode571
worker/firewaller/firewaller_test.go:571: // Check that closing a single
used port is closed.
// Closing a port used just once changes the environment.

https://codereview.appspot.com/6713054/diff/5001/worker/firewaller/firewaller_test.go#newcode576
worker/firewaller/firewaller_test.go:576: // Check that closing the last
usage of a port closes it globally.
// Closing the last port also modifies the environment.

https://codereview.appspot.com/6713054/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

https://codereview.appspot.com/6713054/diff/10002/worker/firewaller/firewaller.go
File worker/firewaller/firewaller.go (right):

https://codereview.appspot.com/6713054/diff/10002/worker/firewaller/firewaller.go#newcode70
worker/firewaller/firewaller.go:70: err = fw.environ.ClosePorts(ports)
Frank, what happens if you close ports when stuff is running?

Some out-of-the-box thinking is needed. If we have to discuss trivial
stuff repeatedly, there's an inflection point where it's easier to
implement than to review.

https://codereview.appspot.com/6713054/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

On Fri, Oct 19, 2012 at 12:24 PM, <email address hidden> wrote:
> If it's trivial and you already have a concept in mind I don't see
> please let me know, I really appreciate it.
(...)
> I removed my initial concept which relied on the retrieved initial open
> ports from the provider and still does a correct port counting later

Your initial concept had the problems I described. Your new concept
has the problems I described. Both are issues I consider obvious
(closing ports that should be open while services are running!?).

What you have is:

1) Ports that are known open in the provider
2) Simple ways to compute what you need open
3) Simple ways to close and open at will

I cannot break down this problem for you further without implementing
the solution myself, which gets back to the point I raised: there's an
inflection point where reviews are counterproductive if compared to
implementing it myself and asking other people to review it.

Please think about the problem. If you cannot solve it, let's talk
next week and see what kind of problem you'd appreciate working on
instead.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

https://codereview.appspot.com/6713054/diff/14001/worker/firewaller/firewaller.go
File worker/firewaller/firewaller.go (right):

https://codereview.appspot.com/6713054/diff/14001/worker/firewaller/firewaller.go#newcode191
worker/firewaller/firewaller.go:191: // Reset port counter, will be set
during gathering of the units.
Why? Doesn't that mean we'll run OpenPort on the environment for every
single port that we just confirmed is open?

https://codereview.appspot.com/6713054/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

LGTM, assuming that this works with real usage.

https://codereview.appspot.com/6713054/diff/9002/worker/firewaller/firewaller.go
File worker/firewaller/firewaller.go (right):

https://codereview.appspot.com/6713054/diff/9002/worker/firewaller/firewaller.go#newcode30
worker/firewaller/firewaller.go:30: globalPortsCount map[state.Port]int
s/PortsFlag/PortOpen/
s/PortsCount/PortRef/

https://codereview.appspot.com/6713054/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'worker/firewaller/firewaller.go'
--- worker/firewaller/firewaller.go 2012-10-01 12:29:05 +0000
+++ worker/firewaller/firewaller.go 2012-10-22 15:47:21 +0000
@@ -3,6 +3,7 @@
3import (3import (
4 "fmt"4 "fmt"
5 "launchpad.net/juju-core/environs"5 "launchpad.net/juju-core/environs"
6 "launchpad.net/juju-core/environs/config"
6 "launchpad.net/juju-core/log"7 "launchpad.net/juju-core/log"
7 "launchpad.net/juju-core/state"8 "launchpad.net/juju-core/state"
8 "launchpad.net/juju-core/state/watcher"9 "launchpad.net/juju-core/state/watcher"
@@ -13,17 +14,20 @@
13// Firewaller watches the state for ports opened or closed14// Firewaller watches the state for ports opened or closed
14// and reflects those changes onto the backing environment.15// and reflects those changes onto the backing environment.
15type Firewaller struct {16type Firewaller struct {
16 tomb tomb.Tomb17 tomb tomb.Tomb
17 st *state.State18 st *state.State
18 environ environs.Environ19 environ environs.Environ
19 environWatcher *state.EnvironConfigWatcher20 environWatcher *state.EnvironConfigWatcher
20 machinesWatcher *state.MachinesWatcher21 machinesWatcher *state.MachinesWatcher
21 machineds map[int]*machineData22 machineds map[int]*machineData
22 unitsChange chan *unitsChange23 unitsChange chan *unitsChange
23 unitds map[string]*unitData24 unitds map[string]*unitData
24 portsChange chan *portsChange25 portsChange chan *portsChange
25 serviceds map[string]*serviceData26 serviceds map[string]*serviceData
26 exposedChange chan *exposedChange27 exposedChange chan *exposedChange
28 globalMode bool
29 globalPortsFlag map[state.Port]bool
30 globalPortsCount map[state.Port]int
27}31}
2832
29// NewFirewaller returns a new Firewaller.33// NewFirewaller returns a new Firewaller.
@@ -54,6 +58,11 @@
54 if err != nil {58 if err != nil {
55 return err59 return err
56 }60 }
61 if fw.environ.Config().FirewallMode() == config.FwGlobal {
62 if err := fw.initGlobalMode(); err != nil {
63 return err
64 }
65 }
57 for {66 for {
58 select {67 select {
59 case <-fw.tomb.Dying():68 case <-fw.tomb.Dying():
@@ -129,6 +138,61 @@
129 panic("not reached")138 panic("not reached")
130}139}
131140
141// initGlobalMode retrieves the ports that need to be open globally,
142// opens them and also closes not needed open ports left from an
143// earlier run.
144func (fw *Firewaller) initGlobalMode() error {
145 fw.globalMode = true
146 fw.globalPortsFlag = make(map[state.Port]bool)
147 fw.globalPortsCount = make(map[state.Port]int)
148 initialPorts, err := fw.environ.Ports()
149 if err != nil {
150 return err
151 }
152 // Retrieve ports to be open from state.
153 services, err := fw.st.AllServices()
154 if err != nil {
155 return err
156 }
157 for _, service := range services {
158 if !service.IsExposed() {
159 continue
160 }
161 units, err := service.AllUnits()
162 if err != nil {
163 return err
164 }
165 for _, unit := range units {
166 ports := unit.OpenedPorts()
167 for _, port := range ports {
168 fw.globalPortsFlag[port] = true
169 }
170 }
171 }
172 openedPorts := []state.Port{}
173 for port := range fw.globalPortsFlag {
174 openedPorts = append(openedPorts, port)
175 }
176 // Check which ports to open or to close.
177 toOpen := diff(openedPorts, initialPorts)
178 toClose := diff(initialPorts, openedPorts)
179 if len(toOpen) > 0 {
180 if err := fw.environ.OpenPorts(toOpen); err != nil {
181 return err
182 }
183 state.SortPorts(toOpen)
184 log.Printf("firewaller: initially opened ports %v in environment", toOpen)
185 }
186 if len(toClose) > 0 {
187 if err := fw.environ.ClosePorts(toClose); err != nil {
188 return err
189 }
190 state.SortPorts(toClose)
191 log.Printf("firewaller: initially closed ports %v in environment", toClose)
192 }
193 return nil
194}
195
132// flushUnits opens and closes ports for the passed unit data.196// flushUnits opens and closes ports for the passed unit data.
133func (fw *Firewaller) flushUnits(unitds []*unitData) error {197func (fw *Firewaller) flushUnits(unitds []*unitData) error {
134 machineds := map[int]*machineData{}198 machineds := map[int]*machineData{}
@@ -161,7 +225,55 @@
161 toOpen := diff(want, machined.ports)225 toOpen := diff(want, machined.ports)
162 toClose := diff(machined.ports, want)226 toClose := diff(machined.ports, want)
163 machined.ports = want227 machined.ports = want
164228 if fw.globalMode {
229 return fw.flushGlobalPorts(toOpen, toClose)
230 }
231 return fw.flushInstancePorts(machined, toOpen, toClose)
232}
233
234// flushGlobalPorts opens and closes global ports in the environment.
235// It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events
236// modify the environment.
237func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []state.Port) error {
238 // Filter which ports are really to open or close.
239 var toOpen, toClose []state.Port
240 for _, port := range rawOpen {
241 if !fw.globalPortsFlag[port] {
242 toOpen = append(toOpen, port)
243 fw.globalPortsFlag[port] = true
244 }
245 fw.globalPortsCount[port]++
246 }
247 for _, port := range rawClose {
248 fw.globalPortsCount[port]--
249 if fw.globalPortsCount[port] == 0 {
250 toClose = append(toClose, port)
251 delete(fw.globalPortsFlag, port)
252 delete(fw.globalPortsCount, port)
253 }
254 }
255 // Open and close the ports.
256 if len(toOpen) > 0 {
257 if err := fw.environ.OpenPorts(toOpen); err != nil {
258 // TODO(mue) Add local retry logic.
259 return err
260 }
261 state.SortPorts(toOpen)
262 log.Printf("firewaller: opened ports %v in environment", toOpen)
263 }
264 if len(toClose) > 0 {
265 if err := fw.environ.ClosePorts(toClose); err != nil {
266 // TODO(mue) Add local retry logic.
267 return err
268 }
269 state.SortPorts(toClose)
270 log.Printf("firewaller: closed ports %v in environment", toClose)
271 }
272 return nil
273}
274
275// flushGlobalPorts opens and closes ports global on the machine.
276func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []state.Port) error {
165 // If there's nothing to do, do nothing.277 // If there's nothing to do, do nothing.
166 // This is important because when a machine is first created,278 // This is important because when a machine is first created,
167 // it will have no instance id but also no open ports -279 // it will have no instance id but also no open ports -
@@ -169,7 +281,6 @@
169 if len(toOpen) == 0 && len(toClose) == 0 {281 if len(toOpen) == 0 && len(toClose) == 0 {
170 return nil282 return nil
171 }283 }
172 // Open and close the ports.
173 m, err := machined.machine()284 m, err := machined.machine()
174 if state.IsNotFound(err) {285 if state.IsNotFound(err) {
175 return nil286 return nil
@@ -185,9 +296,9 @@
185 if err != nil {296 if err != nil {
186 return err297 return err
187 }298 }
299 // Open and close the ports.
188 if len(toOpen) > 0 {300 if len(toOpen) > 0 {
189 err = instances[0].OpenPorts(machined.id, toOpen)301 if err := instances[0].OpenPorts(machined.id, toOpen); err != nil {
190 if err != nil {
191 // TODO(mue) Add local retry logic.302 // TODO(mue) Add local retry logic.
192 return err303 return err
193 }304 }
@@ -195,8 +306,7 @@
195 log.Printf("firewaller: opened ports %v on machine %d", toOpen, machined.id)306 log.Printf("firewaller: opened ports %v on machine %d", toOpen, machined.id)
196 }307 }
197 if len(toClose) > 0 {308 if len(toClose) > 0 {
198 err = instances[0].ClosePorts(machined.id, toClose)309 if err := instances[0].ClosePorts(machined.id, toClose); err != nil {
199 if err != nil {
200 // TODO(mue) Add local retry logic.310 // TODO(mue) Add local retry logic.
201 return err311 return err
202 }312 }
203313
=== modified file 'worker/firewaller/firewaller_test.go'
--- worker/firewaller/firewaller_test.go 2012-10-09 08:18:40 +0000
+++ worker/firewaller/firewaller_test.go 2012-10-22 15:47:21 +0000
@@ -3,6 +3,7 @@
3import (3import (
4 . "launchpad.net/gocheck"4 . "launchpad.net/gocheck"
5 "launchpad.net/juju-core/environs"5 "launchpad.net/juju-core/environs"
6 "launchpad.net/juju-core/environs/config"
6 "launchpad.net/juju-core/environs/dummy"7 "launchpad.net/juju-core/environs/dummy"
7 "launchpad.net/juju-core/juju/testing"8 "launchpad.net/juju-core/juju/testing"
8 "launchpad.net/juju-core/state"9 "launchpad.net/juju-core/state"
@@ -49,6 +50,32 @@
49 panic("unreachable")50 panic("unreachable")
50}51}
5152
53// assertEnvironPorts retrieves the open ports of environment and compares them
54// to the expected.
55func (s *FirewallerSuite) assertEnvironPorts(c *C, expected []state.Port) {
56 s.State.StartSync()
57 start := time.Now()
58 for {
59 got, err := s.Conn.Environ.Ports()
60 if err != nil {
61 c.Fatal(err)
62 return
63 }
64 state.SortPorts(got)
65 state.SortPorts(expected)
66 if reflect.DeepEqual(got, expected) {
67 c.Succeed()
68 return
69 }
70 if time.Since(start) > 5*time.Second {
71 c.Fatalf("timed out: expected %q; got %q", expected, got)
72 return
73 }
74 time.Sleep(50 * time.Millisecond)
75 }
76 panic("unreachable")
77}
78
52var _ = Suite(&FirewallerSuite{})79var _ = Suite(&FirewallerSuite{})
5380
54func (s *FirewallerSuite) SetUpTest(c *C) {81func (s *FirewallerSuite) SetUpTest(c *C) {
@@ -72,6 +99,32 @@
72 return u, m99 return u, m
73}100}
74101
102func (s *FirewallerSuite) setGlobalMode(c *C) func(*C) {
103 oldConfig := s.Conn.Environ.Config()
104 restore := func(rc *C) {
105 attrs := oldConfig.AllAttrs()
106 attrs["admin-secret"] = ""
107 oldConfig, err := oldConfig.Apply(attrs)
108 rc.Assert(err, IsNil)
109 err = s.Conn.Environ.SetConfig(oldConfig)
110 rc.Assert(err, IsNil)
111 err = s.State.SetEnvironConfig(oldConfig)
112 rc.Assert(err, IsNil)
113 }
114
115 attrs := s.Conn.Environ.Config().AllAttrs()
116 attrs["firewall-mode"] = config.FwGlobal
117 attrs["admin-secret"] = ""
118 newConfig, err := s.Conn.Environ.Config().Apply(attrs)
119 c.Assert(err, IsNil)
120 err = s.Conn.Environ.SetConfig(newConfig)
121 c.Assert(err, IsNil)
122 err = s.State.SetEnvironConfig(newConfig)
123 c.Assert(err, IsNil)
124
125 return restore
126}
127
75// startInstance starts a new instance for the given machine.128// startInstance starts a new instance for the given machine.
76func (s *FirewallerSuite) startInstance(c *C, m *state.Machine) environs.Instance {129func (s *FirewallerSuite) startInstance(c *C, m *state.Machine) environs.Instance {
77 inst, err := s.Conn.Environ.StartInstance(m.Id(), testing.InvalidStateInfo(m.Id()), nil)130 inst, err := s.Conn.Environ.StartInstance(m.Id(), testing.InvalidStateInfo(m.Id()), nil)
@@ -482,3 +535,184 @@
482 err = s.State.RemoveMachine(m.Id())535 err = s.State.RemoveMachine(m.Id())
483 c.Assert(err, IsNil)536 c.Assert(err, IsNil)
484}537}
538
539func (s *FirewallerSuite) TestGlobalMode(c *C) {
540 // Change configuration.
541 restore := s.setGlobalMode(c)
542 defer restore(c)
543
544 // Start firewall and open ports.
545 fw := firewaller.NewFirewaller(s.State)
546 defer func() { c.Assert(fw.Stop(), IsNil) }()
547
548 svc1, err := s.State.AddService("wordpress", s.charm)
549 c.Assert(err, IsNil)
550 err = svc1.SetExposed()
551 c.Assert(err, IsNil)
552
553 u1, m1 := s.addUnit(c, svc1)
554 s.startInstance(c, m1)
555 err = u1.OpenPort("tcp", 80)
556 c.Assert(err, IsNil)
557 err = u1.OpenPort("tcp", 8080)
558 c.Assert(err, IsNil)
559
560 svc2, err := s.State.AddService("moinmoin", s.charm)
561 c.Assert(err, IsNil)
562 err = svc2.SetExposed()
563 c.Assert(err, IsNil)
564
565 u2, m2 := s.addUnit(c, svc2)
566 s.startInstance(c, m2)
567 err = u2.OpenPort("tcp", 80)
568 c.Assert(err, IsNil)
569
570 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
571
572 // Closing a port opened by a different unit won't touch the environment.
573 err = u1.ClosePort("tcp", 80)
574 c.Assert(err, IsNil)
575 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
576
577 // Closing a port used just once changes the environment.
578 err = u1.ClosePort("tcp", 8080)
579 c.Assert(err, IsNil)
580 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}})
581
582 // Closing the last port also modifies the environment.
583 err = u2.ClosePort("tcp", 80)
584 c.Assert(err, IsNil)
585 s.assertEnvironPorts(c, nil)
586}
587
588func (s *FirewallerSuite) TestGlobalModeRestart(c *C) {
589 // Change configuration.
590 restore := s.setGlobalMode(c)
591 defer restore(c)
592
593 // Start firewall and open ports.
594 fw := firewaller.NewFirewaller(s.State)
595
596 svc, err := s.State.AddService("wordpress", s.charm)
597 c.Assert(err, IsNil)
598 err = svc.SetExposed()
599 c.Assert(err, IsNil)
600
601 u, m := s.addUnit(c, svc)
602 s.startInstance(c, m)
603 err = u.OpenPort("tcp", 80)
604 c.Assert(err, IsNil)
605 err = u.OpenPort("tcp", 8080)
606 c.Assert(err, IsNil)
607
608 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
609
610 // Stop firewall and close one and open a different port.
611 err = fw.Stop()
612 c.Assert(err, IsNil)
613
614 err = u.ClosePort("tcp", 8080)
615 c.Assert(err, IsNil)
616 err = u.OpenPort("tcp", 8888)
617 c.Assert(err, IsNil)
618
619 // Start firewall and check port.
620 fw = firewaller.NewFirewaller(s.State)
621 defer func() { c.Assert(fw.Stop(), IsNil) }()
622
623 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8888}})
624}
625
626func (s *FirewallerSuite) TestGlobalModeRestartUnexposedService(c *C) {
627 // Change configuration.
628 restore := s.setGlobalMode(c)
629 defer restore(c)
630
631 // Start firewall and open ports.
632 fw := firewaller.NewFirewaller(s.State)
633
634 svc, err := s.State.AddService("wordpress", s.charm)
635 c.Assert(err, IsNil)
636 err = svc.SetExposed()
637 c.Assert(err, IsNil)
638
639 u, m := s.addUnit(c, svc)
640 s.startInstance(c, m)
641 err = u.OpenPort("tcp", 80)
642 c.Assert(err, IsNil)
643 err = u.OpenPort("tcp", 8080)
644 c.Assert(err, IsNil)
645
646 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
647
648 // Stop firewall and clear exposed flag on service.
649 err = fw.Stop()
650 c.Assert(err, IsNil)
651
652 err = svc.ClearExposed()
653 c.Assert(err, IsNil)
654
655 // Start firewall and check port.
656 fw = firewaller.NewFirewaller(s.State)
657 defer func() { c.Assert(fw.Stop(), IsNil) }()
658
659 s.assertEnvironPorts(c, nil)
660}
661
662func (s *FirewallerSuite) TestGlobalModeRestartPortCount(c *C) {
663 // Change configuration.
664 restore := s.setGlobalMode(c)
665 defer restore(c)
666
667 // Start firewall and open ports.
668 fw := firewaller.NewFirewaller(s.State)
669
670 svc1, err := s.State.AddService("wordpress", s.charm)
671 c.Assert(err, IsNil)
672 err = svc1.SetExposed()
673 c.Assert(err, IsNil)
674
675 u1, m1 := s.addUnit(c, svc1)
676 s.startInstance(c, m1)
677 err = u1.OpenPort("tcp", 80)
678 c.Assert(err, IsNil)
679 err = u1.OpenPort("tcp", 8080)
680 c.Assert(err, IsNil)
681
682 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
683
684 // Stop firewall and add another service using the port.
685 err = fw.Stop()
686 c.Assert(err, IsNil)
687
688 svc2, err := s.State.AddService("moinmoin", s.charm)
689 c.Assert(err, IsNil)
690 err = svc2.SetExposed()
691 c.Assert(err, IsNil)
692
693 u2, m2 := s.addUnit(c, svc2)
694 s.startInstance(c, m2)
695 err = u2.OpenPort("tcp", 80)
696 c.Assert(err, IsNil)
697
698 // Start firewall and check port.
699 fw = firewaller.NewFirewaller(s.State)
700 defer func() { c.Assert(fw.Stop(), IsNil) }()
701
702 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
703
704 // Closing a port opened by a different unit won't touch the environment.
705 err = u1.ClosePort("tcp", 80)
706 c.Assert(err, IsNil)
707 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}, {"tcp", 8080}})
708
709 // Closing a port used just once changes the environment.
710 err = u1.ClosePort("tcp", 8080)
711 c.Assert(err, IsNil)
712 s.assertEnvironPorts(c, []state.Port{{"tcp", 80}})
713
714 // Closing the last port also modifies the environment.
715 err = u2.ClosePort("tcp", 80)
716 c.Assert(err, IsNil)
717 s.assertEnvironPorts(c, nil)
718}

Subscribers

People subscribed via source and target branches