1package command
2
3import (
4	"fmt"
5	"testing"
6
7	"github.com/hashicorp/nomad/api"
8	"github.com/hashicorp/nomad/nomad/mock"
9	"github.com/hashicorp/nomad/nomad/structs"
10	"github.com/hashicorp/nomad/testutil"
11	"github.com/mitchellh/cli"
12	"github.com/posener/complete"
13	"github.com/stretchr/testify/assert"
14	"github.com/stretchr/testify/require"
15)
16
17func TestAllocRestartCommand_Implements(t *testing.T) {
18	var _ cli.Command = &AllocRestartCommand{}
19}
20
21func TestAllocRestartCommand_Fails(t *testing.T) {
22	srv, client, url := testServer(t, true, nil)
23	defer srv.Shutdown()
24
25	require := require.New(t)
26	ui := new(cli.MockUi)
27	cmd := &AllocRestartCommand{Meta: Meta{Ui: ui}}
28
29	// Fails on misuse
30	require.Equal(cmd.Run([]string{"some", "garbage", "args"}), 1, "Expected failure")
31	require.Contains(ui.ErrorWriter.String(), commandErrorText(cmd), "Expected help output")
32	ui.ErrorWriter.Reset()
33
34	// Fails on connection failure
35	require.Equal(cmd.Run([]string{"-address=nope", "foobar"}), 1, "expected failure")
36	require.Contains(ui.ErrorWriter.String(), "Error querying allocation")
37	ui.ErrorWriter.Reset()
38
39	// Fails on missing alloc
40	require.Equal(cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}), 1)
41	require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
42	ui.ErrorWriter.Reset()
43
44	// Fail on identifier with too few characters
45	require.Equal(cmd.Run([]string{"-address=" + url, "2"}), 1)
46	require.Contains(ui.ErrorWriter.String(), "must contain at least two characters")
47	ui.ErrorWriter.Reset()
48
49	// Identifiers with uneven length should produce a query result
50	require.Equal(cmd.Run([]string{"-address=" + url, "123"}), 1)
51	require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
52	ui.ErrorWriter.Reset()
53
54	// Wait for a node to be ready
55	testutil.WaitForResult(func() (bool, error) {
56		nodes, _, err := client.Nodes().List(nil)
57		if err != nil {
58			return false, err
59		}
60		for _, node := range nodes {
61			if _, ok := node.Drivers["mock_driver"]; ok &&
62				node.Status == structs.NodeStatusReady {
63				return true, nil
64			}
65		}
66		return false, fmt.Errorf("no ready nodes")
67	}, func(err error) {
68		t.Fatalf("err: %v", err)
69	})
70
71	jobID := "job1_sfx"
72	job1 := testJob(jobID)
73	resp, _, err := client.Jobs().Register(job1, nil)
74	require.NoError(err)
75	if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
76		t.Fatalf("status code non zero saw %d", code)
77	}
78	// get an alloc id
79	allocId1 := ""
80	if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
81		if len(allocs) > 0 {
82			allocId1 = allocs[0].ID
83		}
84	}
85	require.NotEmpty(allocId1, "unable to find allocation")
86
87	// Fails on not found task
88	require.Equal(cmd.Run([]string{"-address=" + url, allocId1, "fooooobarrr"}), 1)
89	require.Contains(ui.ErrorWriter.String(), "Could not find task named")
90	ui.ErrorWriter.Reset()
91}
92
93func TestAllocRestartCommand_Run(t *testing.T) {
94	srv, client, url := testServer(t, true, nil)
95	defer srv.Shutdown()
96
97	require := require.New(t)
98
99	// Wait for a node to be ready
100	testutil.WaitForResult(func() (bool, error) {
101		nodes, _, err := client.Nodes().List(nil)
102		if err != nil {
103			return false, err
104		}
105		for _, node := range nodes {
106			if _, ok := node.Drivers["mock_driver"]; ok &&
107				node.Status == structs.NodeStatusReady {
108				return true, nil
109			}
110		}
111		return false, fmt.Errorf("no ready nodes")
112	}, func(err error) {
113		t.Fatalf("err: %v", err)
114	})
115
116	ui := new(cli.MockUi)
117	cmd := &AllocRestartCommand{Meta: Meta{Ui: ui}}
118
119	jobID := "job1_sfx"
120	job1 := testJob(jobID)
121	resp, _, err := client.Jobs().Register(job1, nil)
122	require.NoError(err)
123	if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
124		t.Fatalf("status code non zero saw %d", code)
125	}
126	// get an alloc id
127	allocId1 := ""
128	if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
129		if len(allocs) > 0 {
130			allocId1 = allocs[0].ID
131		}
132	}
133	require.NotEmpty(allocId1, "unable to find allocation")
134
135	// Wait for alloc to be running
136	testutil.WaitForResult(func() (bool, error) {
137		alloc, _, err := client.Allocations().Info(allocId1, nil)
138		if err != nil {
139			return false, err
140		}
141		if alloc.ClientStatus == api.AllocClientStatusRunning {
142			return true, nil
143		}
144		return false, fmt.Errorf("alloc is not running, is: %s", alloc.ClientStatus)
145	}, func(err error) {
146		t.Fatalf("err: %v", err)
147	})
148
149	require.Equal(cmd.Run([]string{"-address=" + url, allocId1}), 0, "expected successful exit code")
150
151	ui.OutputWriter.Reset()
152}
153
154func TestAllocRestartCommand_AutocompleteArgs(t *testing.T) {
155	assert := assert.New(t)
156
157	srv, _, url := testServer(t, true, nil)
158	defer srv.Shutdown()
159
160	ui := new(cli.MockUi)
161	cmd := &AllocRestartCommand{Meta: Meta{Ui: ui, flagAddress: url}}
162
163	// Create a fake alloc
164	state := srv.Agent.Server().State()
165	a := mock.Alloc()
166	assert.Nil(state.UpsertAllocs(1000, []*structs.Allocation{a}))
167
168	prefix := a.ID[:5]
169	args := complete.Args{Last: prefix}
170	predictor := cmd.AutocompleteArgs()
171
172	res := predictor.Predict(args)
173	assert.Equal(1, len(res))
174	assert.Equal(a.ID, res[0])
175}
176