Merge lp:~mvo/snappy/15.04-watchdog-config into lp:~snappy-dev/snappy/15.04-deprecated

Proposed by Michael Vogt
Status: Superseded
Proposed branch: lp:~mvo/snappy/15.04-watchdog-config
Merge into: lp:~snappy-dev/snappy/15.04-deprecated
Diff against target: 613 lines (+429/-3)
2 files modified
coreconfig/config.go (+186/-3)
coreconfig/config_test.go (+243/-0)
To merge this branch: bzr merge lp:~mvo/snappy/15.04-watchdog-config
Reviewer Review Type Date Requested Status
Snappy Developers Pending
Review via email: mp+270556@code.launchpad.net

This proposal has been superseded by a proposal from 2015-09-10.

Description of the change

Enable watchdog configuration via ubuntu-core-config

To post a comment you must log in.
lp:~mvo/snappy/15.04-watchdog-config updated
473. By Michael Vogt

merged parent

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'coreconfig/config.go'
2--- coreconfig/config.go 2015-04-14 16:46:57 +0000
3+++ coreconfig/config.go 2015-09-09 15:59:02 +0000
4@@ -22,6 +22,7 @@
5 "io/ioutil"
6 "os"
7 "os/exec"
8+ "path/filepath"
9 "reflect"
10 "strings"
11 "syscall"
12@@ -40,14 +41,36 @@
13 autopilotTimerDisabled string = "disabled"
14 )
15
16+var (
17+ modprobePath string = "/etc/modprobe.d/ubuntu-core.conf"
18+ networkRoot string = "/etc/network/interfaces.d/"
19+ pppRoot string = "/etc/ppp/"
20+ watchdogConfigPath string = "/etc/watchdog.conf"
21+ watchdogStartupPath string = "/etc/default/watchdog"
22+)
23+
24 // ErrInvalidUnitStatus signals that a unit is not returning a status
25 // of "enabled" or "disabled".
26 var ErrInvalidUnitStatus = errors.New("invalid unit status")
27
28 type systemConfig struct {
29- Autopilot *bool `yaml:"autopilot,omitempty"`
30- Timezone *string `yaml:"timezone,omitempty"`
31- Hostname *string `yaml:"hostname,omitempty"`
32+ Autopilot *bool `yaml:"autopilot,omitempty"`
33+ Timezone *string `yaml:"timezone,omitempty"`
34+ Hostname *string `yaml:"hostname,omitempty"`
35+ Modprobe *string `yaml:"modprobe,omitempty"`
36+ Network []passthroughConfig `yaml:"network,omitempty"`
37+ PPP []passthroughConfig `yaml:"ppp,omitempty"`
38+ Watchdog *watchdogConfig `yaml:"watchdog,omitempty"`
39+}
40+
41+type passthroughConfig struct {
42+ Name string `yaml:"name,omitempty"`
43+ Content string `yaml:"content,omitempty"`
44+}
45+
46+type watchdogConfig struct {
47+ Startup string `yaml:"startup,omitempty"`
48+ Config string `yaml:"config,omitempty"`
49 }
50
51 type coreConfig struct {
52@@ -73,11 +96,31 @@
53 if err != nil {
54 return nil, err
55 }
56+ modprobe, err := getModprobe()
57+ if err != nil {
58+ return nil, err
59+ }
60+ network, err := getNetwork()
61+ if err != nil {
62+ return nil, err
63+ }
64+ ppp, err := getPPP()
65+ if err != nil {
66+ return nil, err
67+ }
68+ watchdog, err := getWatchdog()
69+ if err != nil {
70+ return nil, err
71+ }
72
73 config := &systemConfig{
74 Autopilot: &autopilot,
75 Timezone: &tz,
76 Hostname: &hostname,
77+ Modprobe: &modprobe,
78+ Network: network,
79+ PPP: ppp,
80+ Watchdog: watchdog,
81 }
82
83 return config, nil
84@@ -103,6 +146,18 @@
85 return string(out), nil
86 }
87
88+func passthroughEqual(a, b []passthroughConfig) bool {
89+ if len(a) != len(b) {
90+ return false
91+ }
92+ for i, v := range a {
93+ if v != b[i] {
94+ return false
95+ }
96+ }
97+ return true
98+}
99+
100 // Set is used to configure settings for the system, this is meant to
101 // be used as an interface for snappy config to satisfy the ubuntu-core
102 // hook.
103@@ -151,6 +206,38 @@
104 if err := setHostname(*newConfig.Hostname); err != nil {
105 return "", err
106 }
107+ case "Modprobe":
108+ if *oldConfig.Modprobe == *newConfig.Modprobe {
109+ continue
110+ }
111+
112+ if err := setModprobe(*newConfig.Modprobe); err != nil {
113+ return "", err
114+ }
115+ case "Network":
116+ if passthroughEqual(oldConfig.Network, newConfig.Network) {
117+ continue
118+ }
119+
120+ if err := setNetwork(newConfig.Network); err != nil {
121+ return "", err
122+ }
123+ case "PPP":
124+ if passthroughEqual(oldConfig.PPP, newConfig.PPP) {
125+ continue
126+ }
127+
128+ if err := setPPP(newConfig.PPP); err != nil {
129+ return "", err
130+ }
131+ case "Watchdog":
132+ if oldConfig.Watchdog != nil && *oldConfig.Watchdog == *newConfig.Watchdog {
133+ continue
134+ }
135+
136+ if err := setWatchdog(newConfig.Watchdog); err != nil {
137+ return "", err
138+ }
139 }
140 }
141
142@@ -184,6 +271,102 @@
143 return ioutil.WriteFile(tzFile(), []byte(timezone), 0644)
144 }
145
146+func getPassthrough(rootDir string) (pc []passthroughConfig, err error) {
147+ filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
148+ if info.IsDir() {
149+ return nil
150+ }
151+ content, err := ioutil.ReadFile(path)
152+ if err != nil {
153+ return err
154+ }
155+ pc = append(pc, passthroughConfig{
156+ Name: path[len(rootDir)+1:],
157+ Content: string(content),
158+ })
159+ return nil
160+ })
161+
162+ return pc, nil
163+
164+}
165+func setPassthrough(rootDir string, pc []passthroughConfig) error {
166+ for _, c := range pc {
167+ path := filepath.Join(rootDir, c.Name)
168+ if c.Content == "" {
169+ os.Remove(path)
170+ continue
171+ }
172+
173+ if err := ioutil.WriteFile(path, []byte(c.Content), 0644); err != nil {
174+ return err
175+ }
176+ }
177+
178+ return nil
179+}
180+
181+var getNetwork = func() (pc []passthroughConfig, err error) {
182+ return getPassthrough(networkRoot)
183+}
184+
185+var setNetwork = func(pc []passthroughConfig) error {
186+ return setPassthrough(networkRoot, pc)
187+}
188+
189+var getPPP = func() (pc []passthroughConfig, err error) {
190+ return getPassthrough(pppRoot)
191+}
192+
193+var setPPP = func(pc []passthroughConfig) error {
194+ return setPassthrough(pppRoot, pc)
195+}
196+
197+// getModprobe returns the current modprobe config
198+var getModprobe = func() (string, error) {
199+ modprobe, err := ioutil.ReadFile(modprobePath)
200+ if err != nil && !os.IsNotExist(err) {
201+ return "", err
202+ }
203+
204+ return string(modprobe), nil
205+}
206+
207+// setModprobe sets the specified modprobe config
208+var setModprobe = func(modprobe string) error {
209+ return ioutil.WriteFile(modprobePath, []byte(modprobe), 0644)
210+}
211+
212+// getWatchdog returns the current watchdog config
213+var getWatchdog = func() (*watchdogConfig, error) {
214+ startup, err := ioutil.ReadFile(watchdogStartupPath)
215+ if err != nil && !os.IsNotExist(err) {
216+ return nil, err
217+ }
218+ config, err := ioutil.ReadFile(watchdogConfigPath)
219+ if err != nil && !os.IsNotExist(err) {
220+ return nil, err
221+ }
222+
223+ // nil to make the yaml empty
224+ if len(startup) == 0 && len(config) == 0 {
225+ return nil, nil
226+ }
227+
228+ return &watchdogConfig{
229+ Startup: string(startup),
230+ Config: string(config)}, nil
231+}
232+
233+// setWatchdog sets the specified watchdog config
234+var setWatchdog = func(wf *watchdogConfig) error {
235+ if err := ioutil.WriteFile(watchdogStartupPath, []byte(wf.Startup), 0644); err != nil {
236+ return err
237+ }
238+
239+ return ioutil.WriteFile(watchdogConfigPath, []byte(wf.Config), 0644)
240+}
241+
242 // for testing purposes
243 var (
244 cmdAutopilotEnabled = []string{"is-enabled", autopilotTimer}
245
246=== modified file 'coreconfig/config_test.go'
247--- coreconfig/config_test.go 2015-08-02 12:21:07 +0000
248+++ coreconfig/config_test.go 2015-09-09 15:59:02 +0000
249@@ -25,6 +25,7 @@
250 "testing"
251
252 . "gopkg.in/check.v1"
253+ "launchpad.net/snappy/helpers"
254 )
255
256 // Hook up check.v1 into the "go test" runner.
257@@ -46,6 +47,11 @@
258 originalCmdAutopilotEnabled = cmdAutopilotEnabled
259 originalCmdSystemctl = cmdSystemctl
260 originalHostnamePath = hostnamePath
261+ originalModprobePath = modprobePath
262+ originalNetworkRoot = networkRoot
263+ originalPppRoot = pppRoot
264+ originalWatchdogStartupPath = watchdogStartupPath
265+ originalWatchdogConfigPath = watchdogConfigPath
266 )
267
268 type ConfigTestSuite struct {
269@@ -72,6 +78,11 @@
270 hostname = host
271 return nil
272 }
273+
274+ networkRoot = c.MkDir()
275+ pppRoot = c.MkDir()
276+ watchdogConfigPath = filepath.Join(c.MkDir(), "watchdog-config")
277+ watchdogStartupPath = filepath.Join(c.MkDir(), "watchdog-startup")
278 }
279
280 func (cts *ConfigTestSuite) TearDownTest(c *C) {
281@@ -90,6 +101,11 @@
282 cmdStopAutopilot = originalCmdStopAutopilot
283 cmdAutopilotEnabled = originalCmdAutopilotEnabled
284 cmdSystemctl = originalCmdSystemctl
285+ modprobePath = originalModprobePath
286+ networkRoot = originalNetworkRoot
287+ pppRoot = originalPppRoot
288+ watchdogStartupPath = originalWatchdogStartupPath
289+ watchdogConfigPath = originalWatchdogConfigPath
290 }
291
292 // TestGet is a broad test, close enough to be an integration test for
293@@ -101,6 +117,7 @@
294 autopilot: false
295 timezone: America/Argentina/Cordoba
296 hostname: testhost
297+ modprobe: ""
298 `
299
300 rawConfig, err := Get()
301@@ -116,6 +133,7 @@
302 autopilot: true
303 timezone: America/Argentina/Mendoza
304 hostname: testhost
305+ modprobe: ""
306 `
307
308 cmdAutopilotEnabled = []string{"-c", "echo enabled"}
309@@ -132,6 +150,7 @@
310 autopilot: false
311 timezone: America/Argentina/Mendoza
312 hostname: testhost
313+ modprobe: ""
314 `
315
316 rawConfig, err := Set(expected)
317@@ -147,6 +166,7 @@
318 autopilot: true
319 timezone: America/Argentina/Cordoba
320 hostname: testhost
321+ modprobe: ""
322 `
323
324 enabled := false
325@@ -165,6 +185,7 @@
326 autopilot: false
327 timezone: America/Argentina/Cordoba
328 hostname: NEWtesthost
329+ modprobe: ""
330 `
331
332 rawConfig, err := Set(expected)
333@@ -178,6 +199,7 @@
334 autopilot: false
335 timezone America/Argentina/Mendoza
336 hostname: testhost
337+ modprobe: ""
338 `
339
340 rawConfig, err := Set(input)
341@@ -191,6 +213,7 @@
342 autopilot: false
343 timezone: America/Argentina/Cordoba
344 hostname: testhost
345+ modprobe: ""
346 `
347
348 rawConfig, err := Set(input)
349@@ -204,12 +227,14 @@
350 autopilot: false
351 timezone: America/Argentina/Cordoba
352 hostname: testhost
353+ modprobe: ""
354 `
355
356 input := `config:
357 ubuntu-core:
358 autopilot: false
359 timezone: America/Argentina/Cordoba
360+ modprobe: ""
361 `
362
363 rawConfig, err := Set(input)
364@@ -247,6 +272,7 @@
365 autopilot: false
366 timezone: America/Argentina/Mendoza
367 hostname: testhost
368+ modprobe: ""
369 `
370
371 rawConfig, err := Set(input)
372@@ -268,6 +294,7 @@
373 autopilot: true
374 timezone: America/Argentina/Mendoza
375 hostname: testhost
376+ modprobe: ""
377 `
378
379 enabled := false
380@@ -285,6 +312,7 @@
381 autopilot: false
382 timezone: America/Argentina/Cordoba
383 hostname: NEWtesthost
384+ modprobe: ""
385 `
386
387 setHostname = func(string) error { return errors.New("this is bad") }
388@@ -300,6 +328,7 @@
389 autopilot: false
390 timezone: America/Argentina/Cordoba
391 hostname: NEWtesthost
392+ modprobe: ""
393 `
394
395 getHostname = func() (string, error) { return "", errors.New("this is bad") }
396@@ -396,3 +425,217 @@
397 err := setHostname("newhostname")
398 c.Assert(err, DeepEquals, expectedErr)
399 }
400+
401+func (cts *ConfigTestSuite) TestModprobe(c *C) {
402+ modprobePath = filepath.Join(c.MkDir(), "test.conf")
403+
404+ err := setModprobe("blacklist floppy")
405+ c.Assert(err, IsNil)
406+
407+ modprobe, err := getModprobe()
408+ c.Assert(err, IsNil)
409+ c.Assert(modprobe, Equals, "blacklist floppy")
410+}
411+
412+func (cts *ConfigTestSuite) TestModprobeYaml(c *C) {
413+ modprobePath = filepath.Join(c.MkDir(), "test.conf")
414+
415+ input := `config:
416+ ubuntu-core:
417+ modprobe: |
418+ blacklist floppy
419+ softdep mlx4_core post: mlx4_en
420+`
421+ _, err := Set(input)
422+ c.Assert(err, IsNil)
423+
424+ // ensure its really there
425+ content, err := ioutil.ReadFile(modprobePath)
426+ c.Assert(err, IsNil)
427+ c.Assert(string(content), Equals, "blacklist floppy\nsoftdep mlx4_core post: mlx4_en\n")
428+}
429+
430+func (cts *ConfigTestSuite) TestNetworkGet(c *C) {
431+ path := filepath.Join(networkRoot, "eth0")
432+ content := "auto eth0"
433+ err := ioutil.WriteFile(path, []byte(content), 0644)
434+ c.Assert(err, IsNil)
435+
436+ nc, err := getNetwork()
437+ c.Assert(err, IsNil)
438+ c.Assert(nc, DeepEquals, []passthroughConfig{
439+ {Name: "eth0", Content: "auto eth0"},
440+ })
441+}
442+
443+func (cts *ConfigTestSuite) TestNetworkSet(c *C) {
444+ nc := []passthroughConfig{
445+ {Name: "eth0", Content: "auto eth0"},
446+ }
447+ path := filepath.Join(networkRoot, nc[0].Name)
448+ err := setNetwork(nc)
449+ c.Assert(err, IsNil)
450+ content, err := ioutil.ReadFile(path)
451+ c.Assert(err, IsNil)
452+ c.Assert(string(content), Equals, nc[0].Content)
453+}
454+
455+func (cts *ConfigTestSuite) TestNetworkSetEmptyRemoves(c *C) {
456+ path := filepath.Join(networkRoot, "eth0")
457+ content := "auto eth0"
458+ err := ioutil.WriteFile(path, []byte(content), 0644)
459+ c.Assert(err, IsNil)
460+
461+ // empty content removes
462+ nc := []passthroughConfig{
463+ {Name: "eth0", Content: ""},
464+ }
465+ err = setNetwork(nc)
466+ c.Assert(err, IsNil)
467+ _, err = ioutil.ReadFile(path)
468+ c.Assert(helpers.FileExists(path), Equals, false)
469+}
470+
471+func (cts *ConfigTestSuite) TestPppGet(c *C) {
472+ path := filepath.Join(pppRoot, "chap-secrets")
473+ content := "password"
474+ err := ioutil.WriteFile(path, []byte(content), 0644)
475+ c.Assert(err, IsNil)
476+
477+ nc, err := getPPP()
478+ c.Assert(err, IsNil)
479+ c.Assert(nc, DeepEquals, []passthroughConfig{
480+ {Name: "chap-secrets", Content: "password"},
481+ })
482+}
483+
484+func (cts *ConfigTestSuite) TestPppSet(c *C) {
485+ nc := []passthroughConfig{
486+ {Name: "chap-secrets", Content: "another secret"},
487+ }
488+ path := filepath.Join(pppRoot, nc[0].Name)
489+ err := setPPP(nc)
490+ c.Assert(err, IsNil)
491+ content, err := ioutil.ReadFile(path)
492+ c.Assert(err, IsNil)
493+ c.Assert(string(content), Equals, nc[0].Content)
494+}
495+
496+func (cts *ConfigTestSuite) TestNetworkSetViaYaml(c *C) {
497+ modprobePath = filepath.Join(c.MkDir(), "test.conf")
498+
499+ input := `
500+config:
501+ ubuntu-core:
502+ network:
503+ - name: eth0
504+ content: auto dhcp
505+`
506+ _, err := Set(input)
507+ c.Assert(err, IsNil)
508+
509+ // ensure its really there
510+ content, err := ioutil.ReadFile(filepath.Join(networkRoot, "eth0"))
511+ c.Assert(err, IsNil)
512+ c.Assert(string(content), Equals, "auto dhcp")
513+}
514+
515+func (cts *ConfigTestSuite) TestPPPSetViaYaml(c *C) {
516+ modprobePath = filepath.Join(c.MkDir(), "test.conf")
517+
518+ input := `
519+config:
520+ ubuntu-core:
521+ ppp:
522+ - name: chap-secret
523+ content: password
524+`
525+ _, err := Set(input)
526+ c.Assert(err, IsNil)
527+
528+ // ensure its really there
529+ content, err := ioutil.ReadFile(filepath.Join(pppRoot, "chap-secret"))
530+ c.Assert(err, IsNil)
531+ c.Assert(string(content), Equals, "password")
532+}
533+
534+func (cts *ConfigTestSuite) TestPassthroughConfigEqual(c *C) {
535+ a := []passthroughConfig{
536+ {Name: "key", Content: "value"},
537+ }
538+ b := []passthroughConfig{
539+ {Name: "key", Content: "value"},
540+ }
541+ c.Assert(passthroughEqual(a, b), Equals, true)
542+}
543+
544+func (cts *ConfigTestSuite) TestPassthroughConfigNotEqualDifferentSize(c *C) {
545+ a := []passthroughConfig{}
546+ b := []passthroughConfig{
547+ {Name: "key", Content: "value"},
548+ }
549+ c.Assert(passthroughEqual(a, b), Equals, false)
550+}
551+
552+func (cts *ConfigTestSuite) TestPassthroughConfigNotEqualDifferentKeys(c *C) {
553+ a := []passthroughConfig{
554+ {Name: "key", Content: "value"},
555+ }
556+ b := []passthroughConfig{
557+ {Name: "other-key", Content: "value"},
558+ }
559+ c.Assert(passthroughEqual(a, b), Equals, false)
560+}
561+
562+func (cts *ConfigTestSuite) TestWatchdogGet(c *C) {
563+ startup := "# some startup watchdog config"
564+ err := ioutil.WriteFile(watchdogStartupPath, []byte(startup), 0644)
565+ c.Assert(err, IsNil)
566+
567+ config := "# some watchdog config"
568+ err = ioutil.WriteFile(watchdogConfigPath, []byte(config), 0644)
569+ c.Assert(err, IsNil)
570+
571+ wc, err := getWatchdog()
572+ c.Assert(err, IsNil)
573+ c.Assert(wc, DeepEquals, &watchdogConfig{
574+ Startup: startup, Config: config,
575+ })
576+}
577+
578+func (cts *ConfigTestSuite) TestWatchdogSet(c *C) {
579+ wc := &watchdogConfig{
580+ Startup: "startup", Config: "secret",
581+ }
582+ err := setWatchdog(wc)
583+ c.Assert(err, IsNil)
584+
585+ content, err := ioutil.ReadFile(watchdogStartupPath)
586+ c.Assert(err, IsNil)
587+ c.Assert(string(content), Equals, wc.Startup)
588+
589+ content, err = ioutil.ReadFile(watchdogConfigPath)
590+ c.Assert(err, IsNil)
591+ c.Assert(string(content), Equals, wc.Config)
592+}
593+
594+func (cts *ConfigTestSuite) TestWatchdogSetViaYaml(c *C) {
595+ input := `
596+config:
597+ ubuntu-core:
598+ watchdog:
599+ startup: some startup
600+ config: some config
601+`
602+ _, err := Set(input)
603+ c.Assert(err, IsNil)
604+
605+ // ensure its really there
606+ content, err := ioutil.ReadFile(watchdogStartupPath)
607+ c.Assert(err, IsNil)
608+ c.Assert(string(content), Equals, "some startup")
609+
610+ content, err = ioutil.ReadFile(watchdogConfigPath)
611+ c.Assert(err, IsNil)
612+ c.Assert(string(content), Equals, "some config")
613+}

Subscribers

People subscribed via source and target branches