Add expanded mount syntax to Compose schema and types.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2017-01-19 16:48:30 -05:00
parent 3cfc3e30a8
commit 65c899bee5
5 changed files with 98 additions and 22 deletions

File diff suppressed because one or more lines are too long

View file

@ -235,7 +235,37 @@
},
"user": {"type": "string"},
"userns_mode": {"type": "string"},
"volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"volumes": {
"type": "array",
"items": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"required": ["type"],
"properties": {
"type": {"type": "string"},
"source": {"type": "string"},
"target": {"type": "string"},
"read_only": {"type": "boolean"},
"bind": {
"type": "object",
"properties": {
"propagation": {"type": "string"}
}
},
"volume": {
"type": "object",
"properties": {
"nocopy": {"type": "boolean"}
}
}
}
}
],
"uniqueItems": true
}
},
"working_dir": {"type": "string"}
},
"additionalProperties": false

View file

@ -78,18 +78,22 @@ func Validate(config map[string]interface{}, version string) error {
func toError(result *gojsonschema.Result) error {
err := getMostSpecificError(result.Errors())
description := getDescription(err)
return fmt.Errorf("%s %s", err.Field(), description)
return err
}
func getDescription(err gojsonschema.ResultError) string {
if err.Type() == "invalid_type" {
if expectedType, ok := err.Details()["expected"].(string); ok {
func getDescription(err validationError) string {
switch err.parent.Type() {
case "invalid_type":
if expectedType, ok := err.parent.Details()["expected"].(string); ok {
return fmt.Sprintf("must be a %s", humanReadableType(expectedType))
}
case "number_one_of", "number_any_of":
if err.child == nil {
return err.parent.Description()
}
return err.child.Description()
}
return err.Description()
return err.parent.Description()
}
func humanReadableType(definition string) string {
@ -113,23 +117,45 @@ func humanReadableType(definition string) string {
return definition
}
func getMostSpecificError(errors []gojsonschema.ResultError) gojsonschema.ResultError {
var mostSpecificError gojsonschema.ResultError
type validationError struct {
parent gojsonschema.ResultError
child gojsonschema.ResultError
}
for _, err := range errors {
if mostSpecificError == nil {
mostSpecificError = err
} else if specificity(err) > specificity(mostSpecificError) {
mostSpecificError = err
} else if specificity(err) == specificity(mostSpecificError) {
func (err validationError) Error() string {
description := getDescription(err)
return fmt.Sprintf("%s %s", err.parent.Field(), description)
}
func getMostSpecificError(errors []gojsonschema.ResultError) validationError {
mostSpecificError := 0
for i, err := range errors {
if specificity(err) > specificity(errors[mostSpecificError]) {
mostSpecificError = i
continue
}
if specificity(err) == specificity(errors[mostSpecificError]) {
// Invalid type errors win in a tie-breaker for most specific field name
if err.Type() == "invalid_type" && mostSpecificError.Type() != "invalid_type" {
mostSpecificError = err
if err.Type() == "invalid_type" && errors[mostSpecificError].Type() != "invalid_type" {
mostSpecificError = i
}
}
}
return mostSpecificError
if mostSpecificError+1 == len(errors) {
return validationError{parent: errors[mostSpecificError]}
}
switch errors[mostSpecificError].Type() {
case "number_one_of", "number_any_of":
return validationError{
parent: errors[mostSpecificError],
child: errors[mostSpecificError+1],
}
default:
return validationError{parent: errors[mostSpecificError]}
}
}
func specificity(err gojsonschema.ResultError) int {

View file

@ -119,7 +119,7 @@ type ServiceConfig struct {
Tty bool `mapstructure:"tty"`
Ulimits map[string]*UlimitsConfig
User string
Volumes []string
Volumes []ServiceVolumeConfig
WorkingDir string `mapstructure:"working_dir"`
}
@ -223,6 +223,26 @@ type ServicePortConfig struct {
Protocol string
}
// ServiceVolumeConfig are references to a volume used by a service
type ServiceVolumeConfig struct {
Type string
Source string
Target string
ReadOnly string `mapstructure:"read_only"`
Bind *ServiceVolumeBind
Volume *ServiceVolumeVolume
}
// ServiceVolumeBind are options for a service volume of type bind
type ServiceVolumeBind struct {
Propogation string
}
// ServiceVolumeVolume are options for a service volume of type volume
type ServiceVolumeVolume struct {
NoCopy bool `mapstructure:"nocopy"`
}
// ServiceSecretConfig is the secret configuration for a service
type ServiceSecretConfig struct {
Source string

View file

@ -100,7 +100,7 @@ func (m *MountOpt) Set(value string) error {
case "volume-nocopy":
volumeOptions().NoCopy, err = strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("invalid value for populate: %s", value)
return fmt.Errorf("invalid value for volume-nocopy: %s", value)
}
case "volume-label":
setValueOnMap(volumeOptions().Labels, value)