From d43f847877b488f711ee92836b3ae2fce933c5ec Mon Sep 17 00:00:00 2001
From: Dave Wapstra <dwapstra@cisco.com>
Date: Sat, 25 Mar 2023 05:48:48 +1300
Subject: [PATCH 1/2] Allow user to specify Linux OS packages

---
 src/pyatsimagebuilder/Dockerfile.template | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/pyatsimagebuilder/Dockerfile.template b/src/pyatsimagebuilder/Dockerfile.template
index f8bdcbb..1fa25b6 100644
--- a/src/pyatsimagebuilder/Dockerfile.template
+++ b/src/pyatsimagebuilder/Dockerfile.template
@@ -13,8 +13,13 @@ RUN apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false upd
     && chmod +x /bin/tini \
     && pip3 install --upgrade --no-cache-dir setuptools pip virtualenv \
     && virtualenv ${WORKSPACE} \
-    && ${WORKSPACE}/bin/pip install --no-cache-dir psutil \
-    && apt-get remove -y curl build-essential \
+    && ${WORKSPACE}/bin/pip install --no-cache-dir psutil
+
+{% if image.linux_packages -%}
+RUN apt-get install -y --no-install-recommends {% for item in image.linux_packages -%}{{ item }}{% endfor %}
+{% endif %}
+
+RUN apt-get remove -y curl build-essential \
     && apt-get autoremove -y \
     && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
     && apt-get clean \

From 0e301fcc4f78262da98464c5f87b1853447a1351 Mon Sep 17 00:00:00 2001
From: Dave Wapstra <dwapstra@cisco.com>
Date: Wed, 19 Apr 2023 16:24:29 -0700
Subject: [PATCH 2/2] Update builder to support user specified linux packages

---
 src/pyatsimagebuilder/Dockerfile.template |  2 ++
 src/pyatsimagebuilder/builder.py          | 11 ++++++++---
 src/pyatsimagebuilder/image.py            | 13 +++++++++++--
 src/pyatsimagebuilder/schema.py           |  6 ++++++
 4 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/src/pyatsimagebuilder/Dockerfile.template b/src/pyatsimagebuilder/Dockerfile.template
index 1fa25b6..4423e04 100644
--- a/src/pyatsimagebuilder/Dockerfile.template
+++ b/src/pyatsimagebuilder/Dockerfile.template
@@ -16,7 +16,9 @@ RUN apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false upd
     && ${WORKSPACE}/bin/pip install --no-cache-dir psutil
 
 {% if image.linux_packages -%}
+# Linux packages
 RUN apt-get install -y --no-install-recommends {% for item in image.linux_packages -%}{{ item }}{% endfor %}
+
 {% endif %}
 
 RUN apt-get remove -y curl build-essential \
diff --git a/src/pyatsimagebuilder/builder.py b/src/pyatsimagebuilder/builder.py
index 9b44fde..0a439df 100644
--- a/src/pyatsimagebuilder/builder.py
+++ b/src/pyatsimagebuilder/builder.py
@@ -27,6 +27,8 @@
 IMAGE_BUILD_SUCCESSUL = \
     re.compile(r' *Successfully built (?P<image_id>[a-z0-9]{12}) *$')
 
+logger = logging.getLogger(__name__)
+
 
 class ImageBuilder(object):
     def __init__(self, config, logger=logging.getLogger(__name__)):
@@ -72,19 +74,22 @@ def run(self, keep_context=False, tag=None, no_cache=True, dry_run=False):
         self.context = Context(keep=keep_context, logger=self._logger)
 
         with self.context:
+            logger.debug(f'builder config {self.config}')
 
             # create our installation directory
             self.context.mkdir(INSTALLATION)
             self.context.mkdir(INSTALLATION / REQUIREMENTS)
 
-            self._populate_context()
-
             # Tag for docker image   argument (cli) > config (yaml) > None
             self.image.tag = tag or self.config.get('tag', None)
 
             # Get Arch for image
             self.image.platform = self.config.get('platform', None)
 
+            self.image.linux_packages = self.config.get('linux_packages', [])
+
+            self._populate_context()
+
             # Start docker build
             if not dry_run:
                 self._logger.info('Building image')
@@ -409,7 +414,7 @@ def _process_pip_config(self, config):
     def _build_image(self, no_cache=False):
 
         # copy entrypoint to the context
-        self._logger.info('Copying entrypoint to context')
+        self._logger.debug('Copying entrypoint to context')
         self.context.copy(HERE / 'docker-entrypoint.sh',
                           INSTALLATION / 'entrypoint.sh')
 
diff --git a/src/pyatsimagebuilder/image.py b/src/pyatsimagebuilder/image.py
index 2c25522..3a52bb9 100644
--- a/src/pyatsimagebuilder/image.py
+++ b/src/pyatsimagebuilder/image.py
@@ -1,8 +1,11 @@
 import os
 import docker
+import logging
 
 from jinja2 import Environment, FileSystemLoader
 
+logger = logging.getLogger(__name__)
+
 JINJA2_ENV = Environment(loader=FileSystemLoader(os.path.dirname(__file__)),
                          trim_blocks=True,
                          lstrip_blocks=True)
@@ -10,6 +13,7 @@
 DEFAULT_BASE_IMAGE_LABEL = '3.7.9-slim'
 DEFAULT_TINI_VERSION = '0.18.0'
 DEFAULT_WORKSPACE_NAME = 'pyats'
+DEFAULT_LINUX_PACKAGES = []
 
 DOCKERIMAGE_TEMPLATE = 'Dockerfile.template'
 
@@ -23,7 +27,8 @@ def __init__(self,
                  base_image=DEFAULT_BASE_IMAGE,
                  base_image_label=DEFAULT_BASE_IMAGE_LABEL,
                  tini_version=DEFAULT_TINI_VERSION,
-                 workspace_name=DEFAULT_WORKSPACE_NAME):
+                 workspace_name=DEFAULT_WORKSPACE_NAME,
+                 linux_packages=DEFAULT_LINUX_PACKAGES):
 
         self._template = JINJA2_ENV.get_template(DOCKERIMAGE_TEMPLATE)
 
@@ -40,13 +45,17 @@ def __init__(self,
         # environment variables
         self.env = env or {}
 
+        self.linux_packages = linux_packages
+
         # commands to run before/after pip installation
         self.pre_pip_cmds = pre_pip_cmds
         self.post_pip_cmds = post_pip_cmds
 
 
     def manifest(self):
-        return self._template.render(image=self)
+        output =  self._template.render(image=self)
+        logger.debug(f'Dockerfile:\n{output}')
+        return output
 
     def push(self, remote_tag=None, credentials=None):
         """
diff --git a/src/pyatsimagebuilder/schema.py b/src/pyatsimagebuilder/schema.py
index c7d4571..605a014 100644
--- a/src/pyatsimagebuilder/schema.py
+++ b/src/pyatsimagebuilder/schema.py
@@ -65,6 +65,12 @@
                 'type': 'string'
             }
         },
+        'linux_packages': {
+            'type': 'array',
+            'items': {
+                'type': 'string'
+            }
+        },
         # pip-config takes either a dict to give to configparser, or an already
         # formatted config string
         'pip-config': {