From 7811fcee6af3f0ed895fcbf5814d7cbe3bd9e39c Mon Sep 17 00:00:00 2001 From: krhubert Date: Tue, 25 Jun 2019 20:24:12 +0200 Subject: [PATCH] Add more validation to service struct --- service/dependency.go | 4 +-- service/event.go | 2 +- service/parameter.go | 2 +- service/service.go | 10 +++---- service/service_validate.go | 57 +++++++++++++++++++++++++++++++++++++ service/task.go | 4 +-- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/service/dependency.go b/service/dependency.go index 819706aa3..9a86b1393 100644 --- a/service/dependency.go +++ b/service/dependency.go @@ -16,7 +16,7 @@ type Dependency struct { VolumesFrom []string `hash:"name:4" validate:"unique,dive,printascii"` // Ports holds ports configuration for container. - Ports []string `hash:"name:5"` + Ports []string `hash:"name:5" validate:"unique,dive,portmap"` // Command is the Docker command which will be executed when container started. Command string `hash:"name:6" validate:"printascii"` @@ -25,5 +25,5 @@ type Dependency struct { Args []string `hash:"name:7" validate:"dive,printascii"` // Env is a slice of environment variables in key=value format. - Env []string `hash:"name:8" validate:"unique,dive,printascii"` + Env []string `hash:"name:8" validate:"unique,dive,env"` } diff --git a/service/event.go b/service/event.go index 6b617c553..d651d80a2 100644 --- a/service/event.go +++ b/service/event.go @@ -12,7 +12,7 @@ type Event struct { Description string `hash:"name:3" validate:"printascii"` // Data holds the input parameters of event. - Data []*Parameter `hash:"name:4"` + Data []*Parameter `hash:"name:4" validate:"dive,required"` } // GetEvent returns event eventKey of service. diff --git a/service/parameter.go b/service/parameter.go index 9941f8655..8afbd9094 100644 --- a/service/parameter.go +++ b/service/parameter.go @@ -22,5 +22,5 @@ type Parameter struct { Repeated bool `hash:"name:6"` // Definition of the structure of the object when the type is object - Object []*Parameter `hash:"name:7"` + Object []*Parameter `hash:"name:7" validate:"unique,dive,required"` } diff --git a/service/service.go b/service/service.go index 705ba02db..4ea8defb1 100644 --- a/service/service.go +++ b/service/service.go @@ -31,19 +31,19 @@ type Service struct { Sid string `hash:"name:1" validate:"omitempty,printascii,max=63,domain"` // Name is the service name. - Name string `hash:"name:2" validate:"required,printascii,min=1"` + Name string `hash:"name:2" validate:"required,printascii"` // Description is service description. Description string `hash:"name:3" validate:"printascii"` // Tasks are the list of tasks that service can execute. - Tasks []*Task `hash:"name:4"` + Tasks []*Task `hash:"name:4" validate:"dive,required"` // Events are the list of events that service can emit. - Events []*Event `hash:"name:5"` + Events []*Event `hash:"name:5" validate:"dive,required"` // Dependencies are the Docker containers that service can depend on. - Dependencies []*Dependency `hash:"name:6"` + Dependencies []*Dependency `hash:"name:6" validate:"dive,required"` // Configuration of the service Configuration *Dependency `hash:"name:8"` @@ -53,7 +53,7 @@ type Service struct { Repository string `hash:"name:7" validate:"omitempty,uri"` // Source is the hash id of service's source code on IPFS. - Source string `hash:"name:9"` + Source string `hash:"name:9" validate:"required,printascii"` } // StatusType of the service. diff --git a/service/service_validate.go b/service/service_validate.go index c9673799c..28b918320 100644 --- a/service/service_validate.go +++ b/service/service_validate.go @@ -35,6 +35,10 @@ func ValidateService(service *Service) error { } } + if err := isServiceKeysUnique(service); err != nil { + errs = append(errs, err) + } + // validate configuration image if service.Configuration.Image != "" { errs = append(errs, errors.New("configuration.image is not allowed")) @@ -82,6 +86,59 @@ func ValidateService(service *Service) error { return errs.ErrorOrNil() } +// isServiceKeysUnique checks uniqueness of service deps/tasks/events/params keys. +func isServiceKeysUnique(service *Service) error { + var errs xerrors.Errors + exist := make(map[string]bool) + for _, dep := range service.Dependencies { + if exist[dep.Key] { + errs = append(errs, fmt.Errorf("dependencies[%s] already exist", dep.Key)) + } + exist[dep.Key] = true + } + + exist = make(map[string]bool) + for _, task := range service.Tasks { + if exist[task.Key] { + errs = append(errs, fmt.Errorf("tasks[%s] already exist", task.Key)) + } + exist[task.Key] = true + + existparam := make(map[string]bool) + for _, param := range task.Inputs { + if existparam[param.Key] { + errs = append(errs, fmt.Errorf("tasks[%s].inputs[%s] already exist", task.Key, param.Key)) + } + existparam[param.Key] = true + } + + existparam = make(map[string]bool) + for _, param := range task.Outputs { + if existparam[param.Key] { + errs = append(errs, fmt.Errorf("tasks[%s].outputs[%s] already exist", task.Key, param.Key)) + } + existparam[param.Key] = true + } + } + + exist = make(map[string]bool) + for _, event := range service.Events { + if exist[event.Key] { + errs = append(errs, fmt.Errorf("events[%s] already exist", event.Key)) + } + exist[event.Key] = true + + existparam := make(map[string]bool) + for _, param := range event.Data { + if existparam[param.Key] { + errs = append(errs, fmt.Errorf("events[%s].data[%s] already exist", event.Key, param.Key)) + } + existparam[param.Key] = true + } + } + return errs.ErrorOrNil() +} + func newValidator() (*validator.Validate, ut.Translator) { en := en.New() uni := ut.New(en, en) diff --git a/service/task.go b/service/task.go index 020d923aa..a4c993d0d 100644 --- a/service/task.go +++ b/service/task.go @@ -12,10 +12,10 @@ type Task struct { Description string `hash:"name:3" validate:"printascii"` // Inputs are the definition of the execution inputs of task. - Inputs []*Parameter `hash:"name:4"` + Inputs []*Parameter `hash:"name:4" validate:"dive,required"` // Outputs are the definition of the execution results of task. - Outputs []*Parameter `hash:"name:5"` + Outputs []*Parameter `hash:"name:5" validate:"dive,required"` } // GetTask returns task taskKey of service.