2015-06-05 22:21:17 +00:00
|
|
|
package structs
|
|
|
|
|
|
|
|
import (
|
2015-11-02 21:24:59 +00:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2015-06-05 22:21:17 +00:00
|
|
|
"reflect"
|
2015-09-15 18:23:03 +00:00
|
|
|
"strings"
|
2015-06-05 22:21:17 +00:00
|
|
|
"testing"
|
2015-11-02 21:24:59 +00:00
|
|
|
"time"
|
2015-06-05 22:21:17 +00:00
|
|
|
)
|
|
|
|
|
2015-09-15 18:23:03 +00:00
|
|
|
func TestJob_Validate(t *testing.T) {
|
|
|
|
j := &Job{}
|
|
|
|
err := j.Validate()
|
|
|
|
mErr := err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "job region") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[1].Error(), "job ID") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[2].Error(), "job name") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[3].Error(), "job type") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[4].Error(), "priority") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[5].Error(), "datacenters") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[6].Error(), "task groups") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
j = &Job{
|
|
|
|
Region: "global",
|
|
|
|
ID: GenerateUUID(),
|
|
|
|
Name: "my-job",
|
|
|
|
Type: JobTypeService,
|
|
|
|
Priority: 50,
|
|
|
|
Datacenters: []string{"dc1"},
|
|
|
|
TaskGroups: []*TaskGroup{
|
|
|
|
&TaskGroup{
|
|
|
|
Name: "web",
|
2015-11-02 21:24:59 +00:00
|
|
|
RestartPolicy: &RestartPolicy{
|
|
|
|
Interval: 5 * time.Minute,
|
|
|
|
Delay: 10 * time.Second,
|
|
|
|
Attempts: 10,
|
|
|
|
},
|
2015-09-15 18:23:03 +00:00
|
|
|
},
|
|
|
|
&TaskGroup{
|
|
|
|
Name: "web",
|
2015-11-02 21:24:59 +00:00
|
|
|
RestartPolicy: &RestartPolicy{
|
|
|
|
Interval: 5 * time.Minute,
|
|
|
|
Delay: 10 * time.Second,
|
|
|
|
Attempts: 10,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&TaskGroup{
|
|
|
|
RestartPolicy: &RestartPolicy{
|
|
|
|
Interval: 5 * time.Minute,
|
|
|
|
Delay: 10 * time.Second,
|
|
|
|
Attempts: 10,
|
|
|
|
},
|
2015-09-15 18:23:03 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
err = j.Validate()
|
|
|
|
mErr = err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from group 1") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[1].Error(), "group 3 missing name") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[2].Error(), "Task group 1 validation failed") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTaskGroup_Validate(t *testing.T) {
|
2015-11-02 21:24:59 +00:00
|
|
|
tg := &TaskGroup{
|
|
|
|
RestartPolicy: &RestartPolicy{
|
|
|
|
Interval: 5 * time.Minute,
|
|
|
|
Delay: 10 * time.Second,
|
|
|
|
Attempts: 10,
|
|
|
|
},
|
|
|
|
}
|
2015-09-15 18:23:03 +00:00
|
|
|
err := tg.Validate()
|
|
|
|
mErr := err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "group name") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[1].Error(), "count must be positive") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[2].Error(), "Missing tasks") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tg = &TaskGroup{
|
|
|
|
Name: "web",
|
|
|
|
Count: 1,
|
|
|
|
Tasks: []*Task{
|
|
|
|
&Task{Name: "web"},
|
|
|
|
&Task{Name: "web"},
|
|
|
|
&Task{},
|
|
|
|
},
|
2015-11-02 21:24:59 +00:00
|
|
|
RestartPolicy: &RestartPolicy{
|
|
|
|
Interval: 5 * time.Minute,
|
|
|
|
Delay: 10 * time.Second,
|
|
|
|
Attempts: 10,
|
|
|
|
},
|
2015-09-15 18:23:03 +00:00
|
|
|
}
|
|
|
|
err = tg.Validate()
|
|
|
|
mErr = err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "2 redefines 'web' from task 1") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[1].Error(), "Task 3 missing name") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[2].Error(), "Task 1 validation failed") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTask_Validate(t *testing.T) {
|
|
|
|
task := &Task{}
|
|
|
|
err := task.Validate()
|
|
|
|
mErr := err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "task name") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[1].Error(), "task driver") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mErr.Errors[2].Error(), "task resources") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
task = &Task{
|
|
|
|
Name: "web",
|
|
|
|
Driver: "docker",
|
|
|
|
Resources: &Resources{},
|
|
|
|
}
|
|
|
|
err = task.Validate()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-11 19:50:16 +00:00
|
|
|
func TestConstraint_Validate(t *testing.T) {
|
|
|
|
c := &Constraint{}
|
|
|
|
err := c.Validate()
|
|
|
|
mErr := err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "Missing constraint operand") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c = &Constraint{
|
|
|
|
LTarget: "$attr.kernel.name",
|
|
|
|
RTarget: "linux",
|
|
|
|
Operand: "=",
|
|
|
|
}
|
|
|
|
err = c.Validate()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform additional regexp validation
|
2015-10-26 20:47:56 +00:00
|
|
|
c.Operand = ConstraintRegex
|
2015-10-11 19:50:16 +00:00
|
|
|
c.RTarget = "(foo"
|
|
|
|
err = c.Validate()
|
|
|
|
mErr = err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "missing closing") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform version validation
|
2015-10-26 20:47:56 +00:00
|
|
|
c.Operand = ConstraintVersion
|
2015-10-11 19:50:16 +00:00
|
|
|
c.RTarget = "~> foo"
|
|
|
|
err = c.Validate()
|
|
|
|
mErr = err.(*multierror.Error)
|
|
|
|
if !strings.Contains(mErr.Errors[0].Error(), "Malformed constraint") {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-13 22:04:36 +00:00
|
|
|
func TestResource_NetIndex(t *testing.T) {
|
2015-08-05 00:23:42 +00:00
|
|
|
r := &Resources{
|
|
|
|
Networks: []*NetworkResource{
|
2015-09-13 22:04:36 +00:00
|
|
|
&NetworkResource{Device: "eth0"},
|
|
|
|
&NetworkResource{Device: "lo0"},
|
|
|
|
&NetworkResource{Device: ""},
|
2015-08-05 00:23:42 +00:00
|
|
|
},
|
|
|
|
}
|
2015-09-13 22:04:36 +00:00
|
|
|
if idx := r.NetIndex(&NetworkResource{Device: "eth0"}); idx != 0 {
|
2015-08-05 00:23:42 +00:00
|
|
|
t.Fatalf("Bad: %d", idx)
|
|
|
|
}
|
2015-09-13 22:04:36 +00:00
|
|
|
if idx := r.NetIndex(&NetworkResource{Device: "lo0"}); idx != 1 {
|
2015-08-05 00:23:42 +00:00
|
|
|
t.Fatalf("Bad: %d", idx)
|
|
|
|
}
|
2015-09-13 22:04:36 +00:00
|
|
|
if idx := r.NetIndex(&NetworkResource{Device: "eth1"}); idx != -1 {
|
2015-09-12 23:21:57 +00:00
|
|
|
t.Fatalf("Bad: %d", idx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-05 00:32:57 +00:00
|
|
|
func TestResource_Superset(t *testing.T) {
|
|
|
|
r1 := &Resources{
|
2015-09-23 18:14:32 +00:00
|
|
|
CPU: 2000,
|
2015-08-05 00:32:57 +00:00
|
|
|
MemoryMB: 2048,
|
|
|
|
DiskMB: 10000,
|
|
|
|
IOPS: 100,
|
|
|
|
}
|
|
|
|
r2 := &Resources{
|
2015-09-23 18:14:32 +00:00
|
|
|
CPU: 2000,
|
2015-08-05 00:32:57 +00:00
|
|
|
MemoryMB: 1024,
|
|
|
|
DiskMB: 5000,
|
|
|
|
IOPS: 50,
|
|
|
|
}
|
|
|
|
|
2015-09-14 01:38:11 +00:00
|
|
|
if s, _ := r1.Superset(r1); !s {
|
2015-08-05 00:32:57 +00:00
|
|
|
t.Fatalf("bad")
|
|
|
|
}
|
2015-09-14 01:38:11 +00:00
|
|
|
if s, _ := r1.Superset(r2); !s {
|
2015-08-05 00:32:57 +00:00
|
|
|
t.Fatalf("bad")
|
|
|
|
}
|
2015-09-14 01:38:11 +00:00
|
|
|
if s, _ := r2.Superset(r1); s {
|
2015-08-05 00:32:57 +00:00
|
|
|
t.Fatalf("bad")
|
|
|
|
}
|
2015-09-14 01:38:11 +00:00
|
|
|
if s, _ := r2.Superset(r2); !s {
|
2015-08-05 00:32:57 +00:00
|
|
|
t.Fatalf("bad")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-05 00:41:02 +00:00
|
|
|
func TestResource_Add(t *testing.T) {
|
|
|
|
r1 := &Resources{
|
2015-09-23 18:14:32 +00:00
|
|
|
CPU: 2000,
|
2015-08-05 00:41:02 +00:00
|
|
|
MemoryMB: 2048,
|
|
|
|
DiskMB: 10000,
|
|
|
|
IOPS: 100,
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
|
|
|
CIDR: "10.0.0.0/8",
|
|
|
|
MBits: 100,
|
|
|
|
ReservedPorts: []int{22},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r2 := &Resources{
|
2015-09-23 18:14:32 +00:00
|
|
|
CPU: 2000,
|
2015-08-05 00:41:02 +00:00
|
|
|
MemoryMB: 1024,
|
|
|
|
DiskMB: 5000,
|
|
|
|
IOPS: 50,
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
2015-09-12 23:33:41 +00:00
|
|
|
IP: "10.0.0.1",
|
2015-08-05 00:41:02 +00:00
|
|
|
MBits: 50,
|
|
|
|
ReservedPorts: []int{80},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r1.Add(r2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expect := &Resources{
|
2015-09-23 18:14:32 +00:00
|
|
|
CPU: 3000,
|
2015-08-05 00:41:02 +00:00
|
|
|
MemoryMB: 3072,
|
|
|
|
DiskMB: 15000,
|
|
|
|
IOPS: 150,
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
|
|
|
CIDR: "10.0.0.0/8",
|
|
|
|
MBits: 150,
|
|
|
|
ReservedPorts: []int{22, 80},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expect.Networks, r1.Networks) {
|
|
|
|
t.Fatalf("bad: %#v %#v", expect, r1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-13 00:04:09 +00:00
|
|
|
func TestResource_Add_Network(t *testing.T) {
|
|
|
|
r1 := &Resources{}
|
|
|
|
r2 := &Resources{
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
|
|
|
MBits: 50,
|
2015-09-22 20:33:16 +00:00
|
|
|
DynamicPorts: []string{"http", "https"},
|
2015-09-13 00:04:09 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r3 := &Resources{
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
|
|
|
MBits: 25,
|
2015-09-22 20:33:16 +00:00
|
|
|
DynamicPorts: []string{"admin"},
|
2015-09-13 00:04:09 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r1.Add(r2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
err = r1.Add(r3)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expect := &Resources{
|
|
|
|
Networks: []*NetworkResource{
|
|
|
|
&NetworkResource{
|
|
|
|
MBits: 75,
|
2015-09-22 20:33:16 +00:00
|
|
|
DynamicPorts: []string{"http", "https", "admin"},
|
2015-09-13 00:04:09 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expect.Networks, r1.Networks) {
|
2015-09-14 01:38:11 +00:00
|
|
|
t.Fatalf("bad: %#v %#v", expect.Networks[0], r1.Networks[0])
|
2015-09-13 00:04:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-23 05:33:29 +00:00
|
|
|
func TestMapDynamicPorts(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{80, 443, 3306, 8080},
|
|
|
|
DynamicPorts: []string{"mysql", "admin"},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := map[string]int{
|
|
|
|
"mysql": 3306,
|
|
|
|
"admin": 8080,
|
|
|
|
}
|
|
|
|
actual := resources.MapDynamicPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-24 22:11:44 +00:00
|
|
|
func TestMapDynamicPortsEmpty(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{},
|
|
|
|
DynamicPorts: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := map[string]int{}
|
|
|
|
actual := resources.MapDynamicPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapDynamicPortsStaticOnly(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{80, 443},
|
|
|
|
DynamicPorts: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := map[string]int{}
|
|
|
|
actual := resources.MapDynamicPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapDynamicPortsOnly(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{3306, 8080},
|
|
|
|
DynamicPorts: []string{"mysql", "admin"},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := map[string]int{
|
|
|
|
"mysql": 3306,
|
|
|
|
"admin": 8080,
|
|
|
|
}
|
|
|
|
actual := resources.MapDynamicPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-24 05:28:34 +00:00
|
|
|
func TestListStaticPorts(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{80, 443, 3306, 8080},
|
|
|
|
DynamicPorts: []string{"mysql", "admin"},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := []int{80, 443}
|
|
|
|
actual := resources.ListStaticPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-24 22:11:44 +00:00
|
|
|
func TestListStaticPortsEmpty(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{},
|
|
|
|
DynamicPorts: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := []int{}
|
|
|
|
actual := resources.ListStaticPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestListStaticPortsOnly(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{80, 443},
|
|
|
|
DynamicPorts: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := []int{80, 443}
|
|
|
|
actual := resources.ListStaticPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestListStaticPortsDynamicOnly(t *testing.T) {
|
|
|
|
resources := &NetworkResource{
|
|
|
|
ReservedPorts: []int{3306, 8080},
|
|
|
|
DynamicPorts: []string{"mysql", "admin"},
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := []int{}
|
|
|
|
actual := resources.ListStaticPorts()
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expected, actual) {
|
|
|
|
t.Fatalf("Expected %#v; found %#v", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-05 22:21:17 +00:00
|
|
|
func TestEncodeDecode(t *testing.T) {
|
|
|
|
type FooRequest struct {
|
|
|
|
Foo string
|
|
|
|
Bar int
|
|
|
|
Baz bool
|
|
|
|
}
|
|
|
|
arg := &FooRequest{
|
|
|
|
Foo: "test",
|
|
|
|
Bar: 42,
|
|
|
|
Baz: true,
|
|
|
|
}
|
|
|
|
buf, err := Encode(1, arg)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var out FooRequest
|
|
|
|
err = Decode(buf[1:], &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(arg, &out) {
|
|
|
|
t.Fatalf("bad: %#v %#v", arg, out)
|
|
|
|
}
|
|
|
|
}
|