github.com/manicqin/nomad@v0.9.5/command/node_status_test.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/nomad/api"
    10  	"github.com/hashicorp/nomad/command/agent"
    11  	"github.com/hashicorp/nomad/testutil"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/posener/complete"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestNodeStatusCommand_Implements(t *testing.T) {
    18  	t.Parallel()
    19  	var _ cli.Command = &NodeStatusCommand{}
    20  }
    21  
    22  func TestNodeStatusCommand_Self(t *testing.T) {
    23  	t.Parallel()
    24  	// Start in dev mode so we get a node registration
    25  	srv, client, url := testServer(t, true, func(c *agent.Config) {
    26  		c.NodeName = "mynode"
    27  	})
    28  	defer srv.Shutdown()
    29  
    30  	ui := new(cli.MockUi)
    31  	cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}}
    32  
    33  	// Wait for a node to appear
    34  	var nodeID string
    35  	testutil.WaitForResult(func() (bool, error) {
    36  		nodes, _, err := client.Nodes().List(nil)
    37  		if err != nil {
    38  			return false, err
    39  		}
    40  		if len(nodes) == 0 {
    41  			return false, fmt.Errorf("missing node")
    42  		}
    43  		nodeID = nodes[0].ID
    44  		return true, nil
    45  	}, func(err error) {
    46  		t.Fatalf("err: %s", err)
    47  	})
    48  
    49  	// Query self node
    50  	if code := cmd.Run([]string{"-address=" + url, "-self"}); code != 0 {
    51  		t.Fatalf("expected exit 0, got: %d", code)
    52  	}
    53  	out := ui.OutputWriter.String()
    54  	if !strings.Contains(out, "mynode") {
    55  		t.Fatalf("expect to find mynode, got: %s", out)
    56  	}
    57  	if !strings.Contains(out, "No allocations placed") {
    58  		t.Fatalf("should not dump allocations")
    59  	}
    60  	ui.OutputWriter.Reset()
    61  
    62  	// Request full id output
    63  	if code := cmd.Run([]string{"-address=" + url, "-self", "-verbose"}); code != 0 {
    64  		t.Fatalf("expected exit 0, got: %d", code)
    65  	}
    66  	out = ui.OutputWriter.String()
    67  	if !strings.Contains(out, nodeID) {
    68  		t.Fatalf("expected full node id %q, got: %s", nodeID, out)
    69  	}
    70  	ui.OutputWriter.Reset()
    71  }
    72  
    73  func TestNodeStatusCommand_Run(t *testing.T) {
    74  	t.Parallel()
    75  	// Start in dev mode so we get a node registration
    76  	srv, client, url := testServer(t, true, func(c *agent.Config) {
    77  		c.NodeName = "mynode"
    78  	})
    79  	defer srv.Shutdown()
    80  
    81  	ui := new(cli.MockUi)
    82  	cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}}
    83  
    84  	// Wait for a node to appear
    85  	var nodeID string
    86  	testutil.WaitForResult(func() (bool, error) {
    87  		nodes, _, err := client.Nodes().List(nil)
    88  		if err != nil {
    89  			return false, err
    90  		}
    91  		if len(nodes) == 0 {
    92  			return false, fmt.Errorf("missing node")
    93  		}
    94  		nodeID = nodes[0].ID
    95  		return true, nil
    96  	}, func(err error) {
    97  		t.Fatalf("err: %s", err)
    98  	})
    99  
   100  	// Query all node statuses
   101  	if code := cmd.Run([]string{"-address=" + url}); code != 0 {
   102  		t.Fatalf("expected exit 0, got: %d", code)
   103  	}
   104  	out := ui.OutputWriter.String()
   105  	if !strings.Contains(out, "mynode") {
   106  		t.Fatalf("expect to find mynode, got: %s", out)
   107  	}
   108  	ui.OutputWriter.Reset()
   109  
   110  	// Query a single node
   111  	if code := cmd.Run([]string{"-address=" + url, nodeID}); code != 0 {
   112  		t.Fatalf("expected exit 0, got: %d", code)
   113  	}
   114  	out = ui.OutputWriter.String()
   115  	if !strings.Contains(out, "mynode") {
   116  		t.Fatalf("expect to find mynode, got: %s", out)
   117  	}
   118  	ui.OutputWriter.Reset()
   119  
   120  	// Query single node in short view
   121  	if code := cmd.Run([]string{"-address=" + url, "-short", nodeID}); code != 0 {
   122  		t.Fatalf("expected exit 0, got: %d", code)
   123  	}
   124  	out = ui.OutputWriter.String()
   125  	if !strings.Contains(out, "mynode") {
   126  		t.Fatalf("expect to find mynode, got: %s", out)
   127  	}
   128  	if !strings.Contains(out, "No allocations placed") {
   129  		t.Fatalf("should not dump allocations")
   130  	}
   131  
   132  	// Query a single node based on a prefix that is even without the hyphen
   133  	if code := cmd.Run([]string{"-address=" + url, nodeID[:13]}); code != 0 {
   134  		t.Fatalf("expected exit 0, got: %d", code)
   135  	}
   136  	out = ui.OutputWriter.String()
   137  	if !strings.Contains(out, "mynode") {
   138  		t.Fatalf("expect to find mynode, got: %s", out)
   139  	}
   140  	if !strings.Contains(out, nodeID) {
   141  		t.Fatalf("expected node id %q, got: %s", nodeID, out)
   142  	}
   143  	ui.OutputWriter.Reset()
   144  
   145  	// Request full id output
   146  	if code := cmd.Run([]string{"-address=" + url, "-verbose", nodeID[:4]}); code != 0 {
   147  		t.Fatalf("expected exit 0, got: %d", code)
   148  	}
   149  	out = ui.OutputWriter.String()
   150  	if !strings.Contains(out, nodeID) {
   151  		t.Fatalf("expected full node id %q, got: %s", nodeID, out)
   152  	}
   153  	ui.OutputWriter.Reset()
   154  
   155  	// Identifiers with uneven length should produce a query result
   156  	if code := cmd.Run([]string{"-address=" + url, nodeID[:3]}); code != 0 {
   157  		t.Fatalf("expected exit 0, got: %d", code)
   158  	}
   159  	out = ui.OutputWriter.String()
   160  	if !strings.Contains(out, "mynode") {
   161  		t.Fatalf("expect to find mynode, got: %s", out)
   162  	}
   163  }
   164  
   165  func TestNodeStatusCommand_Fails(t *testing.T) {
   166  	t.Parallel()
   167  	srv, _, url := testServer(t, false, nil)
   168  	defer srv.Shutdown()
   169  
   170  	ui := new(cli.MockUi)
   171  	cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}}
   172  
   173  	// Fails on misuse
   174  	if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
   175  		t.Fatalf("expected exit code 1, got: %d", code)
   176  	}
   177  	if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
   178  		t.Fatalf("expected help output, got: %s", out)
   179  	}
   180  	ui.ErrorWriter.Reset()
   181  
   182  	// Fails on connection failure
   183  	if code := cmd.Run([]string{"-address=nope"}); code != 1 {
   184  		t.Fatalf("expected exit code 1, got: %d", code)
   185  	}
   186  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying node status") {
   187  		t.Fatalf("expected failed query error, got: %s", out)
   188  	}
   189  	ui.ErrorWriter.Reset()
   190  
   191  	// Fails on nonexistent node
   192  	if code := cmd.Run([]string{"-address=" + url, "12345678-abcd-efab-cdef-123456789abc"}); code != 1 {
   193  		t.Fatalf("expected exit 1, got: %d", code)
   194  	}
   195  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "No node(s) with prefix") {
   196  		t.Fatalf("expected not found error, got: %s", out)
   197  	}
   198  	ui.ErrorWriter.Reset()
   199  
   200  	// Fail on identifier with too few characters
   201  	if code := cmd.Run([]string{"-address=" + url, "1"}); code != 1 {
   202  		t.Fatalf("expected exit 1, got: %d", code)
   203  	}
   204  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") {
   205  		t.Fatalf("expected too few characters error, got: %s", out)
   206  	}
   207  	ui.ErrorWriter.Reset()
   208  
   209  	// Failed on both -json and -t options are specified
   210  	if code := cmd.Run([]string{"-address=" + url, "-json", "-t", "{{.ID}}"}); code != 1 {
   211  		t.Fatalf("expected exit 1, got: %d", code)
   212  	}
   213  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Both json and template formatting are not allowed") {
   214  		t.Fatalf("expected getting formatter error, got: %s", out)
   215  	}
   216  }
   217  
   218  func TestNodeStatusCommand_AutocompleteArgs(t *testing.T) {
   219  	assert := assert.New(t)
   220  	t.Parallel()
   221  
   222  	srv, client, url := testServer(t, true, nil)
   223  	defer srv.Shutdown()
   224  
   225  	// Wait for a node to appear
   226  	var nodeID string
   227  	testutil.WaitForResult(func() (bool, error) {
   228  		nodes, _, err := client.Nodes().List(nil)
   229  		if err != nil {
   230  			return false, err
   231  		}
   232  		if len(nodes) == 0 {
   233  			return false, fmt.Errorf("missing node")
   234  		}
   235  		nodeID = nodes[0].ID
   236  		return true, nil
   237  	}, func(err error) {
   238  		t.Fatalf("err: %s", err)
   239  	})
   240  
   241  	ui := new(cli.MockUi)
   242  	cmd := &NodeStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}}
   243  
   244  	prefix := nodeID[:len(nodeID)-5]
   245  	args := complete.Args{Last: prefix}
   246  	predictor := cmd.AutocompleteArgs()
   247  
   248  	res := predictor.Predict(args)
   249  	assert.Equal(1, len(res))
   250  	assert.Equal(nodeID, res[0])
   251  }
   252  
   253  func TestNodeStatusCommand_FormatDrain(t *testing.T) {
   254  	t.Parallel()
   255  	assert := assert.New(t)
   256  
   257  	node := &api.Node{}
   258  
   259  	assert.Equal("false", formatDrain(node))
   260  
   261  	node.DrainStrategy = &api.DrainStrategy{}
   262  	assert.Equal("true; no deadline", formatDrain(node))
   263  
   264  	node.DrainStrategy = &api.DrainStrategy{}
   265  	node.DrainStrategy.Deadline = -1 * time.Second
   266  	assert.Equal("true; force drain", formatDrain(node))
   267  
   268  	// formatTime special cases Unix(0, 0), so increment by 1
   269  	node.DrainStrategy = &api.DrainStrategy{}
   270  	node.DrainStrategy.ForceDeadline = time.Unix(1, 0).UTC()
   271  	assert.Equal("true; 1970-01-01T00:00:01Z deadline", formatDrain(node))
   272  
   273  	node.DrainStrategy.IgnoreSystemJobs = true
   274  	assert.Equal("true; 1970-01-01T00:00:01Z deadline; ignoring system jobs", formatDrain(node))
   275  }