Combined cherry-pick to allow TCIB to run config-less

1) Allow the container build tools to run config-less

This change allows the container build tools to run without a
provided configuration file. The container configuration file
still in service was derived from our old kolla implementation
which no longer needs to exist. The build process will now
simply walk the build path and build all containers found when
a configuration file is not defined or present. Logging has
been added to inform the user of the tools discoveries which
should provide a nice user experience when running container
builds.

In the event a container configuration file is used, the tools
will now ensure all dependent images are inherently part of the
build preparation.

Tests have been updated to ensure that the client is operational
in both a config and config-less situation.

Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit 4d23eee41e)

2) Update dependency logic to correctly index parents

This change adjusts the parent indexer so that it will correctly
discover base images that maybe be implicitly required.

Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit f081d6afd6)

These changes improve the TCIB user experience and ensure we're future
proofing our container build tools.

Change-Id: Ibadef29b24232544dbc8cfc0b5dc8d0238d09ed6
Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit 7d598f850b)
This commit is contained in:
Kevin Carter 2020-08-20 17:04:33 -05:00
parent bd22a7fb1d
commit 86c9fa7070
2 changed files with 96 additions and 24 deletions

View File

@ -206,9 +206,7 @@ class TestContainerImages(deploy_fakes.TestDeployOvercloud):
("config_file", "not-a-file-config.yaml"),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(IOError, self.cmd.take_action, parsed_args)
self.check_parser(self.cmd, arglist, verifylist)
def test_image_build_failure_no_config_dir(self):
arglist = ["--config-path", "not-a-path"]

View File

@ -13,6 +13,7 @@
# under the License.
#
import collections
import logging
import os
import re
@ -54,7 +55,7 @@ class Build(command.Command):
auth_required = False
log = logging.getLogger(__name__ + ".Build")
identified_images = list()
image_parents = dict()
image_parents = collections.OrderedDict()
image_paths = dict()
def get_parser(self, prog_name):
@ -351,6 +352,39 @@ class Build(command.Command):
self.image_paths[tree] = os.path.join(work_dir, tree)
utils.makedirs(dir_path=self.image_paths[tree])
def process_images(self, expected_images, parsed_args, image_configs):
"""Process all of expected images and ensure we have valid config.
:param expected_images: List of expected images.
:type expected_images: List.
:param parsed_args: Parsed arguments.
:type parsed_args: Object.
:param image_configs: Hash of pre-processed images.
:type image_configs: Dict.
:returns List:
"""
image_configs = collections.OrderedDict()
for image in expected_images:
if image != "container-images" and image not in image_configs:
self.log.debug("processing image configs".format(image))
image_config = self.find_image(
image,
self.tcib_config_path,
parsed_args.base
)
if not image_config:
self.log.error(
"Image processing failure: {}".format(image)
)
raise RuntimeError(
"Container image specified, but no"
" config was provided. Image: {}".format(image)
)
image_configs[image] = image_config
return image_configs
def take_action(self, parsed_args):
self.config_file = os.path.expanduser(parsed_args.config_file)
self.config_path = os.path.expanduser(parsed_args.config_path)
@ -377,12 +411,6 @@ class Build(command.Command):
os.path.dirname(self.tcib_config_path),
parsed_args.config_file,
)
if not os.path.isfile(self.config_file):
raise IOError(
"Configuration file {} was not found.".format(
self.config_file
)
)
self.log.debug("take_action({})".format(parsed_args))
excludes = parsed_args.excludes
@ -421,25 +449,70 @@ class Build(command.Command):
)
utils.makedirs(work_dir)
with open(self.config_file, "r") as f:
containers_yaml = yaml.safe_load(f)
if os.path.isfile(self.config_file):
self.log.info(
"Configuration file found: {}".format(self.config_file)
)
with open(self.config_file, "r") as f:
containers_yaml = yaml.safe_load(f)
for c in containers_yaml["container_images"]:
entry = dict(c)
if not entry.get("image_source", "") == "tripleo":
continue
image = self.imagename_to_regex(entry.get("imagename"))
if image and image not in excludes:
images_to_prepare.append(image)
for c in containers_yaml["container_images"]:
entry = dict(c)
if not entry.get("image_source", "") == "tripleo":
continue
image = self.imagename_to_regex(entry.get("imagename"))
if image and image not in excludes:
images_to_prepare.append(image)
else:
self.log.warning(
"Configuration file not found: {}".format(self.config_file)
)
self.log.warning(
"All identified images will be prepared: {}".format(
self.config_file
)
)
images_to_prepare.extend(self.identified_images)
# NOTE(cloudnull): Ensure all dependent images are in the build
# tree. Once an image has been added to the
# prepare array, we walk it back and ensure
# dependencies are also part of the build
# process.
image_configs = collections.OrderedDict() # hash
image_configs.update(
self.process_images(
expected_images=images_to_prepare,
parsed_args=parsed_args,
image_configs=image_configs
)
)
_parents = self.process_images(
expected_images=list(self.image_parents.values()),
parsed_args=parsed_args,
image_configs=image_configs
)
for key, value in _parents.items():
image_configs[key] = value
image_configs.move_to_end(key, last=False)
images_to_prepare.insert(0, key)
if "os" in image_configs: # Second image prepared if found
image_configs.move_to_end("os", last=False)
if "base" in image_configs: # First image prepared if found
image_configs.move_to_end("base", last=False)
self.log.debug(
"Images being prepared: {}".format(
[i[0] for i in [(k, v) for k, v in image_configs.items()]]
)
)
tcib_inventory = {"all": {"hosts": {}}}
tcib_inventory_hosts = tcib_inventory["all"]["hosts"]
for image in images_to_prepare:
image_config = self.find_image(
image, self.tcib_config_path, parsed_args.base
)
for image, image_config in [(k, v) for k, v in image_configs.items()]:
self.log.debug("processing image config {}".format(image))
if image == "base":
image_name = image_from = parsed_args.base
else:
@ -560,6 +633,7 @@ class Build(command.Command):
# Ensure anything not intended to be built is excluded
excludes.extend(self.rectify_excludes(images_to_prepare))
self.log.info("Images being excluded: {}".format(excludes))
if not parsed_args.skip_build:
bb = buildah.BuildahBuilder(