Currently when creating a Dockerfile for a container, we include the
full set of base packages, along with the packages for the project
itself. If building a Perl binding, this would require us to install
the base package, libvirt packages and Perl packages. With the use
of the "--inherit libvirt-fedora-30" arg, it is possible to have a
dockerfile that only adds the Perl packages, getting everything
else from the parent image.
For example, a full Dockerfile for libvirt-go would thus be:
FROM libvirt-centos-7:latest
RUN yum install -y \
golang && \
yum autoremove -y && \
yum clean all -y
Note there is no need to set ENV either, as that's inherited from the
parent container.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
guests/lcitool | 108 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 98 insertions(+), 10 deletions(-)
diff --git a/guests/lcitool b/guests/lcitool
index 689a8cf..b3afb6a 100755
--- a/guests/lcitool
+++ b/guests/lcitool
@@ -396,6 +396,12 @@ class Application:
help="target architecture for cross compiler",
)
+ def add_inherit_arg(parser):
+ parser.add_argument(
+ "-i", "--inherit",
+ help="inherit from an intermediate container image",
+ )
+
def add_wait_arg(parser):
parser.add_argument(
"-w", "--wait",
@@ -442,6 +448,7 @@ class Application:
add_hosts_arg(dockerfileparser)
add_projects_arg(dockerfileparser)
add_cross_arch_arg(dockerfileparser)
+ add_inherit_arg(dockerfileparser)
def _execute_playbook(self, playbook, hosts, projects, git_revision):
base = Util.get_base()
@@ -644,11 +651,11 @@ class Application:
with open(keyfile, "r") as r:
return r.read().rstrip()
- def _dockerfile_build_varmap(self, facts, mappings, pip_mappings, projects,
cross_arch):
+ def _dockerfile_build_varmap(self, facts, mappings, pip_mappings, projects,
cross_arch, needbase):
if facts["package_format"] == "deb":
- varmap = self._dockerfile_build_varmap_deb(facts, mappings, pip_mappings,
projects, cross_arch)
+ varmap = self._dockerfile_build_varmap_deb(facts, mappings, pip_mappings,
projects, cross_arch, needbase)
if facts["package_format"] == "rpm":
- varmap = self._dockerfile_build_varmap_rpm(facts, mappings, pip_mappings,
projects, cross_arch)
+ varmap = self._dockerfile_build_varmap_rpm(facts, mappings, pip_mappings,
projects, cross_arch, needbase)
varmap["package_manager"] = facts["package_manager"]
varmap["cc"] = facts["cc"]
@@ -662,7 +669,7 @@ class Application:
return varmap
- def _dockerfile_build_varmap_deb(self, facts, mappings, pip_mappings, projects,
cross_arch):
+ def _dockerfile_build_varmap_deb(self, facts, mappings, pip_mappings, projects,
cross_arch, needbase):
package_format = facts["package_format"]
package_manager = facts["package_manager"]
os_name = facts["os_name"]
@@ -682,7 +689,10 @@ class Application:
# We need to add the base project manually here: the standard
# machinery hides it because it's an implementation detail
- for project in projects + ["base"]:
+ pkgprojects = projects
+ if needbase:
+ pkgprojects = projects + ["base"]
+ for project in pkgprojects:
for package in self._projects.get_packages(project):
cross_policy = "native"
for key in cross_keys:
@@ -730,7 +740,7 @@ class Application:
return varmap
- def _dockerfile_build_varmap_rpm(self, facts, mappings, pip_mappings, projects,
cross_arch):
+ def _dockerfile_build_varmap_rpm(self, facts, mappings, pip_mappings, projects,
cross_arch, needbase):
package_format = facts["package_format"]
package_manager = facts["package_manager"]
os_name = facts["os_name"]
@@ -744,7 +754,10 @@ class Application:
# We need to add the base project manually here: the standard
# machinery hides it because it's an implementation detail
- for project in projects + ["base"]:
+ pkgprojects = projects
+ if needbase:
+ pkgprojects = projects + ["base"]
+ for project in pkgprojects:
for package in self._projects.get_packages(project):
for key in keys:
if key in mappings[package]:
@@ -791,7 +804,7 @@ class Application:
return varmap
- def _dockerfile_format(self, facts, cross_arch, varmap):
+ def _dockerfile_base_format(self, facts, cross_arch, varmap):
package_format = facts["package_format"]
package_manager = facts["package_manager"]
os_name = facts["os_name"]
@@ -931,6 +944,74 @@ class Application:
ENV CONFIGURE_OPTS "--host={cross_abi}"
""").format(**varmap))
+ def _dockerfile_child_format(self, facts, cross_arch, inherit, varmap):
+ package_format = facts["package_format"]
+ package_manager = facts["package_manager"]
+ os_name = facts["os_name"]
+ os_version = facts["os_version"]
+
+ print("FROM {}".format(inherit))
+
+ commands = []
+
+ if package_format == "deb":
+ commands.extend([
+ "export DEBIAN_FRONTEND=noninteractive",
+ "{package_manager} update",
+ "{package_manager} install --no-install-recommends -y {pkgs}",
+ "{package_manager} autoremove -y",
+ "{package_manager} autoclean -y",
+ ])
+ elif package_format == "rpm":
+ commands.extend([
+ "{package_manager} install -y {pkgs}",
+ ])
+
+ # openSUSE doesn't seem to have a convenient way to remove all
+ # unnecessary packages, but CentOS and Fedora do
+ if os_name == "OpenSUSE":
+ commands.extend([
+ "{package_manager} clean --all",
+ ])
+ else:
+ commands.extend([
+ "{package_manager} autoremove -y",
+ "{package_manager} clean all -y",
+ ])
+
+
+ script = "\nRUN " + (" && \\\n ".join(commands)) +
"\n"
+ sys.stdout.write(script.format(**varmap))
+
+ if cross_arch:
+ cross_commands = []
+
+ # Intentionally a separate RUN command from the above
+ # so that the common packages of all cross-built images
+ # share a Docker image layer.
+ if package_format == "deb":
+ cross_commands.extend([
+ "export DEBIAN_FRONTEND=noninteractive",
+ "{package_manager} update",
+ "{package_manager} install --no-install-recommends -y
{cross_pkgs}",
+ "{package_manager} autoremove -y",
+ "{package_manager} autoclean -y",
+ ])
+ elif package_format == "rpm":
+ cross_commands.extend([
+ "{package_manager} install -y {cross_pkgs}",
+ "{package_manager} clean all -y",
+ ])
+
+ cross_script = "\nRUN " + (" && \\\n
".join(cross_commands)) + "\n"
+ sys.stdout.write(cross_script.format(**varmap))
+
+ if "pip_pkgs" in varmap:
+ sys.stdout.write(textwrap.dedent("""
+ RUN pip3 install {pip_pkgs}
+ """).format(**varmap))
+
+
def _action_dockerfile(self, args):
mappings = self._projects.get_mappings()
pip_mappings = self._projects.get_pip_mappings()
@@ -947,6 +1028,7 @@ class Application:
os_version = facts["os_version"]
os_full = os_name + os_version
cross_arch = args.cross_arch
+ inherit = args.inherit
if package_format not in ["deb", "rpm"]:
raise Exception("Host {} doesn't support
Dockerfiles".format(host))
@@ -983,8 +1065,14 @@ class Application:
)
)
- varmap = self._dockerfile_build_varmap(facts, mappings, pip_mappings, projects,
cross_arch)
- self._dockerfile_format(facts, cross_arch, varmap)
+ needbase = True
+ if inherit is not None:
+ needbase = False
+ varmap = self._dockerfile_build_varmap(facts, mappings, pip_mappings, projects,
cross_arch, needbase)
+ if inherit is None:
+ self._dockerfile_base_format(facts, cross_arch, varmap)
+ else:
+ self._dockerfile_child_format(facts, cross_arch, inherit, varmap)
def run(self):
args = self._parser.parse_args()
--
2.24.1