github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/helpers_test.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"reflect"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/hashicorp/nomad/api"
    15  	"github.com/hashicorp/nomad/helper"
    16  	"github.com/hashicorp/nomad/helper/flatmap"
    17  	"github.com/kr/pretty"
    18  	"github.com/mitchellh/cli"
    19  )
    20  
    21  func TestHelpers_FormatKV(t *testing.T) {
    22  	t.Parallel()
    23  	in := []string{"alpha|beta", "charlie|delta", "echo|"}
    24  	out := formatKV(in)
    25  
    26  	expect := "alpha   = beta\n"
    27  	expect += "charlie = delta\n"
    28  	expect += "echo    = <none>"
    29  
    30  	if out != expect {
    31  		t.Fatalf("expect: %s, got: %s", expect, out)
    32  	}
    33  }
    34  
    35  func TestHelpers_FormatList(t *testing.T) {
    36  	t.Parallel()
    37  	in := []string{"alpha|beta||delta"}
    38  	out := formatList(in)
    39  
    40  	expect := "alpha  beta  <none>  delta"
    41  
    42  	if out != expect {
    43  		t.Fatalf("expect: %s, got: %s", expect, out)
    44  	}
    45  }
    46  
    47  func TestHelpers_NodeID(t *testing.T) {
    48  	t.Parallel()
    49  	srv, _, _ := testServer(t, false, nil)
    50  	defer srv.Shutdown()
    51  
    52  	meta := Meta{Ui: new(cli.MockUi)}
    53  	client, err := meta.Client()
    54  	if err != nil {
    55  		t.FailNow()
    56  	}
    57  
    58  	// This is because there is no client
    59  	if _, err := getLocalNodeID(client); err == nil {
    60  		t.Fatalf("getLocalNodeID() should fail")
    61  	}
    62  }
    63  
    64  func TestHelpers_LineLimitReader_NoTimeLimit(t *testing.T) {
    65  	t.Parallel()
    66  	helloString := `hello
    67  world
    68  this
    69  is
    70  a
    71  test`
    72  
    73  	noLines := "jskdfhjasdhfjkajkldsfdlsjkahfkjdsafa"
    74  
    75  	cases := []struct {
    76  		Input       string
    77  		Output      string
    78  		Lines       int
    79  		SearchLimit int
    80  	}{
    81  		{
    82  			Input:       helloString,
    83  			Output:      helloString,
    84  			Lines:       6,
    85  			SearchLimit: 1000,
    86  		},
    87  		{
    88  			Input: helloString,
    89  			Output: `world
    90  this
    91  is
    92  a
    93  test`,
    94  			Lines:       5,
    95  			SearchLimit: 1000,
    96  		},
    97  		{
    98  			Input:       helloString,
    99  			Output:      `test`,
   100  			Lines:       1,
   101  			SearchLimit: 1000,
   102  		},
   103  		{
   104  			Input:       helloString,
   105  			Output:      "",
   106  			Lines:       0,
   107  			SearchLimit: 1000,
   108  		},
   109  		{
   110  			Input:       helloString,
   111  			Output:      helloString,
   112  			Lines:       6,
   113  			SearchLimit: 1, // Exceed the limit
   114  		},
   115  		{
   116  			Input:       noLines,
   117  			Output:      noLines,
   118  			Lines:       10,
   119  			SearchLimit: 1000,
   120  		},
   121  		{
   122  			Input:       noLines,
   123  			Output:      noLines,
   124  			Lines:       10,
   125  			SearchLimit: 2,
   126  		},
   127  	}
   128  
   129  	for i, c := range cases {
   130  		in := ioutil.NopCloser(strings.NewReader(c.Input))
   131  		limit := NewLineLimitReader(in, c.Lines, c.SearchLimit, 0)
   132  		outBytes, err := ioutil.ReadAll(limit)
   133  		if err != nil {
   134  			t.Fatalf("case %d failed: %v", i, err)
   135  		}
   136  
   137  		out := string(outBytes)
   138  		if out != c.Output {
   139  			t.Fatalf("case %d: got %q; want %q", i, out, c.Output)
   140  		}
   141  	}
   142  }
   143  
   144  type testReadCloser struct {
   145  	data chan []byte
   146  }
   147  
   148  func (t *testReadCloser) Read(p []byte) (n int, err error) {
   149  	select {
   150  	case b, ok := <-t.data:
   151  		if !ok {
   152  			return 0, io.EOF
   153  		}
   154  
   155  		return copy(p, b), nil
   156  	case <-time.After(10 * time.Millisecond):
   157  		return 0, nil
   158  	}
   159  }
   160  
   161  func (t *testReadCloser) Close() error {
   162  	close(t.data)
   163  	return nil
   164  }
   165  
   166  func TestHelpers_LineLimitReader_TimeLimit(t *testing.T) {
   167  	t.Parallel()
   168  	// Create the test reader
   169  	in := &testReadCloser{data: make(chan []byte)}
   170  
   171  	// Set up the reader such that it won't hit the line/buffer limit and could
   172  	// only terminate if it hits the time limit
   173  	limit := NewLineLimitReader(in, 1000, 1000, 100*time.Millisecond)
   174  
   175  	expected := []byte("hello world")
   176  
   177  	resultCh := make(chan struct{})
   178  	go func() {
   179  		outBytes, err := ioutil.ReadAll(limit)
   180  		if err != nil {
   181  			t.Fatalf("ReadAll failed: %v", err)
   182  		}
   183  
   184  		if reflect.DeepEqual(outBytes, expected) {
   185  			close(resultCh)
   186  			return
   187  		}
   188  	}()
   189  
   190  	// Send the data
   191  	in.data <- expected
   192  	in.Close()
   193  
   194  	select {
   195  	case <-resultCh:
   196  	case <-time.After(1 * time.Second):
   197  		t.Fatalf("did not exit by time limit")
   198  	}
   199  }
   200  
   201  const (
   202  	job = `job "job1" {
   203          type = "service"
   204          datacenters = [ "dc1" ]
   205          group "group1" {
   206                  count = 1
   207                  task "task1" {
   208                          driver = "exec"
   209                          resources = {}
   210                  }
   211                  restart{
   212                          attempts = 10
   213                          mode = "delay"
   214  						interval = "15s"
   215                  }
   216          }
   217  }`
   218  )
   219  
   220  var (
   221  	expectedApiJob = &api.Job{
   222  		ID:          helper.StringToPtr("job1"),
   223  		Name:        helper.StringToPtr("job1"),
   224  		Type:        helper.StringToPtr("service"),
   225  		Datacenters: []string{"dc1"},
   226  		TaskGroups: []*api.TaskGroup{
   227  			{
   228  				Name:  helper.StringToPtr("group1"),
   229  				Count: helper.IntToPtr(1),
   230  				RestartPolicy: &api.RestartPolicy{
   231  					Attempts: helper.IntToPtr(10),
   232  					Interval: helper.TimeToPtr(15 * time.Second),
   233  					Mode:     helper.StringToPtr("delay"),
   234  				},
   235  
   236  				Tasks: []*api.Task{
   237  					{
   238  						Driver:    "exec",
   239  						Name:      "task1",
   240  						Resources: &api.Resources{},
   241  					},
   242  				},
   243  			},
   244  		},
   245  	}
   246  )
   247  
   248  // Test APIJob with local jobfile
   249  func TestJobGetter_LocalFile(t *testing.T) {
   250  	t.Parallel()
   251  	fh, err := ioutil.TempFile("", "nomad")
   252  	if err != nil {
   253  		t.Fatalf("err: %s", err)
   254  	}
   255  	defer os.Remove(fh.Name())
   256  	_, err = fh.WriteString(job)
   257  	if err != nil {
   258  		t.Fatalf("err: %s", err)
   259  	}
   260  
   261  	j := &JobGetter{}
   262  	aj, err := j.ApiJob(fh.Name())
   263  	if err != nil {
   264  		t.Fatalf("err: %s", err)
   265  	}
   266  
   267  	if !reflect.DeepEqual(expectedApiJob, aj) {
   268  		eflat := flatmap.Flatten(expectedApiJob, nil, false)
   269  		aflat := flatmap.Flatten(aj, nil, false)
   270  		t.Fatalf("got:\n%v\nwant:\n%v", aflat, eflat)
   271  	}
   272  }
   273  
   274  // Test StructJob with jobfile from HTTP Server
   275  func TestJobGetter_HTTPServer(t *testing.T) {
   276  	t.Parallel()
   277  	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   278  		fmt.Fprintf(w, job)
   279  	})
   280  	go http.ListenAndServe("127.0.0.1:12345", nil)
   281  
   282  	// Wait until HTTP Server starts certainly
   283  	time.Sleep(100 * time.Millisecond)
   284  
   285  	j := &JobGetter{}
   286  	aj, err := j.ApiJob("http://127.0.0.1:12345/")
   287  	if err != nil {
   288  		t.Fatalf("err: %s", err)
   289  	}
   290  	if !reflect.DeepEqual(expectedApiJob, aj) {
   291  		for _, d := range pretty.Diff(expectedApiJob, aj) {
   292  			t.Logf(d)
   293  		}
   294  		t.Fatalf("Unexpected file")
   295  	}
   296  }
   297  
   298  func TestPrettyTimeDiff(t *testing.T) {
   299  	now := time.Now()
   300  	test_cases := []struct {
   301  		t1  time.Time
   302  		t2  time.Time
   303  		exp string
   304  	}{
   305  		{now, time.Unix(0, 0), ""}, // This is the upgrade path case
   306  		{now, now.Add(-10 * time.Millisecond), "0s ago"},
   307  		{now, now.Add(-10 * time.Millisecond), "0s ago"},
   308  		{now, now.Add(-740 * time.Second), "12m20s ago"},
   309  		{now, now.Add(-12 * time.Minute), "12m ago"},
   310  		{now, now.Add(-60 * time.Minute), "1h ago"},
   311  		{now, now.Add(-80 * time.Minute), "1h20m ago"},
   312  		{now, now.Add(-6 * time.Hour), "6h ago"},
   313  		{now, now.Add(-22165 * time.Second), "6h9m ago"},
   314  		{now, now.Add(-100 * time.Hour), "4d4h ago"},
   315  		{now, now.Add(-438000 * time.Minute), "10mo4d ago"},
   316  		{now, now.Add(-20460 * time.Hour), "2y4mo ago"},
   317  	}
   318  	for _, tc := range test_cases {
   319  		out := prettyTimeDiff(tc.t2, tc.t1)
   320  		if out != tc.exp {
   321  			t.Fatalf("expected :%v but got :%v", tc.exp, out)
   322  		}
   323  	}
   324  
   325  	var t1 time.Time
   326  	out := prettyTimeDiff(t1, time.Now())
   327  
   328  	if out != "" {
   329  		t.Fatalf("Expected empty output but got:%v", out)
   330  	}
   331  
   332  }