diff --git a/glide.lock b/glide.lock index 8eea6d7c9d..77902429bb 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: c7cb14f4249738a47020f9dc1964832921c3f5b8bf5a1c50f5b2fa15eaebb6fe -updated: 2016-12-26T10:22:49.439519344+05:30 +hash: 88aef2926941901e180522117085c9a72141a988d1284778fc867f704e43dab0 +updated: 2017-01-03T14:38:14.561105912-05:00 imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -190,7 +190,7 @@ imports: - name: github.com/docker/go-units version: 0bbddae09c5a5419a8c6dcdd7ff90da3d450393b - name: github.com/docker/libcompose - version: fbdac0a6a80837c63eb6c8f43514f7bb3f32df6c + version: 2e320f69cafc70683514ee6226d40e2fe868e6d3 subpackages: - config - logger @@ -210,7 +210,7 @@ imports: - name: github.com/evanphx/json-patch version: 465937c80b3c07a7c7ad20cc934898646a91c1de - name: github.com/fatih/structs - version: dc3312cb1a4513a366c4c9e622ad55c32df12ed3 + version: a720dfa8df582c51dee1b36feabb906bde1588bd - name: github.com/flynn/go-shlex version: 3f9db97f856818214da2e1057f8ad84803971cff - name: github.com/fsnotify/fsnotify @@ -405,7 +405,7 @@ imports: - name: github.com/pelletier/go-buffruneio version: df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d - name: github.com/pelletier/go-toml - version: 017119f7a78a0b5fc0ea39ef6be09f03acf3345d + version: 439fbba1f887c286024370cb4f281ba815c4c7d7 - name: github.com/prometheus/client_golang version: e51041b3fa41cece0dca035740ba6411905be473 subpackages: @@ -425,15 +425,15 @@ imports: - name: github.com/Sirupsen/logrus version: 881bee4e20a5d11a6a88a5667c6f292072ac1963 - name: github.com/spf13/afero - version: 2f30b2a92c0e5700bcfe4715891adb1f2a7a406d + version: 90dd71edc4d0a8b3511dc12ea15d617d03be09e0 subpackages: - mem - name: github.com/spf13/cast - version: 24b6558033ffe202bf42f0f3b870dcc798dd2ba8 + version: 56a7ecbeb18dde53c6db4bd96b541fd9741b8d44 - name: github.com/spf13/cobra - version: b62566898a99f2db9c68ed0026aa0a052e59678d + version: 1dd5ff2e11b6dca62fdcb275eb804b94607d8b06 - name: github.com/spf13/jwalterweatherman - version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 + version: c3ba57c1a2668b9484e43bf9932c88f71992f977 - name: github.com/spf13/pflag version: 25f8b5b07aece3207895bf19f7ab517eb3b22a40 - name: github.com/spf13/viper @@ -447,7 +447,7 @@ imports: - name: github.com/xeipuuv/gojsonreference version: e02fc20de94c78484cd5ffb007f8af96be030a45 - name: github.com/xeipuuv/gojsonschema - version: 59f99ebfe5f712a0055e321231fc3be94e4e00b2 + version: f06f290571ce81ab347174c6f7ad2e1865af41a7 - name: golang.org/x/net version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 subpackages: diff --git a/glide.yaml b/glide.yaml index 491060be14..1af47f1326 100644 --- a/glide.yaml +++ b/glide.yaml @@ -11,7 +11,7 @@ import: - package: github.com/spf13/viper - package: github.com/docker/libcompose - version: fbdac0a6a80837c63eb6c8f43514f7bb3f32df6c + version: 2e320f69cafc70683514ee6226d40e2fe868e6d3 subpackages: - config - lookup diff --git a/vendor/github.com/docker/libcompose/config/interpolation.go b/vendor/github.com/docker/libcompose/config/interpolation.go index fc420de9a0..66c987a713 100644 --- a/vendor/github.com/docker/libcompose/config/interpolation.go +++ b/vendor/github.com/docker/libcompose/config/interpolation.go @@ -102,7 +102,7 @@ func parseLine(line string, mapping func(string) string) (string, bool) { return buffer.String(), true } -func parseConfig(option, service string, data *interface{}, mapping func(string) string) error { +func parseConfig(key string, data *interface{}, mapping func(string) string) error { switch typedData := (*data).(type) { case string: var success bool @@ -110,11 +110,11 @@ func parseConfig(option, service string, data *interface{}, mapping func(string) *data, success = parseLine(typedData, mapping) if !success { - return fmt.Errorf("Invalid interpolation format for \"%s\" option in service \"%s\": \"%s\"", option, service, typedData) + return fmt.Errorf("Invalid interpolation format for key \"%s\": \"%s\"", key, typedData) } case []interface{}: for k, v := range typedData { - err := parseConfig(option, service, &v, mapping) + err := parseConfig(key, &v, mapping) if err != nil { return err @@ -124,7 +124,7 @@ func parseConfig(option, service string, data *interface{}, mapping func(string) } case map[interface{}]interface{}: for k, v := range typedData { - err := parseConfig(option, service, &v, mapping) + err := parseConfig(key, &v, mapping) if err != nil { return err @@ -137,33 +137,21 @@ func parseConfig(option, service string, data *interface{}, mapping func(string) return nil } -// Interpolate replaces variables in the raw map representation of the project file -func Interpolate(environmentLookup EnvironmentLookup, config *RawServiceMap) error { - for k, v := range *config { - for k2, v2 := range v { - err := parseConfig(k2, k, &v2, func(s string) string { - values := environmentLookup.Lookup(s, k, nil) +// Interpolate replaces variables in a map entry +func Interpolate(key string, data *interface{}, environmentLookup EnvironmentLookup) error { + return parseConfig(key, data, func(s string) string { + values := environmentLookup.Lookup(s, nil) - if len(values) == 0 { - logrus.Warnf("The %s variable is not set. Substituting a blank string.", s) - return "" - } - - // Use first result if many are given - value := values[0] - - // Environment variables come in key=value format - // Return everything past first '=' - return strings.SplitN(value, "=", 2)[1] - }) - - if err != nil { - return err - } - - (*config)[k][k2] = v2 + if len(values) == 0 { + logrus.Warnf("The %s variable is not set. Substituting a blank string.", s) + return "" } - } - return nil + // Use first result if many are given + value := values[0] + + // Environment variables come in key=value format + // Return everything past first '=' + return strings.SplitN(value, "=", 2)[1] + }) } diff --git a/vendor/github.com/docker/libcompose/config/merge.go b/vendor/github.com/docker/libcompose/config/merge.go index 1c56d7be0e..f40619d4c2 100644 --- a/vendor/github.com/docker/libcompose/config/merge.go +++ b/vendor/github.com/docker/libcompose/config/merge.go @@ -29,18 +29,8 @@ func CreateConfig(bytes []byte) (*Config, error) { if err := yaml.Unmarshal(bytes, &config); err != nil { return nil, err } - if config.Version == "2" { - for key, value := range config.Networks { - if value == nil { - config.Networks[key] = &NetworkConfig{} - } - } - for key, value := range config.Volumes { - if value == nil { - config.Volumes[key] = &VolumeConfig{} - } - } - } else { + + if config.Version != "2" { var baseRawServices RawServiceMap if err := yaml.Unmarshal(bytes, &baseRawServices); err != nil { return nil, err @@ -48,6 +38,13 @@ func CreateConfig(bytes []byte) (*Config, error) { config.Services = baseRawServices } + if config.Volumes == nil { + config.Volumes = make(map[string]interface{}) + } + if config.Networks == nil { + config.Networks = make(map[string]interface{}) + } + return &config, nil } @@ -63,6 +60,34 @@ func Merge(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup } baseRawServices := config.Services + if options.Interpolate { + if err := InterpolateRawServiceMap(&baseRawServices, environmentLookup); err != nil { + return "", nil, nil, nil, err + } + + for k, v := range config.Volumes { + if err := Interpolate(k, &v, environmentLookup); err != nil { + return "", nil, nil, nil, err + } + config.Volumes[k] = v + } + + for k, v := range config.Networks { + if err := Interpolate(k, &v, environmentLookup); err != nil { + return "", nil, nil, nil, err + } + config.Networks[k] = v + } + } + + if options.Preprocess != nil { + var err error + baseRawServices, err = options.Preprocess(baseRawServices) + if err != nil { + return "", nil, nil, nil, err + } + } + var serviceConfigs map[string]*ServiceConfig if config.Version == "2" { var err error @@ -91,7 +116,29 @@ func Merge(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup } } - return config.Version, serviceConfigs, config.Volumes, config.Networks, nil + var volumes map[string]*VolumeConfig + var networks map[string]*NetworkConfig + if err := utils.Convert(config.Volumes, &volumes); err != nil { + return "", nil, nil, nil, err + } + if err := utils.Convert(config.Networks, &networks); err != nil { + return "", nil, nil, nil, err + } + + return config.Version, serviceConfigs, volumes, networks, nil +} + +// InterpolateRawServiceMap replaces varialbse in raw service map struct based on environment lookup +func InterpolateRawServiceMap(baseRawServices *RawServiceMap, environmentLookup EnvironmentLookup) error { + for k, v := range *baseRawServices { + for k2, v2 := range v { + if err := Interpolate(k2, &v2, environmentLookup); err != nil { + return err + } + (*baseRawServices)[k][k2] = v2 + } + } + return nil } func adjustValues(configs map[string]*ServiceConfig) { diff --git a/vendor/github.com/docker/libcompose/config/merge_v1.go b/vendor/github.com/docker/libcompose/config/merge_v1.go index c1eeeae730..dab39144f0 100644 --- a/vendor/github.com/docker/libcompose/config/merge_v1.go +++ b/vendor/github.com/docker/libcompose/config/merge_v1.go @@ -10,20 +10,6 @@ import ( // MergeServicesV1 merges a v1 compose file into an existing set of service configs func MergeServicesV1(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, datas RawServiceMap, options *ParseOptions) (map[string]*ServiceConfigV1, error) { - if options.Interpolate { - if err := Interpolate(environmentLookup, &datas); err != nil { - return nil, err - } - } - - if options.Preprocess != nil { - var err error - datas, err = options.Preprocess(datas) - if err != nil { - return nil, err - } - } - if options.Validate { if err := validate(datas); err != nil { return nil, err @@ -117,8 +103,7 @@ func parseV1(resourceLookup ResourceLookup, environmentLookup EnvironmentLookup, baseRawServices := config.Services if options.Interpolate { - err = Interpolate(environmentLookup, &baseRawServices) - if err != nil { + if err = InterpolateRawServiceMap(&baseRawServices, environmentLookup); err != nil { return nil, err } } diff --git a/vendor/github.com/docker/libcompose/config/merge_v2.go b/vendor/github.com/docker/libcompose/config/merge_v2.go index e9e5678ba6..1f7bbd341c 100644 --- a/vendor/github.com/docker/libcompose/config/merge_v2.go +++ b/vendor/github.com/docker/libcompose/config/merge_v2.go @@ -3,6 +3,7 @@ package config import ( "fmt" "path" + "strings" "github.com/Sirupsen/logrus" "github.com/docker/libcompose/utils" @@ -10,20 +11,6 @@ import ( // MergeServicesV2 merges a v2 compose file into an existing set of service configs func MergeServicesV2(existingServices *ServiceConfigs, environmentLookup EnvironmentLookup, resourceLookup ResourceLookup, file string, datas RawServiceMap, options *ParseOptions) (map[string]*ServiceConfig, error) { - if options.Interpolate { - if err := Interpolate(environmentLookup, &datas); err != nil { - return nil, err - } - } - - if options.Preprocess != nil { - var err error - datas, err = options.Preprocess(datas) - if err != nil { - return nil, err - } - } - if options.Validate { if err := validateV2(datas); err != nil { return nil, err @@ -49,6 +36,19 @@ func MergeServicesV2(existingServices *ServiceConfigs, environmentLookup Environ datas[name] = data } + if options.Validate { + var errs []string + for name, data := range datas { + err := validateServiceConstraintsv2(data, name) + if err != nil { + errs = append(errs, err.Error()) + } + } + if len(errs) != 0 { + return nil, fmt.Errorf(strings.Join(errs, "\n")) + } + } + serviceConfigs := make(map[string]*ServiceConfig) if err := utils.Convert(datas, &serviceConfigs); err != nil { return nil, err @@ -108,8 +108,7 @@ func parseV2(resourceLookup ResourceLookup, environmentLookup EnvironmentLookup, baseRawServices := config.Services if options.Interpolate { - err = Interpolate(environmentLookup, &baseRawServices) - if err != nil { + if err = InterpolateRawServiceMap(&baseRawServices, environmentLookup); err != nil { return nil, err } } diff --git a/vendor/github.com/docker/libcompose/config/types.go b/vendor/github.com/docker/libcompose/config/types.go index d548e20677..18291ac722 100644 --- a/vendor/github.com/docker/libcompose/config/types.go +++ b/vendor/github.com/docker/libcompose/config/types.go @@ -8,7 +8,7 @@ import ( // EnvironmentLookup defines methods to provides environment variable loading. type EnvironmentLookup interface { - Lookup(key, serviceName string, config *ServiceConfig) []string + Lookup(key string, config *ServiceConfig) []string } // ResourceLookup defines methods to provides file loading. @@ -30,22 +30,27 @@ type ServiceConfigV1 struct { ContainerName string `yaml:"container_name,omitempty"` Devices []string `yaml:"devices,omitempty"` DNS yaml.Stringorslice `yaml:"dns,omitempty"` + DNSOpts []string `yaml:"dns_opt,omitempty"` DNSSearch yaml.Stringorslice `yaml:"dns_search,omitempty"` Dockerfile string `yaml:"dockerfile,omitempty"` DomainName string `yaml:"domainname,omitempty"` Entrypoint yaml.Command `yaml:"entrypoint,flow,omitempty"` EnvFile yaml.Stringorslice `yaml:"env_file,omitempty"` Environment yaml.MaporEqualSlice `yaml:"environment,omitempty"` + GroupAdd []string `yaml:"group_add,omitempty"` Hostname string `yaml:"hostname,omitempty"` Image string `yaml:"image,omitempty"` + Isolation string `yaml:"isolation,omitempty"` Labels yaml.SliceorMap `yaml:"labels,omitempty"` Links yaml.MaporColonSlice `yaml:"links,omitempty"` LogDriver string `yaml:"log_driver,omitempty"` MacAddress string `yaml:"mac_address,omitempty"` - MemLimit yaml.StringorInt `yaml:"mem_limit,omitempty"` - MemSwapLimit yaml.StringorInt `yaml:"memswap_limit,omitempty"` + MemLimit yaml.MemStringorInt `yaml:"mem_limit,omitempty"` + MemSwapLimit yaml.MemStringorInt `yaml:"memswap_limit,omitempty"` + MemSwappiness yaml.MemStringorInt `yaml:"mem_swappiness,omitempty"` Name string `yaml:"name,omitempty"` Net string `yaml:"net,omitempty"` + OomScoreAdj yaml.StringorInt `yaml:"oom_score_adj,omitempty"` Pid string `yaml:"pid,omitempty"` Uts string `yaml:"uts,omitempty"` Ipc string `yaml:"ipc,omitempty"` @@ -53,9 +58,11 @@ type ServiceConfigV1 struct { Privileged bool `yaml:"privileged,omitempty"` Restart string `yaml:"restart,omitempty"` ReadOnly bool `yaml:"read_only,omitempty"` - ShmSize yaml.StringorInt `yaml:"shm_size,omitempty"` + ShmSize yaml.MemStringorInt `yaml:"shm_size,omitempty"` StdinOpen bool `yaml:"stdin_open,omitempty"` SecurityOpt []string `yaml:"security_opt,omitempty"` + StopSignal string `yaml:"stop_signal,omitempty"` + Tmpfs yaml.Stringorslice `yaml:"tmpfs,omitempty"` Tty bool `yaml:"tty,omitempty"` User string `yaml:"user,omitempty"` VolumeDriver string `yaml:"volume_driver,omitempty"` @@ -89,6 +96,7 @@ type ServiceConfig struct { Devices []string `yaml:"devices,omitempty"` DependsOn []string `yaml:"depends_on,omitempty"` DNS yaml.Stringorslice `yaml:"dns,omitempty"` + DNSOpts []string `yaml:"dns_opt,omitempty"` DNSSearch yaml.Stringorslice `yaml:"dns_search,omitempty"` DomainName string `yaml:"domainname,omitempty"` Entrypoint yaml.Command `yaml:"entrypoint,flow,omitempty"` @@ -98,23 +106,28 @@ type ServiceConfig struct { Extends yaml.MaporEqualSlice `yaml:"extends,omitempty"` ExternalLinks []string `yaml:"external_links,omitempty"` ExtraHosts []string `yaml:"extra_hosts,omitempty"` + GroupAdd []string `yaml:"group_add,omitempty"` Image string `yaml:"image,omitempty"` + Isolation string `yaml:"isolation,omitempty"` Hostname string `yaml:"hostname,omitempty"` Ipc string `yaml:"ipc,omitempty"` Labels yaml.SliceorMap `yaml:"labels,omitempty"` Links yaml.MaporColonSlice `yaml:"links,omitempty"` Logging Log `yaml:"logging,omitempty"` MacAddress string `yaml:"mac_address,omitempty"` - MemLimit yaml.StringorInt `yaml:"mem_limit,omitempty"` - MemSwapLimit yaml.StringorInt `yaml:"memswap_limit,omitempty"` + MemLimit yaml.MemStringorInt `yaml:"mem_limit,omitempty"` + MemSwapLimit yaml.MemStringorInt `yaml:"memswap_limit,omitempty"` + MemSwappiness yaml.MemStringorInt `yaml:"mem_swappiness,omitempty"` NetworkMode string `yaml:"network_mode,omitempty"` Networks *yaml.Networks `yaml:"networks,omitempty"` + OomScoreAdj yaml.StringorInt `yaml:"oom_score_adj,omitempty"` Pid string `yaml:"pid,omitempty"` Ports []string `yaml:"ports,omitempty"` Privileged bool `yaml:"privileged,omitempty"` SecurityOpt []string `yaml:"security_opt,omitempty"` - ShmSize yaml.StringorInt `yaml:"shm_size,omitempty"` + ShmSize yaml.MemStringorInt `yaml:"shm_size,omitempty"` StopSignal string `yaml:"stop_signal,omitempty"` + Tmpfs yaml.Stringorslice `yaml:"tmpfs,omitempty"` VolumeDriver string `yaml:"volume_driver,omitempty"` Volumes *yaml.Volumes `yaml:"volumes,omitempty"` VolumesFrom []string `yaml:"volumes_from,omitempty"` @@ -159,10 +172,10 @@ type NetworkConfig struct { // Config holds libcompose top level configuration type Config struct { - Version string `yaml:"version,omitempty"` - Services RawServiceMap `yaml:"services,omitempty"` - Volumes map[string]*VolumeConfig `yaml:"volumes,omitempty"` - Networks map[string]*NetworkConfig `yaml:"networks,omitempty"` + Version string `yaml:"version,omitempty"` + Services RawServiceMap `yaml:"services,omitempty"` + Volumes map[string]interface{} `yaml:"volumes,omitempty"` + Networks map[string]interface{} `yaml:"networks,omitempty"` } // NewServiceConfigs initializes a new Configs struct diff --git a/vendor/github.com/docker/libcompose/config/validation.go b/vendor/github.com/docker/libcompose/config/validation.go index 3f5c7c1adc..a459c1ed2c 100644 --- a/vendor/github.com/docker/libcompose/config/validation.go +++ b/vendor/github.com/docker/libcompose/config/validation.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/docker/libcompose/utils" "github.com/xeipuuv/gojsonschema" ) @@ -70,55 +71,22 @@ func getValue(val interface{}, context string) string { return "" } -// Converts map[interface{}]interface{} to map[string]interface{} recursively -// gojsonschema only accepts map[string]interface{} func convertServiceMapKeysToStrings(serviceMap RawServiceMap) RawServiceMap { newServiceMap := make(RawServiceMap) - for k, v := range serviceMap { newServiceMap[k] = convertServiceKeysToStrings(v) } - return newServiceMap } func convertServiceKeysToStrings(service RawService) RawService { newService := make(RawService) - for k, v := range service { - newService[k] = convertKeysToStrings(v) + newService[k] = utils.ConvertKeysToStrings(v) } - return newService } -func convertKeysToStrings(item interface{}) interface{} { - switch typedDatas := item.(type) { - - case map[interface{}]interface{}: - newMap := make(map[string]interface{}) - - for key, value := range typedDatas { - stringKey := key.(string) - newMap[stringKey] = convertKeysToStrings(value) - } - return newMap - - case []interface{}: - // newArray := make([]interface{}, 0) will cause golint to complain - var newArray []interface{} - newArray = make([]interface{}, 0) - - for _, value := range typedDatas { - newArray = append(newArray, convertKeysToStrings(value)) - } - return newArray - - default: - return item - } -} - var dockerConfigHints = map[string]string{ "cpu_share": "cpu_shares", "add_host": "extra_hosts", @@ -319,3 +287,36 @@ func validateServiceConstraints(service RawService, serviceName string) error { return nil } + +func validateServiceConstraintsv2(service RawService, serviceName string) error { + if err := setupSchemaLoaders(servicesSchemaDataV2, &schemaV2, &schemaLoaderV2, &constraintSchemaLoaderV2); err != nil { + return err + } + + service = convertServiceKeysToStrings(service) + + var validationErrors []string + + dataLoader := gojsonschema.NewGoLoader(service) + + result, err := gojsonschema.Validate(constraintSchemaLoaderV2, dataLoader) + if err != nil { + return err + } + + if !result.Valid() { + for _, err := range result.Errors() { + if err.Type() == "required" { + _, containsImage := service["image"] + _, containsBuild := service["build"] + + if containsBuild || !containsImage && !containsBuild { + validationErrors = append(validationErrors, fmt.Sprintf("Service '%s' has neither an image nor a build context specified. At least one must be provided.", serviceName)) + } + } + } + return fmt.Errorf(strings.Join(validationErrors, "\n")) + } + + return nil +} diff --git a/vendor/github.com/docker/libcompose/lookup/composable.go b/vendor/github.com/docker/libcompose/lookup/composable.go index c48987fef4..ff76480b37 100644 --- a/vendor/github.com/docker/libcompose/lookup/composable.go +++ b/vendor/github.com/docker/libcompose/lookup/composable.go @@ -13,10 +13,10 @@ type ComposableEnvLookup struct { // Lookup creates a string slice of string containing a "docker-friendly" environment string // in the form of 'key=value'. It loop through the lookups and returns the latest value if // more than one lookup return a result. -func (l *ComposableEnvLookup) Lookup(key, serviceName string, config *config.ServiceConfig) []string { +func (l *ComposableEnvLookup) Lookup(key string, config *config.ServiceConfig) []string { result := []string{} for _, lookup := range l.Lookups { - env := lookup.Lookup(key, serviceName, config) + env := lookup.Lookup(key, config) if len(env) == 1 { result = env } diff --git a/vendor/github.com/docker/libcompose/lookup/envfile.go b/vendor/github.com/docker/libcompose/lookup/envfile.go index 65bd986747..62241255cc 100644 --- a/vendor/github.com/docker/libcompose/lookup/envfile.go +++ b/vendor/github.com/docker/libcompose/lookup/envfile.go @@ -16,7 +16,7 @@ type EnvfileLookup struct { // Lookup creates a string slice of string containing a "docker-friendly" environment string // in the form of 'key=value'. It gets environment values using a '.env' file in the specified // path. -func (l *EnvfileLookup) Lookup(key, serviceName string, config *config.ServiceConfig) []string { +func (l *EnvfileLookup) Lookup(key string, config *config.ServiceConfig) []string { envs, err := opts.ParseEnvFile(l.Path) if err != nil { return []string{} diff --git a/vendor/github.com/docker/libcompose/lookup/simple_env.go b/vendor/github.com/docker/libcompose/lookup/simple_env.go index 6f8c25bf20..40ce128433 100644 --- a/vendor/github.com/docker/libcompose/lookup/simple_env.go +++ b/vendor/github.com/docker/libcompose/lookup/simple_env.go @@ -15,7 +15,7 @@ type OsEnvLookup struct { // in the form of 'key=value'. It gets environment values using os.Getenv. // If the os environment variable does not exists, the slice is empty. serviceName and config // are not used at all in this implementation. -func (o *OsEnvLookup) Lookup(key, serviceName string, config *config.ServiceConfig) []string { +func (o *OsEnvLookup) Lookup(key string, config *config.ServiceConfig) []string { ret := os.Getenv(key) if ret == "" { return []string{} diff --git a/vendor/github.com/docker/libcompose/project/project.go b/vendor/github.com/docker/libcompose/project/project.go index d74398dd31..48a4242829 100644 --- a/vendor/github.com/docker/libcompose/project/project.go +++ b/vendor/github.com/docker/libcompose/project/project.go @@ -141,7 +141,7 @@ func (p *Project) CreateService(name string) (Service, error) { env = parts[0] } - for _, value := range p.context.EnvironmentLookup.Lookup(env, name, &config) { + for _, value := range p.context.EnvironmentLookup.Lookup(env, &config) { parsedEnv = append(parsedEnv, value) } } @@ -151,7 +151,7 @@ func (p *Project) CreateService(name string) (Service, error) { // check the environment for extra build Args that are set but not given a value in the compose file for arg, value := range config.Build.Args { if value == "\x00" { - envValue := p.context.EnvironmentLookup.Lookup(arg, name, &config) + envValue := p.context.EnvironmentLookup.Lookup(arg, &config) // depending on what we get back we do different things switch l := len(envValue); l { case 0: @@ -160,7 +160,7 @@ func (p *Project) CreateService(name string) (Service, error) { parts := strings.SplitN(envValue[0], "=", 2) config.Build.Args[parts[0]] = parts[1] default: - return nil, fmt.Errorf("Tried to set Build Arg %#v to multi-value %#v.", arg, envValue) + return nil, fmt.Errorf("tried to set Build Arg %#v to multi-value %#v", arg, envValue) } } } @@ -267,10 +267,12 @@ func (p *Project) handleNetworkConfig() { serviceConfig.Networks = &yaml.Networks{ Networks: []*yaml.Network{ { - Name: "default", + Name: "default", + RealName: fmt.Sprintf("%s_%s", p.Name, "default"), }, }, } + p.AddNetworkConfig("default", &config.NetworkConfig{}) } // Consolidate the name of the network // FIXME(vdemeester) probably shouldn't be there, maybe move that to interface/factory diff --git a/vendor/github.com/docker/libcompose/project/project_create.go b/vendor/github.com/docker/libcompose/project/project_create.go index 7efe2b3c12..9141b719cb 100644 --- a/vendor/github.com/docker/libcompose/project/project_create.go +++ b/vendor/github.com/docker/libcompose/project/project_create.go @@ -14,6 +14,9 @@ func (p *Project) Create(ctx context.Context, options options.Create, services . if options.NoRecreate && options.ForceRecreate { return fmt.Errorf("no-recreate and force-recreate cannot be combined") } + if err := p.initialize(ctx); err != nil { + return err + } return p.perform(events.ProjectCreateStart, events.ProjectCreateDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) { wrapper.Do(wrappers, events.ServiceCreateStart, events.ServiceCreate, func(service Service) error { return service.Create(ctx, options) diff --git a/vendor/github.com/docker/libcompose/project/project_ps.go b/vendor/github.com/docker/libcompose/project/project_ps.go index f02c3416f4..2c69018bc4 100644 --- a/vendor/github.com/docker/libcompose/project/project_ps.go +++ b/vendor/github.com/docker/libcompose/project/project_ps.go @@ -1,13 +1,17 @@ package project -import ( - "golang.org/x/net/context" -) +import "golang.org/x/net/context" // Ps list containers for the specified services. func (p *Project) Ps(ctx context.Context, services ...string) (InfoSet, error) { allInfo := InfoSet{} - for _, name := range p.ServiceConfigs.Keys() { + + if len(services) == 0 { + services = p.ServiceConfigs.Keys() + } + + for _, name := range services { + service, err := p.CreateService(name) if err != nil { return nil, err diff --git a/vendor/github.com/docker/libcompose/utils/util.go b/vendor/github.com/docker/libcompose/utils/util.go index 51383cf3b7..971f943357 100644 --- a/vendor/github.com/docker/libcompose/utils/util.go +++ b/vendor/github.com/docker/libcompose/utils/util.go @@ -135,3 +135,28 @@ func Merge(coll1, coll2 []string) []string { } return r } + +// ConvertKeysToStrings converts map[interface{}] to map[string] recursively +func ConvertKeysToStrings(item interface{}) interface{} { + switch typedDatas := item.(type) { + case map[string]interface{}: + for key, value := range typedDatas { + typedDatas[key] = ConvertKeysToStrings(value) + } + return typedDatas + case map[interface{}]interface{}: + newMap := make(map[string]interface{}) + for key, value := range typedDatas { + stringKey := key.(string) + newMap[stringKey] = ConvertKeysToStrings(value) + } + return newMap + case []interface{}: + for i, value := range typedDatas { + typedDatas[i] = ConvertKeysToStrings(value) + } + return typedDatas + default: + return item + } +} diff --git a/vendor/github.com/docker/libcompose/yaml/types_yaml.go b/vendor/github.com/docker/libcompose/yaml/types_yaml.go index b3c1126cf0..5e75e5eca0 100644 --- a/vendor/github.com/docker/libcompose/yaml/types_yaml.go +++ b/vendor/github.com/docker/libcompose/yaml/types_yaml.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/docker/docker/api/types/strslice" + "github.com/docker/go-units" ) // StringorInt represents a string or an integer. @@ -23,6 +24,7 @@ func (s *StringorInt) UnmarshalYAML(unmarshal func(interface{}) error) error { var stringType string if err := unmarshal(&stringType); err == nil { intType, err := strconv.ParseInt(stringType, 10, 64) + if err != nil { return err } @@ -33,6 +35,32 @@ func (s *StringorInt) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("Failed to unmarshal StringorInt") } +// MemStringorInt represents a string or an integer +// the String supports notations like 10m for then Megabyte of memory +type MemStringorInt int64 + +// UnmarshalYAML implements the Unmarshaller interface. +func (s *MemStringorInt) UnmarshalYAML(unmarshal func(interface{}) error) error { + var intType int64 + if err := unmarshal(&intType); err == nil { + *s = MemStringorInt(intType) + return nil + } + + var stringType string + if err := unmarshal(&stringType); err == nil { + intType, err := units.RAMInBytes(stringType) + + if err != nil { + return err + } + *s = MemStringorInt(intType) + return nil + } + + return errors.New("Failed to unmarshal MemStringorInt") +} + // Stringorslice represents // Using engine-api Strslice and augment it with YAML marshalling stuff. a string or an array of strings. type Stringorslice strslice.StrSlice diff --git a/vendor/github.com/fatih/structs/structs.go b/vendor/github.com/fatih/structs/structs.go index 06da62094e..be3816abc1 100644 --- a/vendor/github.com/fatih/structs/structs.go +++ b/vendor/github.com/fatih/structs/structs.go @@ -530,15 +530,22 @@ func (s *Struct) nested(val reflect.Value) interface{} { finalVal = m } case reflect.Map: - v := val.Type().Elem() - if v.Kind() == reflect.Ptr { - v = v.Elem() + // get the element type of the map + mapElem := val.Type() + switch val.Type().Kind() { + case reflect.Ptr, reflect.Array, reflect.Map, + reflect.Slice, reflect.Chan: + mapElem = val.Type().Elem() + if mapElem.Kind() == reflect.Ptr { + mapElem = mapElem.Elem() + } } // only iterate over struct types, ie: map[string]StructType, // map[string][]StructType, - if v.Kind() == reflect.Struct || - (v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) { + if mapElem.Kind() == reflect.Struct || + (mapElem.Kind() == reflect.Slice && + mapElem.Elem().Kind() == reflect.Struct) { m := make(map[string]interface{}, val.Len()) for _, k := range val.MapKeys() { m[k.String()] = s.nested(val.MapIndex(k)) diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go index 4b378d4efc..4ba134c3d9 100644 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ b/vendor/github.com/pelletier/go-toml/lexer.go @@ -131,7 +131,7 @@ func (l *tomlLexer) lexVoid() tomlLexStateFn { case '[': return l.lexTableKey case '#': - return l.lexComment + return l.lexComment(l.lexVoid) case '=': return l.lexEqual case '\r': @@ -182,7 +182,7 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn { case '}': return l.lexRightCurlyBrace case '#': - return l.lexComment + return l.lexComment(l.lexRvalue) case '"': return l.lexString case '\'': @@ -309,15 +309,17 @@ func (l *tomlLexer) lexKey() tomlLexStateFn { return l.lexVoid } -func (l *tomlLexer) lexComment() tomlLexStateFn { - for next := l.peek(); next != '\n' && next != eof; next = l.peek() { - if next == '\r' && l.follow("\r\n") { - break +func (l *tomlLexer) lexComment(previousState tomlLexStateFn) tomlLexStateFn { + return func() tomlLexStateFn { + for next := l.peek(); next != '\n' && next != eof; next = l.peek() { + if next == '\r' && l.follow("\r\n") { + break + } + l.next() } - l.next() + l.ignore() + return previousState } - l.ignore() - return l.lexVoid } func (l *tomlLexer) lexLeftBracket() tomlLexStateFn { diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index 735bdafb36..494ba54580 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -128,14 +128,16 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { m.mu.RUnlock() if ok { return &os.PathError{"mkdir", name, ErrFileExists} - } else { - m.mu.Lock() - item := mem.CreateDir(name) - m.getData()[name] = item - m.registerWithParent(item) - m.mu.Unlock() - m.Chmod(name, perm) } + + m.mu.Lock() + item := mem.CreateDir(name) + m.getData()[name] = item + m.registerWithParent(item) + m.mu.Unlock() + + m.Chmod(name, perm) + return nil } @@ -313,7 +315,10 @@ func (m *MemMapFs) Stat(name string) (os.FileInfo, error) { func (m *MemMapFs) Chmod(name string, mode os.FileMode) error { name = normalizePath(name) + + m.mu.RLock() f, ok := m.getData()[name] + m.mu.RUnlock() if !ok { return &os.PathError{"chmod", name, ErrFileNotFound} } @@ -327,7 +332,10 @@ func (m *MemMapFs) Chmod(name string, mode os.FileMode) error { func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error { name = normalizePath(name) + + m.mu.RLock() f, ok := m.getData()[name] + m.mu.RUnlock() if !ok { return &os.PathError{"chtimes", name, ErrFileNotFound} } diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go index 23f0fe8cdd..1aaa1ff83f 100644 --- a/vendor/github.com/spf13/cast/caste.go +++ b/vendor/github.com/spf13/cast/caste.go @@ -18,15 +18,21 @@ import ( func ToTimeE(i interface{}) (tim time.Time, err error) { i = indirect(i) - switch s := i.(type) { + switch v := i.(type) { case time.Time: - return s, nil + return v, nil case string: - d, e := StringToDate(s) + d, e := StringToDate(v) if e == nil { return d, nil } return time.Time{}, fmt.Errorf("Could not parse Date/Time format: %v\n", e) + case int: + return time.Unix(int64(v), 0), nil + case int32: + return time.Unix(int64(v), 0), nil + case int64: + return time.Unix(v, 0), nil default: return time.Time{}, fmt.Errorf("Unable to Cast %#v to Time\n", i) } diff --git a/vendor/github.com/spf13/jwalterweatherman/default_notepad.go b/vendor/github.com/spf13/jwalterweatherman/default_notepad.go new file mode 100644 index 0000000000..ea8003d908 --- /dev/null +++ b/vendor/github.com/spf13/jwalterweatherman/default_notepad.go @@ -0,0 +1,103 @@ +// Copyright © 2016 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package jwalterweatherman + +import ( + "io" + "io/ioutil" + "log" + "os" +) + +var ( + TRACE *log.Logger + DEBUG *log.Logger + INFO *log.Logger + WARN *log.Logger + ERROR *log.Logger + CRITICAL *log.Logger + FATAL *log.Logger + + LOG *log.Logger + FEEDBACK *Feedback + + defaultNotepad *Notepad +) + +func reloadDefaultNotepad() { + TRACE = defaultNotepad.TRACE + DEBUG = defaultNotepad.DEBUG + INFO = defaultNotepad.INFO + WARN = defaultNotepad.WARN + ERROR = defaultNotepad.ERROR + CRITICAL = defaultNotepad.CRITICAL + FATAL = defaultNotepad.FATAL + + LOG = defaultNotepad.LOG + FEEDBACK = defaultNotepad.FEEDBACK +} + +func init() { + defaultNotepad = NewNotepad(LevelInfo, LevelTrace, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) + reloadDefaultNotepad() +} + +// SetLogThreshold set the log threshold for the default notepad. Trace by default. +func SetLogThreshold(threshold Threshold) { + defaultNotepad.SetLogThreshold(threshold) + reloadDefaultNotepad() +} + +// SetLogOutput set the log output for the default notepad. Discarded by default. +func SetLogOutput(handle io.Writer) { + defaultNotepad.SetLogOutput(handle) + reloadDefaultNotepad() +} + +// SetStdoutThreshold set the standard output threshold for the default notepad. +// Info by default. +func SetStdoutThreshold(threshold Threshold) { + defaultNotepad.SetStdoutThreshold(threshold) + reloadDefaultNotepad() +} + +// SetPrefix set the prefix for the default logger. Empty by default. +func SetPrefix(prefix string) { + defaultNotepad.SetPrefix(prefix) + reloadDefaultNotepad() +} + +// SetFlags set the flags for the default logger. "log.Ldate | log.Ltime" by default. +func SetFlags(flags int) { + defaultNotepad.SetFlags(flags) + reloadDefaultNotepad() +} + +// Level returns the current global log threshold. +func LogThreshold() Threshold { + return defaultNotepad.logThreshold +} + +// Level returns the current global output threshold. +func StdoutThreshold() Threshold { + return defaultNotepad.stdoutThreshold +} + +// LogCountForLevel returns the number of log invocations for a given threshold. +func LogCountForLevel(l Threshold) uint64 { + return defaultNotepad.LogCountForLevel(l) +} + +// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations +// greater than or equal to a given threshold. +func LogCountForLevelsGreaterThanorEqualTo(threshold Threshold) uint64 { + return defaultNotepad.LogCountForLevelsGreaterThanorEqualTo(threshold) +} + +// ResetLogCounters resets the invocation counters for all levels. +func ResetLogCounters() { + defaultNotepad.ResetLogCounters() +} diff --git a/vendor/github.com/spf13/jwalterweatherman/log_counter.go b/vendor/github.com/spf13/jwalterweatherman/log_counter.go new file mode 100644 index 0000000000..570db1d4c0 --- /dev/null +++ b/vendor/github.com/spf13/jwalterweatherman/log_counter.go @@ -0,0 +1,56 @@ +// Copyright © 2016 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package jwalterweatherman + +import ( + "sync/atomic" +) + +type logCounter struct { + counter uint64 +} + +func (c *logCounter) incr() { + atomic.AddUint64(&c.counter, 1) +} + +func (c *logCounter) resetCounter() { + atomic.StoreUint64(&c.counter, 0) +} + +func (c *logCounter) getCount() uint64 { + return atomic.LoadUint64(&c.counter) +} + +func (c *logCounter) Write(p []byte) (n int, err error) { + c.incr() + + return len(p), nil +} + +// LogCountForLevel returns the number of log invocations for a given threshold. +func (n *Notepad) LogCountForLevel(l Threshold) uint64 { + return n.logCounters[l].getCount() +} + +// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations +// greater than or equal to a given threshold. +func (n *Notepad) LogCountForLevelsGreaterThanorEqualTo(threshold Threshold) uint64 { + var cnt uint64 + + for i := int(threshold); i < len(n.logCounters); i++ { + cnt += n.LogCountForLevel(Threshold(i)) + } + + return cnt +} + +// ResetLogCounters resets the invocation counters for all levels. +func (n *Notepad) ResetLogCounters() { + for _, np := range n.logCounters { + np.resetCounter() + } +} diff --git a/vendor/github.com/spf13/jwalterweatherman/notepad.go b/vendor/github.com/spf13/jwalterweatherman/notepad.go new file mode 100644 index 0000000000..ea3458662e --- /dev/null +++ b/vendor/github.com/spf13/jwalterweatherman/notepad.go @@ -0,0 +1,177 @@ +// Copyright © 2016 Steve Francia . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package jwalterweatherman + +import ( + "fmt" + "io" + "log" + "os" +) + +type Threshold int + +const ( + LevelTrace Threshold = iota + LevelDebug + LevelInfo + LevelWarn + LevelError + LevelCritical + LevelFatal +) + +var prefixes map[Threshold]string = map[Threshold]string{ + LevelTrace: "TRACE ", + LevelDebug: "DEBUG ", + LevelInfo: "INFO ", + LevelWarn: "WARN ", + LevelError: "ERROR ", + LevelCritical: "CRITICAL ", + LevelFatal: "FATAL ", +} + +// Notepad is where you leave a note ! +type Notepad struct { + TRACE *log.Logger + DEBUG *log.Logger + INFO *log.Logger + WARN *log.Logger + ERROR *log.Logger + CRITICAL *log.Logger + FATAL *log.Logger + + LOG *log.Logger + FEEDBACK *Feedback + + loggers []**log.Logger + logHandle io.Writer + outHandle io.Writer + logThreshold Threshold + stdoutThreshold Threshold + prefix string + flags int + + // One per Threshold + logCounters [7]*logCounter +} + +// NewNotepad create a new notepad. +func NewNotepad(outThreshold Threshold, logThreshold Threshold, outHandle, logHandle io.Writer, prefix string, flags int) *Notepad { + n := &Notepad{} + + n.loggers = append(n.loggers, &n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL) + n.logHandle = logHandle + n.outHandle = outHandle + n.logThreshold = logThreshold + n.stdoutThreshold = outThreshold + + if len(prefix) != 0 { + n.prefix = "[" + prefix + "] " + } else { + n.prefix = "" + } + + n.flags = flags + + n.LOG = log.New(n.logHandle, + "LOG: ", + n.flags) + + n.FEEDBACK = &Feedback{n} + + n.init() + + return n +} + +// Feedback is special. It writes plainly to the output while +// logging with the standard extra information (date, file, etc) +// Only Println and Printf are currently provided for this +type Feedback struct { + *Notepad +} + +// init create the loggers for each level depending on the notepad thresholds +func (n *Notepad) init() { + bothHandle := io.MultiWriter(n.outHandle, n.logHandle) + + for t, logger := range n.loggers { + threshold := Threshold(t) + counter := &logCounter{} + n.logCounters[t] = counter + + switch { + case threshold >= n.logThreshold && threshold >= n.stdoutThreshold: + *logger = log.New(io.MultiWriter(counter, bothHandle), n.prefix+prefixes[threshold], n.flags) + + case threshold >= n.logThreshold: + *logger = log.New(io.MultiWriter(counter, n.logHandle), n.prefix+prefixes[threshold], n.flags) + + case threshold >= n.stdoutThreshold: + *logger = log.New(io.MultiWriter(counter, os.Stdout), n.prefix+prefixes[threshold], n.flags) + + default: + *logger = log.New(counter, n.prefix+prefixes[threshold], n.flags) + } + } +} + +// SetLogThreshold change the threshold above which messages are written to the +// log file +func (n *Notepad) SetLogThreshold(threshold Threshold) { + n.logThreshold = threshold + n.init() +} + +// SetLogOutput change the file where log messages are written +func (n *Notepad) SetLogOutput(handle io.Writer) { + n.logHandle = handle + n.init() +} + +// SetStdoutThreshold change the threshold above which messages are written to the +// standard output +func (n *Notepad) SetStdoutThreshold(threshold Threshold) { + n.stdoutThreshold = threshold + n.init() +} + +// SetPrefix change the prefix used by the notepad. Prefixes are displayed between +// brackets at the begining of the line. An empty prefix won't be displayed at all. +func (n *Notepad) SetPrefix(prefix string) { + if len(prefix) != 0 { + n.prefix = "[" + prefix + "] " + } else { + n.prefix = "" + } + n.init() +} + +// SetFlags choose which flags the logger will display (after prefix and message +// level). See the package log for more informations on this. +func (n *Notepad) SetFlags(flags int) { + n.flags = flags + n.init() +} + +// Feedback is special. It writes plainly to the output while +// logging with the standard extra information (date, file, etc) +// Only Println and Printf are currently provided for this +func (fb *Feedback) Println(v ...interface{}) { + s := fmt.Sprintln(v...) + fmt.Print(s) + fb.LOG.Output(2, s) +} + +// Feedback is special. It writes plainly to the output while +// logging with the standard extra information (date, file, etc) +// Only Println and Printf are currently provided for this +func (fb *Feedback) Printf(format string, v ...interface{}) { + s := fmt.Sprintf(format, v...) + fmt.Print(s) + fb.LOG.Output(2, s) +} diff --git a/vendor/github.com/spf13/jwalterweatherman/thatswhyyoualwaysleaveanote.go b/vendor/github.com/spf13/jwalterweatherman/thatswhyyoualwaysleaveanote.go deleted file mode 100644 index b64ed469c8..0000000000 --- a/vendor/github.com/spf13/jwalterweatherman/thatswhyyoualwaysleaveanote.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright © 2016 Steve Francia . -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jwalterweatherman - -import ( - "fmt" - "io" - "io/ioutil" - "log" - "os" - "sync/atomic" -) - -// Level describes the chosen log level between -// debug and critical. -type Level int - -type NotePad struct { - Handle io.Writer - Level Level - Prefix string - Logger **log.Logger - counter uint64 -} - -func (n *NotePad) incr() { - atomic.AddUint64(&n.counter, 1) -} - -func (n *NotePad) resetCounter() { - atomic.StoreUint64(&n.counter, 0) -} - -func (n *NotePad) getCount() uint64 { - return atomic.LoadUint64(&n.counter) -} - -type countingWriter struct { - incrFunc func() -} - -func (cw *countingWriter) Write(p []byte) (n int, err error) { - cw.incrFunc() - - return 0, nil -} - -// Feedback is special. It writes plainly to the output while -// logging with the standard extra information (date, file, etc) -// Only Println and Printf are currently provided for this -type Feedback struct{} - -const ( - LevelTrace Level = iota - LevelDebug - LevelInfo - LevelWarn - LevelError - LevelCritical - LevelFatal - DefaultLogThreshold = LevelWarn - DefaultStdoutThreshold = LevelError -) - -var ( - TRACE *log.Logger - DEBUG *log.Logger - INFO *log.Logger - WARN *log.Logger - ERROR *log.Logger - CRITICAL *log.Logger - FATAL *log.Logger - LOG *log.Logger - FEEDBACK Feedback - LogHandle io.Writer = ioutil.Discard - OutHandle io.Writer = os.Stdout - BothHandle io.Writer = io.MultiWriter(LogHandle, OutHandle) - NotePads []*NotePad = []*NotePad{trace, debug, info, warn, err, critical, fatal} - - trace *NotePad = &NotePad{Level: LevelTrace, Handle: os.Stdout, Logger: &TRACE, Prefix: "TRACE: "} - debug *NotePad = &NotePad{Level: LevelDebug, Handle: os.Stdout, Logger: &DEBUG, Prefix: "DEBUG: "} - info *NotePad = &NotePad{Level: LevelInfo, Handle: os.Stdout, Logger: &INFO, Prefix: "INFO: "} - warn *NotePad = &NotePad{Level: LevelWarn, Handle: os.Stdout, Logger: &WARN, Prefix: "WARN: "} - err *NotePad = &NotePad{Level: LevelError, Handle: os.Stdout, Logger: &ERROR, Prefix: "ERROR: "} - critical *NotePad = &NotePad{Level: LevelCritical, Handle: os.Stdout, Logger: &CRITICAL, Prefix: "CRITICAL: "} - fatal *NotePad = &NotePad{Level: LevelFatal, Handle: os.Stdout, Logger: &FATAL, Prefix: "FATAL: "} - logThreshold Level = DefaultLogThreshold - outputThreshold Level = DefaultStdoutThreshold -) - -const ( - DATE = log.Ldate - TIME = log.Ltime - SFILE = log.Lshortfile - LFILE = log.Llongfile - MSEC = log.Lmicroseconds -) - -var logFlags = DATE | TIME | SFILE - -func init() { - SetStdoutThreshold(DefaultStdoutThreshold) -} - -// initialize will setup the jWalterWeatherman standard approach of providing the user -// some feedback and logging a potentially different amount based on independent log and output thresholds. -// By default the output has a lower threshold than logged -// Don't use if you have manually set the Handles of the different levels as it will overwrite them. -func initialize() { - BothHandle = io.MultiWriter(LogHandle, OutHandle) - - for _, n := range NotePads { - if n.Level < outputThreshold && n.Level < logThreshold { - n.Handle = ioutil.Discard - } else if n.Level >= outputThreshold && n.Level >= logThreshold { - n.Handle = BothHandle - } else if n.Level >= outputThreshold && n.Level < logThreshold { - n.Handle = OutHandle - } else { - n.Handle = LogHandle - } - } - - for _, n := range NotePads { - n.Handle = io.MultiWriter(n.Handle, &countingWriter{n.incr}) - *n.Logger = log.New(n.Handle, n.Prefix, logFlags) - } - - LOG = log.New(LogHandle, - "LOG: ", - logFlags) -} - -// Set the log Flags (Available flag: DATE, TIME, SFILE, LFILE and MSEC) -func SetLogFlag(flags int) { - logFlags = flags - initialize() -} - -// Level returns the current global log threshold. -func LogThreshold() Level { - return logThreshold -} - -// Level returns the current global output threshold. -func StdoutThreshold() Level { - return outputThreshold -} - -// Ensures that the level provided is within the bounds of available levels -func levelCheck(level Level) Level { - switch { - case level <= LevelTrace: - return LevelTrace - case level >= LevelFatal: - return LevelFatal - default: - return level - } -} - -// Establishes a threshold where anything matching or above will be logged -func SetLogThreshold(level Level) { - logThreshold = levelCheck(level) - initialize() -} - -// Establishes a threshold where anything matching or above will be output -func SetStdoutThreshold(level Level) { - outputThreshold = levelCheck(level) - initialize() -} - -// Conveniently Sets the Log Handle to a io.writer created for the file behind the given filepath -// Will only append to this file -func SetLogFile(path string) { - file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) - if err != nil { - CRITICAL.Println("Failed to open log file:", path, err) - os.Exit(-1) - } - - INFO.Println("Logging to", file.Name()) - - LogHandle = file - initialize() -} - -// Conveniently Creates a temporary file and sets the Log Handle to a io.writer created for it -func UseTempLogFile(prefix string) { - file, err := ioutil.TempFile(os.TempDir(), prefix) - if err != nil { - CRITICAL.Println(err) - } - - INFO.Println("Logging to", file.Name()) - - LogHandle = file - initialize() -} - -// LogCountForLevel returns the number of log invocations for a given level. -func LogCountForLevel(l Level) uint64 { - for _, np := range NotePads { - if np.Level == l { - return np.getCount() - } - } - return 0 -} - -// LogCountForLevelsGreaterThanorEqualTo returns the number of log invocations -// greater than or equal to a given level threshold. -func LogCountForLevelsGreaterThanorEqualTo(threshold Level) uint64 { - var cnt uint64 - for _, np := range NotePads { - if np.Level >= threshold { - cnt += np.getCount() - } - } - return cnt -} - -// ResetLogCounters resets the invocation counters for all levels. -func ResetLogCounters() { - for _, np := range NotePads { - np.resetCounter() - } -} - -// Disables logging for the entire JWW system -func DiscardLogging() { - LogHandle = ioutil.Discard - initialize() -} - -// Feedback is special. It writes plainly to the output while -// logging with the standard extra information (date, file, etc) -// Only Println and Printf are currently provided for this -func (fb *Feedback) Println(v ...interface{}) { - s := fmt.Sprintln(v...) - fmt.Print(s) - LOG.Output(2, s) -} - -// Feedback is special. It writes plainly to the output while -// logging with the standard extra information (date, file, etc) -// Only Println and Printf are currently provided for this -func (fb *Feedback) Printf(format string, v ...interface{}) { - s := fmt.Sprintf(format, v...) - fmt.Print(s) - LOG.Output(2, s) -} diff --git a/vendor/github.com/xeipuuv/gojsonschema/errors.go b/vendor/github.com/xeipuuv/gojsonschema/errors.go index f22fa653f0..f69f22d37e 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/errors.go +++ b/vendor/github.com/xeipuuv/gojsonschema/errors.go @@ -2,10 +2,18 @@ package gojsonschema import ( "bytes" + "sync" "text/template" ) -var errorTemplates *template.Template +var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"),sync.RWMutex{}} + +// template.Template is not thread-safe for writing, so some locking is done +// sync.RWMutex is used for efficiently locking when new templates are created +type errorTemplate struct { + *template.Template + sync.RWMutex +} type ( // RequiredError. ErrorDetails: property string @@ -241,14 +249,17 @@ func formatErrorDescription(s string, details ErrorDetails) string { var descrAsBuffer bytes.Buffer var err error - if errorTemplates == nil { - errorTemplates = template.New("all-errors") - } - + errorTemplates.RLock() tpl = errorTemplates.Lookup(s) + errorTemplates.RUnlock() + if tpl == nil { + errorTemplates.Lock() tpl = errorTemplates.New(s) + tpl, err = tpl.Parse(s) + errorTemplates.Unlock() + if err != nil { return err.Error() } diff --git a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go index 0e865eb0eb..1edc702ec0 100644 --- a/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go +++ b/vendor/github.com/xeipuuv/gojsonschema/jsonLoader.go @@ -101,7 +101,9 @@ func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, er } func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory { - return &DefaultJSONLoaderFactory{} + return &FileSystemJSONLoaderFactory{ + fs: l.fs, + } } // NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.