Skip to content

Commit

Permalink
config: added ReadAndWatchFile function
Browse files Browse the repository at this point in the history
This function will update configuration whenever a file changes.

This change introduces many data races (actually, it inherits these data
races from howeyc/fsnotify). This issue has been reported as
howeyc/fsnotify#29.

Closes #2.
  • Loading branch information
Francisco Souza committed Feb 6, 2013
1 parent 7b8d16d commit f2f261d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 3 deletions.
51 changes: 48 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package config

import (
"fmt"
"github.com/howeyc/fsnotify"
"io"
"io/ioutil"
"launchpad.net/goyaml"
Expand All @@ -22,6 +23,10 @@ var (
mut sync.RWMutex
)

func readConfigBytes(data []byte, out interface{}) error {
return goyaml.Unmarshal(data, out)
}

// ReadConfigBytes receives a slice of bytes and builds the internal
// configuration object.
//
Expand All @@ -30,7 +35,15 @@ var (
func ReadConfigBytes(data []byte) error {
mut.Lock()
defer mut.Unlock()
return goyaml.Unmarshal(data, &configs)
return readConfigBytes(data, &configs)
}

func readConfigFile(filePath string, out interface{}) error {
data, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
return readConfigBytes(data, out)
}

// ReadConfigFile reads the content of a file and calls ReadConfigBytes to
Expand All @@ -39,11 +52,43 @@ func ReadConfigBytes(data []byte) error {
// It returns error if it can not read the given file or if the file contents
// is not valid yaml.
func ReadConfigFile(filePath string) error {
data, err := ioutil.ReadFile(filePath)
return readConfigFile(filePath, &configs)
}

// ReadAndWatchConfigFile reads and watchs for changes in the configuration
// file. Whenever the file change, and its contents are valid YAML, the
// configuration gets updated. With this function, daemons that use this
// package may reload configuration without restarting.
func ReadAndWatchConfigFile(filePath string) error {
err := ReadConfigFile(filePath)
if err != nil {
return err
}
w, err := fsnotify.NewWatcher()
if err != nil {
return err
}
err = w.Watch(filePath)
if err != nil {
return err
}
return ReadConfigBytes(data)
go func() {
for {
select {
case e := <-w.Event:
if e.IsModify() {
var tmp map[interface{}]interface{}
if readConfigFile(filePath, &tmp) == nil {
mut.Lock()
configs = tmp
mut.Unlock()
}
}
case <-w.Error: // just ignore errors
}
}
}()
return nil
}

// WriteConfigFile writes the configuration to the disc, using the given path.
Expand Down
18 changes: 18 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package config
import (
. "launchpad.net/gocheck"
"os"
"os/exec"
"runtime"
"testing"
"time"
)

func Test(t *testing.T) { TestingT(t) }
Expand Down Expand Up @@ -71,6 +73,22 @@ func (s *S) TestConfigFile(c *C) {
c.Assert(configs, DeepEquals, expected)
}

func (s *S) TestWatchConfigFile(c *C) {
err := exec.Command("cp", "testdata/config.yml", "/tmp/config-test.yml").Run()
c.Assert(err, IsNil)
err = ReadAndWatchConfigFile("/tmp/config-test.yml")
c.Assert(err, IsNil)
c.Assert(configs, DeepEquals, expected)
err = exec.Command("cp", "testdata/config2.yml", "/tmp/config-test.yml").Run()
c.Assert(err, IsNil)
time.Sleep(1e9)
expectedAuth := map[interface{}]interface{}{
"salt": "xpta",
"key": "sometoken1234",
}
c.Assert(configs["auth"], DeepEquals, expectedAuth)
}

func (s *S) TestWriteConfigFile(c *C) {
Set("database:host", "127.0.0.1")
Set("database:port", 3306)
Expand Down
23 changes: 23 additions & 0 deletions testdata/config2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2013 config authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

database:
host: 127.0.0.1
port: 8080
auth:
salt: xpta
key: sometoken1234
xpto: ble
istrue: false
fakebool: foo
names:
- Mary
- John
- Anthony
- Gopher
multiple-types:
- Mary
- 50
- 5.3
- true

0 comments on commit f2f261d

Please sign in to comment.