123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- package swarm
- import (
- "context"
- "runtime"
- "testing"
- "time"
- "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/client"
- "github.com/docker/docker/testutil/daemon"
- "github.com/docker/docker/testutil/environment"
- "gotest.tools/v3/assert"
- "gotest.tools/v3/poll"
- "gotest.tools/v3/skip"
- )
- // ServicePoll tweaks the pollSettings for `service`
- func ServicePoll(config *poll.Settings) {
- // Override the default pollSettings for `service` resource here ...
- config.Timeout = 15 * time.Second
- config.Delay = 100 * time.Millisecond
- if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
- config.Timeout = 90 * time.Second
- }
- }
- // 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
- }
- }
- // NewSwarm creates a swarm daemon for testing
- func NewSwarm(ctx context.Context, t *testing.T, testEnv *environment.Execution, ops ...daemon.Option) *daemon.Daemon {
- t.Helper()
- skip.If(t, testEnv.IsRemoteDaemon)
- skip.If(t, testEnv.DaemonInfo.OSType == "windows")
- skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
- if testEnv.DaemonInfo.ExperimentalBuild {
- ops = append(ops, daemon.WithExperimental())
- }
- d := daemon.New(t, ops...)
- d.StartAndSwarmInit(ctx, t)
- return d
- }
- // 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.
- func CreateService(ctx context.Context, t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
- t.Helper()
- client := d.NewClientT(t)
- defer client.Close()
- spec := CreateServiceSpec(t, opts...)
- resp, err := client.ServiceCreate(ctx, spec, types.ServiceCreateOptions{})
- assert.NilError(t, err, "error creating service")
- return resp.ID
- }
- // CreateServiceSpec creates a default service-spec, and applies the provided options
- func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec {
- t.Helper()
- var spec swarmtypes.ServiceSpec
- ServiceWithImage("busybox:latest")(&spec)
- ServiceWithCommand([]string{"/bin/top"})(&spec)
- ServiceWithReplicas(1)(&spec)
- for _, o := range opts {
- o(&spec)
- }
- return spec
- }
- // 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
- }
- }
- // 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
- }
- }
- // 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,
- },
- }
- }
- }
- // 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
- }
- }
- // ServiceWithName sets the name of the service
- func ServiceWithName(name string) ServiceSpecOpt {
- return func(spec *swarmtypes.ServiceSpec) {
- spec.Annotations.Name = name
- }
- }
- // 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
- }
- }
- // 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
- }
- }
- // ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec.
- func ServiceWithCapabilities(add []string, drop []string) ServiceSpecOpt {
- return func(spec *swarmtypes.ServiceSpec) {
- ensureContainerSpec(spec)
- spec.TaskTemplate.ContainerSpec.CapabilityAdd = add
- spec.TaskTemplate.ContainerSpec.CapabilityDrop = drop
- }
- }
- // ServiceWithPidsLimit sets the PidsLimit option of the service's Resources.Limits.
- func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
- return func(spec *swarmtypes.ServiceSpec) {
- ensureResources(spec)
- spec.TaskTemplate.Resources.Limits.Pids = limit
- }
- }
- // GetRunningTasks gets the list of running tasks for a service
- func GetRunningTasks(ctx context.Context, t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
- t.Helper()
- tasks, err := c.TaskList(ctx, types.TaskListOptions{
- Filters: filters.NewArgs(
- filters.Arg("service", serviceID),
- filters.Arg("desired-state", "running"),
- ),
- })
- assert.NilError(t, err)
- return tasks
- }
- // ExecTask runs the passed in exec config on the given task
- func ExecTask(ctx context.Context, t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
- t.Helper()
- client := d.NewClientT(t)
- defer client.Close()
- resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config)
- assert.NilError(t, err, "error creating exec")
- startCheck := types.ExecStartCheck{}
- attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck)
- assert.NilError(t, err, "error attaching to exec")
- return attach
- }
- 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{}
- }
- }
- func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
- if spec.TaskTemplate.ContainerSpec == nil {
- spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
- }
- }
- func ensurePlacement(spec *swarmtypes.ServiceSpec) {
- if spec.TaskTemplate.Placement == nil {
- spec.TaskTemplate.Placement = &swarmtypes.Placement{}
- }
- }
|