func DecodeCheckDefinition(raw interface{}) (*CheckDefinition, error) {
	if err := FixupCheckType(raw); err != nil {
		return nil, err
	}
	var md mapstructure.Metadata
	var result CheckDefinition
	msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	})
	if err != nil {
		return nil, err
	}
	if err := msdec.Decode(raw); err != nil {
		return nil, err
	}
	return &result, nil
}
DecodeCheckDefinition is used to decode a check definition
package agent
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/hashicorp/consul/consul"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/hashicorp/consul/watch"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/mitchellh/mapstructure"
)
type PortConfig struct {
	DNS     int  
	HTTP    int  
	HTTPS   int  
	RPC     int  
	SerfLan int `mapstructure:"serf_lan"`  
	SerfWan int `mapstructure:"serf_wan"`  
	Server  int  
}
type AddressConfig struct {
	DNS   string  
	HTTP  string  
	HTTPS string  
	RPC   string  
}
type DNSConfig struct {
	NodeTTL    time.Duration `mapstructure:"-"`
	NodeTTLRaw string        `mapstructure:"node_ttl" json:"-"`
	ServiceTTL    map[string]time.Duration `mapstructure:"-"`
	ServiceTTLRaw map[string]string        `mapstructure:"service_ttl" json:"-"`
	AllowStale bool `mapstructure:"allow_stale"`
	EnableTruncate bool `mapstructure:"enable_truncate"`
	MaxStale    time.Duration `mapstructure:"-"`
	MaxStaleRaw string        `mapstructure:"max_stale" json:"-"`
	OnlyPassing bool `mapstructure:"only_passing"`
}
type Config struct {
	Bootstrap bool `mapstructure:"bootstrap"`
	BootstrapExpect int `mapstructure:"bootstrap_expect"`
	Server bool `mapstructure:"server"`
	Datacenter string `mapstructure:"datacenter"`
	DataDir string `mapstructure:"data_dir"`
	DNSRecursor string `mapstructure:"recursor"`
	DNSRecursors []string `mapstructure:"recursors"`
	DNSConfig DNSConfig `mapstructure:"dns_config"`
	Domain string `mapstructure:"domain"`
	EncryptKey string `mapstructure:"encrypt" json:"-"`
	LogLevel string `mapstructure:"log_level"`
	NodeName string `mapstructure:"node_name"`
	ClientAddr string `mapstructure:"client_addr"`
	BindAddr string `mapstructure:"bind_addr"`
	AdvertiseAddr string `mapstructure:"advertise_addr"`
	Ports PortConfig
	Addresses AddressConfig
	LeaveOnTerm bool `mapstructure:"leave_on_terminate"`
	SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"`
	StatsiteAddr string `mapstructure:"statsite_addr"`
	StatsdAddr string `mapstructure:"statsd_addr"`
	Protocol int `mapstructure:"protocol"`
	EnableDebug bool `mapstructure:"enable_debug"`
	VerifyIncoming bool `mapstructure:"verify_incoming"`
	VerifyOutgoing bool `mapstructure:"verify_outgoing"`
	CAFile string `mapstructure:"ca_file"`
	CertFile string `mapstructure:"cert_file"`
	KeyFile string `mapstructure:"key_file"`
	ServerName string `mapstructure:"server_name"`
	StartJoin []string `mapstructure:"start_join"`
	StartJoinWan []string `mapstructure:"start_join_wan"`
	RetryJoin []string `mapstructure:"retry_join"`
	RetryMaxAttempts int `mapstructure:"retry_max"`
	RetryInterval    time.Duration `mapstructure:"-" json:"-"`
	RetryIntervalRaw string        `mapstructure:"retry_interval"`
	RetryJoinWan []string `mapstructure:"retry_join_wan"`
	RetryMaxAttemptsWan int `mapstructure:"retry_max_wan"`
	RetryIntervalWan    time.Duration `mapstructure:"-" json:"-"`
	RetryIntervalWanRaw string        `mapstructure:"retry_interval_wan"`
	UiDir string `mapstructure:"ui_dir"`
	PidFile string `mapstructure:"pid_file"`
	EnableSyslog bool `mapstructure:"enable_syslog"`
	SyslogFacility string `mapstructure:"syslog_facility"`
	RejoinAfterLeave bool `mapstructure:"rejoin_after_leave"`
	CheckUpdateInterval    time.Duration `mapstructure:"-"`
	CheckUpdateIntervalRaw string        `mapstructure:"check_update_interval" json:"-"`
	ACLToken string `mapstructure:"acl_token" json:"-"`
	ACLMasterToken string `mapstructure:"acl_master_token" json:"-"`
	ACLDatacenter string `mapstructure:"acl_datacenter"`
	ACLTTL    time.Duration `mapstructure:"-"`
	ACLTTLRaw string        `mapstructure:"acl_ttl"`
	ACLDefaultPolicy string `mapstructure:"acl_default_policy"`
	ACLDownPolicy string `mapstructure:"acl_down_policy"`
	Watches []map[string]interface{} `mapstructure:"watches"`
	DisableRemoteExec bool `mapstructure:"disable_remote_exec"`
	DisableUpdateCheck bool `mapstructure:"disable_update_check"`
	DisableAnonymousSignature bool `mapstructure:"disable_anonymous_signature"`
	HTTPAPIResponseHeaders map[string]string `mapstructure:"http_api_response_headers"`
	AEInterval time.Duration `mapstructure:"-" json:"-"`
	Checks []*CheckDefinition `mapstructure:"-" json:"-"`
	Services []*ServiceDefinition `mapstructure:"-" json:"-"`
	ConsulConfig *consul.Config `mapstructure:"-" json:"-"`
	Revision string `mapstructure:"-"`
	Version string `mapstructure:"-"`
	VersionPrerelease string `mapstructure:"-"`
	WatchPlans []*watch.WatchPlan `mapstructure:"-" json:"-"`
}
func unixSocketAddr(addr string) (string, bool) {
	if !strings.HasPrefix(addr, "unix://") {
		return "", false
	}
	return strings.TrimPrefix(addr, "unix://"), true
}
type dirEnts []os.FileInfo
func DefaultConfig() *Config {
	return &Config{
		Bootstrap:       false,
		BootstrapExpect: 0,
		Server:          false,
		Datacenter:      consul.DefaultDC,
		Domain:          "consul.",
		LogLevel:        "INFO",
		ClientAddr:      "127.0.0.1",
		BindAddr:        "0.0.0.0",
		Ports: PortConfig{
			DNS:     8600,
			HTTP:    8500,
			HTTPS:   -1,
			RPC:     8400,
			SerfLan: consul.DefaultLANSerfPort,
			SerfWan: consul.DefaultWANSerfPort,
			Server:  8300,
		},
		DNSConfig: DNSConfig{
			MaxStale: 5 * time.Second,
		},
		SyslogFacility:      "LOCAL0",
		Protocol:            consul.ProtocolVersionMax,
		CheckUpdateInterval: 5 * time.Minute,
		AEInterval:          time.Minute,
		ACLTTL:              30 * time.Second,
		ACLDownPolicy:       "extend-cache",
		ACLDefaultPolicy:    "allow",
		RetryInterval:       30 * time.Second,
		RetryIntervalWan:    30 * time.Second,
	}
}
func (c *Config) EncryptBytes() ([]byte, error) {
	return base64.StdEncoding.DecodeString(c.EncryptKey)
}
func (c *Config) ClientListener(override string, port int) (net.Addr, error) {
	var addr string
	if override != "" {
		addr = override
	} else {
		addr = c.ClientAddr
	}
	if path, ok := unixSocketAddr(addr); ok {
		return &net.UnixAddr{Name: path, Net: "unix"}, nil
	}
	ip := net.ParseIP(addr)
	if ip == nil {
		return nil, fmt.Errorf("Failed to parse IP: %v", addr)
	}
	return &net.TCPAddr{IP: ip, Port: port}, nil
}
func DecodeConfig(r io.Reader) (*Config, error) {
	var raw interface{}
	var result Config
	dec := json.NewDecoder(r)
	if err := dec.Decode(&raw); err != nil {
		return nil, err
	}
	if obj, ok := raw.(map[string]interface{}); ok {
		if sub, ok := obj["services"]; ok {
			if list, ok := sub.([]interface{}); ok {
				for _, srv := range list {
					service, err := DecodeServiceDefinition(srv)
					if err != nil {
						return nil, err
					}
					result.Services = append(result.Services, service)
				}
			}
		}
		if sub, ok := obj["service"]; ok {
			service, err := DecodeServiceDefinition(sub)
			if err != nil {
				return nil, err
			}
			result.Services = append(result.Services, service)
		}
		if sub, ok := obj["checks"]; ok {
			if list, ok := sub.([]interface{}); ok {
				for _, chk := range list {
					check, err := DecodeCheckDefinition(chk)
					if err != nil {
						return nil, err
					}
					result.Checks = append(result.Checks, check)
				}
			}
		}
		if sub, ok := obj["check"]; ok {
			check, err := DecodeCheckDefinition(sub)
			if err != nil {
				return nil, err
			}
			result.Checks = append(result.Checks, check)
		}
	}
	var md mapstructure.Metadata
	msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	})
	if err != nil {
		return nil, err
	}
	if err := msdec.Decode(raw); err != nil {
		return nil, err
	}
	allowedKeys := []string{"service", "services", "check", "checks"}
	var unused []string
	for _, field := range md.Unused {
		if !strContains(allowedKeys, field) {
			unused = append(unused, field)
		}
	}
	if len(unused) > 0 {
		return nil, fmt.Errorf("Config has invalid keys: %s", strings.Join(unused, ","))
	}
	if raw := result.DNSConfig.NodeTTLRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("NodeTTL invalid: %v", err)
		}
		result.DNSConfig.NodeTTL = dur
	}
	if raw := result.DNSConfig.MaxStaleRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("MaxStale invalid: %v", err)
		}
		result.DNSConfig.MaxStale = dur
	}
	if len(result.DNSConfig.ServiceTTLRaw) != 0 {
		if result.DNSConfig.ServiceTTL == nil {
			result.DNSConfig.ServiceTTL = make(map[string]time.Duration)
		}
		for service, raw := range result.DNSConfig.ServiceTTLRaw {
			dur, err := time.ParseDuration(raw)
			if err != nil {
				return nil, fmt.Errorf("ServiceTTL %s invalid: %v", service, err)
			}
			result.DNSConfig.ServiceTTL[service] = dur
		}
	}
	if raw := result.CheckUpdateIntervalRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("CheckUpdateInterval invalid: %v", err)
		}
		result.CheckUpdateInterval = dur
	}
	if raw := result.ACLTTLRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("ACL TTL invalid: %v", err)
		}
		result.ACLTTL = dur
	}
	if raw := result.RetryIntervalRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("RetryInterval invalid: %v", err)
		}
		result.RetryInterval = dur
	}
	if raw := result.RetryIntervalWanRaw; raw != "" {
		dur, err := time.ParseDuration(raw)
		if err != nil {
			return nil, fmt.Errorf("RetryIntervalWan invalid: %v", err)
		}
		result.RetryIntervalWan = dur
	}
	if result.DNSRecursor != "" {
		result.DNSRecursors = append(result.DNSRecursors, result.DNSRecursor)
	}
	return &result, nil
}
func DecodeServiceDefinition(raw interface{}) (*ServiceDefinition, error) {
	var sub interface{}
	rawMap, ok := raw.(map[string]interface{})
	if !ok {
		goto AFTER_FIX
	}
	if _, ok := rawMap["tags"]; !ok {
		if tag, ok := rawMap["tag"]; ok {
			rawMap["tags"] = []interface{}{tag}
		}
	}
	for k, v := range rawMap {
		if strings.ToLower(k) == "check" {
			sub = v
			break
		}
	}
	if sub == nil {
		goto AFTER_FIX
	}
	if err := FixupCheckType(sub); err != nil {
		return nil, err
	}
AFTER_FIX:
	var md mapstructure.Metadata
	var result ServiceDefinition
	msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: &md,
		Result:   &result,
	})
	if err != nil {
		return nil, err
	}
	if err := msdec.Decode(raw); err != nil {
		return nil, err
	}
	return &result, nil
}
func FixupCheckType(raw interface{}) error {
	rawMap, ok := raw.(map[string]interface{})
	if !ok {
		return nil
	}
	var ttlKey string
	for k, _ := range rawMap {
		if strings.ToLower(k) == "ttl" {
			ttlKey = k
		}
	}
	var intervalKey string
	for k, _ := range rawMap {
		if strings.ToLower(k) == "interval" {
			intervalKey = k
		}
	}
	if ttl, ok := rawMap[ttlKey]; ok {
		ttlS, ok := ttl.(string)
		if ok {
			if dur, err := time.ParseDuration(ttlS); err != nil {
				return err
			} else {
				rawMap[ttlKey] = dur
			}
		}
	}
	if interval, ok := rawMap[intervalKey]; ok {
		intervalS, ok := interval.(string)
		if ok {
			if dur, err := time.ParseDuration(intervalS); err != nil {
				return err
			} else {
				rawMap[intervalKey] = dur
			}
		}
	}
	return nil
}