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  }