Merge lp:~dave-cheney/juju-core/go-cmd-jujud-firewaller into lp:~juju/juju-core/trunk

Proposed by Dave Cheney
Status: Rejected
Rejected by: Gustavo Niemeyer
Proposed branch: lp:~dave-cheney/juju-core/go-cmd-jujud-firewaller
Merge into: lp:~juju/juju-core/trunk
Diff against target: 209 lines (+194/-0)
3 files modified
worker/firewaller/export_test.go (+7/-0)
worker/firewaller/firewaller.go (+72/-0)
worker/firewaller/firewaller_test.go (+115/-0)
To merge this branch: bzr merge lp:~dave-cheney/juju-core/go-cmd-jujud-firewaller
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+112007@code.launchpad.net

Description of the change

cmd/jujud: add firewaller outline

This proposal adds a skeletal firewaller service to complete the
provisioning agent. The firewaller only reacts to changes in the
environment configuration at the moment because it needs the following
(unwritten) pieces:

* the ability to watch all services
* an interface into firewalling services in the environs.Environ interface

https://codereview.appspot.com/6333067/

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

A few thoughts/questions...

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go
File cmd/jujud/firewall.go (right):

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode13
cmd/jujud/firewall.go:13: type Firewaller struct {
A comment would be good, but maybe it's redundant at this stage.
("Firewaller doesn't do anything yet"? ;))

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode24
cmd/jujud/firewall.go:24: st, err := state.Open(info)
Why do we need a whole new *State for a single component? It feels
somewhat wasteful at best, and at worst actively dangerous (surely,
given that 2 separate connections will not necessarily have remotely
consistent views of state, any non-zk-mediated data flow between
components with separate connections is potentially problematic...).

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode38
cmd/jujud/firewall.go:38: defer f.st.Close()
defer f.environWatcher.Stop()

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode50
cmd/jujud/firewall.go:50: return
Equivalent of `f.tomb.Kill(mustErr(f.environWatcher))` (see
state/watcher.go) would I think be better.

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall_test.go
File cmd/jujud/firewall_test.go (right):

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall_test.go#newcode28
cmd/jujud/firewall_test.go:28: _, err = env.Write()
I don't see what this is actually testing... surely we should at least
check the log messages?

https://codereview.appspot.com/6333067/

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

Looks like a nice bootstrap.

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go
File cmd/jujud/firewall.go (right):

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode24
cmd/jujud/firewall.go:24: st, err := state.Open(info)
On 2012/06/26 15:58:56, fwereade wrote:
> Why do we need a whole new *State for a single component? It feels
somewhat
> wasteful at best, and at worst actively dangerous (surely, given that
2 separate
> connections will not necessarily have remotely consistent views of
state, any
> non-zk-mediated data flow between components with separate connections
is
> potentially problematic...).

Agreed. It seems sensible for the firewaller to get a *State instead.

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode53
cmd/jujud/firewall.go:53: f.environ, err =
environs.NewEnviron(config.Map())
Why do we need to watch the environment in the firewaller? Sorry if it's
obvious and I'm missing it.

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode55
cmd/jujud/firewall.go:55: log.Printf("firewaller loaded invalid
environment configuration: %v", err)
If this happens f.environ would be broken at this point (nil), and thus
anything else that depends on it would be dead.

https://codereview.appspot.com/6333067/

269. By Dave Cheney

merge from trunk

270. By Dave Cheney

merge from trunk

271. By Dave Cheney

responding to review feedback

272. By Dave Cheney

responding to review feedback

273. By Dave Cheney

final tweaks

Revision history for this message
William Reade (fwereade) wrote :

Not entirely keen on having multiple environs in the PA still, for
similar resons to not wanting multiple States. Thoughts?

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go
File cmd/jujud/firewall.go (right):

https://codereview.appspot.com/6333067/diff/2001/cmd/jujud/firewall.go#newcode53
cmd/jujud/firewall.go:53: f.environ, err =
environs.NewEnviron(config.Map())
On 2012/07/12 23:18:26, dfc wrote:
> On 2012/06/27 00:11:12, niemeyer wrote:
> > Why do we need to watch the environment in the firewaller? Sorry if
it's
> obvious
> > and I'm missing it.

> Because the plan is the Environ will provide the generic interface for
open port
> / close port.

Hmm, I'd been vaguely expecting the PA itself to manage the
environ-watching and make it available to other thing -- do we
absolutely have to have multiple Environs in the PA?

https://codereview.appspot.com/6333067/

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

This was abandoned by Dave, in favor of Frank's recent work.

Unmerged revisions

273. By Dave Cheney

final tweaks

272. By Dave Cheney

responding to review feedback

271. By Dave Cheney

responding to review feedback

270. By Dave Cheney

merge from trunk

269. By Dave Cheney

merge from trunk

268. By Dave Cheney

merge from trunk

267. By Dave Cheney

gofmt

266. By Dave Cheney

responding to review feedback

265. By Dave Cheney

merge from trunk

264. By Dave Cheney

tweaks

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'worker/firewaller'
2=== added file 'worker/firewaller/export_test.go'
3--- worker/firewaller/export_test.go 1970-01-01 00:00:00 +0000
4+++ worker/firewaller/export_test.go 2012-07-12 23:19:42 +0000
5@@ -0,0 +1,7 @@
6+package firewaller
7+
8+// exported so we can manually close the Firewallers underlying
9+// state connection.
10+func (f *Firewaller) CloseState() error {
11+ return f.st.Close()
12+}
13
14=== added file 'worker/firewaller/firewaller.go'
15--- worker/firewaller/firewaller.go 1970-01-01 00:00:00 +0000
16+++ worker/firewaller/firewaller.go 2012-07-12 23:19:42 +0000
17@@ -0,0 +1,72 @@
18+package firewaller
19+
20+import (
21+ "launchpad.net/juju-core/environs"
22+ "launchpad.net/juju-core/log"
23+ "launchpad.net/juju-core/state"
24+ "launchpad.net/juju-core/state/watcher"
25+ "launchpad.net/tomb"
26+)
27+
28+type Firewaller struct {
29+ st *state.State
30+ info *state.Info
31+ environ environs.Environ
32+ tomb tomb.Tomb
33+}
34+
35+// NewFirewaller returns a Firewaller.
36+func NewFirewaller(st *state.State) (*Firewaller, error) {
37+ f := &Firewaller{
38+ st: st,
39+ }
40+ go f.loop()
41+ return f, nil
42+}
43+
44+func (f *Firewaller) loop() {
45+ defer f.tomb.Done()
46+ defer f.st.Close()
47+ environWatcher := f.st.WatchEnvironConfig()
48+ defer watcher.Stop(environWatcher, &f.tomb)
49+
50+refreshState:
51+ for {
52+ select {
53+ case <-f.tomb.Dying():
54+ return
55+ case config, ok := <-environWatcher.Changes():
56+ if !ok {
57+ f.tomb.Kill(watcher.MustErr(environWatcher))
58+ return
59+ }
60+ var err error
61+ f.environ, err = environs.NewEnviron(config.Map())
62+ if err != nil {
63+ log.Printf("firewaller loaded invalid environment configuration: %v", err)
64+ continue
65+ }
66+ log.Printf("firewaller loaded new environment configuration")
67+ break refreshState
68+ }
69+ }
70+
71+ // TODO watch all machines for open/close port actions
72+ for {
73+ select {
74+ case <-f.tomb.Dying():
75+ return
76+ }
77+ }
78+}
79+
80+// Wait waits for the Firewaller to exit.
81+func (f *Firewaller) Wait() error {
82+ return f.tomb.Wait()
83+}
84+
85+// Stop stops the Firewaller and returns any error encountered while stopping.
86+func (f *Firewaller) Stop() error {
87+ f.tomb.Kill(nil)
88+ return f.tomb.Wait()
89+}
90
91=== added file 'worker/firewaller/firewaller_test.go'
92--- worker/firewaller/firewaller_test.go 1970-01-01 00:00:00 +0000
93+++ worker/firewaller/firewaller_test.go 2012-07-12 23:19:42 +0000
94@@ -0,0 +1,115 @@
95+package firewaller_test
96+
97+import (
98+ . "launchpad.net/gocheck"
99+ "launchpad.net/juju-core/environs"
100+ "launchpad.net/juju-core/environs/dummy"
101+ "launchpad.net/juju-core/state"
102+ "launchpad.net/juju-core/state/testing"
103+ coretesting "launchpad.net/juju-core/testing"
104+ "launchpad.net/juju-core/worker/firewaller"
105+ stdtesting "testing"
106+)
107+
108+func TestPackage(t *stdtesting.T) {
109+ coretesting.ZkTestPackage(t)
110+}
111+
112+type FirewallerSuite struct {
113+ coretesting.LoggingSuite
114+ testing.StateSuite
115+ op <-chan dummy.Operation
116+}
117+
118+func (s *FirewallerSuite) SetUpTest(c *C) {
119+ // Create the operations channel with more than enough space
120+ // for those tests that don't listen on it.
121+ op := make(chan dummy.Operation, 500)
122+ dummy.Listen(op)
123+ s.op = op
124+
125+ env, err := environs.NewEnviron(map[string]interface{}{
126+ "type": "dummy",
127+ "zookeeper": true,
128+ "name": "testing",
129+ })
130+ c.Assert(err, IsNil)
131+ err = env.Bootstrap(false)
132+ c.Assert(err, IsNil)
133+
134+ // Sanity check
135+ info, err := env.StateInfo()
136+ c.Assert(err, IsNil)
137+ c.Assert(info, DeepEquals, s.StateInfo(c))
138+
139+ s.StateSuite.SetUpTest(c)
140+}
141+
142+func (s *FirewallerSuite) TearDownTest(c *C) {
143+ dummy.Reset()
144+ s.StateSuite.TearDownTest(c)
145+ s.LoggingSuite.TearDownTest(c)
146+}
147+
148+var _ = Suite(&FirewallerSuite{})
149+
150+// invalidateEnvironment alters the environment configuration
151+// so the ConfigNode returned from the watcher will not pass
152+// validation.
153+func (s *FirewallerSuite) invalidateEnvironment() error {
154+ env, err := s.State.EnvironConfig()
155+ if err != nil {
156+ return err
157+ }
158+ env.Set("name", 1)
159+ _, err = env.Write()
160+ return err
161+}
162+
163+// fixEnvironment undoes the work of invalidateEnvironment.
164+func (s *FirewallerSuite) fixEnvironment() error {
165+ env, err := s.State.EnvironConfig()
166+ if err != nil {
167+ return err
168+ }
169+ env.Set("name", "testing")
170+ _, err = env.Write()
171+ return err
172+}
173+
174+func (s *FirewallerSuite) newState(c *C) *state.State {
175+ st, err := state.Open(s.StateInfo(c))
176+ c.Assert(err, IsNil)
177+ return st
178+}
179+
180+func stopFirewaller(c *C, f *firewaller.Firewaller) {
181+ c.Assert(f.Stop(), IsNil)
182+}
183+
184+func (s *FirewallerSuite) TestFirewallerStartStop(c *C) {
185+ f, err := firewaller.NewFirewaller(s.newState(c))
186+ c.Assert(err, IsNil)
187+ stopFirewaller(c, f)
188+}
189+
190+func (s *FirewallerSuite) TestFirewallerEnvironmentChange(c *C) {
191+ f, err := firewaller.NewFirewaller(s.newState(c))
192+ c.Assert(err, IsNil)
193+ defer stopFirewaller(c, f)
194+ err = s.invalidateEnvironment()
195+ c.Assert(err, IsNil)
196+ err = s.fixEnvironment()
197+ c.Assert(err, IsNil)
198+}
199+
200+func (s *FirewallerSuite) TestFirewallerStopOnStateClose(c *C) {
201+ f, err := firewaller.NewFirewaller(s.newState(c))
202+ c.Assert(err, IsNil)
203+
204+ f.CloseState()
205+
206+ // must use Check to avoid leaking firewallers.
207+ c.Check(f.Wait(), ErrorMatches, ".* zookeeper is closing")
208+ c.Assert(f.Stop(), ErrorMatches, ".* zookeeper is closing")
209+}

Subscribers

People subscribed via source and target branches