github.com/ThomasObenaus/nomad@v0.11.1/command/job_plan_test.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/nomad/api"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/mitchellh/cli"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestPlanCommand_Implements(t *testing.T) {
    18  	t.Parallel()
    19  	var _ cli.Command = &JobRunCommand{}
    20  }
    21  
    22  func TestPlanCommand_Fails(t *testing.T) {
    23  	t.Parallel()
    24  	ui := new(cli.MockUi)
    25  	cmd := &JobPlanCommand{Meta: Meta{Ui: ui}}
    26  
    27  	// Create a server
    28  	s := testutil.NewTestServer(t, nil)
    29  	defer s.Stop()
    30  	os.Setenv("NOMAD_ADDR", fmt.Sprintf("http://%s", s.HTTPAddr))
    31  
    32  	// Fails on misuse
    33  	if code := cmd.Run([]string{"some", "bad", "args"}); code != 255 {
    34  		t.Fatalf("expected exit code 1, got: %d", code)
    35  	}
    36  	if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
    37  		t.Fatalf("expected help output, got: %s", out)
    38  	}
    39  	ui.ErrorWriter.Reset()
    40  
    41  	// Fails when specified file does not exist
    42  	if code := cmd.Run([]string{"/unicorns/leprechauns"}); code != 255 {
    43  		t.Fatalf("expect exit 255, got: %d", code)
    44  	}
    45  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") {
    46  		t.Fatalf("expect getting job struct error, got: %s", out)
    47  	}
    48  	ui.ErrorWriter.Reset()
    49  
    50  	// Fails on invalid HCL
    51  	fh1, err := ioutil.TempFile("", "nomad")
    52  	if err != nil {
    53  		t.Fatalf("err: %s", err)
    54  	}
    55  	defer os.Remove(fh1.Name())
    56  	if _, err := fh1.WriteString("nope"); err != nil {
    57  		t.Fatalf("err: %s", err)
    58  	}
    59  	if code := cmd.Run([]string{fh1.Name()}); code != 255 {
    60  		t.Fatalf("expect exit 255, got: %d", code)
    61  	}
    62  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting job struct") {
    63  		t.Fatalf("expect parsing error, got: %s", out)
    64  	}
    65  	ui.ErrorWriter.Reset()
    66  
    67  	// Fails on invalid job spec
    68  	fh2, err := ioutil.TempFile("", "nomad")
    69  	if err != nil {
    70  		t.Fatalf("err: %s", err)
    71  	}
    72  	defer os.Remove(fh2.Name())
    73  	if _, err := fh2.WriteString(`job "job1" {}`); err != nil {
    74  		t.Fatalf("err: %s", err)
    75  	}
    76  	if code := cmd.Run([]string{fh2.Name()}); code != 255 {
    77  		t.Fatalf("expect exit 255, got: %d", code)
    78  	}
    79  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error during plan") {
    80  		t.Fatalf("expect validation error, got: %s", out)
    81  	}
    82  	ui.ErrorWriter.Reset()
    83  
    84  	// Fails on connection failure (requires a valid job)
    85  	fh3, err := ioutil.TempFile("", "nomad")
    86  	if err != nil {
    87  		t.Fatalf("err: %s", err)
    88  	}
    89  	defer os.Remove(fh3.Name())
    90  	_, err = fh3.WriteString(`
    91  job "job1" {
    92  	type = "service"
    93  	datacenters = [ "dc1" ]
    94  	group "group1" {
    95  		count = 1
    96  		task "task1" {
    97  			driver = "exec"
    98  			resources = {
    99  				cpu = 1000
   100  				memory = 512
   101  			}
   102  		}
   103  	}
   104  }`)
   105  	if err != nil {
   106  		t.Fatalf("err: %s", err)
   107  	}
   108  	if code := cmd.Run([]string{"-address=nope", fh3.Name()}); code != 255 {
   109  		t.Fatalf("expected exit code 255, got: %d", code)
   110  	}
   111  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error during plan") {
   112  		t.Fatalf("expected failed query error, got: %s", out)
   113  	}
   114  }
   115  
   116  func TestPlanCommand_From_STDIN(t *testing.T) {
   117  	t.Parallel()
   118  	stdinR, stdinW, err := os.Pipe()
   119  	if err != nil {
   120  		t.Fatalf("err: %s", err)
   121  	}
   122  
   123  	ui := new(cli.MockUi)
   124  	cmd := &JobPlanCommand{
   125  		Meta:      Meta{Ui: ui},
   126  		JobGetter: JobGetter{testStdin: stdinR},
   127  	}
   128  
   129  	go func() {
   130  		stdinW.WriteString(`
   131  job "job1" {
   132    type = "service"
   133    datacenters = [ "dc1" ]
   134    group "group1" {
   135                  count = 1
   136                  task "task1" {
   137                          driver = "exec"
   138                          resources = {
   139                                  cpu = 1000
   140                                  memory = 512
   141                          }
   142                  }
   143          }
   144  }`)
   145  		stdinW.Close()
   146  	}()
   147  
   148  	args := []string{"-"}
   149  	if code := cmd.Run(args); code != 255 {
   150  		t.Fatalf("expected exit code 255, got %d: %q", code, ui.ErrorWriter.String())
   151  	}
   152  
   153  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "connection refused") {
   154  		t.Fatalf("expected connection refused error, got: %s", out)
   155  	}
   156  	ui.ErrorWriter.Reset()
   157  }
   158  
   159  func TestPlanCommand_From_URL(t *testing.T) {
   160  	t.Parallel()
   161  	ui := new(cli.MockUi)
   162  	cmd := &JobPlanCommand{
   163  		Meta: Meta{Ui: ui},
   164  	}
   165  
   166  	args := []string{"https://example.com/foo/bar"}
   167  	if code := cmd.Run(args); code != 255 {
   168  		t.Fatalf("expected exit code 255, got %d: %q", code, ui.ErrorWriter.String())
   169  	}
   170  
   171  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error getting jobfile") {
   172  		t.Fatalf("expected error getting jobfile, got: %s", out)
   173  	}
   174  }
   175  
   176  func TestPlanCommad_Preemptions(t *testing.T) {
   177  	t.Parallel()
   178  	ui := new(cli.MockUi)
   179  	cmd := &JobPlanCommand{Meta: Meta{Ui: ui}}
   180  	require := require.New(t)
   181  
   182  	// Only one preempted alloc
   183  	resp1 := &api.JobPlanResponse{
   184  		Annotations: &api.PlanAnnotations{
   185  			PreemptedAllocs: []*api.AllocationListStub{
   186  				{
   187  					ID:        "alloc1",
   188  					JobID:     "jobID1",
   189  					TaskGroup: "meta",
   190  					JobType:   "batch",
   191  					Namespace: "test",
   192  				},
   193  			},
   194  		},
   195  	}
   196  	cmd.addPreemptions(resp1)
   197  	out := ui.OutputWriter.String()
   198  	require.Contains(out, "Alloc ID")
   199  	require.Contains(out, "alloc1")
   200  
   201  	// Less than 10 unique job ids
   202  	var preemptedAllocs []*api.AllocationListStub
   203  	for i := 0; i < 12; i++ {
   204  		job_id := "job" + strconv.Itoa(i%4)
   205  		alloc := &api.AllocationListStub{
   206  			ID:        "alloc",
   207  			JobID:     job_id,
   208  			TaskGroup: "meta",
   209  			JobType:   "batch",
   210  			Namespace: "test",
   211  		}
   212  		preemptedAllocs = append(preemptedAllocs, alloc)
   213  	}
   214  
   215  	resp2 := &api.JobPlanResponse{
   216  		Annotations: &api.PlanAnnotations{
   217  			PreemptedAllocs: preemptedAllocs,
   218  		},
   219  	}
   220  	ui.OutputWriter.Reset()
   221  	cmd.addPreemptions(resp2)
   222  	out = ui.OutputWriter.String()
   223  	require.Contains(out, "Job ID")
   224  	require.Contains(out, "Namespace")
   225  
   226  	// More than 10 unique job IDs
   227  	preemptedAllocs = make([]*api.AllocationListStub, 0)
   228  	var job_type string
   229  	for i := 0; i < 20; i++ {
   230  		job_id := "job" + strconv.Itoa(i)
   231  		if i%2 == 0 {
   232  			job_type = "service"
   233  		} else {
   234  			job_type = "batch"
   235  		}
   236  		alloc := &api.AllocationListStub{
   237  			ID:        "alloc",
   238  			JobID:     job_id,
   239  			TaskGroup: "meta",
   240  			JobType:   job_type,
   241  			Namespace: "test",
   242  		}
   243  		preemptedAllocs = append(preemptedAllocs, alloc)
   244  	}
   245  
   246  	resp3 := &api.JobPlanResponse{
   247  		Annotations: &api.PlanAnnotations{
   248  			PreemptedAllocs: preemptedAllocs,
   249  		},
   250  	}
   251  	ui.OutputWriter.Reset()
   252  	cmd.addPreemptions(resp3)
   253  	out = ui.OutputWriter.String()
   254  	require.Contains(out, "Job Type")
   255  	require.Contains(out, "batch")
   256  	require.Contains(out, "service")
   257  }