open-nomad/e2e/consul/consul.go

234 lines
6.8 KiB
Go

package consul
import (
"fmt"
"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"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/require"
)
const (
consulJobBasic = "consul/input/consul_example.nomad"
consulJobCanaryTags = "consul/input/canary_tags.nomad"
)
type ConsulE2ETest struct {
framework.TC
jobIds []string
}
func init() {
framework.AddSuites(&framework.TestSuite{
Component: "Consul",
CanRunLocal: true,
Consul: true,
Cases: []framework.TestCase{
new(ConsulE2ETest),
new(ScriptChecksE2ETest),
},
})
}
func (tc *ConsulE2ETest) BeforeAll(f *framework.F) {
e2eutil.WaitForLeader(f.T(), tc.Nomad())
e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
}
func (tc *ConsulE2ETest) AfterEach(f *framework.F) {
if os.Getenv("NOMAD_TEST_SKIPCLEANUP") == "1" {
return
}
for _, id := range tc.jobIds {
tc.Nomad().Jobs().Deregister(id, true, nil)
}
tc.jobIds = []string{}
tc.Nomad().System().GarbageCollect()
}
// TestConsulRegistration asserts that a job registers services with tags in Consul.
func (tc *ConsulE2ETest) TestConsulRegistration(f *framework.F) {
t := f.T()
nomadClient := tc.Nomad()
catalog := tc.Consul().Catalog()
jobId := "consul" + uuid.Generate()[0:8]
tc.jobIds = append(tc.jobIds, jobId)
allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), nomadClient, consulJobBasic, jobId, "")
require.Equal(t, 3, len(allocs))
allocIDs := e2eutil.AllocIDsFromAllocationListStubs(allocs)
e2eutil.WaitForAllocsRunning(t, tc.Nomad(), allocIDs)
expectedTags := []string{
"cache",
"global",
}
// Assert services get registered
testutil.WaitForResult(func() (bool, error) {
services, _, err := catalog.Service("consul-example", "", nil)
if err != nil {
return false, fmt.Errorf("error contacting Consul: %v", err)
}
if expected := 3; len(services) != expected {
return false, fmt.Errorf("expected %d services but found %d", expected, len(services))
}
for _, s := range services {
// If we've made it this far the tags should *always* match
require.True(t, helper.CompareSliceSetString(expectedTags, s.ServiceTags))
}
return true, nil
}, func(err error) {
t.Fatalf("error waiting for services to be registered: %v", err)
})
// Stop the job
e2eutil.WaitForJobStopped(t, nomadClient, jobId)
// Verify that services were deregistered in Consul
testutil.WaitForResult(func() (bool, error) {
s, _, err := catalog.Service("consul-example", "", nil)
if err != nil {
return false, err
}
return len(s) == 0, fmt.Errorf("expected 0 services but found: %v", s)
}, func(err error) {
t.Fatalf("error waiting for services to be deregistered: %v", err)
})
}
// TestCanaryInplaceUpgrades verifies setting and unsetting canary tags
func (tc *ConsulE2ETest) TestCanaryInplaceUpgrades(f *framework.F) {
t := f.T()
nomadClient := tc.Nomad()
consulClient := tc.Consul()
jobId := "consul" + uuid.Generate()[0:8]
tc.jobIds = append(tc.jobIds, jobId)
allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), nomadClient, consulJobCanaryTags, jobId, "")
require.Equal(t, 2, len(allocs))
allocIDs := e2eutil.AllocIDsFromAllocationListStubs(allocs)
e2eutil.WaitForAllocsRunning(t, nomadClient, allocIDs)
// Start a deployment
job, _, err := nomadClient.Jobs().Info(jobId, nil)
require.NoError(t, err)
job.Meta = map[string]string{"version": "2"}
resp, _, err := nomadClient.Jobs().Register(job, nil)
require.NoError(t, err)
require.NotEmpty(t, resp.EvalID)
// Eventually have a canary
var activeDeploy *api.Deployment
testutil.WaitForResult(func() (bool, error) {
deploys, _, err := nomadClient.Jobs().Deployments(jobId, false, nil)
if err != nil {
return false, err
}
if expected := 2; len(deploys) != expected {
return false, fmt.Errorf("expected 2 deploys but found %v", deploys)
}
for _, d := range deploys {
if d.Status == structs.DeploymentStatusRunning {
activeDeploy = d
break
}
}
if activeDeploy == nil {
return false, fmt.Errorf("no running deployments: %v", deploys)
}
if expected := 1; len(activeDeploy.TaskGroups["consul_canary_test"].PlacedCanaries) != expected {
return false, fmt.Errorf("expected %d placed canaries but found %#v",
expected, activeDeploy.TaskGroups["consul_canary_test"])
}
return true, nil
}, func(err error) {
t.Fatalf("error while waiting for deploys: %v", err)
})
allocID := activeDeploy.TaskGroups["consul_canary_test"].PlacedCanaries[0]
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := nomadClient.Allocations().Info(allocID, nil)
if err != nil {
return false, err
}
if alloc.DeploymentStatus == nil {
return false, fmt.Errorf("canary alloc %s has no deployment status", allocID)
}
if alloc.DeploymentStatus.Healthy == nil {
return false, fmt.Errorf("canary alloc %s has no deployment health: %#v",
allocID, alloc.DeploymentStatus)
}
return *alloc.DeploymentStatus.Healthy, fmt.Errorf("expected healthy canary but found: %#v",
alloc.DeploymentStatus)
}, func(err error) {
t.Fatalf("error waiting for canary to be healthy: %v", err)
})
// Check Consul for canary tags
testutil.WaitForResult(func() (bool, error) {
consulServices, _, err := consulClient.Catalog().Service("canarytest", "", nil)
if err != nil {
return false, err
}
for _, s := range consulServices {
if helper.CompareSliceSetString([]string{"canary", "foo"}, s.ServiceTags) {
return true, nil
}
}
return false, fmt.Errorf(`could not find service tags {"canary", "foo"}: %#v`, consulServices)
}, func(err error) {
t.Fatalf("error waiting for canary tags: %v", err)
})
// Promote canary
{
resp, _, err := nomadClient.Deployments().PromoteAll(activeDeploy.ID, nil)
require.NoError(t, err)
require.NotEmpty(t, resp.EvalID)
}
// Eventually canary is promoted
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := nomadClient.Allocations().Info(allocID, nil)
if err != nil {
return false, err
}
return !alloc.DeploymentStatus.Canary, fmt.Errorf("still a canary")
}, func(err error) {
t.Fatalf("error waiting for canary to be promoted: %v", err)
})
// Verify that no instances have canary tags
expected := []string{"foo", "bar"}
testutil.WaitForResult(func() (bool, error) {
consulServices, _, err := consulClient.Catalog().Service("canarytest", "", nil)
if err != nil {
return false, err
}
for _, s := range consulServices {
if !helper.CompareSliceSetString(expected, s.ServiceTags) {
return false, fmt.Errorf("expected %#v Consul tags but found %#v",
expected, s.ServiceTags)
}
}
return true, nil
}, func(err error) {
t.Fatalf("error waiting for non-canary tags: %v", err)
})
}