services/config: move mdns-based config to experimental.

mdns-based config distribution mechanism is not being used for now.
move services/config to experimental/users/mdnsconfig

https://github.com/veyron/release-issues/issues/156

Change-Id: Iac87ab387deed6b2f8c117f50439636f7d43d5d7
diff --git a/services/config/lib/config.go b/services/config/lib/config.go
deleted file mode 100644
index 4a1fbbd..0000000
--- a/services/config/lib/config.go
+++ /dev/null
@@ -1,417 +0,0 @@
-// A mdns based config service.  We make an mdns group for each config.
-// Mdns is probably an over kill for this but why use two when one will do?
-
-// The service provides an eventually consistent map to all nodes on the
-// network.  The winning map is the one with the highest version number.
-// If a server stores the map in a file, it can also be a source of the
-// map
-
-package config
-
-import (
-	"fmt"
-	"io/ioutil"
-	"path/filepath"
-	"sort"
-	"strconv"
-	"strings"
-	"sync"
-
-	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
-
-	"github.com/presotto/go-mdns-sd"
-	"github.com/presotto/go-mdns-sd/go_dns"
-)
-
-const maxDNSStringLength = 254
-
-type config struct {
-	version uint64
-	pairs   map[string]string
-}
-
-type configService struct {
-	rwlock  sync.RWMutex // protects elements of this structure.
-	mdns    *mdns.MDNS
-	service string
-	file    string     // file containing config
-	current *config    // the highest numbered config
-	done    chan bool  // closed to tell children to go away
-	change  *sync.Cond // condition variable broadcast to on every config change
-	gen     int        // incremented every config change
-}
-
-const pkgPath = "v.io/x/ref/services/config/lib"
-
-// Errors
-var (
-	errCantParse            = verror.Register(pkgPath+".errCantParse", verror.NoRetry, "{1:}{2:} can't parse{:_}")
-	errEntryTooLong         = verror.Register(pkgPath+".errEntryTooLong", verror.NoRetry, "{1:}{2:} entry {3}:{4} is too long{:_}")
-	errNoFileToRead         = verror.Register(pkgPath+".errNoFileToRead", verror.NoRetry, "{1:}{2:} no file to read{:_}")
-	errFileError            = verror.Register(pkgPath+".errFileError", verror.NoRetry, "{1:}{2:} file {3}{:_}")
-	errMissingLegalVersion  = verror.Register(pkgPath+".errMissingLegalVersion", verror.NoRetry, "{1:}{2:} missing legal version for file{:_}")
-	errMissingConfigVersion = verror.Register(pkgPath+".errMissingConfigVersion", verror.NoRetry, "{1:}{2:} missing config version{:_}")
-	errNoConfig             = verror.Register(pkgPath+".errNoConfig", verror.NoRetry, "{1:}{2:} no config{:_}")
-	errConfigHasNoKey       = verror.Register(pkgPath+".errConfigHasNoKey", verror.NoRetry, "{1:}{2:} config has no key {3}{:_}")
-	errOfferingConfigError  = verror.Register(pkgPath+".errOfferingConfigError", verror.NoRetry, "{1:}{2:} offering config {3}{:_}")
-)
-
-// MDNSConfigService creates a new instance of the config service with the given name.
-// If file is non blank, the initial config is read from file and any learned configs are
-// stored in it.  Only instances with a file to backup will offer configs to the net.
-// All other instances are passive.
-func MDNSConfigService(name, file string, loopback bool, port uint16) (ConfigService, error) {
-	x := filepath.Base(file)
-	if x == "." {
-		x = ""
-	}
-	var ipv4hp, ipv6hp string
-	if port != 0 {
-		ipv4hp = fmt.Sprintf("224.0.0.251:%d", port)
-		ipv6hp = fmt.Sprintf("[FF02::FB]:%d", port)
-	}
-	mdns, err := mdns.NewMDNS(x, ipv4hp, ipv6hp, loopback, false)
-	if err != nil {
-		vlog.Errorf("mdns startup failed: %s", err)
-		return nil, err
-	}
-	cs := &configService{mdns: mdns, service: name + "-config", file: file, done: make(chan bool)}
-	cs.change = sync.NewCond(&cs.rwlock)
-
-	// Read the config file if we have one and offer it to everyone else.
-	if cs.current, err = readFile(file); err != nil {
-		vlog.Errorf("reading initial config: %s", err)
-	}
-	cs.Offer()
-
-	// Watch config changes and remember them.
-	go cs.watcher()
-	return cs, nil
-}
-
-// Stop the service.
-func (cs *configService) Stop() {
-	cs.rwlock.Lock()
-	mdns := cs.mdns
-	cs.mdns = nil
-	cs.rwlock.Unlock()
-	if mdns != nil {
-		mdns.Stop()
-		close(cs.done)
-	}
-}
-
-func newConfig() *config {
-	return &config{pairs: make(map[string]string)}
-}
-
-// parseVersion parses a config version.  The version is a pair of uint32s separated by a '.'.
-// If the second is missing, we assume 0.  The first number is for humans.  The
-// second is to break ties if machines generate configs.
-func parseVersion(s string) (uint64, error) {
-	f := strings.SplitN(s, ".", 2)
-	v, err := strconv.ParseUint(f[0], 10, 32)
-	if err != nil {
-		return 0, err
-	}
-	var r uint64
-	if len(f) == 2 {
-		r, err = strconv.ParseUint(f[1], 10, 32)
-		if err != nil {
-			return 0, err
-		}
-	}
-	return (v << 32) | r, nil
-}
-
-func serializeVersion(version uint64) string {
-	return fmt.Sprintf("%d.%d", version>>32, version&0xffffffff)
-}
-
-// parseEntry parse an entry of the form "key : value" and
-// add it to the map.  White space before and after "key" and
-// value is discarded.
-func (c *config) parseEntry(l string) error {
-	// Ignore lines with nothing but white space or starting with #
-	l = strings.TrimSpace(l)
-	if len(l) == 0 || strings.HasPrefix(l, "#") {
-		return nil
-	}
-	// The reset have to be key<white>*:<white>*value
-	f := strings.SplitN(l, ":", 2)
-	if len(f) != 2 {
-		return verror.New(errCantParse, nil, l)
-	}
-	k := strings.TrimSpace(f[0])
-	v := strings.TrimSpace(f[1])
-	if len(k)+len(v) > maxDNSStringLength {
-		return verror.New(errEntryTooLong, nil, k, v)
-	}
-	c.pairs[k] = v
-	if k != "version" {
-		return nil
-	}
-	var err error
-	c.version, err = parseVersion(v)
-	return err
-}
-
-func serializeEntry(k, v string) (string, error) {
-	if len(k)+len(v) > maxDNSStringLength {
-		return "", verror.New(errEntryTooLong, nil, k, v)
-	}
-	return k + ":" + v, nil
-}
-
-func readFile(file string) (*config, error) {
-	if len(file) == 0 {
-		return nil, verror.New(errNoFileToRead, nil)
-	}
-
-	// The config has to be small so just read it all in one go.
-	b, err := ioutil.ReadFile(file)
-	if err != nil {
-		return nil, err
-	}
-	c := newConfig()
-	for _, l := range strings.Split(string(b), "\n") {
-		if err := c.parseEntry(l); err != nil {
-			return nil, verror.New(errFileError, nil, file, err)
-		}
-	}
-	if _, ok := c.pairs["version"]; !ok {
-		return nil, verror.New(errMissingLegalVersion, nil, file)
-	}
-	return c, nil
-}
-
-// writeFile is called with the write lock held.
-func writeFile(file string, c *config) {
-	if len(file) == 0 || c == nil || len(c.pairs) == 0 {
-		return
-	}
-	var s string
-	for k, v := range c.pairs {
-		e, err := serializeEntry(k, v)
-		if err != nil {
-			vlog.Errorf("writing %s: %s", file, err)
-			return
-		}
-		s += e + "\n"
-	}
-	if err := ioutil.WriteFile(file, []byte(s), 0644); err != nil {
-		vlog.Errorf("writing %s: %q", file, err)
-	}
-}
-
-// rrToConfig converts a set of TXT rrs to a config.
-func rrToConfig(rr *dns.RR_TXT) (*config, error) {
-	c := newConfig()
-	for _, s := range rr.Txt {
-		if err := c.parseEntry(s); err != nil {
-			return nil, err
-		}
-	}
-	// Ignore any config with no version.
-	if _, ok := c.pairs["version"]; !ok {
-		return nil, verror.New(errMissingConfigVersion, nil)
-	}
-	return c, nil
-}
-
-func (cs *configService) watchSingle(key string, c chan Pair) {
-	ov, err := cs.Get(key)
-	oexists := err == nil
-	c <- Pair{Key: key, Value: ov, Nonexistant: !oexists}
-	gen := 0
-	for {
-		v, err := cs.Get(key)
-		exists := err == nil
-		if exists != oexists || v != ov {
-			c <- Pair{Key: key, Value: v, Nonexistant: !exists}
-			ov, oexists = v, exists
-		}
-		// Block waiting for a change.
-		cs.rwlock.Lock()
-		for gen == cs.gen {
-			cs.change.Wait()
-		}
-		gen = cs.gen
-		cs.rwlock.Unlock()
-	}
-}
-
-func (cs *configService) watchAll(c chan Pair) {
-	omap := make(map[string]string, 0)
-	gen := 0
-	for {
-		nmap, err := cs.GetAll()
-		if err != nil {
-			nmap = make(map[string]string, 0)
-		}
-		// See if any key disappeared.
-		for k := range omap {
-			if _, ok := nmap[k]; !ok {
-				c <- Pair{Key: k, Value: "", Nonexistant: true}
-			}
-		}
-		// See if any value changed or new key appeared.
-		for k, v := range nmap {
-			ov, ok := omap[k]
-			if !ok || v != ov {
-				c <- Pair{Key: k, Value: v}
-			}
-		}
-		omap = nmap
-		// Block waiting for a change.
-		cs.rwlock.Lock()
-		for gen == cs.gen {
-			cs.change.Wait()
-		}
-		gen = cs.gen
-		cs.rwlock.Unlock()
-	}
-}
-
-// watcher waits for config changes and remembers them.
-// TODO(p): Should we also watch the file for changes?
-func (cs *configService) watcher() {
-	cs.rwlock.RLock()
-	if cs.mdns == nil {
-		cs.rwlock.RUnlock()
-		return
-	}
-	c := cs.mdns.ServiceMemberWatch(cs.service)
-	cs.mdns.SubscribeToService(cs.service)
-	cs.rwlock.RUnlock()
-	defer close(c)
-	for {
-		select {
-		case si := <-c:
-			var config *config
-			for _, rr := range si.TxtRRs {
-				c, err := rrToConfig(rr)
-				if err != nil {
-					continue
-				}
-				if config == nil || c.version > config.version {
-					config = c
-				}
-			}
-			if config == nil {
-				continue
-			}
-			// This lock is to synchronize writing and reading of the data structure.
-			cs.rwlock.Lock()
-			if cs.current != nil && config.version <= cs.current.version {
-				cs.rwlock.Unlock()
-				continue
-			}
-			cs.current = config
-			cs.gen++
-			cs.change.Broadcast()
-			writeFile(cs.file, cs.current)
-			cs.rwlock.Unlock()
-			cs.Offer()
-		case <-cs.done:
-			return
-		}
-	}
-}
-
-// Get returns the value associated with key.
-func (cs *configService) Get(key string) (string, error) {
-	cs.rwlock.RLock()
-	defer cs.rwlock.RUnlock()
-	if cs.current == nil {
-		return "", verror.New(errNoConfig, nil)
-	}
-	if v, ok := cs.current.pairs[key]; !ok {
-		return "", verror.New(errConfigHasNoKey, nil, key)
-	} else {
-		return v, nil
-	}
-}
-
-// Watch for changes to a particular key.
-func (cs *configService) Watch(key string) chan Pair {
-	c := make(chan Pair)
-	go cs.watchSingle(key, c)
-	return c
-}
-
-// Get returns the complete configuration.
-func (cs *configService) GetAll() (map[string]string, error) {
-	cs.rwlock.RLock()
-	defer cs.rwlock.RUnlock()
-	if cs.current == nil {
-		return nil, verror.New(errNoConfig, nil)
-	}
-	// Copy so caller can't change the map under our feet.
-	reply := make(map[string]string)
-	for k, v := range cs.current.pairs {
-		reply[k] = v
-	}
-	return reply, nil
-}
-
-// WatchAll watches for changes to any key.
-func (cs *configService) WatchAll() chan Pair {
-	c := make(chan Pair)
-	go cs.watchAll(c)
-	return c
-}
-
-// Offer offers the pairs for the config to other servers.
-func (cs *configService) Offer() {
-	cs.rwlock.RLock()
-	defer cs.rwlock.RUnlock()
-	if cs.mdns == nil || cs.current == nil || len(cs.file) == 0 {
-		return
-	}
-	// First sort the keys to get a canonical list for the txt entries.
-	keys := make([]string, 0)
-	for k := range cs.current.pairs {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-	// Convert config to a slice of strings.
-	var txt []string
-	for _, k := range keys {
-		e, err := serializeEntry(k, cs.current.pairs[k])
-		if err != nil {
-			verror.New(errOfferingConfigError, nil, cs.file, err)
-			return
-		}
-		txt = append(txt, e)
-	}
-
-	// Send (and keep sending).
-	cs.mdns.AddService(cs.service, "", 0, txt...)
-}
-
-// Reread the config file and remember it if the version is newer than current.
-func (cs *configService) Reread() error {
-	cs.rwlock.Lock()
-	file := cs.file
-	if len(file) == 0 {
-		cs.rwlock.Unlock()
-		return nil
-	}
-	c, err := readFile(file)
-	if err != nil {
-		cs.rwlock.Unlock()
-		return err
-	}
-	if cs.current != nil && c.version <= cs.current.version {
-		cs.rwlock.Unlock()
-		return nil
-	}
-	cs.current = c
-	cs.gen++
-	cs.change.Broadcast()
-	cs.rwlock.Unlock()
-	cs.Offer()
-	return nil
-}
diff --git a/services/config/lib/config_test.go b/services/config/lib/config_test.go
deleted file mode 100644
index b3bc4dd..0000000
--- a/services/config/lib/config_test.go
+++ /dev/null
@@ -1,319 +0,0 @@
-package config
-
-import (
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"math/rand"
-	"os"
-	"testing"
-	"time"
-)
-
-// A config file to be written to disk.
-var configFileA = `
-the:rain
- in:spain
-falls: mainly
- on : the 
-# something ugly this way comes
-plain:	today
-version : 1
-`
-
-// A map referring to the config in configFileA.
-var referenceA = map[string]string{
-	"the":     "rain",
-	"in":      "spain",
-	"falls":   "mainly",
-	"on":      "the",
-	"plain":   "today",
-	"version": "1",
-}
-
-// Another config file to be written to disk.
-var configFileB = `
-the:rain
- in:spain
-falls: mainly
- on : my foot 
-# something ugly this way comes
-version : 1.1
-`
-
-// A map referring to the config in configFileB.
-var referenceB = map[string]string{
-	"the":     "rain",
-	"in":      "spain",
-	"falls":   "mainly",
-	"on":      "my foot",
-	"version": "1.1",
-}
-
-func compare(actual map[string]string, reference map[string]string) error {
-	for k, rv := range reference {
-		av, ok := actual[k]
-		if !ok {
-			return fmt.Errorf("missing entry for key %q", k)
-		}
-		if av != rv {
-			return fmt.Errorf("bad value for key %q: expected %q, got %q", k, rv, av)
-		}
-	}
-	for k, av := range actual {
-		if _, ok := reference[k]; !ok {
-			return fmt.Errorf("unexpected pair %q : %q", k, av)
-		}
-	}
-	return nil
-}
-
-func compareConfigToReference(cs ConfigService, ref map[string]string) error {
-	actual, err := cs.GetAll()
-	if err != nil {
-		return err
-	}
-	return compare(actual, ref)
-}
-
-func compareFileToReference(file string, ref map[string]string) error {
-	c, err := readFile(file)
-	if err != nil {
-		return err
-	}
-	return compare(c.pairs, ref)
-}
-
-func waitForConsistency(cs ConfigService, ref map[string]string) error {
-	finalerr := errors.New("inconsistent")
-	for loops := 0; loops < 30; loops++ {
-		actual, err := cs.GetAll()
-		if err == nil {
-			err := compare(actual, ref)
-			if err == nil {
-				return nil
-			}
-			finalerr = err
-		}
-		time.Sleep(500 * time.Millisecond)
-	}
-	return errors.New("timeout waiting for consistency: " + finalerr.Error())
-}
-
-func pickRandomPort() uint16 {
-	rand.Seed(time.Now().UnixNano())
-	return uint16(rand.Intn(1000)) + 5354
-}
-
-func testConfig(fileA, fileB, fileD string) error {
-	port := pickRandomPort()
-
-	// Write a config to fileA.
-	if err := ioutil.WriteFile(fileA, []byte(configFileA), 0644); err != nil {
-		return fmt.Errorf("writing initial %s: %s", fileA, err)
-	}
-
-	// Start a config service with a file and compare the parsed file to the reference.
-	csa, err := MDNSConfigService("foo", fileA, true, port)
-	if err != nil {
-		return fmt.Errorf("creating service %s", err)
-	}
-	defer csa.Stop()
-	if err := compareConfigToReference(csa, referenceA); err != nil {
-		return fmt.Errorf("confA ~ refA: %s", err)
-	}
-
-	// Create a second instance with a non existant file.
-	csb, err := MDNSConfigService("foo", fileB, true, port)
-	if err != nil {
-		return fmt.Errorf("creating service %s", err)
-	}
-	defer csb.Stop()
-
-	// Create a third passive instance with no file.
-	csc, err := MDNSConfigService("foo", "", true, port)
-	if err != nil {
-		return fmt.Errorf("creating service %s", err)
-	}
-	defer csc.Stop()
-
-	// Loop till the second instance gets a config or we time out.
-	if err := waitForConsistency(csb, referenceA); err != nil {
-		return fmt.Errorf("confB ~ refA: %s", err)
-	}
-
-	// Make sure that the new instance updated its file.
-	if err := compareFileToReference(fileB, referenceA); err != nil {
-		return fmt.Errorf("fileB ~ refA: %s", err)
-	}
-
-	// Rewrite/Reread the second instance's file, make sure the it rereads it correctly.
-	if err := ioutil.WriteFile(fileB, []byte(configFileB), 0644); err != nil {
-		return fmt.Errorf("writing fileB: %s", err)
-	}
-	if err := csb.Reread(); err != nil {
-		return fmt.Errorf("Rereading fileB: %s", err)
-	}
-	if err := compareConfigToReference(csb, referenceB); err != nil {
-		return fmt.Errorf("confB ~ refB: %s", err)
-	}
-	// Loop till the first instance changes its config or we time out.
-	if err := waitForConsistency(csa, referenceB); err != nil {
-		return fmt.Errorf("confA ~ refB: %s", err)
-	}
-
-	// Make sure that the first instance updated its file.
-	if err := compareFileToReference(fileA, referenceB); err != nil {
-		return fmt.Errorf("fileA ~ refB: %s", err)
-	}
-
-	// Loop till the passive instance changes its config or we time out.
-	if err := waitForConsistency(csc, referenceB); err != nil {
-		return fmt.Errorf("confC ~ refB: %s", err)
-	}
-
-	// Create an instance with an lower numbered version.  Make sure it doesn't
-	// overcome the higher numbered one.
-	if err := ioutil.WriteFile(fileD, []byte(configFileA), 0644); err != nil {
-		return fmt.Errorf("writing initial %s: %s", fileD, err)
-	}
-	csd, err := MDNSConfigService("foo", fileD, true, port)
-	if err != nil {
-		return fmt.Errorf("creating service %s", err)
-	}
-	defer csd.Stop()
-	if err := waitForConsistency(csd, referenceB); err != nil {
-		return fmt.Errorf("confD ~ refB: %s", err)
-	}
-	if err := waitForConsistency(csc, referenceA); err == nil {
-		return errors.New("eventual consistency picked wrong version for C")
-	}
-
-	return nil
-}
-
-func createTempFile(t *testing.T, base string) string {
-	f, err := ioutil.TempFile("", base)
-	if err != nil {
-		t.Fatal("creating temp file: %s", err)
-	}
-	f.Close()
-	return f.Name()
-}
-
-func TestConfig(t *testing.T) {
-	// Create temporary files.
-	fileA := createTempFile(t, "testconfigA")
-	defer os.Remove(fileA)
-	fileB := createTempFile(t, "testconfigB")
-	defer os.Remove(fileB)
-	fileD := createTempFile(t, "testconfigD")
-	defer os.Remove(fileD)
-
-	if err := testConfig(fileA, fileB, fileD); err != nil {
-		t.Fatal(err)
-	}
-}
-
-func TestConfigStream(t *testing.T) {
-	// Create temporary file.
-	fileA := createTempFile(t, "testconfigA")
-	defer os.Remove(fileA)
-
-	// Start a cs service.
-	cs, err := MDNSConfigService("foo", fileA, true, pickRandomPort())
-	if err != nil {
-		t.Fatal("creating service %s", err)
-	}
-
-	// Watch a single key and all keys.
-	ws := cs.Watch("version")
-	wa := cs.WatchAll()
-
-	// Since there's no config yet, we should just get a
-	// deleted entry for the single key.
-	select {
-	case p := <-ws:
-		if !p.Nonexistant {
-			t.Fatal("expected Nonexistant: got %v", p)
-		}
-	case <-time.After(2 * time.Second):
-		t.Fatal("timeout waiting for single config var")
-	}
-
-	// Write a config to the file and read it.
-	if err := ioutil.WriteFile(fileA, []byte(configFileA), 0644); err != nil {
-		t.Fatal("writing initial %s: %s", fileA, err)
-	}
-	if err := cs.Reread(); err != nil {
-		t.Fatal("rereading config: %s", err)
-	}
-
-	// We should now have a version number.
-	select {
-	case p := <-ws:
-		if p.Nonexistant || p.Key != "version" || p.Value != "1" {
-			t.Fatal("expected [version, 1, false]: got %v", p)
-		}
-
-	case <-time.After(2 * time.Second):
-		t.Fatal("timeout waiting for single config var")
-	}
-
-	// And we should be streamed the whole version.
-	cmap := make(map[string]string, 0)
-L:
-	for {
-		select {
-		case p := <-wa:
-			if p.Nonexistant {
-				t.Fatal("unexpected %v", p)
-			}
-			cmap[p.Key] = p.Value
-			err := compare(referenceA, cmap)
-			if err == nil {
-				break L
-			}
-		case <-time.After(2 * time.Second):
-			t.Fatal("timeout waiting for all config vars")
-		}
-	}
-
-	// Change and reread the config file.
-	if err := ioutil.WriteFile(fileA, []byte(configFileB), 0644); err != nil {
-		t.Fatal("writing initial %s: %s", fileA, err)
-	}
-	if err := cs.Reread(); err != nil {
-		t.Fatal("rereading config: %s", err)
-	}
-
-	// The version should have changed.
-	select {
-	case p := <-ws:
-		if p.Nonexistant || p.Key != "version" || p.Value != "1.1" {
-			t.Fatal("expected [version, 1.1, false]: got %v", p)
-		}
-
-	case <-time.After(2 * time.Second):
-		t.Fatal("timeout waiting for single config var")
-	}
-
-	// And we should now be consistent with referenceB
-L2:
-	for {
-		select {
-		case p := <-wa:
-			if p.Nonexistant {
-				delete(cmap, p.Key)
-				break
-			}
-			cmap[p.Key] = p.Value
-			err := compare(referenceB, cmap)
-			if err == nil {
-				break L2
-			}
-		case <-time.After(2 * time.Second):
-			t.Fatal("timeout waiting for all config vars")
-		}
-	}
-}
diff --git a/services/config/lib/model.go b/services/config/lib/model.go
deleted file mode 100644
index 92aa459..0000000
--- a/services/config/lib/model.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package config
-
-// ConfigService exists to get things going when you plug a device into a network.
-// Anything that we currently pass to apps as environment variables (proxy server,
-// global name server, ...) should come from here.
-
-// Both keys and values can have embedded white space but they can't start
-// or end with whitespace.  keys cannot include ':'s.
-
-type Pair struct {
-	Key         string
-	Value       string
-	Nonexistant bool
-}
-
-type ConfigService interface {
-	// Stop stops a config service.
-	Stop()
-
-	// Get returns the value associated with name or an error if no value exists
-	// or can be determined.
-	Get(name string) (string, error)
-
-	// GetAll returns all attribute/value pairs as a map or an error if no config
-	// can be found.
-	GetAll() (map[string]string, error)
-
-	// Watch returns a stream of values for a particuar key.
-	Watch(key string) chan Pair
-
-	// Watch returns a stream key, value pairs.
-	WatchAll() chan Pair
-
-	// Reread the config info (for example, from a file).  In particular
-	// this says nothing about where the config resides or how it is read.
-	Reread() error
-}