e8dc902781
Integration tests will now configure clients to propagate traces as well as create spans for all tests. Some extra changes were needed (or desired for trace propagation) in the test helpers to pass through tracing spans via context. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
384 lines
13 KiB
Go
384 lines
13 KiB
Go
package service // import "github.com/docker/docker/integration/service"
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/api/types/versions"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/integration/internal/network"
|
|
"github.com/docker/docker/integration/internal/swarm"
|
|
"github.com/docker/docker/testutil"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
"gotest.tools/v3/poll"
|
|
"gotest.tools/v3/skip"
|
|
)
|
|
|
|
func TestServiceUpdateLabel(t *testing.T) {
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
cli := d.NewClientT(t)
|
|
defer cli.Close()
|
|
|
|
serviceName := "TestService_" + t.Name()
|
|
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName))
|
|
service := getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
|
|
|
|
// add label to empty set
|
|
service.Spec.Labels["foo"] = "bar"
|
|
_, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
|
|
|
|
// add label to non-empty set
|
|
service.Spec.Labels["foo2"] = "bar"
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar", "foo2": "bar"}))
|
|
|
|
delete(service.Spec.Labels, "foo2")
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
|
|
|
|
delete(service.Spec.Labels, "foo")
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
|
|
|
|
// now make sure we can add again
|
|
service.Spec.Labels["foo"] = "bar"
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceSpecIsUpdated(ctx, cli, serviceID, service.Version.Index), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
|
|
|
|
err = cli.ServiceRemove(ctx, serviceID)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func TestServiceUpdateSecrets(t *testing.T) {
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
cli := d.NewClientT(t)
|
|
defer cli.Close()
|
|
|
|
secretName := "TestSecret_" + t.Name()
|
|
secretTarget := "targetName"
|
|
resp, err := cli.SecretCreate(ctx, swarmtypes.SecretSpec{
|
|
Annotations: swarmtypes.Annotations{
|
|
Name: secretName,
|
|
},
|
|
Data: []byte("TESTINGDATA"),
|
|
})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, resp.ID != "")
|
|
|
|
serviceName := "TestService_" + t.Name()
|
|
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName))
|
|
service := getService(ctx, t, cli, serviceID)
|
|
|
|
// add secret
|
|
service.Spec.TaskTemplate.ContainerSpec.Secrets = append(service.Spec.TaskTemplate.ContainerSpec.Secrets,
|
|
&swarmtypes.SecretReference{
|
|
File: &swarmtypes.SecretReferenceFileTarget{
|
|
Name: secretTarget,
|
|
UID: "0",
|
|
GID: "0",
|
|
Mode: 0o600,
|
|
},
|
|
SecretID: resp.ID,
|
|
SecretName: secretName,
|
|
},
|
|
)
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
|
|
service = getService(ctx, t, cli, serviceID)
|
|
secrets := service.Spec.TaskTemplate.ContainerSpec.Secrets
|
|
assert.Assert(t, is.Equal(1, len(secrets)))
|
|
|
|
secret := *secrets[0]
|
|
assert.Check(t, is.Equal(secretName, secret.SecretName))
|
|
assert.Check(t, nil != secret.File)
|
|
assert.Check(t, is.Equal(secretTarget, secret.File.Name))
|
|
|
|
// remove
|
|
service.Spec.TaskTemplate.ContainerSpec.Secrets = []*swarmtypes.SecretReference{}
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets)))
|
|
|
|
err = cli.ServiceRemove(ctx, serviceID)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func TestServiceUpdateConfigs(t *testing.T) {
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
cli := d.NewClientT(t)
|
|
defer cli.Close()
|
|
|
|
configName := "TestConfig_" + t.Name()
|
|
configTarget := "targetName"
|
|
resp, err := cli.ConfigCreate(ctx, swarmtypes.ConfigSpec{
|
|
Annotations: swarmtypes.Annotations{
|
|
Name: configName,
|
|
},
|
|
Data: []byte("TESTINGDATA"),
|
|
})
|
|
assert.NilError(t, err)
|
|
assert.Check(t, resp.ID != "")
|
|
|
|
serviceName := "TestService_" + t.Name()
|
|
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithName(serviceName))
|
|
service := getService(ctx, t, cli, serviceID)
|
|
|
|
// add config
|
|
service.Spec.TaskTemplate.ContainerSpec.Configs = append(service.Spec.TaskTemplate.ContainerSpec.Configs,
|
|
&swarmtypes.ConfigReference{
|
|
File: &swarmtypes.ConfigReferenceFileTarget{
|
|
Name: configTarget,
|
|
UID: "0",
|
|
GID: "0",
|
|
Mode: 0o600,
|
|
},
|
|
ConfigID: resp.ID,
|
|
ConfigName: configName,
|
|
},
|
|
)
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
|
|
service = getService(ctx, t, cli, serviceID)
|
|
configs := service.Spec.TaskTemplate.ContainerSpec.Configs
|
|
assert.Assert(t, is.Equal(1, len(configs)))
|
|
|
|
config := *configs[0]
|
|
assert.Check(t, is.Equal(configName, config.ConfigName))
|
|
assert.Check(t, nil != config.File)
|
|
assert.Check(t, is.Equal(configTarget, config.File.Name))
|
|
|
|
// remove
|
|
service.Spec.TaskTemplate.ContainerSpec.Configs = []*swarmtypes.ConfigReference{}
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs)))
|
|
|
|
err = cli.ServiceRemove(ctx, serviceID)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func TestServiceUpdateNetwork(t *testing.T) {
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
cli := d.NewClientT(t)
|
|
defer cli.Close()
|
|
|
|
// Create a overlay network
|
|
testNet := "testNet" + t.Name()
|
|
overlayID := network.CreateNoError(ctx, t, cli, testNet,
|
|
network.WithDriver("overlay"))
|
|
|
|
var instances uint64 = 1
|
|
// Create service with the overlay network
|
|
serviceName := "TestServiceUpdateNetworkRM_" + t.Name()
|
|
serviceID := swarm.CreateService(ctx, t, d,
|
|
swarm.ServiceWithReplicas(instances),
|
|
swarm.ServiceWithName(serviceName),
|
|
swarm.ServiceWithNetwork(testNet))
|
|
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, instances), swarm.ServicePoll)
|
|
service := getService(ctx, t, cli, serviceID)
|
|
netInfo, err := cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
|
|
Verbose: true,
|
|
Scope: "swarm",
|
|
})
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, len(netInfo.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox")
|
|
|
|
// Remove network from service
|
|
service.Spec.TaskTemplate.Networks = []swarmtypes.NetworkAttachmentConfig{}
|
|
_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
|
|
netInfo, err = cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
|
|
Verbose: true,
|
|
Scope: "swarm",
|
|
})
|
|
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, len(netInfo.Containers) == 0, "Load balancing endpoint still exists in network")
|
|
|
|
err = cli.NetworkRemove(ctx, overlayID)
|
|
assert.NilError(t, err)
|
|
|
|
err = cli.ServiceRemove(ctx, serviceID)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
// TestServiceUpdatePidsLimit tests creating and updating a service with PidsLimit
|
|
func TestServiceUpdatePidsLimit(t *testing.T) {
|
|
skip.If(
|
|
t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
|
|
"setting pidslimit for services is not supported before api v1.41",
|
|
)
|
|
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
|
tests := []struct {
|
|
name string
|
|
pidsLimit int64
|
|
expected int64
|
|
}{
|
|
{
|
|
name: "create service with PidsLimit 300",
|
|
pidsLimit: 300,
|
|
expected: 300,
|
|
},
|
|
{
|
|
name: "unset PidsLimit to 0",
|
|
pidsLimit: 0,
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "update PidsLimit to 100",
|
|
pidsLimit: 100,
|
|
expected: 100,
|
|
},
|
|
}
|
|
|
|
ctx := setupTest(t)
|
|
|
|
d := swarm.NewSwarm(ctx, t, testEnv)
|
|
defer d.Stop(t)
|
|
cli := d.NewClientT(t)
|
|
defer func() { _ = cli.Close() }()
|
|
var (
|
|
serviceID string
|
|
service swarmtypes.Service
|
|
)
|
|
for i, tc := range tests {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx := testutil.StartSpan(ctx, t)
|
|
if i == 0 {
|
|
serviceID = swarm.CreateService(ctx, t, d, swarm.ServiceWithPidsLimit(tc.pidsLimit))
|
|
} else {
|
|
service = getService(ctx, t, cli, serviceID)
|
|
if service.Spec.TaskTemplate.Resources == nil {
|
|
service.Spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
|
|
}
|
|
if service.Spec.TaskTemplate.Resources.Limits == nil {
|
|
service.Spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
|
|
}
|
|
service.Spec.TaskTemplate.Resources.Limits.Pids = tc.pidsLimit
|
|
_, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
assert.NilError(t, err)
|
|
poll.WaitOn(t, serviceIsUpdated(ctx, cli, serviceID), swarm.ServicePoll)
|
|
}
|
|
|
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, 1), swarm.ServicePoll)
|
|
service = getService(ctx, t, cli, serviceID)
|
|
container := getServiceTaskContainer(ctx, t, cli, serviceID)
|
|
assert.Equal(t, service.Spec.TaskTemplate.Resources.Limits.Pids, tc.expected)
|
|
if tc.expected == 0 {
|
|
if container.HostConfig.Resources.PidsLimit != nil {
|
|
t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil")
|
|
}
|
|
} else {
|
|
assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil)
|
|
assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
|
|
err := cli.ServiceRemove(ctx, serviceID)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON {
|
|
t.Helper()
|
|
tasks, err := cli.TaskList(ctx, types.TaskListOptions{
|
|
Filters: filters.NewArgs(
|
|
filters.Arg("service", serviceID),
|
|
filters.Arg("desired-state", "running"),
|
|
),
|
|
})
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, len(tasks) > 0)
|
|
|
|
ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, ctr.State.Running, true)
|
|
return ctr
|
|
}
|
|
|
|
func getService(ctx context.Context, t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service {
|
|
t.Helper()
|
|
service, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
|
assert.NilError(t, err)
|
|
return service
|
|
}
|
|
|
|
func serviceIsUpdated(ctx context.Context, client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
|
return func(log poll.LogT) poll.Result {
|
|
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
|
switch {
|
|
case err != nil:
|
|
return poll.Error(err)
|
|
case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
|
|
return poll.Success()
|
|
default:
|
|
if service.UpdateStatus != nil {
|
|
return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message)
|
|
}
|
|
return poll.Continue("waiting for service %s to be updated", serviceID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func serviceSpecIsUpdated(ctx context.Context, client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result {
|
|
return func(log poll.LogT) poll.Result {
|
|
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
|
switch {
|
|
case err != nil:
|
|
return poll.Error(err)
|
|
case service.Version.Index > serviceOldVersion:
|
|
return poll.Success()
|
|
default:
|
|
return poll.Continue("waiting for service %s to be updated", serviceID)
|
|
}
|
|
}
|
|
}
|