github.com/camronlevanger/libcompose@v0.4.1-0.20180423130544-6bb86d53fa21/config/schema_helpers.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "strings" 6 7 "github.com/docker/go-connections/nat" 8 "github.com/xeipuuv/gojsonschema" 9 ) 10 11 var ( 12 schemaLoaderV1 gojsonschema.JSONLoader 13 constraintSchemaLoaderV1 gojsonschema.JSONLoader 14 schemaLoaderV2 gojsonschema.JSONLoader 15 constraintSchemaLoaderV2 gojsonschema.JSONLoader 16 schemaV1 map[string]interface{} 17 schemaV2 map[string]interface{} 18 ) 19 20 func init() { 21 if err := setupSchemaLoaders(schemaDataV1, &schemaV1, &schemaLoaderV1, &constraintSchemaLoaderV1); err != nil { 22 panic(err) 23 } 24 25 if err := setupSchemaLoaders(servicesSchemaDataV2, &schemaV2, &schemaLoaderV2, &constraintSchemaLoaderV2); err != nil { 26 panic(err) 27 } 28 } 29 30 type ( 31 environmentFormatChecker struct{} 32 portsFormatChecker struct{} 33 ) 34 35 func (checker environmentFormatChecker) IsFormat(input string) bool { 36 // If the value is a boolean, a warning should be given 37 // However, we can't determine type since gojsonschema converts the value to a string 38 // Adding a function with an interface{} parameter to gojsonschema is probably the best way to handle this 39 return true 40 } 41 42 func (checker portsFormatChecker) IsFormat(input string) bool { 43 _, _, err := nat.ParsePortSpecs([]string{input}) 44 return err == nil 45 } 46 47 func setupSchemaLoaders(schemaData string, schema *map[string]interface{}, schemaLoader, constraintSchemaLoader *gojsonschema.JSONLoader) error { 48 if *schema != nil { 49 return nil 50 } 51 52 var schemaRaw interface{} 53 err := json.Unmarshal([]byte(schemaData), &schemaRaw) 54 if err != nil { 55 return err 56 } 57 58 *schema = schemaRaw.(map[string]interface{}) 59 60 gojsonschema.FormatCheckers.Add("environment", environmentFormatChecker{}) 61 gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{}) 62 gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{}) 63 *schemaLoader = gojsonschema.NewGoLoader(schemaRaw) 64 65 definitions := (*schema)["definitions"].(map[string]interface{}) 66 constraints := definitions["constraints"].(map[string]interface{}) 67 service := constraints["service"].(map[string]interface{}) 68 *constraintSchemaLoader = gojsonschema.NewGoLoader(service) 69 70 return nil 71 } 72 73 // gojsonschema doesn't provide a list of valid types for a property 74 // This parses the schema manually to find all valid types 75 func parseValidTypesFromSchema(schema map[string]interface{}, context string) []string { 76 contextSplit := strings.Split(context, ".") 77 key := contextSplit[len(contextSplit)-1] 78 79 definitions := schema["definitions"].(map[string]interface{}) 80 service := definitions["service"].(map[string]interface{}) 81 properties := service["properties"].(map[string]interface{}) 82 property := properties[key].(map[string]interface{}) 83 84 var validTypes []string 85 86 if val, ok := property["oneOf"]; ok { 87 validConditions := val.([]interface{}) 88 89 for _, validCondition := range validConditions { 90 condition := validCondition.(map[string]interface{}) 91 validTypes = append(validTypes, condition["type"].(string)) 92 } 93 } else if val, ok := property["$ref"]; ok { 94 reference := val.(string) 95 if reference == "#/definitions/string_or_list" { 96 return []string{"string", "array"} 97 } else if reference == "#/definitions/list_of_strings" { 98 return []string{"array"} 99 } else if reference == "#/definitions/list_or_dict" { 100 return []string{"array", "object"} 101 } 102 } 103 104 return validTypes 105 }