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

     1  package scalingpolicies
     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/uuid"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  type ScalingPolicyE2ETest struct {
    14  	framework.TC
    15  	namespaceIDs     []string
    16  	namespacedJobIDs [][2]string
    17  }
    18  
    19  func init() {
    20  	framework.AddSuites(&framework.TestSuite{
    21  		Component:   "ScalingPolicies",
    22  		CanRunLocal: true,
    23  		Cases: []framework.TestCase{
    24  			new(ScalingPolicyE2ETest),
    25  		},
    26  	})
    27  
    28  }
    29  
    30  func (tc *ScalingPolicyE2ETest) BeforeAll(f *framework.F) {
    31  	e2eutil.WaitForLeader(f.T(), tc.Nomad())
    32  	e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
    33  }
    34  
    35  func (tc *ScalingPolicyE2ETest) 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.Assert().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.Assert().NoError(err)
    50  	}
    51  	tc.namespaceIDs = []string{}
    52  
    53  	_, err := e2eutil.Command("nomad", "system", "gc")
    54  	f.Assert().NoError(err)
    55  }
    56  
    57  // TestScalingPolicies multi-namespace scaling policy test which performs reads
    58  // and job manipulations to ensure Nomad behaves as expected.
    59  func (tc *ScalingPolicyE2ETest) TestScalingPolicies(f *framework.F) {
    60  	t := f.T()
    61  
    62  	// Create our non-default namespace.
    63  	_, err := e2eutil.Command("nomad", "namespace", "apply", "NamespaceA")
    64  	f.NoError(err, "could not create namespace")
    65  	tc.namespaceIDs = append(tc.namespaceIDs, "NamespaceA")
    66  
    67  	// Register the jobs, capturing their IDs.
    68  	jobDefault1 := tc.run(f, "scalingpolicies/input/namespace_default_1.nomad", "default", []string{"running"})
    69  	jobDefault2 := tc.run(f, "scalingpolicies/input/namespace_default_1.nomad", "default", []string{"running"})
    70  	jobA := tc.run(f, "scalingpolicies/input/namespace_a_1.nomad", "NamespaceA", []string{"running"})
    71  
    72  	// Setup some reused query options.
    73  	defaultQueryOpts := api.QueryOptions{Namespace: "default"}
    74  	aQueryOpts := api.QueryOptions{Namespace: "NamespaceA"}
    75  
    76  	// Perform initial listings to check each namespace has the correct number
    77  	// of policies.
    78  	defaultPolicyList, _, err := tc.Nomad().Scaling().ListPolicies(&defaultQueryOpts)
    79  	require.NoError(t, err)
    80  	require.Len(t, defaultPolicyList, 2)
    81  
    82  	policyListA, _, err := tc.Nomad().Scaling().ListPolicies(&aQueryOpts)
    83  	require.NoError(t, err)
    84  	require.Len(t, policyListA, 1)
    85  
    86  	// Deregister a job from the default namespace and then check all the
    87  	// response objects.
    88  	_, _, err = tc.Nomad().Jobs().Deregister(jobDefault1, true, &api.WriteOptions{Namespace: "default"})
    89  	require.NoError(t, err)
    90  
    91  	for i, namespacedJob := range tc.namespacedJobIDs {
    92  		if namespacedJob[1] == jobDefault1 && namespacedJob[0] == "default" {
    93  			tc.namespacedJobIDs = append(tc.namespacedJobIDs[:i], tc.namespacedJobIDs[i+1:]...)
    94  			break
    95  		}
    96  	}
    97  
    98  	defaultPolicyList, _, err = tc.Nomad().Scaling().ListPolicies(&defaultQueryOpts)
    99  	require.NoError(t, err)
   100  	require.Len(t, defaultPolicyList, 1)
   101  
   102  	defaultPolicy := defaultPolicyList[0]
   103  	require.True(t, defaultPolicy.Enabled)
   104  	require.Equal(t, "horizontal", defaultPolicy.Type)
   105  	require.Equal(t, defaultPolicy.Target["Namespace"], "default")
   106  	require.Equal(t, defaultPolicy.Target["Job"], jobDefault2)
   107  	require.Equal(t, defaultPolicy.Target["Group"], "horizontally_scalable")
   108  
   109  	defaultPolicyInfo, _, err := tc.Nomad().Scaling().GetPolicy(defaultPolicy.ID, &defaultQueryOpts)
   110  	require.NoError(t, err)
   111  	require.Equal(t, *defaultPolicyInfo.Min, int64(1))
   112  	require.Equal(t, *defaultPolicyInfo.Max, int64(10))
   113  	require.Equal(t, defaultPolicyInfo.Policy["cooldown"], "13m")
   114  	require.Equal(t, defaultPolicyInfo.Target["Namespace"], "default")
   115  	require.Equal(t, defaultPolicyInfo.Target["Job"], jobDefault2)
   116  	require.Equal(t, defaultPolicyInfo.Target["Group"], "horizontally_scalable")
   117  
   118  	// Check response objects from the namespace with name "NamespaceA".
   119  	aPolicyList, _, err := tc.Nomad().Scaling().ListPolicies(&aQueryOpts)
   120  	require.NoError(t, err)
   121  	require.Len(t, aPolicyList, 1)
   122  
   123  	aPolicy := aPolicyList[0]
   124  	require.True(t, aPolicy.Enabled)
   125  	require.Equal(t, "horizontal", aPolicy.Type)
   126  	require.Equal(t, aPolicy.Target["Namespace"], "NamespaceA")
   127  	require.Equal(t, aPolicy.Target["Job"], jobA)
   128  	require.Equal(t, aPolicy.Target["Group"], "horizontally_scalable")
   129  
   130  	aPolicyInfo, _, err := tc.Nomad().Scaling().GetPolicy(aPolicy.ID, &aQueryOpts)
   131  	require.NoError(t, err)
   132  	require.Equal(t, *aPolicyInfo.Min, int64(1))
   133  	require.Equal(t, *aPolicyInfo.Max, int64(10))
   134  	require.Equal(t, aPolicyInfo.Policy["cooldown"], "13m")
   135  	require.Equal(t, aPolicyInfo.Target["Namespace"], "NamespaceA")
   136  	require.Equal(t, aPolicyInfo.Target["Job"], jobA)
   137  	require.Equal(t, aPolicyInfo.Target["Group"], "horizontally_scalable")
   138  
   139  	// List policies using the splat namespace operator.
   140  	splatPolicyList, _, err := tc.Nomad().Scaling().ListPolicies(&api.QueryOptions{Namespace: "*"})
   141  	require.NoError(t, err)
   142  	require.Len(t, splatPolicyList, 2)
   143  
   144  	// Deregister the job from the "NamespaceA" namespace and then check the
   145  	// response objects.
   146  	_, _, err = tc.Nomad().Jobs().Deregister(jobA, true, &api.WriteOptions{Namespace: "NamespaceA"})
   147  	require.NoError(t, err)
   148  
   149  	for i, namespacedJob := range tc.namespacedJobIDs {
   150  		if namespacedJob[1] == jobA && namespacedJob[0] == "NamespaceA" {
   151  			tc.namespacedJobIDs = append(tc.namespacedJobIDs[:i], tc.namespacedJobIDs[i+1:]...)
   152  			break
   153  		}
   154  	}
   155  
   156  	aPolicyList, _, err = tc.Nomad().Scaling().ListPolicies(&aQueryOpts)
   157  	require.NoError(t, err)
   158  	require.Len(t, aPolicyList, 0)
   159  
   160  	// Update the running job scaling policy and ensure the changes are
   161  	// reflected.
   162  	err = e2eutil.Register(jobDefault2, "scalingpolicies/input/namespace_default_2.nomad")
   163  	require.NoError(t, err)
   164  
   165  	defaultPolicyList, _, err = tc.Nomad().Scaling().ListPolicies(&defaultQueryOpts)
   166  	require.NoError(t, err)
   167  	require.Len(t, defaultPolicyList, 1)
   168  
   169  	defaultPolicyInfo, _, err = tc.Nomad().Scaling().GetPolicy(defaultPolicyList[0].ID, &defaultQueryOpts)
   170  	require.NoError(t, err)
   171  	require.Equal(t, *defaultPolicyInfo.Min, int64(1))
   172  	require.Equal(t, *defaultPolicyInfo.Max, int64(11))
   173  	require.Equal(t, defaultPolicyInfo.Policy["cooldown"], "14m")
   174  	require.Equal(t, defaultPolicyInfo.Target["Namespace"], "default")
   175  	require.Equal(t, defaultPolicyInfo.Target["Job"], jobDefault2)
   176  	require.Equal(t, defaultPolicyInfo.Target["Group"], "horizontally_scalable")
   177  
   178  }
   179  
   180  // run is a helper which runs a job within a namespace, providing the caller
   181  // with the generated jobID.
   182  func (tc *ScalingPolicyE2ETest) run(f *framework.F, jobSpec, ns string, expected []string) string {
   183  	jobID := "test-scaling-policy-" + uuid.Generate()[0:8]
   184  	f.NoError(e2eutil.Register(jobID, jobSpec))
   185  	tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{ns, jobID})
   186  	f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, ns, expected), "job should be running")
   187  	return jobID
   188  }