github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/cli/compose/schema/schema.go (about) 1 package schema 2 3 //go:generate go-bindata -pkg schema -nometadata data 4 5 import ( 6 "fmt" 7 "strings" 8 "time" 9 10 "github.com/xeipuuv/gojsonschema" 11 ) 12 13 type portsFormatChecker struct{} 14 15 func (checker portsFormatChecker) IsFormat(input string) bool { 16 // TODO: implement this 17 return true 18 } 19 20 type durationFormatChecker struct{} 21 22 func (checker durationFormatChecker) IsFormat(input string) bool { 23 _, err := time.ParseDuration(input) 24 return err == nil 25 } 26 27 func init() { 28 gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{}) 29 gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{}) 30 gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{}) 31 } 32 33 // Validate uses the jsonschema to validate the configuration 34 func Validate(config map[string]interface{}) error { 35 schemaData, err := Asset("data/config_schema_v3.0.json") 36 if err != nil { 37 return err 38 } 39 40 schemaLoader := gojsonschema.NewStringLoader(string(schemaData)) 41 dataLoader := gojsonschema.NewGoLoader(config) 42 43 result, err := gojsonschema.Validate(schemaLoader, dataLoader) 44 if err != nil { 45 return err 46 } 47 48 if !result.Valid() { 49 return toError(result) 50 } 51 52 return nil 53 } 54 55 func toError(result *gojsonschema.Result) error { 56 err := getMostSpecificError(result.Errors()) 57 description := getDescription(err) 58 return fmt.Errorf("%s %s", err.Field(), description) 59 } 60 61 func getDescription(err gojsonschema.ResultError) string { 62 if err.Type() == "invalid_type" { 63 if expectedType, ok := err.Details()["expected"].(string); ok { 64 return fmt.Sprintf("must be a %s", humanReadableType(expectedType)) 65 } 66 } 67 68 return err.Description() 69 } 70 71 func humanReadableType(definition string) string { 72 if definition[0:1] == "[" { 73 allTypes := strings.Split(definition[1:len(definition)-1], ",") 74 for i, t := range allTypes { 75 allTypes[i] = humanReadableType(t) 76 } 77 return fmt.Sprintf( 78 "%s or %s", 79 strings.Join(allTypes[0:len(allTypes)-1], ", "), 80 allTypes[len(allTypes)-1], 81 ) 82 } 83 if definition == "object" { 84 return "mapping" 85 } 86 if definition == "array" { 87 return "list" 88 } 89 return definition 90 } 91 92 func getMostSpecificError(errors []gojsonschema.ResultError) gojsonschema.ResultError { 93 var mostSpecificError gojsonschema.ResultError 94 95 for _, err := range errors { 96 if mostSpecificError == nil { 97 mostSpecificError = err 98 } else if specificity(err) > specificity(mostSpecificError) { 99 mostSpecificError = err 100 } else if specificity(err) == specificity(mostSpecificError) { 101 // Invalid type errors win in a tie-breaker for most specific field name 102 if err.Type() == "invalid_type" && mostSpecificError.Type() != "invalid_type" { 103 mostSpecificError = err 104 } 105 } 106 } 107 108 return mostSpecificError 109 } 110 111 func specificity(err gojsonschema.ResultError) int { 112 return len(strings.Split(err.Field(), ".")) 113 }