Commit 63c2dea8 authored by Swann Perarnau's avatar Swann Perarnau

[feature] Add kill command

This patch adds a command to kill the parent process of a container
based on the container uuid, triggering the death of the container.

The os.kill command interacts pretty badly with the custom built
children handling, causing us to catch unwanted exceptions in an effort
to keep the code running. The waitpid code was also missing a bit about
catching children exiting because of signals, so we fixed that.

At this point, two things should be paid attention to:
  - we don't distinguish properly between a container and a command.
  This will probably cause issues later, as it should be possible to
  launch multiple programs in the same container, and for partitions to
  survive the death of the parent process.
  - the message format is growing more complex, but without any
  component having strong ownership over it. This will probably cause
  stability issues in the long term, as the format complexifies and we
  lose track of the fields expected from everyone.
parent 2f470afb
...@@ -98,6 +98,26 @@ class CommandLineInterface(object): ...@@ -98,6 +98,26 @@ class CommandLineInterface(object):
self.logger.info("list response: %r", msg) self.logger.info("list response: %r", msg)
break break
def do_kill(self, argv):
"""Connect to the NRM and ask to kill a container by uuid.
The NRM should respond to us on the pub socket with a message
containing the exit status of the top process of the container."""
command = {'command': 'kill',
'uuid': argv.uuid
}
while(True):
self.upstream_pub_socket.send_json(command)
msg = self.upstream_sub_socket.recv_json()
self.logger.info("new message: %r", msg)
# ignore other messages
if isinstance(msg, dict) and msg.get('type') == 'container':
if msg['event'] == 'exit' and msg['uuid'] == argv.uuid:
self.logger.info("container exit: %r", msg)
break
def do_setpower(self, argv): def do_setpower(self, argv):
""" Connect to the NRM and ask to change the power limit. """ Connect to the NRM and ask to change the power limit.
...@@ -137,6 +157,11 @@ class CommandLineInterface(object): ...@@ -137,6 +157,11 @@ class CommandLineInterface(object):
nargs=argparse.REMAINDER) nargs=argparse.REMAINDER)
parser_run.set_defaults(func=self.do_run) parser_run.set_defaults(func=self.do_run)
# kill container
parser_kill = subparsers.add_parser("kill")
parser_kill.add_argument("uuid", help="uuid of the container")
parser_kill.set_defaults(func=self.do_kill)
# list containers # list containers
parser_list = subparsers.add_parser("list") parser_list = subparsers.add_parser("list")
parser_list.set_defaults(func=self.do_list) parser_list.set_defaults(func=self.do_list)
......
...@@ -4,9 +4,11 @@ from aci import ImageManifest ...@@ -4,9 +4,11 @@ from aci import ImageManifest
from collections import namedtuple from collections import namedtuple
import logging import logging
import os import os
import signal
from subprograms import ChrtClient, NodeOSClient, resources from subprograms import ChrtClient, NodeOSClient, resources
import sys import sys
Container = namedtuple('Container', ['uuid', 'manifest', 'pid']) Container = namedtuple('Container', ['uuid', 'manifest', 'pid'])
...@@ -93,6 +95,16 @@ class ContainerManager(object): ...@@ -93,6 +95,16 @@ class ContainerManager(object):
del self.containers[uuid] del self.containers[uuid]
del self.pids[c.pid] del self.pids[c.pid]
def kill(self, uuid):
"""Kill all the processes of a container."""
if uuid in self.containers:
c = self.containers[uuid]
self.logger.debug("killing %r:", c)
try:
os.kill(c.pid, signal.SIGKILL)
except OSError:
pass
def list(self): def list(self):
"""List the containers in the system.""" """List the containers in the system."""
fields = ['uuid', 'pid'] fields = ['uuid', 'pid']
......
...@@ -123,6 +123,7 @@ class Daemon(object): ...@@ -123,6 +123,7 @@ class Daemon(object):
self.containerpids[pid] = msg['uuid'] self.containerpids[pid] = msg['uuid']
# TODO: obviously we need to send more info than that # TODO: obviously we need to send more info than that
update = {'type': 'container', update = {'type': 'container',
'event': 'start',
'uuid': msg['uuid'], 'uuid': msg['uuid'],
'errno': 0, 'errno': 0,
'pid': pid, 'pid': pid,
...@@ -130,10 +131,15 @@ class Daemon(object): ...@@ -130,10 +131,15 @@ class Daemon(object):
self.upstream_pub.send_json(update) self.upstream_pub.send_json(update)
else: else:
update = {'type': 'container', update = {'type': 'container',
'event': 'start',
'uuid': msg['uuid'], 'uuid': msg['uuid'],
'errno': pid, 'errno': pid,
} }
self.upstream_pub.send_json(update) self.upstream_pub.send_json(update)
elif command == 'kill':
self.logger.info("asked to kill container: %r", msg)
response = self.container_manager.kill(msg['uuid'])
# no update here, as it will trigger child exit
elif command == 'list': elif command == 'list':
self.logger.info("asked for container list: %r", msg) self.logger.info("asked for container list: %r", msg)
response = self.container_manager.list() response = self.container_manager.list()
...@@ -194,13 +200,13 @@ class Daemon(object): ...@@ -194,13 +200,13 @@ class Daemon(object):
# check if its a pid we care about # check if its a pid we care about
if pid in self.containerpids: if pid in self.containerpids:
# check if this is an exit # check if this is an exit
if os.WIFEXITED(status): if os.WIFEXITED(status) or os.WIFSIGNALED(status):
uuid = self.containerpids[pid] uuid = self.containerpids[pid]
self.container_manager.delete(uuid) self.container_manager.delete(uuid)
msg = {'type': 'container', msg = {'type': 'container',
'event': 'exit', 'event': 'exit',
'status': status, 'status': status,
'uuid': None, 'uuid': uuid,
} }
self.upstream_pub.send_json(msg) self.upstream_pub.send_json(msg)
else: else:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment