2018-02-02 17:23:12 -05:00
|
|
|
package swarm
|
|
|
|
|
|
|
|
import (
|
2017-06-15 15:06:08 -04:00
|
|
|
"context"
|
2018-03-26 23:36:55 -04:00
|
|
|
"runtime"
|
2018-02-02 17:23:12 -05:00
|
|
|
"testing"
|
2018-03-26 23:36:55 -04:00
|
|
|
"time"
|
2018-02-02 17:23:12 -05:00
|
|
|
|
2017-06-15 15:06:08 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/api/types/filters"
|
2018-02-02 17:23:12 -05:00
|
|
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
2019-01-13 15:15:34 -05:00
|
|
|
"github.com/docker/docker/client"
|
2019-08-29 16:52:40 -04:00
|
|
|
"github.com/docker/docker/testutil/daemon"
|
|
|
|
"github.com/docker/docker/testutil/environment"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
"gotest.tools/v3/poll"
|
|
|
|
"gotest.tools/v3/skip"
|
2018-02-02 17:23:12 -05:00
|
|
|
)
|
|
|
|
|
2018-03-26 23:36:55 -04:00
|
|
|
// ServicePoll tweaks the pollSettings for `service`
|
|
|
|
func ServicePoll(config *poll.Settings) {
|
|
|
|
// Override the default pollSettings for `service` resource here ...
|
2019-12-11 11:05:03 -05:00
|
|
|
config.Timeout = 15 * time.Second
|
2018-04-18 10:18:53 -04:00
|
|
|
config.Delay = 100 * time.Millisecond
|
2018-03-26 23:36:55 -04:00
|
|
|
if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
|
2018-05-17 03:42:42 -04:00
|
|
|
config.Timeout = 90 * time.Second
|
2018-03-26 23:36:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkPoll tweaks the pollSettings for `network`
|
|
|
|
func NetworkPoll(config *poll.Settings) {
|
|
|
|
// Override the default pollSettings for `network` resource here ...
|
|
|
|
config.Timeout = 30 * time.Second
|
|
|
|
config.Delay = 100 * time.Millisecond
|
|
|
|
|
|
|
|
if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
|
|
|
|
config.Timeout = 50 * time.Second
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerPoll tweaks the pollSettings for `container`
|
|
|
|
func ContainerPoll(config *poll.Settings) {
|
|
|
|
// Override the default pollSettings for `container` resource here ...
|
|
|
|
|
|
|
|
if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
|
|
|
|
config.Timeout = 30 * time.Second
|
|
|
|
config.Delay = 100 * time.Millisecond
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-02 17:36:59 -05:00
|
|
|
// NewSwarm creates a swarm daemon for testing
|
2019-09-17 13:44:35 -04:00
|
|
|
func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...daemon.Option) *daemon.Daemon {
|
2018-04-18 11:16:55 -04:00
|
|
|
t.Helper()
|
2018-04-25 05:03:43 -04:00
|
|
|
skip.If(t, testEnv.IsRemoteDaemon)
|
2018-04-19 05:14:15 -04:00
|
|
|
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
2020-03-13 09:37:09 -04:00
|
|
|
skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
|
2018-04-11 06:10:17 -04:00
|
|
|
if testEnv.DaemonInfo.ExperimentalBuild {
|
2019-09-30 08:23:56 -04:00
|
|
|
ops = append(ops, daemon.WithExperimental())
|
2018-02-02 17:23:12 -05:00
|
|
|
}
|
2018-04-11 06:10:17 -04:00
|
|
|
d := daemon.New(t, ops...)
|
2018-04-13 11:02:56 -04:00
|
|
|
d.StartAndSwarmInit(t)
|
2018-02-02 17:23:12 -05:00
|
|
|
return d
|
|
|
|
}
|
2017-06-15 15:06:08 -04:00
|
|
|
|
|
|
|
// ServiceSpecOpt is used with `CreateService` to pass in service spec modifiers
|
|
|
|
type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
|
|
|
|
|
|
|
|
// CreateService creates a service on the passed in swarm daemon.
|
2018-04-11 06:10:17 -04:00
|
|
|
func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
|
2018-04-18 11:16:55 -04:00
|
|
|
t.Helper()
|
2017-06-15 15:06:08 -04:00
|
|
|
|
2018-04-13 11:02:56 -04:00
|
|
|
client := d.NewClientT(t)
|
|
|
|
defer client.Close()
|
2017-06-15 15:06:08 -04:00
|
|
|
|
2018-11-05 09:59:26 -05:00
|
|
|
spec := CreateServiceSpec(t, opts...)
|
2017-06-15 15:06:08 -04:00
|
|
|
resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{})
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, "error creating service")
|
2017-06-15 15:06:08 -04:00
|
|
|
return resp.ID
|
|
|
|
}
|
|
|
|
|
2018-11-05 09:59:26 -05:00
|
|
|
// CreateServiceSpec creates a default service-spec, and applies the provided options
|
|
|
|
func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec {
|
|
|
|
t.Helper()
|
2017-06-15 15:06:08 -04:00
|
|
|
var spec swarmtypes.ServiceSpec
|
|
|
|
ServiceWithImage("busybox:latest")(&spec)
|
|
|
|
ServiceWithCommand([]string{"/bin/top"})(&spec)
|
|
|
|
ServiceWithReplicas(1)(&spec)
|
2018-11-05 09:59:26 -05:00
|
|
|
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&spec)
|
|
|
|
}
|
2017-06-15 15:06:08 -04:00
|
|
|
return spec
|
|
|
|
}
|
|
|
|
|
2019-12-11 11:05:03 -05:00
|
|
|
// ServiceWithMode sets the mode of the service to the provided mode.
|
|
|
|
func ServiceWithMode(mode swarmtypes.ServiceMode) func(*swarmtypes.ServiceSpec) {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
spec.Mode = mode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 06:47:38 -04:00
|
|
|
// ServiceWithInit sets whether the service should use init or not
|
|
|
|
func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Init = b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:06:08 -04:00
|
|
|
// ServiceWithImage sets the image to use for the service
|
|
|
|
func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Image = image
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceWithCommand sets the command to use for the service
|
|
|
|
func ServiceWithCommand(cmd []string) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Command = cmd
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceWithConfig adds the config reference to the service
|
|
|
|
func ServiceWithConfig(configRef *swarmtypes.ConfigReference) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Configs = append(spec.TaskTemplate.ContainerSpec.Configs, configRef)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceWithSecret adds the secret reference to the service
|
|
|
|
func ServiceWithSecret(secretRef *swarmtypes.SecretReference) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, secretRef)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceWithReplicas sets the replicas for the service
|
|
|
|
func ServiceWithReplicas(n uint64) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
spec.Mode = swarmtypes.ServiceMode{
|
|
|
|
Replicated: &swarmtypes.ReplicatedService{
|
|
|
|
Replicas: &n,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-30 08:28:37 -04:00
|
|
|
// ServiceWithMaxReplicas sets the max replicas for the service
|
|
|
|
func ServiceWithMaxReplicas(n uint64) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensurePlacement(spec)
|
|
|
|
spec.TaskTemplate.Placement.MaxReplicas = n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:06:08 -04:00
|
|
|
// ServiceWithName sets the name of the service
|
|
|
|
func ServiceWithName(name string) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
spec.Annotations.Name = name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 17:26:29 -04:00
|
|
|
// ServiceWithNetwork sets the network of the service
|
|
|
|
func ServiceWithNetwork(network string) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
spec.TaskTemplate.Networks = append(spec.TaskTemplate.Networks,
|
|
|
|
swarmtypes.NetworkAttachmentConfig{Target: network})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceWithEndpoint sets the Endpoint of the service
|
|
|
|
func ServiceWithEndpoint(endpoint *swarmtypes.EndpointSpec) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
spec.EndpointSpec = endpoint
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-22 16:24:14 -04:00
|
|
|
// ServiceWithSysctls sets the Sysctls option of the service's ContainerSpec.
|
|
|
|
func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
|
|
|
spec.TaskTemplate.ContainerSpec.Sysctls = sysctls
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-05 11:28:24 -04:00
|
|
|
// ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec.
|
2020-07-23 14:03:15 -04:00
|
|
|
func ServiceWithCapabilities(add []string, drop []string) ServiceSpecOpt {
|
2019-05-05 11:28:24 -04:00
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
|
|
|
ensureContainerSpec(spec)
|
2020-07-23 14:03:15 -04:00
|
|
|
spec.TaskTemplate.ContainerSpec.CapabilityAdd = add
|
|
|
|
spec.TaskTemplate.ContainerSpec.CapabilityDrop = drop
|
2019-05-05 11:28:24 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 15:11:51 -04:00
|
|
|
// ServiceWithPidsLimit sets the PidsLimit option of the service's Resources.Limits.
|
2019-09-09 08:51:07 -04:00
|
|
|
func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
|
|
|
|
return func(spec *swarmtypes.ServiceSpec) {
|
2020-05-08 15:11:51 -04:00
|
|
|
ensureResources(spec)
|
|
|
|
spec.TaskTemplate.Resources.Limits.Pids = limit
|
2019-09-09 08:51:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:06:08 -04:00
|
|
|
// GetRunningTasks gets the list of running tasks for a service
|
2019-01-13 15:15:34 -05:00
|
|
|
func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
|
2018-04-18 11:16:55 -04:00
|
|
|
t.Helper()
|
2017-06-15 15:06:08 -04:00
|
|
|
|
2019-01-13 15:15:34 -05:00
|
|
|
tasks, err := c.TaskList(context.Background(), types.TaskListOptions{
|
|
|
|
Filters: filters.NewArgs(
|
|
|
|
filters.Arg("service", serviceID),
|
|
|
|
filters.Arg("desired-state", "running"),
|
|
|
|
),
|
|
|
|
})
|
2017-06-15 15:06:08 -04:00
|
|
|
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err)
|
2017-06-15 15:06:08 -04:00
|
|
|
return tasks
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExecTask runs the passed in exec config on the given task
|
2018-04-11 06:10:17 -04:00
|
|
|
func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
|
2018-04-18 11:16:55 -04:00
|
|
|
t.Helper()
|
2018-04-13 11:02:56 -04:00
|
|
|
client := d.NewClientT(t)
|
|
|
|
defer client.Close()
|
2017-06-15 15:06:08 -04:00
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, "error creating exec")
|
2017-06-15 15:06:08 -04:00
|
|
|
|
|
|
|
startCheck := types.ExecStartCheck{}
|
|
|
|
attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck)
|
2018-03-13 15:28:34 -04:00
|
|
|
assert.NilError(t, err, "error attaching to exec")
|
2017-06-15 15:06:08 -04:00
|
|
|
return attach
|
|
|
|
}
|
|
|
|
|
2020-05-08 15:11:51 -04:00
|
|
|
func ensureResources(spec *swarmtypes.ServiceSpec) {
|
|
|
|
if spec.TaskTemplate.Resources == nil {
|
|
|
|
spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
|
|
|
|
}
|
|
|
|
if spec.TaskTemplate.Resources.Limits == nil {
|
|
|
|
spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
|
|
|
|
}
|
|
|
|
if spec.TaskTemplate.Resources.Reservations == nil {
|
|
|
|
spec.TaskTemplate.Resources.Reservations = &swarmtypes.Resources{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:06:08 -04:00
|
|
|
func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
|
|
|
|
if spec.TaskTemplate.ContainerSpec == nil {
|
|
|
|
spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
|
|
|
|
}
|
|
|
|
}
|
2018-09-30 08:28:37 -04:00
|
|
|
|
|
|
|
func ensurePlacement(spec *swarmtypes.ServiceSpec) {
|
|
|
|
if spec.TaskTemplate.Placement == nil {
|
|
|
|
spec.TaskTemplate.Placement = &swarmtypes.Placement{}
|
|
|
|
}
|
|
|
|
}
|