Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 1 | package config |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "strings" |
| 6 | "sync" |
| 7 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 8 | "veyron.io/veyron/veyron2/verror" |
| 9 | "veyron.io/veyron/veyron2/vom" |
Bogdan Caprita | a4d9ee4 | 2014-06-20 16:42:53 -0700 | [diff] [blame] | 10 | ) |
| 11 | |
| 12 | var ErrKeyNotFound = verror.NotFoundf("config key not found") |
| 13 | |
| 14 | // TODO(caprita): Move the interface to veyron2 and integrate with |
| 15 | // veyron/services/config. |
| 16 | |
| 17 | // Config defines a simple key-value configuration. Keys and values are |
| 18 | // strings, and a key can have exactly one value. The client is responsible for |
| 19 | // encoding structured values, or multiple values, in the provided string. |
| 20 | // |
| 21 | // Config data can come from several sources: |
| 22 | // - passed from parent process to child process through pipe; |
| 23 | // - using environment variables or flags; |
| 24 | // - via the neighborhood-based config service; |
| 25 | // - by RPCs using the Config idl; |
| 26 | // - manually, by calling the Set method. |
| 27 | // |
| 28 | // This interface makes no assumptions about the source of the configuration, |
| 29 | // but provides a unified API for accessing it. |
| 30 | type Config interface { |
| 31 | // Set sets the value for the key. If the key already exists in the |
| 32 | // config, its value is overwritten. |
| 33 | Set(key, value string) |
| 34 | // Get returns the value for the key. If the key doesn't exist in the |
| 35 | // config, Get returns an error. |
| 36 | Get(key string) (string, error) |
| 37 | // Serialize serializes the config to a string. |
| 38 | Serialize() (string, error) |
| 39 | // MergeFrom deserializes config information from a string created using |
| 40 | // Serialize(), and merges this information into the config, updating |
| 41 | // values for keys that already exist and creating new key-value pairs |
| 42 | // for keys that don't. |
| 43 | MergeFrom(string) error |
| 44 | } |
| 45 | |
| 46 | type cfg struct { |
| 47 | sync.RWMutex |
| 48 | m map[string]string |
| 49 | } |
| 50 | |
| 51 | // New creates a new empty config. |
| 52 | func New() Config { |
| 53 | return &cfg{m: make(map[string]string)} |
| 54 | } |
| 55 | |
| 56 | func (c cfg) Set(key, value string) { |
| 57 | c.Lock() |
| 58 | defer c.Unlock() |
| 59 | c.m[key] = value |
| 60 | } |
| 61 | |
| 62 | func (c cfg) Get(key string) (string, error) { |
| 63 | c.RLock() |
| 64 | defer c.RUnlock() |
| 65 | v, ok := c.m[key] |
| 66 | if !ok { |
| 67 | return "", ErrKeyNotFound |
| 68 | } |
| 69 | return v, nil |
| 70 | } |
| 71 | |
| 72 | func (c cfg) Serialize() (string, error) { |
| 73 | var buf bytes.Buffer |
| 74 | c.RLock() |
| 75 | defer c.RUnlock() |
| 76 | if err := vom.NewEncoder(&buf).Encode(c.m); err != nil { |
| 77 | return "", err |
| 78 | } |
| 79 | return buf.String(), nil |
| 80 | } |
| 81 | |
| 82 | func (c cfg) MergeFrom(serialized string) error { |
| 83 | var newM map[string]string |
| 84 | if err := vom.NewDecoder(strings.NewReader(serialized)).Decode(&newM); err != nil { |
| 85 | return err |
| 86 | } |
| 87 | c.Lock() |
| 88 | defer c.Unlock() |
| 89 | for k, v := range newM { |
| 90 | c.m[k] = v |
| 91 | } |
| 92 | return nil |
| 93 | } |