Inject log-driver for podman containers

Currently, docker daemon runtime has a default --log-driver set
to journald.

Podman lack of daemon prevent such a global application, meaning
we have to set that driver for each and every container when we
either create or run them.

Notes:
- podman only supports "json-file", and it's not even a json.
- docker json-file doesn't support "path" option, making this output
  unusable in the end: logs end in
  /var/lib/docker/containers/ID/ID-json.log

Related-Bug: #1814897
Change-Id: I7ac10a9b42ecae73a77b624f5350c424d4c3030a
This commit is contained in:
Cédric Jeanneret 2019-02-06 14:05:11 +01:00
parent f3a5c24fb8
commit fba5e04fdd
7 changed files with 82 additions and 15 deletions

View File

@ -25,7 +25,8 @@ __version__ = pbr.version.VersionInfo('paunch').version_string()
def apply(config_id, config, managed_by, labels=None, cont_cmd='docker',
default_runtime=None, log_level=None, log_file=None):
default_runtime=None, log_level=None, log_file=None,
cont_log_path=None):
"""Execute supplied container configuration.
:param str config_id: Unique config ID, should not be re-used until any
@ -40,7 +41,9 @@ def apply(config_id, config, managed_by, labels=None, cont_cmd='docker',
:param str cont_cmd: Optional override to the container command to run.
:param str default_runtime: (deprecated) does nothing.
:param int log_level: optional log level for loggers
:param int log_file: optional log file for messages
:param str log_file: optional log file for messages
:param str cont_log_path: optional log path for containers. Works only for
podman engine. Must be an absolute path.
:returns (list, list, int) lists of stdout and stderr for each execution,
and a single return code representing the
@ -68,7 +71,8 @@ def apply(config_id, config, managed_by, labels=None, cont_cmd='docker',
config=config,
runner=r,
labels=labels,
log=log
log=log,
cont_log_path=cont_log_path
)
else:
log.error("container runtime not supported: %s" % cont_cmd)

View File

@ -24,13 +24,15 @@ from paunch.utils import systemd
class BaseBuilder(object):
def __init__(self, config_id, config, runner, labels, log=None):
def __init__(self, config_id, config, runner, labels, log=None,
cont_log_path=None):
self.config_id = config_id
self.config = config
self.labels = labels
self.runner = runner
# Leverage pre-configured logger
self.log = log or common.configure_logging(__name__)
self.cont_log_path = cont_log_path
def apply(self):
@ -79,6 +81,7 @@ class BaseBuilder(object):
'--name',
container_name
]
self.label_arguments(cmd, container)
self.container_run_args(cmd, container)
elif action == 'exec':

View File

@ -11,19 +11,34 @@
# under the License.
#
import os
from paunch.builder import base
class PodmanBuilder(base.BaseBuilder):
def __init__(self, config_id, config, runner, labels=None, log=None):
def __init__(self, config_id, config, runner, labels=None, log=None,
cont_log_path=None):
super(PodmanBuilder, self).__init__(config_id, config, runner,
labels, log)
labels, log, cont_log_path)
def container_run_args(self, cmd, container):
cconfig = self.config[container]
if cconfig.get('detach', True):
cmd.append('--detach=true')
if self.cont_log_path is not None:
if os.path.isabs(self.cont_log_path):
if not os.path.exists(self.cont_log_path):
os.makedirs(self.cont_log_path)
log_path = os.path.join(self.cont_log_path, container)
logging = ['--log-driver', 'json-file',
'--log-opt', 'path=%s' % log_path]
cmd.extend(logging)
else:
raise ValueError('cont_log_path passed but not absolute.')
self.list_or_string_arg(cconfig, cmd, 'env_file', '--env-file')
# TODO(sbaker): support the dict layout for this property
for v in cconfig.get('environment', []):

View File

@ -65,6 +65,13 @@ class Apply(command.Command):
choices=['docker', 'podman'],
help=('Default runtime for containers. Can be docker or podman.'),
)
parser.add_argument(
'--container-log-path',
dest='cont_log_path',
default=None,
help=('Absolute directory path for container log. Works only for '
'podman container engine')
)
return parser
def take_action(self, parsed_args):
@ -85,7 +92,8 @@ class Apply(command.Command):
labels=labels,
cont_cmd=parsed_args.default_runtime,
log_level=log_level,
log_file=log_file
log_file=log_file,
cont_log_path=parsed_args.cont_log_path
)
return rc

View File

@ -24,11 +24,12 @@ from paunch.utils import systemd
class BaseRunner(object):
def __init__(self, managed_by, cont_cmd, log=None):
def __init__(self, managed_by, cont_cmd, log=None, cont_log_path=None):
self.managed_by = managed_by
self.cont_cmd = cont_cmd
# Leverage pre-configured logger
self.log = log or common.configure_logging(__name__)
self.cont_log_path = cont_log_path
@staticmethod
def execute(cmd, log=None, quiet=False):
@ -277,9 +278,11 @@ class DockerRunner(BaseRunner):
class PodmanRunner(BaseRunner):
def __init__(self, managed_by, cont_cmd=None, log=None):
def __init__(self, managed_by, cont_cmd=None, log=None,
cont_log_path=None):
cont_cmd = cont_cmd or 'podman'
super(PodmanRunner, self).__init__(managed_by, cont_cmd, log)
super(PodmanRunner, self).__init__(managed_by, cont_cmd, log,
cont_log_path)
def rename_container(self, container, name):
# TODO(emilien) podman doesn't support rename, we'll handle it
@ -331,6 +334,7 @@ class PodmanRunner(BaseRunner):
config=config_data,
runner=self,
labels=None,
log=self.log
log=self.log,
cont_log_path=self.cont_log_path
)
builder.apply()

View File

@ -106,7 +106,8 @@ class TestPaunchPodmanRuntime(base.TestCase):
config={'bar': 'baz'},
managed_by='tester',
labels=None,
cont_cmd='podman')
cont_cmd='podman',
cont_log_path=None)
runner.assert_called_once_with('tester', cont_cmd='podman',
log=mock.ANY)
builder.assert_called_once_with(
@ -114,7 +115,30 @@ class TestPaunchPodmanRuntime(base.TestCase):
config={'bar': 'baz'},
runner=runner.return_value,
labels=None,
log=mock.ANY
log=mock.ANY,
cont_log_path=None
)
builder.return_value.apply.assert_called_once_with()
@mock.patch('paunch.builder.podman.PodmanBuilder', autospec=True)
@mock.patch('paunch.runner.PodmanRunner', autospec=True)
def test_apply_container_log(self, runner, builder):
paunch.apply(
config_id='foo',
config={'bar': 'baz'},
managed_by='tester',
labels=None,
cont_cmd='podman',
cont_log_path='/var/log')
runner.assert_called_once_with('tester', cont_cmd='podman',
log=mock.ANY)
builder.assert_called_once_with(
config_id='foo',
config={'bar': 'baz'},
runner=runner.return_value,
labels=None,
log=mock.ANY,
cont_log_path='/var/log'
)
builder.return_value.apply.assert_called_once_with()
@ -126,7 +150,8 @@ class TestPaunchPodmanRuntime(base.TestCase):
config={'bar': 'baz'},
managed_by='tester',
labels={'bink': 'boop'},
cont_cmd='podman')
cont_cmd='podman',
cont_log_path=None)
runner.assert_called_once_with('tester', cont_cmd='podman',
log=mock.ANY)
@ -135,7 +160,8 @@ class TestPaunchPodmanRuntime(base.TestCase):
config={'bar': 'baz'},
runner=runner.return_value,
labels={'bink': 'boop'},
log=mock.ANY
log=mock.ANY,
cont_log_path=None
)
builder.return_value.apply.assert_called_once_with()

View File

@ -0,0 +1,7 @@
---
features:
- Add new "cont_log_path". It must point to a directory, where the container
engine will puts logs issued from containers standard output. It works only
for podman.
- Add new "--container-log-path" allowing to actually set cont_log_path from
the CLI call directly.