schema_helpers.go 2.7 KB

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