Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

e2e: add job scaling test suite. #9767

Merged
merged 1 commit into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ import (
_ "github.com/hashicorp/nomad/e2e/podman"
_ "github.com/hashicorp/nomad/e2e/quotas"
_ "github.com/hashicorp/nomad/e2e/rescheduling"
_ "github.com/hashicorp/nomad/e2e/scaling"
_ "github.com/hashicorp/nomad/e2e/scalingpolicies"
_ "github.com/hashicorp/nomad/e2e/spread"
_ "github.com/hashicorp/nomad/e2e/systemsched"
_ "github.com/hashicorp/nomad/e2e/scalingpolicies"
_ "github.com/hashicorp/nomad/e2e/taskevents"
_ "github.com/hashicorp/nomad/e2e/vaultsecrets"
_ "github.com/hashicorp/nomad/e2e/volumes"
Expand Down
34 changes: 34 additions & 0 deletions e2e/scaling/input/namespace_a_1.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
job "horizontally_scalable" {
datacenters = ["dc1"]
type = "service"
namespace = "NamespaceA"

update {
health_check = "task_states"
}

constraint {
attribute = "${attr.kernel.name}"
value = "linux"
}

group "horizontally_scalable" {

scaling {
min = 2
max = 3
enabled = true

policy {}
}

task "test" {
driver = "raw_exec"

config {
command = "bash"
args = ["-c", "sleep 15000"]
}
}
}
}
33 changes: 33 additions & 0 deletions e2e/scaling/input/namespace_default_1.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
job "horizontally_scalable" {
datacenters = ["dc1"]
type = "service"

update {
health_check = "task_states"
}

constraint {
attribute = "${attr.kernel.name}"
value = "linux"
}

group "horizontally_scalable" {

scaling {
min = 2
max = 3
enabled = true

policy {}
}

task "test" {
driver = "raw_exec"

config {
command = "bash"
args = ["-c", "sleep 15000"]
}
}
}
}
35 changes: 35 additions & 0 deletions e2e/scaling/input/namespace_default_2.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
job "horizontally_scalable" {
datacenters = ["dc1"]
type = "service"

update {
health_check = "task_states"
}

constraint {
attribute = "${attr.kernel.name}"
value = "linux"
}

group "horizontally_scalable" {

count = 4

scaling {
min = 2
max = 3
enabled = true

policy {}
}

task "test" {
driver = "raw_exec"

config {
command = "bash"
args = ["-c", "sleep 15000"]
}
}
}
}
35 changes: 35 additions & 0 deletions e2e/scaling/input/namespace_default_3.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
job "horizontally_scalable" {
datacenters = ["dc1"]
type = "service"

update {
health_check = "task_states"
}

constraint {
attribute = "${attr.kernel.name}"
value = "linux"
}

group "horizontally_scalable" {

count = 4

scaling {
min = 5
max = 6
enabled = true

policy {}
}

task "test" {
driver = "raw_exec"

config {
command = "bash"
args = ["-c", "sleep 15000"]
}
}
}
}
164 changes: 164 additions & 0 deletions e2e/scaling/scaling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package scaling

import (
"os"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/e2e/e2eutil"
"github.com/hashicorp/nomad/e2e/framework"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/uuid"
)

type ScalingE2ETest struct {
framework.TC
namespaceIDs []string
namespacedJobIDs [][2]string
}

func init() {
framework.AddSuites(&framework.TestSuite{
Component: "Scaling",
CanRunLocal: true,
Cases: []framework.TestCase{
new(ScalingE2ETest),
},
})

}

func (tc *ScalingE2ETest) BeforeAll(f *framework.F) {
e2eutil.WaitForLeader(f.T(), tc.Nomad())
e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
}

func (tc *ScalingE2ETest) AfterEach(f *framework.F) {
if os.Getenv("NOMAD_TEST_SKIPCLEANUP") == "1" {
return
}

for _, namespacedJob := range tc.namespacedJobIDs {
_, err := e2eutil.Command("nomad", "job", "stop", "-purge", "-namespace",
namespacedJob[0], namespacedJob[1])
f.NoError(err)
}
tc.namespacedJobIDs = [][2]string{}

for _, ns := range tc.namespaceIDs {
_, err := e2eutil.Command("nomad", "namespace", "delete", ns)
f.NoError(err)
}
tc.namespaceIDs = []string{}

_, err := e2eutil.Command("nomad", "system", "gc")
f.NoError(err)
}

// TestScalingBasic performs basic scaling e2e tests within a single namespace.
func (tc *ScalingE2ETest) TestScalingBasic(f *framework.F) {
defaultNS := "default"

// Register a job with a scaling policy. The group doesn't include the
// count parameter, therefore Nomad should dynamically set this value to
// the policy min.
jobID := "test-scaling-" + uuid.Generate()[0:8]
f.NoError(e2eutil.Register(jobID, "scaling/input/namespace_default_1.nomad"))
tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{defaultNS, jobID})
f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, defaultNS, []string{"running", "running"}),
"job should be running with 2 allocs")

// Ensure we wait for the deployment to finish, otherwise scaling will
// fail.
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, defaultNS, "successful", nil))

// Simple scaling action.
testMeta := map[string]interface{}{"scaling-e2e-test": "value"}
scaleResp, _, err := tc.Nomad().Jobs().Scale(
jobID, "horizontally_scalable", helper.IntToPtr(3),
"Nomad e2e testing", false, testMeta, nil)
f.NoError(err)
f.NotEmpty(scaleResp.EvalID)
f.NoError(e2eutil.WaitForAllocStatusExpected(jobID, defaultNS, []string{"running", "running", "running"}),
"job should be running with 3 allocs")

// Ensure we wait for the deployment to finish, otherwise scaling will
// fail for this reason.
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, defaultNS, "successful", nil))

// Attempt break break the policy min/max parameters.
_, _, err = tc.Nomad().Jobs().Scale(
jobID, "horizontally_scalable", helper.IntToPtr(4),
"Nomad e2e testing", false, nil, nil)
f.Error(err)
_, _, err = tc.Nomad().Jobs().Scale(
jobID, "horizontally_scalable", helper.IntToPtr(1),
"Nomad e2e testing", false, nil, nil)
f.Error(err)

// Check the scaling events.
statusResp, _, err := tc.Nomad().Jobs().ScaleStatus(jobID, nil)
f.NoError(err)
f.Len(statusResp.TaskGroups["horizontally_scalable"].Events, 1)
f.Equal(testMeta, statusResp.TaskGroups["horizontally_scalable"].Events[0].Meta)

// Remove the job.
_, _, err = tc.Nomad().Jobs().Deregister(jobID, true, nil)
f.NoError(err)
f.NoError(tc.Nomad().System().GarbageCollect())
tc.namespacedJobIDs = [][2]string{}

// Attempt job registrations where the group count violates the policy
// min/max parameters.
f.Error(e2eutil.Register(jobID, "scaling/input/namespace_default_2.nomad"))
f.Error(e2eutil.Register(jobID, "scaling/input/namespace_default_3.nomad"))
}

// TestScalingNamespaces runs tests to ensure the job scaling endpoint adheres
// to Nomad's basic namespace principles.
func (tc *ScalingE2ETest) TestScalingNamespaces(f *framework.F) {

defaultNS := "default"
ANS := "NamespaceA"

// Create our non-default namespace.
_, err := e2eutil.Command("nomad", "namespace", "apply", ANS)
f.NoError(err, "could not create namespace")
tc.namespaceIDs = append(tc.namespaceIDs, ANS)

defaultJobID := "test-scaling-default-" + uuid.Generate()[0:8]
aJobID := "test-scaling-a-" + uuid.Generate()[0:8]

// Register and wait for the job deployments to succeed.
f.NoError(e2eutil.Register(defaultJobID, "scaling/input/namespace_default_1.nomad"))
f.NoError(e2eutil.Register(aJobID, "scaling/input/namespace_a_1.nomad"))
f.NoError(e2eutil.WaitForLastDeploymentStatus(defaultJobID, defaultNS, "successful", nil))
f.NoError(e2eutil.WaitForLastDeploymentStatus(aJobID, ANS, "successful", nil))

tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{defaultNS, defaultJobID})
tc.namespacedJobIDs = append(tc.namespacedJobIDs, [2]string{ANS, aJobID})

// Setup the WriteOptions for each namespace.
defaultWriteOpts := api.WriteOptions{Namespace: defaultNS}
aWriteOpts := api.WriteOptions{Namespace: ANS}

// We shouldn't be able to trigger scaling across the namespace boundary.
_, _, err = tc.Nomad().Jobs().Scale(
defaultJobID, "horizontally_scalable", helper.IntToPtr(3),
"Nomad e2e testing", false, nil, &aWriteOpts)
f.Error(err)
_, _, err = tc.Nomad().Jobs().Scale(
aJobID, "horizontally_scalable", helper.IntToPtr(3),
"Nomad e2e testing", false, nil, &defaultWriteOpts)
f.Error(err)

// We should be able to trigger scaling when using the correct namespace,
// duh.
_, _, err = tc.Nomad().Jobs().Scale(
defaultJobID, "horizontally_scalable", helper.IntToPtr(3),
"Nomad e2e testing", false, nil, &defaultWriteOpts)
f.NoError(err)
_, _, err = tc.Nomad().Jobs().Scale(
aJobID, "horizontally_scalable", helper.IntToPtr(3),
"Nomad e2e testing", false, nil, &aWriteOpts)
f.NoError(err)
}