open-nomad/nomad/job_endpoint_hook_connect_test.go
Nick Ethier d171189afc
nomad: recanonicalize network after connect hook (#8407)
* nomad: recanonicalize network after connect hook
2020-07-10 10:59:51 -04:00

198 lines
4.9 KiB
Go

package nomad
import (
"fmt"
"testing"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/stretchr/testify/require"
)
func TestJobEndpointConnect_isSidecarForService(t *testing.T) {
t.Parallel()
cases := []struct {
t *structs.Task // task
s string // service
r bool // result
}{
{
&structs.Task{},
"api",
false,
},
{
&structs.Task{
Kind: "connect-proxy:api",
},
"api",
true,
},
{
&structs.Task{
Kind: "connect-proxy:api",
},
"db",
false,
},
{
&structs.Task{
Kind: "api",
},
"api",
false,
},
}
for _, c := range cases {
require.Equal(t, c.r, isSidecarForService(c.t, c.s))
}
}
func TestJobEndpointConnect_groupConnectHook(t *testing.T) {
t.Parallel()
// Test that connect-proxy task is inserted for backend service
job := mock.Job()
job.TaskGroups[0] = &structs.TaskGroup{
Networks: structs.Networks{
{
Mode: "bridge",
},
},
Services: []*structs.Service{
{
Name: "backend",
PortLabel: "8080",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{},
},
},
{
Name: "admin",
PortLabel: "9090",
Connect: &structs.ConsulConnect{
SidecarService: &structs.ConsulSidecarService{},
},
},
},
}
// Expected tasks
tgOut := job.TaskGroups[0].Copy()
tgOut.Tasks = []*structs.Task{
newConnectTask(tgOut.Services[0].Name),
newConnectTask(tgOut.Services[1].Name),
}
// Expect sidecar tasks to be properly canonicalized
tgOut.Tasks[0].Canonicalize(job, tgOut)
tgOut.Tasks[1].Canonicalize(job, tgOut)
tgOut.Networks[0].DynamicPorts = []structs.Port{
{
Label: fmt.Sprintf("%s-%s", structs.ConnectProxyPrefix, "backend"),
To: -1,
},
{
Label: fmt.Sprintf("%s-%s", structs.ConnectProxyPrefix, "admin"),
To: -1,
},
}
tgOut.Networks[0].Canonicalize()
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, tgOut, job.TaskGroups[0])
// Test that hook is idempotent
require.NoError(t, groupConnectHook(job, job.TaskGroups[0]))
require.Exactly(t, tgOut, job.TaskGroups[0])
}
// TestJobEndpoint_ConnectInterpolation asserts that when a Connect sidecar
// proxy task is being created for a group service with an interpolated name,
// the service name is interpolated *before the task is created.
//
// See https://github.com/hashicorp/nomad/issues/6853
func TestJobEndpointConnect_ConnectInterpolation(t *testing.T) {
t.Parallel()
server := &Server{logger: testlog.HCLogger(t)}
jobEndpoint := NewJobEndpoints(server)
j := mock.ConnectJob()
j.TaskGroups[0].Services[0].Name = "${JOB}-api"
j, warnings, err := jobEndpoint.admissionMutators(j)
require.NoError(t, err)
require.Nil(t, warnings)
require.Len(t, j.TaskGroups[0].Tasks, 2)
require.Equal(t, "connect-proxy-my-job-api", j.TaskGroups[0].Tasks[1].Name)
}
func TestJobEndpointConnect_groupConnectSidecarValidate(t *testing.T) {
t.Run("sidecar 0 networks", func(t *testing.T) {
require.EqualError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g1",
Networks: nil,
}), `Consul Connect sidecars require exactly 1 network, found 0 in group "g1"`)
})
t.Run("sidecar non bridge", func(t *testing.T) {
require.EqualError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g2",
Networks: structs.Networks{{
Mode: "host",
}},
}), `Consul Connect sidecar requires bridge network, found "host" in group "g2"`)
})
t.Run("sidecar okay", func(t *testing.T) {
require.NoError(t, groupConnectSidecarValidate(&structs.TaskGroup{
Name: "g3",
Networks: structs.Networks{{
Mode: "bridge",
}},
}))
})
}
func TestJobEndpointConnect_getNamedTaskForNativeService(t *testing.T) {
t.Run("named exists", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "t2")
require.NoError(t, err)
require.Equal(t, "t2", task.Name)
})
t.Run("infer exists", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t2"}},
}, "s1", "")
require.NoError(t, err)
require.Equal(t, "t2", task.Name)
})
t.Run("infer ambiguous", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "")
require.EqualError(t, err, "task for Consul Connect Native service g1->s1 is ambiguous and must be set")
require.Nil(t, task)
})
t.Run("named absent", func(t *testing.T) {
task, err := getNamedTaskForNativeService(&structs.TaskGroup{
Name: "g1",
Tasks: []*structs.Task{{Name: "t1"}, {Name: "t2"}},
}, "s1", "t3")
require.EqualError(t, err, "task t3 named by Consul Connect Native service g1->s1 does not exist")
require.Nil(t, task)
})
}