From d91e969a31aa7251ebfe605ec8bce4d7e60456a4 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 09:56:43 -0700 Subject: [PATCH 1/6] bump(github.com/coreos/go-etcd/etcd): --- .../github.com/coreos/go-etcd/etcd/client.go | 36 ++++++----- .../github.com/coreos/go-etcd/etcd/watch.go | 61 +++++++++---------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/third_party/github.com/coreos/go-etcd/etcd/client.go b/third_party/github.com/coreos/go-etcd/etcd/client.go index 703b4ce..7624c6a 100644 --- a/third_party/github.com/coreos/go-etcd/etcd/client.go +++ b/third_party/github.com/coreos/go-etcd/etcd/client.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net" "net/http" + "net/url" "path" "strings" "time" @@ -39,10 +40,10 @@ func NewClient() *Client { // default leader and machines cluster := Cluster{ - Leader: "0.0.0.0:4001", + Leader: "http://0.0.0.0:4001", Machines: make([]string, 1), } - cluster.Machines[0] = "0.0.0.0:4001" + cluster.Machines[0] = "http://0.0.0.0:4001" config := Config{ // default use http @@ -116,19 +117,26 @@ func (c *Client) SyncCluster() bool { // sync cluster information by providing machine list func (c *Client) internalSyncCluster(machines []string) bool { for _, machine := range machines { - httpPath := c.createHttpPath(machine, "machines") + httpPath := c.createHttpPath(machine, "v1/machines") resp, err := c.httpClient.Get(httpPath) if err != nil { // try another machine in the cluster continue } else { b, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() if err != nil { // try another machine in the cluster continue } // update Machines List c.cluster.Machines = strings.Split(string(b), ",") + + // update leader + // the first one in the machine list is the leader + logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, c.cluster.Machines[0]) + c.cluster.Leader = c.cluster.Machines[0] + logger.Debug("sync.machines ", c.cluster.Machines) return true } @@ -138,9 +146,9 @@ func (c *Client) internalSyncCluster(machines []string) bool { // serverName should contain both hostName and port func (c *Client) createHttpPath(serverName string, _path string) string { - httpPath := path.Join(serverName, _path) - httpPath = c.config.Scheme + "://" + httpPath - return httpPath + u, _ := url.Parse(serverName) + u.Path = path.Join(u.Path, "/", _path) + return u.String() } // Dial with timeout. @@ -149,22 +157,21 @@ func dialTimeout(network, addr string) (net.Conn, error) { } func (c *Client) getHttpPath(s ...string) string { - httpPath := path.Join(c.cluster.Leader, version) + u, _ := url.Parse(c.cluster.Leader) + + u.Path = path.Join(u.Path, "/", version) for _, seg := range s { - httpPath = path.Join(httpPath, seg) + u.Path = path.Join(u.Path, seg) } - httpPath = c.config.Scheme + "://" + httpPath - return httpPath + return u.String() } func (c *Client) updateLeader(httpPath string) { - // httpPath http://127.0.0.1:4001/v1... - leader := strings.Split(httpPath, "://")[1] - // we want to have 127.0.0.1:4001 + u, _ := url.Parse(httpPath) + leader := u.Host - leader = strings.Split(leader, "/")[0] logger.Debugf("update.leader[%s,%s]", c.cluster.Leader, leader) c.cluster.Leader = leader } @@ -181,6 +188,7 @@ func (c *Client) sendRequest(method string, _path string, body string) (*http.Re for { httpPath := c.getHttpPath(_path) + logger.Debug("send.request.to ", httpPath) if body == "" { diff --git a/third_party/github.com/coreos/go-etcd/etcd/watch.go b/third_party/github.com/coreos/go-etcd/etcd/watch.go index c583e9a..5da5565 100644 --- a/third_party/github.com/coreos/go-etcd/etcd/watch.go +++ b/third_party/github.com/coreos/go-etcd/etcd/watch.go @@ -50,46 +50,30 @@ func (c *Client) watchOnce(key string, sinceIndex uint64, stop chan bool) (*stor var resp *http.Response var err error - if sinceIndex == 0 { - // Get request if no index is given - resp, err = c.sendRequest("GET", path.Join("watch", key), "") - - if err != nil { - return nil, err - } - - } else { - - // Post - v := url.Values{} - v.Set("index", fmt.Sprintf("%v", sinceIndex)) - + if stop != nil { ch := make(chan respAndErr) - if stop != nil { - go func() { - resp, err = c.sendRequest("POST", path.Join("watch", key), v.Encode()) + go func() { + resp, err = c.sendWatchRequest(key, sinceIndex) - ch <- respAndErr{resp, err} - }() + ch <- respAndErr{resp, err} + }() - // select at stop or continue to receive - select { + // select at stop or continue to receive + select { - case res := <-ch: - resp, err = res.resp, res.err - - case <-stop: - resp, err = nil, errors.New("User stoped watch") - } - } else { - resp, err = c.sendRequest("POST", path.Join("watch", key), v.Encode()) - } + case res := <-ch: + resp, err = res.resp, res.err - if err != nil { - return nil, err + case <-stop: + resp, err = nil, errors.New("User stoped watch") } + } else { + resp, err = c.sendWatchRequest(key, sinceIndex) + } + if err != nil { + return nil, err } b, err := ioutil.ReadAll(resp.Body) @@ -115,3 +99,16 @@ func (c *Client) watchOnce(key string, sinceIndex uint64, stop chan bool) (*stor return &result, nil } + +func (c *Client) sendWatchRequest(key string, sinceIndex uint64) (*http.Response, error) { + if sinceIndex == 0 { + resp, err := c.sendRequest("GET", path.Join("watch", key), "") + return resp, err + } else { + v := url.Values{} + v.Set("index", fmt.Sprintf("%v", sinceIndex)) + resp, err := c.sendRequest("POST", path.Join("watch", key), v.Encode()) + return resp, err + } + +} From 08a18f8f0c1e07a2bb4762a6342aa4832b45ad3c Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 09:56:45 -0700 Subject: [PATCH 2/6] bump(github.com/coreos/etcd/store): --- .../github.com/coreos/etcd/store/stats.go | 10 +++- .../github.com/coreos/etcd/store/store.go | 50 +++++++++---------- .../github.com/coreos/etcd/store/tree.go | 5 ++ .../github.com/coreos/etcd/store/watcher.go | 16 +++--- .../coreos/etcd/store/watcher_test.go | 2 +- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/third_party/github.com/coreos/etcd/store/stats.go b/third_party/github.com/coreos/etcd/store/stats.go index 15b71e0..b57f4db 100644 --- a/third_party/github.com/coreos/etcd/store/stats.go +++ b/third_party/github.com/coreos/etcd/store/stats.go @@ -18,8 +18,16 @@ type EtcdStats struct { TestAndSets uint64 `json:"testAndSets"` } -// Stats returns the basic statistics information of etcd storage +// Stats returns the basic statistics information of etcd storage since its recent start func (s *Store) Stats() []byte { b, _ := json.Marshal(s.BasicStats) return b } + +// TotalWrites returns the total write operations +// It helps with snapshot +func (s *Store) TotalWrites() uint64 { + bs := s.BasicStats + + return bs.Deletes + bs.Sets + bs.TestAndSets +} diff --git a/third_party/github.com/coreos/etcd/store/store.go b/third_party/github.com/coreos/etcd/store/store.go index 5ef4bc2..d37345f 100644 --- a/third_party/github.com/coreos/etcd/store/store.go +++ b/third_party/github.com/coreos/etcd/store/store.go @@ -3,6 +3,7 @@ package store import ( "encoding/json" "fmt" + etcdErr "github.com/coreos/etcd/error" "path" "strconv" "sync" @@ -27,7 +28,7 @@ type Store struct { // the watching condition. // It is needed so that clone() can atomically replicate the Store // and do the log snapshot in a go routine. - mutex sync.Mutex + mutex sync.RWMutex // WatcherHub is where we register all the clients // who issue a watch request @@ -35,7 +36,7 @@ type Store struct { // The string channel to send messages to the outside world // Now we use it to send changes to the hub of the web service - messager *chan string + messager chan<- string // A map to keep the recent response to the clients ResponseMap map[string]*Response @@ -141,7 +142,7 @@ func CreateStore(max int) *Store { } // Set the messager of the store -func (s *Store) SetMessager(messager *chan string) { +func (s *Store) SetMessager(messager chan<- string) { s.messager = messager } @@ -205,7 +206,7 @@ func (s *Store) internalSet(key string, value string, expireTime time.Time, inde } else { // If we want the permanent node to have expire time - // We need to create create a go routine with a channel + // We need to create a go routine with a channel if isExpire { node.update = make(chan time.Time) go s.monitorExpiration(key, node.update, expireTime) @@ -224,8 +225,7 @@ func (s *Store) internalSet(key string, value string, expireTime time.Time, inde // Send to the messager if s.messager != nil && err == nil { - - *s.messager <- string(msg) + s.messager <- string(msg) } s.addToResponseMap(index, &resp) @@ -240,8 +240,7 @@ func (s *Store) internalSet(key string, value string, expireTime time.Time, inde ok := s.Tree.set(key, Node{value, expireTime, update}) if !ok { - err := NotFile(key) - return nil, err + return nil, etcdErr.NewError(102, "set: "+key) } if isExpire { @@ -257,8 +256,7 @@ func (s *Store) internalSet(key string, value string, expireTime time.Time, inde // Send to the messager if s.messager != nil && err == nil { - - *s.messager <- string(msg) + s.messager <- string(msg) } s.addToResponseMap(index, &resp) @@ -306,8 +304,8 @@ func (s *Store) internalGet(key string) *Response { // If key is a file return the file // If key is a directory reuturn an array of files func (s *Store) Get(key string) ([]byte, error) { - s.mutex.Lock() - defer s.mutex.Unlock() + s.mutex.RLock() + defer s.mutex.RUnlock() resps, err := s.RawGet(key) @@ -315,7 +313,12 @@ func (s *Store) Get(key string) ([]byte, error) { return nil, err } - if len(resps) == 1 { + key = path.Clean("/" + key) + + // If the number of resps == 1 and the response key + // is the key we query, a signal key-value should + // be returned + if len(resps) == 1 && resps[0].Key == key { return json.Marshal(resps[0]) } @@ -390,8 +393,7 @@ func (s *Store) RawGet(key string) ([]*Response, error) { return resps, nil } - err := NotFoundError(key) - return nil, err + return nil, etcdErr.NewError(100, "get: "+key) } func (s *Store) Delete(key string, index uint64) ([]byte, error) { @@ -440,8 +442,7 @@ func (s *Store) internalDelete(key string, index uint64) ([]byte, error) { // notify the messager if s.messager != nil && err == nil { - - *s.messager <- string(msg) + s.messager <- string(msg) } s.addToResponseMap(index, &resp) @@ -449,8 +450,7 @@ func (s *Store) internalDelete(key string, index uint64) ([]byte, error) { return msg, err } else { - err := NotFoundError(key) - return nil, err + return nil, etcdErr.NewError(100, "delete: "+key) } } @@ -465,8 +465,7 @@ func (s *Store) TestAndSet(key string, prevValue string, value string, expireTim resp := s.internalGet(key) if resp == nil { - err := NotFoundError(key) - return nil, err + return nil, etcdErr.NewError(100, "testandset: "+key) } if resp.Value == prevValue { @@ -476,8 +475,8 @@ func (s *Store) TestAndSet(key string, prevValue string, value string, expireTim } else { // If fails, return err - err := TestFail(fmt.Sprintf("TestAndSet: %s!=%s", resp.Value, prevValue)) - return nil, err + return nil, etcdErr.NewError(101, fmt.Sprintf("TestAndSet: %s!=%s", + resp.Value, prevValue)) } } @@ -486,7 +485,7 @@ func (s *Store) TestAndSet(key string, prevValue string, value string, expireTim // The watchHub will send response to the channel when any key under the prefix // changes [since the sinceIndex if given] func (s *Store) AddWatcher(prefix string, watcher *Watcher, sinceIndex uint64) error { - return s.watcher.addWatcher(prefix, watcher, sinceIndex, s.ResponseStartIndex, s.Index, &s.ResponseMap) + return s.watcher.addWatcher(prefix, watcher, sinceIndex, s.ResponseStartIndex, s.Index, s.ResponseMap) } // This function should be created as a go routine to delete the key-value pair @@ -526,8 +525,7 @@ func (s *Store) monitorExpiration(key string, update chan time.Time, expireTime // notify the messager if s.messager != nil && err == nil { - - *s.messager <- string(msg) + s.messager <- string(msg) } return diff --git a/third_party/github.com/coreos/etcd/store/tree.go b/third_party/github.com/coreos/etcd/store/tree.go index 11b8092..3d6d1bf 100644 --- a/third_party/github.com/coreos/etcd/store/tree.go +++ b/third_party/github.com/coreos/etcd/store/tree.go @@ -124,6 +124,11 @@ func (t *tree) set(key string, value Node) bool { func (t *tree) internalGet(key string) (*treeNode, bool) { nodesName := split(key) + // should be able to get root + if len(nodesName) == 1 && nodesName[0] == "" { + return t.Root, true + } + nodeMap := t.Root.NodeMap var i int diff --git a/third_party/github.com/coreos/etcd/store/watcher.go b/third_party/github.com/coreos/etcd/store/watcher.go index dfd9177..17de27b 100644 --- a/third_party/github.com/coreos/etcd/store/watcher.go +++ b/third_party/github.com/coreos/etcd/store/watcher.go @@ -36,14 +36,14 @@ func NewWatcher() *Watcher { // Add a watcher to the watcherHub func (w *WatcherHub) addWatcher(prefix string, watcher *Watcher, sinceIndex uint64, - responseStartIndex uint64, currentIndex uint64, resMap *map[string]*Response) error { + responseStartIndex uint64, currentIndex uint64, resMap map[string]*Response) error { prefix = path.Clean("/" + prefix) if sinceIndex != 0 && sinceIndex >= responseStartIndex { for i := sinceIndex; i <= currentIndex; i++ { if checkResponse(prefix, i, resMap) { - watcher.C <- (*resMap)[strconv.FormatUint(i, 10)] + watcher.C <- resMap[strconv.FormatUint(i, 10)] return nil } } @@ -52,22 +52,18 @@ func (w *WatcherHub) addWatcher(prefix string, watcher *Watcher, sinceIndex uint _, ok := w.watchers[prefix] if !ok { - w.watchers[prefix] = make([]*Watcher, 0) - - w.watchers[prefix] = append(w.watchers[prefix], watcher) - } else { - - w.watchers[prefix] = append(w.watchers[prefix], watcher) } + w.watchers[prefix] = append(w.watchers[prefix], watcher) + return nil } // Check if the response has what we are watching -func checkResponse(prefix string, index uint64, resMap *map[string]*Response) bool { +func checkResponse(prefix string, index uint64, resMap map[string]*Response) bool { - resp, ok := (*resMap)[strconv.FormatUint(index, 10)] + resp, ok := resMap[strconv.FormatUint(index, 10)] if !ok { // not storage system command diff --git a/third_party/github.com/coreos/etcd/store/watcher_test.go b/third_party/github.com/coreos/etcd/store/watcher_test.go index 08e64d1..b5730ed 100644 --- a/third_party/github.com/coreos/etcd/store/watcher_test.go +++ b/third_party/github.com/coreos/etcd/store/watcher_test.go @@ -54,7 +54,7 @@ func TestWatch(t *testing.T) { } } -// BenchmarkWatch creates 10K watchers watch at /foo/[paht] each time. +// BenchmarkWatch creates 10K watchers watch at /foo/[path] each time. // Path is randomly chosen with max depth 10. // It should take less than 15ms to wake up 10K watchers. func BenchmarkWatch(b *testing.B) { From 22aaf99c563fb3de0cb947de15ddcd83f4222bc7 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 09:56:47 -0700 Subject: [PATCH 3/6] bump(github.com/ccding/go-logging/logging): --- .../ccding/go-logging/logging/commands.go | 2 +- .../ccding/go-logging/logging/fields.go | 2 +- .../ccding/go-logging/logging/fields_test.go | 2 +- .../ccding/go-logging/logging/formater.go | 2 +- .../ccding/go-logging/logging/get_go_id.c | 2 +- .../ccding/go-logging/logging/level.go | 2 +- .../ccding/go-logging/logging/logging.go | 43 +++++++++---------- .../ccding/go-logging/logging/logging_test.go | 2 +- .../ccding/go-logging/logging/request.go | 2 +- .../ccding/go-logging/logging/writer.go | 2 +- 10 files changed, 30 insertions(+), 31 deletions(-) diff --git a/third_party/github.com/ccding/go-logging/logging/commands.go b/third_party/github.com/ccding/go-logging/logging/commands.go index 6cf2bb4..be19405 100644 --- a/third_party/github.com/ccding/go-logging/logging/commands.go +++ b/third_party/github.com/ccding/go-logging/logging/commands.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging // Logln receives log request from the client. The request includes a set of diff --git a/third_party/github.com/ccding/go-logging/logging/fields.go b/third_party/github.com/ccding/go-logging/logging/fields.go index 74c05b1..aff57fc 100644 --- a/third_party/github.com/ccding/go-logging/logging/fields.go +++ b/third_party/github.com/ccding/go-logging/logging/fields.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging import ( diff --git a/third_party/github.com/ccding/go-logging/logging/fields_test.go b/third_party/github.com/ccding/go-logging/logging/fields_test.go index 8433850..7efae3b 100644 --- a/third_party/github.com/ccding/go-logging/logging/fields_test.go +++ b/third_party/github.com/ccding/go-logging/logging/fields_test.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging import ( diff --git a/third_party/github.com/ccding/go-logging/logging/formater.go b/third_party/github.com/ccding/go-logging/logging/formater.go index c704d57..8be2a31 100644 --- a/third_party/github.com/ccding/go-logging/logging/formater.go +++ b/third_party/github.com/ccding/go-logging/logging/formater.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging import ( diff --git a/third_party/github.com/ccding/go-logging/logging/get_go_id.c b/third_party/github.com/ccding/go-logging/logging/get_go_id.c index 400848c..f9b216f 100644 --- a/third_party/github.com/ccding/go-logging/logging/get_go_id.c +++ b/third_party/github.com/ccding/go-logging/logging/get_go_id.c @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + // This file defines GetGoId function, which is used to get the id of the // current goroutine. More details about this function are availeble in the // runtime.c file of golang source code. diff --git a/third_party/github.com/ccding/go-logging/logging/level.go b/third_party/github.com/ccding/go-logging/logging/level.go index e640fa6..4ada90a 100644 --- a/third_party/github.com/ccding/go-logging/logging/level.go +++ b/third_party/github.com/ccding/go-logging/logging/level.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging // Level is the type of level. diff --git a/third_party/github.com/ccding/go-logging/logging/logging.go b/third_party/github.com/ccding/go-logging/logging/logging.go index 1981c9a..6467d94 100644 --- a/third_party/github.com/ccding/go-logging/logging/logging.go +++ b/third_party/github.com/ccding/go-logging/logging/logging.go @@ -99,13 +99,15 @@ func RichLogger(name string) (*Logger, error) { func FileLogger(name string, level Level, format string, timeFormat string, file string, sync bool) (*Logger, error) { out, err := os.Create(file) if err != nil { - return new(Logger), err + return nil, err } logger, err := createLogger(name, level, format, timeFormat, out, sync) if err == nil { logger.fd = out + return logger, nil + } else { + return nil, err } - return logger, err } // WriterLogger creates a new logger with a writer @@ -115,38 +117,35 @@ func WriterLogger(name string, level Level, format string, timeFormat string, ou // WriterLogger creates a new logger from a configuration file func ConfigLogger(filename string) (*Logger, error) { - conf, err := config.Read(filename) + conf := config.NewConfig(filename) + err := conf.Read() if err != nil { - return new(Logger), err - } - ok := true - name, ok := conf["name"] - if !ok { - name = "" + return nil, err } - slevel, ok := conf["level"] - if !ok { + name := conf.Get("", "name") + slevel := conf.Get("", "level") + if slevel == "" { slevel = "0" } l, err := strconv.Atoi(slevel) if err != nil { - return new(Logger), err + return nil, err } level := Level(l) - format, ok := conf["format"] - if !ok { + format := conf.Get("", "format") + if format == "" { format = BasicFormat } - timeFormat, ok := conf["timeFormat"] - if !ok { + timeFormat := conf.Get("", "timeFormat") + if timeFormat == "" { timeFormat = DefaultTimeFormat } - ssync, ok := conf["sync"] - if !ok { + ssync := conf.Get("", "sync") + if ssync == "" { ssync = "0" } - file, ok := conf["file"] - if !ok { + file := conf.Get("", "file") + if file == "" { file = DefaultFileName } sync := true @@ -155,7 +154,7 @@ func ConfigLogger(filename string) (*Logger, error) { } else if ssync == "1" { sync = true } else { - return new(Logger), err + return nil, err } return FileLogger(name, level, format, timeFormat, file, sync) } @@ -166,7 +165,7 @@ func createLogger(name string, level Level, format string, timeFormat string, ou err := logger.parseFormat(format) if err != nil { - return logger, err + return nil, err } // asign values to logger diff --git a/third_party/github.com/ccding/go-logging/logging/logging_test.go b/third_party/github.com/ccding/go-logging/logging/logging_test.go index b6e0fa5..fcf4bcc 100644 --- a/third_party/github.com/ccding/go-logging/logging/logging_test.go +++ b/third_party/github.com/ccding/go-logging/logging/logging_test.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging import ( diff --git a/third_party/github.com/ccding/go-logging/logging/request.go b/third_party/github.com/ccding/go-logging/logging/request.go index e823fa7..4b86410 100644 --- a/third_party/github.com/ccding/go-logging/logging/request.go +++ b/third_party/github.com/ccding/go-logging/logging/request.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging // request struct stores the logger request diff --git a/third_party/github.com/ccding/go-logging/logging/writer.go b/third_party/github.com/ccding/go-logging/logging/writer.go index 12d1790..9efeddf 100644 --- a/third_party/github.com/ccding/go-logging/logging/writer.go +++ b/third_party/github.com/ccding/go-logging/logging/writer.go @@ -13,7 +13,7 @@ // limitations under the License. // // author: Cong Ding -// + package logging import ( From 8dae61e1d54c37a7bfd1cd5df74f1824a89814cf Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 09:56:48 -0700 Subject: [PATCH 4/6] bump(github.com/ccding/go-config-reader/config): --- .../ccding/go-config-reader/config/config.go | 142 +++++++++++++++--- 1 file changed, 121 insertions(+), 21 deletions(-) diff --git a/third_party/github.com/ccding/go-config-reader/config/config.go b/third_party/github.com/ccding/go-config-reader/config/config.go index 75a4857..36ca20d 100644 --- a/third_party/github.com/ccding/go-config-reader/config/config.go +++ b/third_party/github.com/ccding/go-config-reader/config/config.go @@ -13,24 +13,62 @@ // limitations under the License. // // author: Cong Ding -// + package config import ( "bufio" "errors" + "fmt" + "io/ioutil" "os" "strings" ) var commentPrefix = []string{"//", "#", ";"} -func Read(filename string) (map[string]string, error) { - var res = map[string]string{} - in, err := os.Open(filename) +// Config struct constructs a new configuration handler. +type Config struct { + filename string + config map[string]map[string]string +} + +// NewConfig function cnstructs a new Config struct with filename. You have to +// call Read() function to let it read from the file. Otherwise you will get +// empty string (i.e., "") when you are calling Get() function. Another usage +// is that you call NewConfig() function and then call Add()/Set() function to +// add new key-values to the configuration. Finally you can call Write() +// function to write the new configuration to the file. +func NewConfig(filename string) *Config { + c := new(Config) + c.filename = filename + c.config = make(map[string]map[string]string) + return c +} + +// Filename function returns the filename of the configuration. +func (c *Config) Filename() string { + return c.filename +} + +// SetFilename function sets the filename of the configuration. +func (c *Config) SetFilename(filename string) { + c.filename = filename +} + +// Reset function reset the map in the configuration. +func (c *Config) Reset() { + c.config = make(map[string]map[string]string) +} + +// Read function reads configurations from the file defined in +// Config.filename. +func (c *Config) Read() error { + in, err := os.Open(c.filename) if err != nil { - return res, err + return err } + defer in.Close() scanner := bufio.NewScanner(in) line := "" section := "" @@ -39,9 +77,9 @@ func Read(filename string) (map[string]string, error) { continue } if line == "" { - sec := checkSection(scanner.Text()) - if sec != "" { - section = sec + "." + sec, ok := checkSection(scanner.Text()) + if ok { + section = sec continue } } @@ -53,41 +91,103 @@ func Read(filename string) (map[string]string, error) { line = line[:len(line)-1] continue } - key, value, err := checkLine(line) - if err != nil { - return res, errors.New("WRONG: " + line) + key, value, ok := checkLine(line) + if !ok { + return errors.New("WRONG: " + line) } - res[section+key] = value + c.Set(section, key, value) line = "" } - in.Close() - return res, nil + return nil +} + +// Get function returns the value of a key in the configuration. If the key +// does not exist, it returns empty string (i.e., ""). +func (c *Config) Get(section string, key string) string { + value, ok := c.config[section][key] + if !ok { + return "" + } + return value } -func checkSection(line string) string { +// Set function updates the value of a key in the configuration. Function +// Set() is exactly the same as function Add(). +func (c *Config) Set(section string, key string, value string) { + _, ok := c.config[section] + if !ok { + c.config[section] = make(map[string]string) + } + c.config[section][key] = value +} + +// Add function adds a new key to the configuration. Function Add() is exactly +// the same as function Set(). +func (c *Config) Add(section string, key string, value string) { + c.Set(section, key, value) +} + +// Del function deletes a key from the configuration. +func (c *Config) Del(section string, key string) { + _, ok := c.config[section] + if ok { + delete(c.config[section], key) + if len(c.config[section]) == 0 { + delete(c.config, section) + } + } +} + +// Write function writes the updated configuration back. +func (c *Config) Write() error { + return nil +} + +// WriteTo function writes the configuration to a new file. This function +// re-organizes the configuration and deletes all the comments. +func (c *Config) WriteTo(filename string) error { + content := "" + for k, v := range c.config { + format := "%v = %v\n" + if k != "" { + content += fmt.Sprintf("[%v]\n", k) + format = "\t" + format + } + for key, value := range v { + content += fmt.Sprintf(format, key, value) + } + } + return ioutil.WriteFile(filename, []byte(content), 0644) +} + +// To check this line is a section or not. If it is not a section, it returns +// "". +func checkSection(line string) (string, bool) { line = strings.TrimSpace(line) lineLen := len(line) if lineLen < 2 { - return "" + return "", false } if line[0] == '[' && line[lineLen-1] == ']' { - return line[1 : lineLen-1] + return line[1 : lineLen-1], true } - return "" + return "", false } -func checkLine(line string) (string, string, error) { +// To check this line is a valid key-value pair or not. +func checkLine(line string) (string, string, bool) { key := "" value := "" sp := strings.SplitN(line, "=", 2) if len(sp) != 2 { - return key, value, errors.New("WRONG: " + line) + return key, value, false } key = strings.TrimSpace(sp[0]) value = strings.TrimSpace(sp[1]) - return key, value, nil + return key, value, true } +// To check this line is a whole line comment or not. func checkComment(line string) bool { line = strings.TrimSpace(line) for p := range commentPrefix { From 4d7c31d86574c15aa72ee8c021e7675f01fb4f5f Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 09:59:24 -0700 Subject: [PATCH 5/6] bump(github.com/coreos/etcd/error): --- .../github.com/coreos/etcd/error/error.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 third_party/github.com/coreos/etcd/error/error.go diff --git a/third_party/github.com/coreos/etcd/error/error.go b/third_party/github.com/coreos/etcd/error/error.go new file mode 100644 index 0000000..dc209f2 --- /dev/null +++ b/third_party/github.com/coreos/etcd/error/error.go @@ -0,0 +1,74 @@ +package error + +import ( + "encoding/json" + "net/http" +) + +var errors map[int]string + +const () + +func init() { + errors = make(map[int]string) + + // command related errors + errors[100] = "Key Not Found" + errors[101] = "The given PrevValue is not equal to the value of the key" + errors[102] = "Not A File" + errors[103] = "Reached the max number of machines in the cluster" + + // Post form related errors + errors[200] = "Value is Required in POST form" + errors[201] = "PrevValue is Required in POST form" + errors[202] = "The given TTL in POST form is not a number" + errors[203] = "The given index in POST form is not a number" + + // raft related errors + errors[300] = "Raft Internal Error" + errors[301] = "During Leader Election" + + // keyword + errors[400] = "The prefix of the given key is a keyword in etcd" + + // etcd related errors + errors[500] = "watcher is cleared due to etcd recovery" + +} + +type Error struct { + ErrorCode int `json:"errorCode"` + Message string `json:"message"` + Cause string `json:"cause,omitempty"` +} + +func NewError(errorCode int, cause string) Error { + return Error{ + ErrorCode: errorCode, + Message: errors[errorCode], + Cause: cause, + } +} + +func Message(code int) string { + return errors[code] +} + +// Only for error interface +func (e Error) Error() string { + return e.Message +} + +func (e Error) toJsonString() string { + b, _ := json.Marshal(e) + return string(b) +} + +func (e Error) Write(w http.ResponseWriter) { + // 3xx is reft internal error + if e.ErrorCode/100 == 3 { + http.Error(w, e.toJsonString(), http.StatusInternalServerError) + } else { + http.Error(w, e.toJsonString(), http.StatusBadRequest) + } +} From 572150b7080623aca8483f80d9b4b101b26ef39c Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 19 Aug 2013 10:04:14 -0700 Subject: [PATCH 6/6] feat(third_party): add etcd/error to deps --- third_party/deps | 1 + 1 file changed, 1 insertion(+) diff --git a/third_party/deps b/third_party/deps index 07386dd..0fbd73c 100755 --- a/third_party/deps +++ b/third_party/deps @@ -1,6 +1,7 @@ packages=" bitbucket.org/kardianos/osext github.com/coreos/go-etcd/etcd + github.com/coreos/etcd/error github.com/coreos/etcd/store github.com/ccding/go-logging/logging github.com/ccding/go-config-reader/config