github.com/hernad/nomad@v1.6.112/e2e/scaling/scaling.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package scaling
     5  
     6  import (
     7  	"os"
     8  
     9  	"github.com/hernad/nomad/api"
    10  	"github.com/hernad/nomad/e2e/e2eutil"
    11  	"github.com/hernad/nomad/e2e/framework"
    12  	"github.com/hernad/nomad/helper/pointer"
    13  	"github.com/hernad/nomad/helper/uuid"
    14  )
    15  
    16  type ScalingE2ETest struct {
    17  	framework.TC
    18  	namespaceIDs     []string
    19  	namespacedJobIDs [][2]string
    20  }
    21  
    22  func init() {
    23  	framework.AddSuites(&framework.TestSuite{
    24  		Component:   "Scaling",
    25  		CanRunLocal: true,
    26  		Cases: []framework.TestCase{
    27  			new(ScalingE2ETest),
    28  		},
    29  	})
    30  
    31  }
    32  
    33  func (tc *ScalingE2ETest) BeforeAll(f *framework.F) {
    34  	e2eutil.WaitForLeader(f.T(), tc.Nomad())
    35  	e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
    36  }
    37  
    38  func (tc *ScalingE2ETest) AfterEach(f *framework.F) {
    39  	if os.Getenv("NOMAD_TEST_SKIPCLEANUP") == "1" {
    40  		return
    41  	}
    42  
    43  	for _, namespacedJob := range tc.namespacedJobIDs {
    44  		err := e2eutil.StopJob(namespacedJob[1], "-purge", "-namespace",
    45  			namespacedJob[0])
    46  		f.NoError(err)
    47  	}
    48  	tc.namespacedJobIDs = [][2]string{}
    49  
    50  	for _, ns := range tc.namespaceIDs {
    51  		_, err := e2eutil.Command("nomad", "namespace", "delete", ns)
    52  		f.NoError(err)
    53  	}
    54  	tc.namespaceIDs = []string{}
    55  
    56  	_, err := e2eutil.Command("nomad", "system", "gc")
    57  	f.NoError(err)
    58  }
    59  
    60  // TestScalingBasic performs basic scaling e2e tests within a single namespace.
    61  func (tc *ScalingE2ETest) TestScalingBasic(f *framework.F) {
    62  	defaultNS := "default"
    63  
    64  	// Register a job with a scaling policy. The group doesn't include the
    65  	// count parameter, therefore Nomad should dynamically set this value to
    66  	// the policy min.
    67  	jobID := "test-scaling-" + uuid.Generate()[0:8]
    68  	f.NoError(e2eutil.Register(jobID, "scaling/input/namespace_default_1.nomad"))
    69  	tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{defaultNS, jobID})
    70  	f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, defaultNS, []string{"running", "running"}),
    71  		"job should be running with 2 allocs")
    72  
    73  	// Ensure we wait for the deployment to finish, otherwise scaling will
    74  	// fail.
    75  	f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, defaultNS, "successful", nil))
    76  
    77  	// Simple scaling action.
    78  	testMeta := map[string]interface{}{"scaling-e2e-test": "value"}
    79  	scaleResp, _, err := tc.Nomad().Jobs().Scale(
    80  		jobID, "horizontally_scalable", pointer.Of(3),
    81  		"Nomad e2e testing", false, testMeta, nil)
    82  	f.NoError(err)
    83  	f.NotEmpty(scaleResp.EvalID)
    84  	f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, defaultNS, []string{"running", "running", "running"}),
    85  		"job should be running with 3 allocs")
    86  
    87  	// Ensure we wait for the deployment to finish, otherwise scaling will
    88  	// fail for this reason.
    89  	f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, defaultNS, "successful", nil))
    90  
    91  	// Attempt break break the policy min/max parameters.
    92  	_, _, err = tc.Nomad().Jobs().Scale(
    93  		jobID, "horizontally_scalable", pointer.Of(4),
    94  		"Nomad e2e testing", false, nil, nil)
    95  	f.Error(err)
    96  	_, _, err = tc.Nomad().Jobs().Scale(
    97  		jobID, "horizontally_scalable", pointer.Of(1),
    98  		"Nomad e2e testing", false, nil, nil)
    99  	f.Error(err)
   100  
   101  	// Check the scaling events.
   102  	statusResp, _, err := tc.Nomad().Jobs().ScaleStatus(jobID, nil)
   103  	f.NoError(err)
   104  	f.Len(statusResp.TaskGroups["horizontally_scalable"].Events, 1)
   105  	f.Equal(testMeta, statusResp.TaskGroups["horizontally_scalable"].Events[0].Meta)
   106  
   107  	// Remove the job.
   108  	_, _, err = tc.Nomad().Jobs().Deregister(jobID, true, nil)
   109  	f.NoError(err)
   110  	f.NoError(tc.Nomad().System().GarbageCollect())
   111  	tc.namespacedJobIDs = [][2]string{}
   112  
   113  	// Attempt job registrations where the group count violates the policy
   114  	// min/max parameters.
   115  	f.Error(e2eutil.Register(jobID, "scaling/input/namespace_default_2.nomad"))
   116  	f.Error(e2eutil.Register(jobID, "scaling/input/namespace_default_3.nomad"))
   117  }
   118  
   119  // TestScalingNamespaces runs tests to ensure the job scaling endpoint adheres
   120  // to Nomad's basic namespace principles.
   121  func (tc *ScalingE2ETest) TestScalingNamespaces(f *framework.F) {
   122  
   123  	defaultNS := "default"
   124  	ANS := "NamespaceA"
   125  
   126  	// Create our non-default namespace.
   127  	_, err := e2eutil.Command("nomad", "namespace", "apply", ANS)
   128  	f.NoError(err, "could not create namespace")
   129  	tc.namespaceIDs = append(tc.namespaceIDs, ANS)
   130  
   131  	defaultJobID := "test-scaling-default-" + uuid.Generate()[0:8]
   132  	aJobID := "test-scaling-a-" + uuid.Generate()[0:8]
   133  
   134  	// Register and wait for the job deployments to succeed.
   135  	f.NoError(e2eutil.Register(defaultJobID, "scaling/input/namespace_default_1.nomad"))
   136  	f.NoError(e2eutil.Register(aJobID, "scaling/input/namespace_a_1.nomad"))
   137  	f.NoError(e2eutil.WaitForLastDeploymentStatus(defaultJobID, defaultNS, "successful", nil))
   138  	f.NoError(e2eutil.WaitForLastDeploymentStatus(aJobID, ANS, "successful", nil))
   139  
   140  	tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{defaultNS, defaultJobID})
   141  	tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{ANS, aJobID})
   142  
   143  	// Setup the WriteOptions for each namespace.
   144  	defaultWriteOpts := api.WriteOptions{Namespace: defaultNS}
   145  	aWriteOpts := api.WriteOptions{Namespace: ANS}
   146  
   147  	// We shouldn't be able to trigger scaling across the namespace boundary.
   148  	_, _, err = tc.Nomad().Jobs().Scale(
   149  		defaultJobID, "horizontally_scalable", pointer.Of(3),
   150  		"Nomad e2e testing", false, nil, &aWriteOpts)
   151  	f.Error(err)
   152  	_, _, err = tc.Nomad().Jobs().Scale(
   153  		aJobID, "horizontally_scalable", pointer.Of(3),
   154  		"Nomad e2e testing", false, nil, &defaultWriteOpts)
   155  	f.Error(err)
   156  
   157  	// We should be able to trigger scaling when using the correct namespace,
   158  	// duh.
   159  	_, _, err = tc.Nomad().Jobs().Scale(
   160  		defaultJobID, "horizontally_scalable", pointer.Of(3),
   161  		"Nomad e2e testing", false, nil, &defaultWriteOpts)
   162  	f.NoError(err)
   163  	_, _, err = tc.Nomad().Jobs().Scale(
   164  		aJobID, "horizontally_scalable", pointer.Of(3),
   165  		"Nomad e2e testing", false, nil, &aWriteOpts)
   166  	f.NoError(err)
   167  }