Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interpolate and then validate services #1852

Merged
merged 1 commit into from
Oct 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions client/task_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,15 @@ func (r *TaskRunner) Run() {
r.logger.Printf("[DEBUG] client: starting task context for '%s' (alloc '%s')",
r.task.Name, r.alloc.ID)

// Create the initial environment, this will be recreated if a Vault token
// is needed
if err := r.setTaskEnv(); err != nil {
r.setState(
structs.TaskStateDead,
structs.NewTaskEvent(structs.TaskSetupFailure).SetSetupError(err))
return
}

if err := r.validateTask(); err != nil {
r.setState(
structs.TaskStateDead,
Expand Down Expand Up @@ -417,6 +426,14 @@ func (r *TaskRunner) validateTask() error {
}
}

// Validate the Service names
for i, service := range r.task.Services {
name := r.taskEnv.ReplaceEnv(service.Name)
if err := service.ValidateName(name); err != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service (%d) failed validation: %v", i, err))
}
}

if len(mErr.Errors) == 1 {
return mErr.Errors[0]
}
Expand Down Expand Up @@ -648,7 +665,7 @@ func (r *TaskRunner) updatedTokenHandler() {
if err := r.setTaskEnv(); err != nil {
r.setState(
structs.TaskStateDead,
structs.NewTaskEvent(structs.TaskDriverFailure).SetDriverError(err).SetFailsTask())
structs.NewTaskEvent(structs.TaskSetupFailure).SetSetupError(err).SetFailsTask())
return
}

Expand Down Expand Up @@ -687,7 +704,7 @@ func (r *TaskRunner) prestart(resultCh chan bool) {
if err := r.setTaskEnv(); err != nil {
r.setState(
structs.TaskStateDead,
structs.NewTaskEvent(structs.TaskDriverFailure).SetDriverError(err).SetFailsTask())
structs.NewTaskEvent(structs.TaskSetupFailure).SetSetupError(err).SetFailsTask())
resultCh <- false
return
}
Expand Down
23 changes: 19 additions & 4 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1909,13 +1909,14 @@ func (s *Service) Canonicalize(job string, taskGroup string, task string) {
func (s *Service) Validate() error {
var mErr multierror.Error

// Ensure the service name is valid per RFC-952 §1
// (https://tools.ietf.org/html/rfc952), RFC-1123 §2.1
// Ensure the service name is valid per the below RFCs but make an exception
// for our interpolation syntax
// RFC-952 §1 (https://tools.ietf.org/html/rfc952), RFC-1123 §2.1
// (https://tools.ietf.org/html/rfc1123), and RFC-2782
// (https://tools.ietf.org/html/rfc2782).
re := regexp.MustCompile(`^(?i:[a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])$`)
re := regexp.MustCompile(`^(?i:[a-z0-9]|[a-z0-9\$][a-zA-Z0-9\-\$\{\}\_\.]*[a-z0-9\}])$`)
if !re.MatchString(s.Name) {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service name must be valid per RFC 1123 and can contain only alphanumeric characters or dashes and must be less than 63 characters long: %q", s.Name))
mErr.Errors = append(mErr.Errors, fmt.Errorf("service name must be valid per RFC 1123 and can contain only alphanumeric characters or dashes: %q", s.Name))
}

for _, c := range s.Checks {
Expand All @@ -1931,6 +1932,20 @@ func (s *Service) Validate() error {
return mErr.ErrorOrNil()
}

// ValidateName checks if the services Name is valid and should be called after
// the name has been interpolated
func (s *Service) ValidateName(name string) error {
// Ensure the service name is valid per RFC-952 §1
// (https://tools.ietf.org/html/rfc952), RFC-1123 §2.1
// (https://tools.ietf.org/html/rfc1123), and RFC-2782
// (https://tools.ietf.org/html/rfc2782).
re := regexp.MustCompile(`^(?i:[a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])$`)
if !re.MatchString(name) {
return fmt.Errorf("service name must be valid per RFC 1123 and can contain only alphanumeric characters or dashes and must be less than 63 characters long: %q", name)
}
return nil
}

// Hash calculates the hash of the check based on it's content and the service
// which owns it
func (s *Service) Hash() string {
Expand Down
12 changes: 10 additions & 2 deletions nomad/structs/structs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ func TestInvalidServiceCheck(t *testing.T) {
Name: "service.name",
PortLabel: "bar",
}
if err := s.Validate(); err == nil {
if err := s.ValidateName(s.Name); err == nil {
t.Fatalf("Service should be invalid (contains a dot): %v", err)
}

Expand All @@ -963,11 +963,19 @@ func TestInvalidServiceCheck(t *testing.T) {
t.Fatalf("Service should be invalid (begins with a hyphen): %v", err)
}

s = Service{
Name: "my-service-${NOMAD_META_FOO}",
PortLabel: "bar",
}
if err := s.Validate(); err != nil {
t.Fatalf("Service should be valid: %v", err)
}

s = Service{
Name: "abcdef0123456789-abcdef0123456789-abcdef0123456789-abcdef0123456",
PortLabel: "bar",
}
if err := s.Validate(); err == nil {
if err := s.ValidateName(s.Name); err == nil {
t.Fatalf("Service should be invalid (too long): %v", err)
}

Expand Down