github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/e2e/scaling/scaling.go (about)

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