func LoadConfigDir(dir string, logger log.Logger) (*Config, error) {
	f, err := os.Open(dir)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf(
			"configuration path must be a directory: %s",
			dir)
	}
	var files []string
	err = nil
	for err != io.EOF {
		var fis []os.FileInfo
		fis, err = f.Readdir(128)
		if err != nil && err != io.EOF {
			return nil, err
		}
		for _, fi := range fis {
			if fi.IsDir() {
				continue
			}
			name := fi.Name()
			skip := true
			if strings.HasSuffix(name, ".hcl") {
				skip = false
			} else if strings.HasSuffix(name, ".json") {
				skip = false
			}
			if skip || isTemporaryFile(name) {
				continue
			}
			path := filepath.Join(dir, name)
			files = append(files, path)
		}
	}
	var result *Config
	for _, f := range files {
		config, err := LoadConfigFile(f, logger)
		if err != nil {
			return nil, fmt.Errorf("Error loading %s: %s", f, err)
		}
		if result == nil {
			result = config
		} else {
			result = result.Merge(config)
		}
	}
	return result, nil
}
LoadConfigDir loads all the configurations in the given directory
in alphabetical order.
package server
import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	log "github.com/mgutz/logxi/v1"
	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
)
type Config struct {
	Listeners []*Listener `hcl:"-"`
	Backend   *Backend    `hcl:"-"`
	HABackend *Backend    `hcl:"-"`
	CacheSize    int  `hcl:"cache_size"`
	DisableCache bool `hcl:"disable_cache"`
	DisableMlock bool `hcl:"disable_mlock"`
	Telemetry *Telemetry `hcl:"telemetry"`
	MaxLeaseTTL        time.Duration `hcl:"-"`
	MaxLeaseTTLRaw     string        `hcl:"max_lease_ttl"`
	DefaultLeaseTTL    time.Duration `hcl:"-"`
	DefaultLeaseTTLRaw string        `hcl:"default_lease_ttl"`
	ClusterName string `hcl:"cluster_name"`
}
func DevConfig(ha bool) *Config {
	ret := &Config{
		DisableCache: false,
		DisableMlock: true,
		Backend: &Backend{
			Type: "inmem",
		},
		Listeners: []*Listener{
			&Listener{
				Type: "tcp",
				Config: map[string]string{
					"address":     "127.0.0.1:8200",
					"tls_disable": "1",
				},
			},
		},
		Telemetry: &Telemetry{},
		MaxLeaseTTL:     32 * 24 * time.Hour,
		DefaultLeaseTTL: 32 * 24 * time.Hour,
	}
	if ha {
		ret.Backend.Type = "inmem_ha"
	}
	return ret
}
type Listener struct {
	Type   string
	Config map[string]string
}
func (l *Listener) GoString() string {
	return fmt.Sprintf("*%#v", *l)
}
type Backend struct {
	Type              string
	RedirectAddr      string
	ClusterAddr       string
	DisableClustering bool
	Config            map[string]string
}
func (b *Backend) GoString() string {
	return fmt.Sprintf("*%#v", *b)
}
type Telemetry struct {
	StatsiteAddr string `hcl:"statsite_address"`
	StatsdAddr   string `hcl:"statsd_address"`
	DisableHostname bool `hcl:"disable_hostname"`
	CirconusAPIToken string `hcl:"circonus_api_token"`
	CirconusAPIApp string `hcl:"circonus_api_app"`
	CirconusAPIURL string `hcl:"circonus_api_url"`
	CirconusSubmissionInterval string `hcl:"circonus_submission_interval"`
	CirconusCheckSubmissionURL string `hcl:"circonus_submission_url"`
	CirconusCheckID string `hcl:"circonus_check_id"`
	CirconusCheckForceMetricActivation string `hcl:"circonus_check_force_metric_activation"`
	CirconusCheckInstanceID string `hcl:"circonus_check_instance_id"`
	CirconusCheckSearchTag string `hcl:"circonus_check_search_tag"`
	CirconusCheckTags string `mapstructure:"circonus_check_tags"`
	CirconusCheckDisplayName string `mapstructure:"circonus_check_display_name"`
	CirconusBrokerID string `hcl:"circonus_broker_id"`
	CirconusBrokerSelectTag string `hcl:"circonus_broker_select_tag"`
}
func (s *Telemetry) GoString() string {
	return fmt.Sprintf("*%#v", *s)
}
func (c *Config) Merge(c2 *Config) *Config {
	if c2 == nil {
		return c
	}
	result := new(Config)
	for _, l := range c.Listeners {
		result.Listeners = append(result.Listeners, l)
	}
	for _, l := range c2.Listeners {
		result.Listeners = append(result.Listeners, l)
	}
	result.Backend = c.Backend
	if c2.Backend != nil {
		result.Backend = c2.Backend
	}
	result.HABackend = c.HABackend
	if c2.HABackend != nil {
		result.HABackend = c2.HABackend
	}
	result.Telemetry = c.Telemetry
	if c2.Telemetry != nil {
		result.Telemetry = c2.Telemetry
	}
	result.CacheSize = c.CacheSize
	if c2.CacheSize != 0 {
		result.CacheSize = c2.CacheSize
	}
	result.DisableCache = c.DisableCache
	if c2.DisableCache {
		result.DisableCache = c2.DisableCache
	}
	result.DisableMlock = c.DisableMlock
	if c2.DisableMlock {
		result.DisableMlock = c2.DisableMlock
	}
	result.MaxLeaseTTL = c.MaxLeaseTTL
	if c2.MaxLeaseTTL > result.MaxLeaseTTL {
		result.MaxLeaseTTL = c2.MaxLeaseTTL
	}
	result.DefaultLeaseTTL = c.DefaultLeaseTTL
	if c2.DefaultLeaseTTL > result.DefaultLeaseTTL {
		result.DefaultLeaseTTL = c2.DefaultLeaseTTL
	}
	result.ClusterName = c.ClusterName
	if c2.ClusterName != "" {
		result.ClusterName = c2.ClusterName
	}
	return result
}
func LoadConfig(path string, logger log.Logger) (*Config, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	if fi.IsDir() {
		return LoadConfigDir(path, logger)
	} else {
		return LoadConfigFile(path, logger)
	}
}
func LoadConfigFile(path string, logger log.Logger) (*Config, error) {
	d, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return ParseConfig(string(d), logger)
}
func ParseConfig(d string, logger log.Logger) (*Config, error) {
	obj, err := hcl.Parse(d)
	if err != nil {
		return nil, err
	}
	var result Config
	if err := hcl.DecodeObject(&result, obj); err != nil {
		return nil, err
	}
	if result.MaxLeaseTTLRaw != "" {
		if result.MaxLeaseTTL, err = time.ParseDuration(result.MaxLeaseTTLRaw); err != nil {
			return nil, err
		}
	}
	if result.DefaultLeaseTTLRaw != "" {
		if result.DefaultLeaseTTL, err = time.ParseDuration(result.DefaultLeaseTTLRaw); err != nil {
			return nil, err
		}
	}
	list, ok := obj.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
	}
	valid := []string{
		"atlas",
		"backend",
		"ha_backend",
		"listener",
		"cache_size",
		"disable_cache",
		"disable_mlock",
		"telemetry",
		"default_lease_ttl",
		"max_lease_ttl",
		"cluster_name",
	}
	if err := checkHCLKeys(list, valid); err != nil {
		return nil, err
	}
	if o := list.Filter("backend"); len(o.Items) > 0 {
		if err := parseBackends(&result, o); err != nil {
			return nil, fmt.Errorf("error parsing 'backend': %s", err)
		}
	}
	if o := list.Filter("ha_backend"); len(o.Items) > 0 {
		if err := parseHABackends(&result, o); err != nil {
			return nil, fmt.Errorf("error parsing 'ha_backend': %s", err)
		}
	}
	if o := list.Filter("listener"); len(o.Items) > 0 {
		if err := parseListeners(&result, o); err != nil {
			return nil, fmt.Errorf("error parsing 'listener': %s", err)
		}
	}
	if o := list.Filter("telemetry"); len(o.Items) > 0 {
		if err := parseTelemetry(&result, o); err != nil {
			return nil, fmt.Errorf("error parsing 'telemetry': %s", err)
		}
	}
	return &result, nil
}