integration: run swarm test tasks under an init
The commands these tasks run - /bin/top by default, httpd in the
ingress-port tests - install no SIGTERM handler, and a signal sent to
PID 1 has no default action. So as PID 1 they ignore the stop signal the
daemon sends them, and the daemon waits out the container's whole stop
timeout before resorting to SIGKILL, on every task teardown:
start clean shutdown of all containers with a 15 seconds timeout...
Container failed to exit within 10s of signal 15 - using the force
Daemon shutdown complete
Ten seconds a task, paid by every swarm test that stops a daemon or
removes a service. Ask for an init: it takes PID 1 and forwards the
signal to a process the default action does apply to. Note it has to be
a graceful stop to be worth anything - a container removed by force is
killed outright and waits for nothing.
Defaulting it in the spec helper reaches every test that builds a
service through it, rather than a line per caller. The iptables-doc
suite and the Init test itself build their specs by hand, so they're
handled where they are - and the case in the Init test that asserts an
unset Init delegates to the daemon configuration now asks for that
explicitly, which is what it was relying on the helper for. The
nftables-doc suite builds one too, but the only section that would use
it is commented out awaiting swarm support for nftables, so there's
nothing there to speed up yet.
Nothing here asserts on PID 1, and nothing depends on a task lingering:
the tests that care about task lifetime wait for the task count to
settle before checking anything, so a quicker drain only brings that
wait forward.
Assisted-By: Claude Opus 5
Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
1 parent
4e92a43120
commit
6f49963455
5 files changed
+31
-6
No files matched your search
@@ -333,15 +333,20 @@ func TestTemplatedConfig(t *testing.T) {
|
||||
|
||||
outBuf.Reset()
|
||||
errBuf.Reset()
|
||||
// Read the mount table rather than running mount(8): busybox's mount stops
|
||||
// parsing at an over-long entry and drops it along with everything after it,
|
||||
// and the task carries one - the init binary is bind-mounted from an overlay
|
||||
// whose options list every lower directory. /proc/mounts is what mount(8)
|
||||
// reads anyway, and cat doesn't parse it.
|
||||
resp = swarm.ExecTask(ctx, t, d, tasks[0], client.ExecCreateOptions{
|
||||
Cmd: []string{"mount"},
|
||||
Cmd: []string{"cat", "/proc/mounts"},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
})
|
||||
|
||||
_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(outBuf.String(), "tmpfs on /templated_config type tmpfs"), "expected to be mounted as tmpfs")
|
||||
assert.Check(t, is.Contains(outBuf.String(), "tmpfs /templated_config tmpfs"), "expected to be mounted as tmpfs")
|
||||
assert.Check(t, is.Equal(errBuf.String(), ""))
|
||||
}
|
||||
|
||||
|
||||
@@ -68,13 +68,23 @@ func CreateService(ctx context.Context, t *testing.T, d *daemon.Daemon, opts ...
|
||||
return resp.ID
|
||||
}
|
||||
|
||||
// CreateServiceSpec creates a default service-spec, and applies the provided options
|
||||
// CreateServiceSpec creates a default service-spec, and applies the provided options.
|
||||
//
|
||||
// The defaults include running tasks under an init, so a test that wants the
|
||||
// daemon's own default instead has to say so with ServiceWithInit(nil) - saying
|
||||
// nothing gets the init.
|
||||
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)
|
||||
// Run tasks under an init. None of the commands these tests run install a
|
||||
// SIGTERM handler, and a signal sent to PID 1 has no default action - so as
|
||||
// PID 1 they ignore the stop signal, and every task teardown waits out the
|
||||
// container's whole stop timeout before the kill. An init takes PID 1 and
|
||||
// forwards the signal to a process the default action applies to.
|
||||
ServiceWithInit(new(true))(&spec)
|
||||
|
||||
for _, o := range opts {
|
||||
o(&spec)
|
||||
|
||||
@@ -366,6 +366,8 @@ func createServices(ctx context.Context, t *testing.T, d *daemon.Daemon, section
|
||||
ContainerSpec: &swarmtypes.ContainerSpec{
|
||||
Image: "busybox:latest",
|
||||
Command: []string{"/bin/top"},
|
||||
// top ignores SIGTERM as PID 1; an init forwards it.
|
||||
Init: new(true),
|
||||
},
|
||||
},
|
||||
EndpointSpec: &swarmtypes.EndpointSpec{
|
||||
|
||||
@@ -355,15 +355,20 @@ func TestTemplatedSecret(t *testing.T) {
|
||||
|
||||
outBuf.Reset()
|
||||
errBuf.Reset()
|
||||
// Read the mount table rather than running mount(8): busybox's mount stops
|
||||
// parsing at an over-long entry and drops it along with everything after it,
|
||||
// and the task carries one - the init binary is bind-mounted from an overlay
|
||||
// whose options list every lower directory. /proc/mounts is what mount(8)
|
||||
// reads anyway, and cat doesn't parse it.
|
||||
resp = swarm.ExecTask(ctx, t, d, tasks[0], client.ExecCreateOptions{
|
||||
Cmd: []string{"mount"},
|
||||
Cmd: []string{"cat", "/proc/mounts"},
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
})
|
||||
|
||||
_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(outBuf.String(), "tmpfs on /run/secrets/templated_secret type tmpfs"), "expected to be mounted as tmpfs")
|
||||
assert.Check(t, is.Contains(outBuf.String(), "tmpfs /run/secrets/templated_secret tmpfs"), "expected to be mounted as tmpfs")
|
||||
assert.Check(t, is.Equal(errBuf.String(), ""))
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ func testServiceCreateInit(ctx context.Context, daemonEnabled bool) func(t *test
|
||||
booleanTrue := true
|
||||
booleanFalse := false
|
||||
|
||||
serviceID := swarm.CreateService(ctx, t, d)
|
||||
// An unset Init delegates to the daemon configuration, which is what this
|
||||
// case is about - so ask for it explicitly, rather than relying on the
|
||||
// spec helper leaving it unset.
|
||||
serviceID := swarm.CreateService(ctx, t, d, swarm.ServiceWithInit(nil))
|
||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, 1), swarm.ServicePoll)
|
||||
i := inspectServiceContainer(ctx, t, apiClient, serviceID)
|
||||
// HostConfig.Init == nil means that it delegates to daemon configuration
|
||||
|
||||
Reference in new issue
Block a user