github.com/hernad/nomad@v1.6.112/command/job_scaling_events_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package command
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hernad/nomad/api"
    12  	"github.com/hernad/nomad/ci"
    13  	"github.com/hernad/nomad/command/agent"
    14  	"github.com/hernad/nomad/helper/pointer"
    15  	"github.com/hernad/nomad/nomad/mock"
    16  	"github.com/hernad/nomad/nomad/structs"
    17  	"github.com/hernad/nomad/testutil"
    18  	"github.com/mitchellh/cli"
    19  	"github.com/shoenig/test/must"
    20  )
    21  
    22  func TestJobScalingEventsCommand_Run(t *testing.T) {
    23  	ci.Parallel(t)
    24  	srv, client, url := testServer(t, true, nil)
    25  	defer srv.Shutdown()
    26  	testutil.WaitForResult(func() (bool, error) {
    27  		nodes, _, err := client.Nodes().List(nil)
    28  		if err != nil {
    29  			return false, err
    30  		}
    31  		if len(nodes) == 0 {
    32  			return false, fmt.Errorf("missing node")
    33  		}
    34  		if _, ok := nodes[0].Drivers["mock_driver"]; !ok {
    35  			return false, fmt.Errorf("mock_driver not ready")
    36  		}
    37  		return true, nil
    38  	}, func(err error) {
    39  		t.Fatalf("err: %s", err)
    40  	})
    41  
    42  	ui := cli.NewMockUi()
    43  	cmd := &JobScalingEventsCommand{Meta: Meta{Ui: ui}}
    44  
    45  	// Register a test job and ensure it is running before moving on.
    46  	resp, _, err := client.Jobs().Register(testJob("scale_events_test_job"), nil)
    47  	if err != nil {
    48  		t.Fatalf("err: %s", err)
    49  	}
    50  	if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
    51  		t.Fatalf("expected waitForSuccess exit code 0, got: %d", code)
    52  	}
    53  
    54  	// List events without passing the jobID which should result in an error.
    55  	if code := cmd.Run([]string{"-address=" + url}); code != 1 {
    56  		t.Fatalf("expected cmd run exit code 1, got: %d", code)
    57  	}
    58  	if out := ui.ErrorWriter.String(); !strings.Contains(out, "This command takes one argument: <job_id>") {
    59  		t.Fatalf("Expected argument error: %v", out)
    60  	}
    61  
    62  	// List events for the job, which should present zero.
    63  	if code := cmd.Run([]string{"-address=" + url, "scale_events_test_job"}); code != 0 {
    64  		t.Fatalf("expected cmd run exit code 0, got: %d", code)
    65  	}
    66  	if out := ui.OutputWriter.String(); !strings.Contains(out, "No events found") {
    67  		t.Fatalf("Expected no events output but got: %v", out)
    68  	}
    69  
    70  	// Perform a scaling action to generate an event.
    71  	_, _, err = client.Jobs().Scale(
    72  		"scale_events_test_job",
    73  		"group1", pointer.Of(2),
    74  		"searchable custom test message", false, nil, nil)
    75  	if err != nil {
    76  		t.Fatalf("err: %s", err)
    77  	}
    78  
    79  	// List the scaling events which should include an entry.
    80  	if code := cmd.Run([]string{"-address=" + url, "scale_events_test_job"}); code != 0 {
    81  		t.Fatalf("expected cmd run exit code 0, got: %d", code)
    82  	}
    83  	if out := ui.OutputWriter.String(); !strings.Contains(out, "Task Group  Count  PrevCount  Date") {
    84  		t.Fatalf("Expected table headers but got: %v", out)
    85  	}
    86  
    87  	// List the scaling events with verbose flag to search for our message as
    88  	// well as the verbose table headers.
    89  	if code := cmd.Run([]string{"-address=" + url, "-verbose", "scale_events_test_job"}); code != 0 {
    90  		t.Fatalf("expected cmd run exit code 0, got: %d", code)
    91  	}
    92  	out := ui.OutputWriter.String()
    93  	if !strings.Contains(out, "searchable custom test message") {
    94  		t.Fatalf("Expected to find scaling message but got: %v", out)
    95  	}
    96  	if !strings.Contains(out, "Eval ID") {
    97  		t.Fatalf("Expected to verbose table headers: %v", out)
    98  	}
    99  }
   100  
   101  func TestJobScalingEventsCommand_ACL(t *testing.T) {
   102  	ci.Parallel(t)
   103  
   104  	// Start server with ACL enabled.
   105  	srv, _, url := testServer(t, true, func(c *agent.Config) {
   106  		c.ACL.Enabled = true
   107  	})
   108  	defer srv.Shutdown()
   109  
   110  	// Create a job.
   111  	job := mock.MinJob()
   112  	state := srv.Agent.Server().State()
   113  	err := state.UpsertJob(structs.MsgTypeTestSetup, 100, nil, job)
   114  	must.NoError(t, err)
   115  
   116  	testCases := []struct {
   117  		name        string
   118  		jobPrefix   bool
   119  		aclPolicy   string
   120  		expectedErr string
   121  	}{
   122  		{
   123  			name:        "no token",
   124  			aclPolicy:   "",
   125  			expectedErr: api.PermissionDeniedErrorContent,
   126  		},
   127  		{
   128  			name: "missing read-job or read-job-scaling",
   129  			aclPolicy: `
   130  namespace "default" {
   131  	capabilities = ["submit-job"]
   132  }
   133  `,
   134  			expectedErr: api.PermissionDeniedErrorContent,
   135  		},
   136  		{
   137  			name: "read-job-scaling allowed",
   138  			aclPolicy: `
   139  namespace "default" {
   140  	capabilities = ["read-job-scaling"]
   141  }
   142  `,
   143  		},
   144  		{
   145  			name: "read-job allowed",
   146  			aclPolicy: `
   147  namespace "default" {
   148  	capabilities = ["read-job"]
   149  }
   150  `,
   151  		},
   152  		{
   153  			name:      "job prefix requires list-job",
   154  			jobPrefix: true,
   155  			aclPolicy: `
   156  namespace "default" {
   157  	capabilities = ["read-job-scaling"]
   158  }
   159  `,
   160  			expectedErr: "job not found",
   161  		},
   162  		{
   163  			name:      "job prefix works with list-job",
   164  			jobPrefix: true,
   165  			aclPolicy: `
   166  namespace "default" {
   167  	capabilities = ["read-job-scaling","list-jobs"]
   168  }
   169  `,
   170  		},
   171  	}
   172  
   173  	for i, tc := range testCases {
   174  		t.Run(tc.name, func(t *testing.T) {
   175  			ui := cli.NewMockUi()
   176  			cmd := &JobScalingEventsCommand{Meta: Meta{Ui: ui}}
   177  			args := []string{
   178  				"-address", url,
   179  			}
   180  
   181  			if tc.aclPolicy != "" {
   182  				// Create ACL token with test case policy and add it to the
   183  				// command.
   184  				policyName := nonAlphaNum.ReplaceAllString(tc.name, "-")
   185  				token := mock.CreatePolicyAndToken(t, state, uint64(302+i), policyName, tc.aclPolicy)
   186  				args = append(args, "-token", token.SecretID)
   187  			}
   188  
   189  			// Add job ID or job ID prefix to the command.
   190  			if tc.jobPrefix {
   191  				args = append(args, job.ID[:3])
   192  			} else {
   193  				args = append(args, job.ID)
   194  			}
   195  
   196  			// Run command.
   197  			code := cmd.Run(args)
   198  			if tc.expectedErr == "" {
   199  				must.Zero(t, code)
   200  			} else {
   201  				must.One(t, code)
   202  				must.StrContains(t, ui.ErrorWriter.String(), tc.expectedErr)
   203  			}
   204  		})
   205  	}
   206  }