Implement podman rename via re-apply of containers

To w/a the missing a container rename feature of podman, implement
renaming via removing of the original container and re-applying it from
the same configs but using the new name.

This fixes idempotency issues when service containers are executed
under ephemeral names created via the paunch's unique containers names
generator, while it is expected to have them executed under its wanted
config names.

Change-Id: If851604d25b6c7982d950bb9e13dceada3bfc161
Closes-Bug: #1805410
Signed-off-by: Bogdan Dobrelya <bdobreli@redhat.com>
This commit is contained in:
Bogdan Dobrelya 2018-12-03 16:23:32 +01:00
parent 934b7e0fde
commit 510f091353
2 changed files with 76 additions and 3 deletions

View File

@ -12,11 +12,13 @@
#
import collections
import jmespath
import json
import random
import string
import subprocess
from paunch.builder import podman
from paunch.utils import common
from paunch.utils import systemd
@ -157,6 +159,28 @@ class BaseRunner(object):
conf_id)
self.remove_containers(conf_id)
def discover_container_config(self, configs, container, name):
'''Find the paunch and runtime configs of a container by name.'''
for conf_id in self.current_config_ids():
jquerry = ("[] | [?(Name=='%s' && "
"Config.Labels.container_name=='%s' && "
"Config.Labels.config_id=='%s')]" %
(container, name, conf_id))
runtime_conf = None
try:
runtime_conf = jmespath.search(jquerry,
configs[conf_id])[0]
result = (conf_id, runtime_conf)
except Exception:
self.log.error("Failed searching container %s "
"for config %s" % (container, conf_id))
result = (None, None)
if runtime_conf:
self.log.debug("Found container %s "
"for config %s" % (container, conf_id))
break
return result
def list_configs(self):
configs = collections.defaultdict(list)
for conf_id in self.current_config_ids():
@ -259,6 +283,54 @@ class PodmanRunner(BaseRunner):
def rename_container(self, container, name):
# TODO(emilien) podman doesn't support rename, we'll handle it
# in paunch itself, probably.
self.log.warning("container renaming isn't supported by podman")
pass
# in paunch itself for now
configs = self.list_configs()
config_id, config = self.discover_container_config(
configs, container, name)
# Get config_data dict by the discovered conf ID,
# paunch needs it for maintaining idempotency within a conf ID
filter_names = ("[] | [?(Name!='%s' && "
"Config.Labels.config_id=='%s')]"
".Name" % (container, config_id))
filter_cdata = ("[] | [?(Name!='%s' && "
"Config.Labels.config_id=='%s')]"
".Config.Labels.config_data" % (container, config_id))
names = None
cdata = None
try:
names = jmespath.search(filter_names, configs[config_id])
cdata = jmespath.search(filter_cdata, configs[config_id])
except jmespath.exceptions.LexerError:
self.log.error("Failed to rename a container %s into %s: "
"used a bad search pattern" % (container, name))
return
if not names or not cdata:
self.log.error("Failed to rename a container %s into %s: "
"no config_data was found" % (container, name))
return
# Rename the wanted container in the config_data fetched from the
# discovered config
config_data = dict(zip(names, map(json.loads, cdata)))
config_data[name] = json.loads(
config.get('Config').get('Labels').get('config_data'))
# Re-apply a container under its amended name using the fetched configs
self.log.debug("Renaming a container known as %s into %s, "
"via re-applying its original config" %
(container, name))
self.log.debug("Removing the destination container %s" % name)
self.stop_container(name)
self.remove_container(name)
self.log.debug("Removing a container known as %s" % container)
self.stop_container(container)
self.remove_container(container)
builder = podman.PodmanBuilder(
config_id=config_id,
config=config_data,
runner=self,
labels=None,
log=self.log
)
builder.apply()

View File

@ -6,3 +6,4 @@ pbr>=2.0.0,!=2.1.0 # Apache-2.0
cliff>=2.6.0 # Apache-2.0
tenacity>=3.2.1 # Apache-2.0
jmespath>=0.9.0 # MIT