2015-09-15 23:44:38 +00:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2016-08-16 12:30:40 +00:00
|
|
|
"fmt"
|
2016-07-20 02:48:16 +00:00
|
|
|
"io"
|
2016-07-13 19:23:33 +00:00
|
|
|
"io/ioutil"
|
2016-08-16 12:30:40 +00:00
|
|
|
"net/http"
|
2016-08-10 14:30:19 +00:00
|
|
|
"os"
|
2016-07-20 02:48:16 +00:00
|
|
|
"reflect"
|
2016-07-13 19:23:33 +00:00
|
|
|
"strings"
|
2015-09-15 23:44:38 +00:00
|
|
|
"testing"
|
2016-07-20 02:48:16 +00:00
|
|
|
"time"
|
2016-04-11 22:20:49 +00:00
|
|
|
|
2017-02-13 23:18:17 +00:00
|
|
|
"github.com/hashicorp/nomad/api"
|
|
|
|
"github.com/hashicorp/nomad/helper"
|
2017-02-16 21:13:29 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/flatmap"
|
2017-02-22 22:15:22 +00:00
|
|
|
"github.com/kr/pretty"
|
2016-04-11 22:20:49 +00:00
|
|
|
"github.com/mitchellh/cli"
|
2015-09-15 23:44:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestHelpers_FormatKV(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2015-09-27 21:04:53 +00:00
|
|
|
in := []string{"alpha|beta", "charlie|delta", "echo|"}
|
2015-09-15 23:44:38 +00:00
|
|
|
out := formatKV(in)
|
|
|
|
|
|
|
|
expect := "alpha = beta\n"
|
2015-09-27 21:04:53 +00:00
|
|
|
expect += "charlie = delta\n"
|
|
|
|
expect += "echo = <none>"
|
2015-09-15 23:44:38 +00:00
|
|
|
|
|
|
|
if out != expect {
|
|
|
|
t.Fatalf("expect: %s, got: %s", expect, out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHelpers_FormatList(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2015-09-15 23:44:38 +00:00
|
|
|
in := []string{"alpha|beta||delta"}
|
|
|
|
out := formatList(in)
|
|
|
|
|
|
|
|
expect := "alpha beta <none> delta"
|
|
|
|
|
|
|
|
if out != expect {
|
|
|
|
t.Fatalf("expect: %s, got: %s", expect, out)
|
|
|
|
}
|
|
|
|
}
|
2016-04-11 22:20:49 +00:00
|
|
|
|
|
|
|
func TestHelpers_NodeID(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2017-07-21 04:07:32 +00:00
|
|
|
srv, _, _ := testServer(t, false, nil)
|
|
|
|
defer srv.Shutdown()
|
2016-04-11 22:20:49 +00:00
|
|
|
|
|
|
|
meta := Meta{Ui: new(cli.MockUi)}
|
|
|
|
client, err := meta.Client()
|
|
|
|
if err != nil {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is because there is no client
|
|
|
|
if _, err := getLocalNodeID(client); err == nil {
|
|
|
|
t.Fatalf("getLocalNodeID() should fail")
|
|
|
|
}
|
|
|
|
}
|
2016-07-13 19:23:33 +00:00
|
|
|
|
2016-07-20 02:48:16 +00:00
|
|
|
func TestHelpers_LineLimitReader_NoTimeLimit(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2016-07-13 19:23:33 +00:00
|
|
|
helloString := `hello
|
|
|
|
world
|
|
|
|
this
|
|
|
|
is
|
|
|
|
a
|
|
|
|
test`
|
|
|
|
|
|
|
|
noLines := "jskdfhjasdhfjkajkldsfdlsjkahfkjdsafa"
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
Lines int
|
|
|
|
SearchLimit int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Input: helloString,
|
|
|
|
Output: helloString,
|
|
|
|
Lines: 6,
|
|
|
|
SearchLimit: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: helloString,
|
|
|
|
Output: `world
|
|
|
|
this
|
|
|
|
is
|
|
|
|
a
|
|
|
|
test`,
|
|
|
|
Lines: 5,
|
|
|
|
SearchLimit: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: helloString,
|
|
|
|
Output: `test`,
|
|
|
|
Lines: 1,
|
|
|
|
SearchLimit: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: helloString,
|
|
|
|
Output: "",
|
|
|
|
Lines: 0,
|
|
|
|
SearchLimit: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: helloString,
|
|
|
|
Output: helloString,
|
|
|
|
Lines: 6,
|
|
|
|
SearchLimit: 1, // Exceed the limit
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: noLines,
|
|
|
|
Output: noLines,
|
|
|
|
Lines: 10,
|
|
|
|
SearchLimit: 1000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Input: noLines,
|
|
|
|
Output: noLines,
|
|
|
|
Lines: 10,
|
|
|
|
SearchLimit: 2,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, c := range cases {
|
|
|
|
in := ioutil.NopCloser(strings.NewReader(c.Input))
|
2016-07-20 02:48:16 +00:00
|
|
|
limit := NewLineLimitReader(in, c.Lines, c.SearchLimit, 0)
|
2016-07-13 19:23:33 +00:00
|
|
|
outBytes, err := ioutil.ReadAll(limit)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("case %d failed: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
out := string(outBytes)
|
|
|
|
if out != c.Output {
|
|
|
|
t.Fatalf("case %d: got %q; want %q", i, out, c.Output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-20 02:48:16 +00:00
|
|
|
|
|
|
|
type testReadCloser struct {
|
|
|
|
data chan []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *testReadCloser) Read(p []byte) (n int, err error) {
|
|
|
|
select {
|
|
|
|
case b, ok := <-t.data:
|
|
|
|
if !ok {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
return copy(p, b), nil
|
|
|
|
case <-time.After(10 * time.Millisecond):
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *testReadCloser) Close() error {
|
|
|
|
close(t.data)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHelpers_LineLimitReader_TimeLimit(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2016-07-20 02:48:16 +00:00
|
|
|
// Create the test reader
|
|
|
|
in := &testReadCloser{data: make(chan []byte)}
|
|
|
|
|
|
|
|
// Set up the reader such that it won't hit the line/buffer limit and could
|
|
|
|
// only terminate if it hits the time limit
|
|
|
|
limit := NewLineLimitReader(in, 1000, 1000, 100*time.Millisecond)
|
|
|
|
|
|
|
|
expected := []byte("hello world")
|
|
|
|
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
outBytes, err := ioutil.ReadAll(limit)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ReadAll failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if reflect.DeepEqual(outBytes, expected) {
|
|
|
|
close(resultCh)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Send the data
|
|
|
|
in.data <- expected
|
|
|
|
in.Close()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
case <-time.After(1 * time.Second):
|
|
|
|
t.Fatalf("did not exit by time limit")
|
|
|
|
}
|
|
|
|
}
|
2016-08-10 14:30:19 +00:00
|
|
|
|
2016-08-16 12:30:40 +00:00
|
|
|
const (
|
|
|
|
job = `job "job1" {
|
2016-08-10 14:30:19 +00:00
|
|
|
type = "service"
|
|
|
|
datacenters = [ "dc1" ]
|
|
|
|
group "group1" {
|
|
|
|
count = 1
|
|
|
|
task "task1" {
|
|
|
|
driver = "exec"
|
|
|
|
resources = {}
|
|
|
|
}
|
2016-08-16 12:30:40 +00:00
|
|
|
restart{
|
2016-08-10 14:30:19 +00:00
|
|
|
attempts = 10
|
2016-08-16 12:30:40 +00:00
|
|
|
mode = "delay"
|
2017-02-15 01:06:30 +00:00
|
|
|
interval = "15s"
|
2016-08-16 12:30:40 +00:00
|
|
|
}
|
2016-08-10 14:30:19 +00:00
|
|
|
}
|
2016-08-16 12:30:40 +00:00
|
|
|
}`
|
|
|
|
)
|
|
|
|
|
2017-02-16 22:00:41 +00:00
|
|
|
var (
|
|
|
|
expectedApiJob = &api.Job{
|
2017-02-13 23:18:17 +00:00
|
|
|
ID: helper.StringToPtr("job1"),
|
|
|
|
Name: helper.StringToPtr("job1"),
|
|
|
|
Type: helper.StringToPtr("service"),
|
|
|
|
Datacenters: []string{"dc1"},
|
|
|
|
TaskGroups: []*api.TaskGroup{
|
|
|
|
{
|
|
|
|
Name: helper.StringToPtr("group1"),
|
|
|
|
Count: helper.IntToPtr(1),
|
|
|
|
RestartPolicy: &api.RestartPolicy{
|
|
|
|
Attempts: helper.IntToPtr(10),
|
2017-02-16 22:00:41 +00:00
|
|
|
Interval: helper.TimeToPtr(15 * time.Second),
|
2017-02-13 23:18:17 +00:00
|
|
|
Mode: helper.StringToPtr("delay"),
|
|
|
|
},
|
|
|
|
|
|
|
|
Tasks: []*api.Task{
|
|
|
|
{
|
2017-02-22 22:15:22 +00:00
|
|
|
Driver: "exec",
|
|
|
|
Name: "task1",
|
|
|
|
Resources: &api.Resources{},
|
2017-02-13 23:18:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2017-02-16 22:00:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Test APIJob with local jobfile
|
|
|
|
func TestJobGetter_LocalFile(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2017-02-16 22:00:41 +00:00
|
|
|
fh, err := ioutil.TempFile("", "nomad")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer os.Remove(fh.Name())
|
|
|
|
_, err = fh.WriteString(job)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
j := &JobGetter{}
|
|
|
|
aj, err := j.ApiJob(fh.Name())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(expectedApiJob, aj) {
|
|
|
|
eflat := flatmap.Flatten(expectedApiJob, nil, false)
|
2017-02-16 21:13:29 +00:00
|
|
|
aflat := flatmap.Flatten(aj, nil, false)
|
|
|
|
t.Fatalf("got:\n%v\nwant:\n%v", aflat, eflat)
|
2016-08-10 14:30:19 +00:00
|
|
|
}
|
2016-08-16 12:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Test StructJob with jobfile from HTTP Server
|
2017-02-16 01:04:53 +00:00
|
|
|
func TestJobGetter_HTTPServer(t *testing.T) {
|
2017-07-21 04:24:21 +00:00
|
|
|
t.Parallel()
|
2016-08-16 12:30:40 +00:00
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
fmt.Fprintf(w, job)
|
|
|
|
})
|
|
|
|
go http.ListenAndServe("127.0.0.1:12345", nil)
|
|
|
|
|
|
|
|
// Wait until HTTP Server starts certainly
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
2016-08-10 14:30:19 +00:00
|
|
|
|
2016-08-16 12:30:40 +00:00
|
|
|
j := &JobGetter{}
|
2017-02-13 23:18:17 +00:00
|
|
|
aj, err := j.ApiJob("http://127.0.0.1:12345/")
|
2016-08-16 12:30:40 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2017-02-16 22:00:41 +00:00
|
|
|
if !reflect.DeepEqual(expectedApiJob, aj) {
|
2017-02-22 22:15:22 +00:00
|
|
|
for _, d := range pretty.Diff(expectedApiJob, aj) {
|
|
|
|
t.Logf(d)
|
|
|
|
}
|
|
|
|
t.Fatalf("Unexpected file")
|
2016-08-16 12:30:40 +00:00
|
|
|
}
|
2016-08-10 14:30:19 +00:00
|
|
|
}
|
2017-10-27 22:24:42 +00:00
|
|
|
|
|
|
|
func TestPrettyTimeDiff(t *testing.T) {
|
2018-04-04 01:14:23 +00:00
|
|
|
// Grab the time and truncate to the nearest second. This allows our tests
|
|
|
|
// to be deterministic since we don't have to worry about rounding.
|
|
|
|
now := time.Now().Truncate(time.Second)
|
|
|
|
|
2017-10-27 22:24:42 +00:00
|
|
|
test_cases := []struct {
|
2017-11-16 15:48:14 +00:00
|
|
|
t1 time.Time
|
|
|
|
t2 time.Time
|
2017-10-27 22:24:42 +00:00
|
|
|
exp string
|
|
|
|
}{
|
2017-12-11 23:58:24 +00:00
|
|
|
{now, time.Unix(0, 0), ""}, // This is the upgrade path case
|
|
|
|
{now, now.Add(-10 * time.Millisecond), "0s ago"},
|
2017-11-16 15:48:14 +00:00
|
|
|
{now, now.Add(-740 * time.Second), "12m20s ago"},
|
|
|
|
{now, now.Add(-12 * time.Minute), "12m ago"},
|
|
|
|
{now, now.Add(-60 * time.Minute), "1h ago"},
|
|
|
|
{now, now.Add(-80 * time.Minute), "1h20m ago"},
|
|
|
|
{now, now.Add(-6 * time.Hour), "6h ago"},
|
2018-03-02 23:43:49 +00:00
|
|
|
{now.Add(-6 * time.Hour), now, "6h from now"},
|
2017-11-16 15:48:14 +00:00
|
|
|
{now, now.Add(-22165 * time.Second), "6h9m ago"},
|
|
|
|
{now, now.Add(-100 * time.Hour), "4d4h ago"},
|
|
|
|
{now, now.Add(-438000 * time.Minute), "10mo4d ago"},
|
|
|
|
{now, now.Add(-20460 * time.Hour), "2y4mo ago"},
|
2017-10-27 22:24:42 +00:00
|
|
|
}
|
|
|
|
for _, tc := range test_cases {
|
2018-04-04 01:14:23 +00:00
|
|
|
t.Run(tc.exp, func(t *testing.T) {
|
|
|
|
out := prettyTimeDiff(tc.t2, tc.t1)
|
|
|
|
if out != tc.exp {
|
|
|
|
t.Fatalf("expected :%v but got :%v", tc.exp, out)
|
|
|
|
}
|
|
|
|
})
|
2017-10-27 22:24:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var t1 time.Time
|
|
|
|
out := prettyTimeDiff(t1, time.Now())
|
|
|
|
|
|
|
|
if out != "" {
|
|
|
|
t.Fatalf("Expected empty output but got:%v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|