github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration/internal/swarm/service.go (about)

     1  package swarm
     2  
     3  import (
     4  	"context"
     5  	"runtime"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/filters"
    11  	swarmtypes "github.com/docker/docker/api/types/swarm"
    12  	"github.com/docker/docker/client"
    13  	"github.com/docker/docker/testutil/daemon"
    14  	"github.com/docker/docker/testutil/environment"
    15  	"gotest.tools/v3/assert"
    16  	"gotest.tools/v3/poll"
    17  	"gotest.tools/v3/skip"
    18  )
    19  
    20  // ServicePoll tweaks the pollSettings for `service`
    21  func ServicePoll(config *poll.Settings) {
    22  	// Override the default pollSettings for `service` resource here ...
    23  	config.Timeout = 15 * time.Second
    24  	config.Delay = 100 * time.Millisecond
    25  	if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
    26  		config.Timeout = 90 * time.Second
    27  	}
    28  }
    29  
    30  // NetworkPoll tweaks the pollSettings for `network`
    31  func NetworkPoll(config *poll.Settings) {
    32  	// Override the default pollSettings for `network` resource here ...
    33  	config.Timeout = 30 * time.Second
    34  	config.Delay = 100 * time.Millisecond
    35  
    36  	if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
    37  		config.Timeout = 50 * time.Second
    38  	}
    39  }
    40  
    41  // ContainerPoll tweaks the pollSettings for `container`
    42  func ContainerPoll(config *poll.Settings) {
    43  	// Override the default pollSettings for `container` resource here ...
    44  
    45  	if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
    46  		config.Timeout = 30 * time.Second
    47  		config.Delay = 100 * time.Millisecond
    48  	}
    49  }
    50  
    51  // NewSwarm creates a swarm daemon for testing
    52  func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...daemon.Option) *daemon.Daemon {
    53  	t.Helper()
    54  	skip.If(t, testEnv.IsRemoteDaemon)
    55  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
    56  	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
    57  	if testEnv.DaemonInfo.ExperimentalBuild {
    58  		ops = append(ops, daemon.WithExperimental())
    59  	}
    60  	d := daemon.New(t, ops...)
    61  	d.StartAndSwarmInit(t)
    62  	return d
    63  }
    64  
    65  // ServiceSpecOpt is used with `CreateService` to pass in service spec modifiers
    66  type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
    67  
    68  // CreateService creates a service on the passed in swarm daemon.
    69  func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
    70  	t.Helper()
    71  
    72  	client := d.NewClientT(t)
    73  	defer client.Close()
    74  
    75  	spec := CreateServiceSpec(t, opts...)
    76  	resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{})
    77  	assert.NilError(t, err, "error creating service")
    78  	return resp.ID
    79  }
    80  
    81  // CreateServiceSpec creates a default service-spec, and applies the provided options
    82  func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec {
    83  	t.Helper()
    84  	var spec swarmtypes.ServiceSpec
    85  	ServiceWithImage("busybox:latest")(&spec)
    86  	ServiceWithCommand([]string{"/bin/top"})(&spec)
    87  	ServiceWithReplicas(1)(&spec)
    88  
    89  	for _, o := range opts {
    90  		o(&spec)
    91  	}
    92  	return spec
    93  }
    94  
    95  // ServiceWithMode sets the mode of the service to the provided mode.
    96  func ServiceWithMode(mode swarmtypes.ServiceMode) func(*swarmtypes.ServiceSpec) {
    97  	return func(spec *swarmtypes.ServiceSpec) {
    98  		spec.Mode = mode
    99  	}
   100  }
   101  
   102  // ServiceWithInit sets whether the service should use init or not
   103  func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
   104  	return func(spec *swarmtypes.ServiceSpec) {
   105  		ensureContainerSpec(spec)
   106  		spec.TaskTemplate.ContainerSpec.Init = b
   107  	}
   108  }
   109  
   110  // ServiceWithImage sets the image to use for the service
   111  func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) {
   112  	return func(spec *swarmtypes.ServiceSpec) {
   113  		ensureContainerSpec(spec)
   114  		spec.TaskTemplate.ContainerSpec.Image = image
   115  	}
   116  }
   117  
   118  // ServiceWithCommand sets the command to use for the service
   119  func ServiceWithCommand(cmd []string) ServiceSpecOpt {
   120  	return func(spec *swarmtypes.ServiceSpec) {
   121  		ensureContainerSpec(spec)
   122  		spec.TaskTemplate.ContainerSpec.Command = cmd
   123  	}
   124  }
   125  
   126  // ServiceWithConfig adds the config reference to the service
   127  func ServiceWithConfig(configRef *swarmtypes.ConfigReference) ServiceSpecOpt {
   128  	return func(spec *swarmtypes.ServiceSpec) {
   129  		ensureContainerSpec(spec)
   130  		spec.TaskTemplate.ContainerSpec.Configs = append(spec.TaskTemplate.ContainerSpec.Configs, configRef)
   131  	}
   132  }
   133  
   134  // ServiceWithSecret adds the secret reference to the service
   135  func ServiceWithSecret(secretRef *swarmtypes.SecretReference) ServiceSpecOpt {
   136  	return func(spec *swarmtypes.ServiceSpec) {
   137  		ensureContainerSpec(spec)
   138  		spec.TaskTemplate.ContainerSpec.Secrets = append(spec.TaskTemplate.ContainerSpec.Secrets, secretRef)
   139  	}
   140  }
   141  
   142  // ServiceWithReplicas sets the replicas for the service
   143  func ServiceWithReplicas(n uint64) ServiceSpecOpt {
   144  	return func(spec *swarmtypes.ServiceSpec) {
   145  		spec.Mode = swarmtypes.ServiceMode{
   146  			Replicated: &swarmtypes.ReplicatedService{
   147  				Replicas: &n,
   148  			},
   149  		}
   150  	}
   151  }
   152  
   153  // ServiceWithMaxReplicas sets the max replicas for the service
   154  func ServiceWithMaxReplicas(n uint64) ServiceSpecOpt {
   155  	return func(spec *swarmtypes.ServiceSpec) {
   156  		ensurePlacement(spec)
   157  		spec.TaskTemplate.Placement.MaxReplicas = n
   158  	}
   159  }
   160  
   161  // ServiceWithName sets the name of the service
   162  func ServiceWithName(name string) ServiceSpecOpt {
   163  	return func(spec *swarmtypes.ServiceSpec) {
   164  		spec.Annotations.Name = name
   165  	}
   166  }
   167  
   168  // ServiceWithNetwork sets the network of the service
   169  func ServiceWithNetwork(network string) ServiceSpecOpt {
   170  	return func(spec *swarmtypes.ServiceSpec) {
   171  		spec.TaskTemplate.Networks = append(spec.TaskTemplate.Networks,
   172  			swarmtypes.NetworkAttachmentConfig{Target: network})
   173  	}
   174  }
   175  
   176  // ServiceWithEndpoint sets the Endpoint of the service
   177  func ServiceWithEndpoint(endpoint *swarmtypes.EndpointSpec) ServiceSpecOpt {
   178  	return func(spec *swarmtypes.ServiceSpec) {
   179  		spec.EndpointSpec = endpoint
   180  	}
   181  }
   182  
   183  // ServiceWithSysctls sets the Sysctls option of the service's ContainerSpec.
   184  func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
   185  	return func(spec *swarmtypes.ServiceSpec) {
   186  		ensureContainerSpec(spec)
   187  		spec.TaskTemplate.ContainerSpec.Sysctls = sysctls
   188  	}
   189  }
   190  
   191  // ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec.
   192  func ServiceWithCapabilities(add []string, drop []string) ServiceSpecOpt {
   193  	return func(spec *swarmtypes.ServiceSpec) {
   194  		ensureContainerSpec(spec)
   195  		spec.TaskTemplate.ContainerSpec.CapabilityAdd = add
   196  		spec.TaskTemplate.ContainerSpec.CapabilityDrop = drop
   197  	}
   198  }
   199  
   200  // ServiceWithPidsLimit sets the PidsLimit option of the service's Resources.Limits.
   201  func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
   202  	return func(spec *swarmtypes.ServiceSpec) {
   203  		ensureResources(spec)
   204  		spec.TaskTemplate.Resources.Limits.Pids = limit
   205  	}
   206  }
   207  
   208  // GetRunningTasks gets the list of running tasks for a service
   209  func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
   210  	t.Helper()
   211  
   212  	tasks, err := c.TaskList(context.Background(), types.TaskListOptions{
   213  		Filters: filters.NewArgs(
   214  			filters.Arg("service", serviceID),
   215  			filters.Arg("desired-state", "running"),
   216  		),
   217  	})
   218  
   219  	assert.NilError(t, err)
   220  	return tasks
   221  }
   222  
   223  // ExecTask runs the passed in exec config on the given task
   224  func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types.ExecConfig) types.HijackedResponse {
   225  	t.Helper()
   226  	client := d.NewClientT(t)
   227  	defer client.Close()
   228  
   229  	ctx := context.Background()
   230  	resp, err := client.ContainerExecCreate(ctx, task.Status.ContainerStatus.ContainerID, config)
   231  	assert.NilError(t, err, "error creating exec")
   232  
   233  	startCheck := types.ExecStartCheck{}
   234  	attach, err := client.ContainerExecAttach(ctx, resp.ID, startCheck)
   235  	assert.NilError(t, err, "error attaching to exec")
   236  	return attach
   237  }
   238  
   239  func ensureResources(spec *swarmtypes.ServiceSpec) {
   240  	if spec.TaskTemplate.Resources == nil {
   241  		spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
   242  	}
   243  	if spec.TaskTemplate.Resources.Limits == nil {
   244  		spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
   245  	}
   246  	if spec.TaskTemplate.Resources.Reservations == nil {
   247  		spec.TaskTemplate.Resources.Reservations = &swarmtypes.Resources{}
   248  	}
   249  }
   250  
   251  func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
   252  	if spec.TaskTemplate.ContainerSpec == nil {
   253  		spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
   254  	}
   255  }
   256  
   257  func ensurePlacement(spec *swarmtypes.ServiceSpec) {
   258  	if spec.TaskTemplate.Placement == nil {
   259  		spec.TaskTemplate.Placement = &swarmtypes.Placement{}
   260  	}
   261  }