github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/command/job_plan_test.go (about)

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