Skip to content

Commit

Permalink
Automatically detect the OS distro of the supplied Docker image
Browse files Browse the repository at this point in the history
Signed-off-by: jkr0103 <jitender.kumar@intel.com>
  • Loading branch information
jkr0103 authored and dimakuv committed Dec 7, 2023
1 parent c498783 commit 03321cf
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 14 deletions.
18 changes: 11 additions & 7 deletions Documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ follows three main stages and produces an image named ``gsc-<image-name>``.

#. **Building Gramine.** The first stage builds Gramine from sources based on
the provided configuration (see :file:`config.yaml`) which includes the
distribution (e.g., Ubuntu 20.04), Gramine repository, and the Intel SGX
driver details. This stage can be skipped if :command:`gsc build` uses a
distribution, Gramine repository, and the Intel SGX driver details.
This stage can be skipped if :command:`gsc build` uses a
pre-built Gramine Docker image.

#. **Graminizing the application image.** The second stage copies the important
Expand Down Expand Up @@ -309,11 +309,15 @@ in :file:`config.yaml.template`.
.. describe:: Distro

Defines Linux distribution to be used to build Gramine in. This distro should
match the distro underlying the application's Docker image; otherwise the
results may be unpredictable. Currently supported distros are Ubuntu 20.04,
Ubuntu 21.04, Ubuntu 22.04, Ubuntu 23.04, Debian 10, Debian 11, Debian 12,
CentOS 8, Red Hat Universal Base Image (UBI) 8 and Red Hat Universal Base
Image (UBI) 8 minimal. Default value is ``ubuntu:20.04``.
match the distro of the supplied Docker image; otherwise the results may be
unpredictable. Currently supported distros are Ubuntu 20.04, Ubuntu 21.04,
Ubuntu 22.04, Ubuntu 23.04, Debian 10, Debian 11, Debian 12, CentOS 8,
Red Hat Universal Base Image (UBI) 8 and Red Hat Universal Base Image (UBI)
8 minimal.

Default value is ``auto`` which means GSC automatically detects the distro
of the supplied Docker image. Users also have the option to provide one of
the supported distros mentioned above.

.. warning::
Please register and subscribe your host RHEL system to the Red Hat
Expand Down
8 changes: 6 additions & 2 deletions config.yaml.template
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Specify the OS distro that is used to build Gramine, i.e., the distro from where the Gramine build
# gets all tools and dependencies from. This distro should match the distro underlying the
# application's Docker image; otherwise the results may be unpredictable.
# application's Docker image; otherwise the results may be unpredictable (if you specify `"auto"`,
# which is recommended, you don't need to worry about the mismatch).
#
# Currently supported distros are:
# - ubuntu:20.04, ubuntu:21.04, ubuntu:22.04, ubuntu:23.04
# - debian:10, debian:11, debian:12
# - centos:8
# - redhat/ubi8:8.8
# - redhat/ubi8-minimal:8.8
Distro: "ubuntu:20.04"

# If Distro is set to "auto", GSC detects the distro automatically by examining the supplied
# Docker image. Alternatively, Distro can be set to one of the supported distros mentioned above.
Distro: "auto"

# If the image has a specific registry, define it here.
# Empty by default; example value: "registry.access.redhat.com/ubi8".
Expand Down
66 changes: 61 additions & 5 deletions gsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# Dmitrii Kuvaiskii <dmitrii.kuvaiskii@intel.com>

import argparse
import json
import hashlib
import os
import pathlib
Expand All @@ -23,6 +22,12 @@
import tomli_w # pylint: disable=import-error
import yaml # pylint: disable=import-error

class DistroRetrievalError(Exception):
def __init__(self, *args):
super().__init__(('Could not automatically detect the OS distro of the supplied Docker '
'image. Please specify OS distro manually in the configuration file.'),
*args)

def test_trueish(value):
if not value:
return False
Expand Down Expand Up @@ -227,6 +232,42 @@ def handle_redhat_repo_configs(distro, tmp_build_path):
# software updates and support from Red Hat.
shutil.copytree(sslclientkey_dir, tmp_build_path / 'pki/entitlement')

def get_image_distro(docker_socket, image_name):
out = docker_socket.containers.run(image_name, entrypoint='cat /etc/os-release', remove=True)
out = out.decode('UTF-8')

os_release = dict(shlex.split(line)[0].split('=') for line in out.splitlines() if line.strip())

# Some OS distros (e.g. Alpine) have very precise versions (e.g. 3.17.3), and to support these
# OS distros, we need to truncate at the 2nd dot.
try:
version_id = '.'.join(os_release['VERSION_ID'].split(".", 2)[:2])
distro = os_release['ID'] + ':' + version_id
except KeyError:
raise DistroRetrievalError

# RedHat specific logic to distinguish between UBI8 and UBI8-minimal
if (os_release['ID'] == 'rhel'):
try:
docker_socket.containers.run(image_name, entrypoint='ls /usr/bin/microdnf', remove=True)
distro = 'redhat/ubi8-minimal:' + version_id
except docker.errors.ContainerError:
distro = 'redhat/ubi8:' + version_id

return distro

def fetch_and_validate_distro_support(docker_socket, image_name, env):
distro = env.globals['Distro']
if distro == 'auto':
distro = get_image_distro(docker_socket, image_name)
env.globals['Distro'] = distro

distro = distro.split(':')[0]
if not os.path.exists(f'templates/{distro}'):
raise FileNotFoundError(f'`{distro}` distro is not supported by GSC.')

return distro

# Command 1: Build unsigned graminized Docker image from original app Docker image.
def gsc_build(args):
original_image_name = args.image # input original-app image name
Expand Down Expand Up @@ -266,9 +307,12 @@ def gsc_build(args):

os.makedirs(tmp_build_path, exist_ok=True)

distro = env.globals['Distro']
try:
distro = fetch_and_validate_distro_support(docker_socket, original_image_name, env)
except Exception as e:
print(e, file=sys.stderr)
sys.exit(1)

distro, _ = distro.split(':')
env.globals.update({'compile_template': f'{distro}/Dockerfile.compile.template'})
env.loader = jinja2.FileSystemLoader('templates/')

Expand Down Expand Up @@ -380,8 +424,16 @@ def gsc_build_gramine(args):
os.makedirs(tmp_build_path, exist_ok=True)

distro = env.globals['Distro']
if distro == 'auto':
print('`gsc build-gramine` does not allow `Distro` set to `auto` in the configuration '
'file.')
sys.exit(1)

distro, _ = distro.split(':')
if not os.path.exists(f'templates/{distro}'):
print(f'{distro} distro is not supported by GSC.')
sys.exit(1)

env.loader = jinja2.FileSystemLoader('templates/')

# generate Dockerfile.compile from Jinja-style templates/<distro>/Dockerfile.compile.template
Expand Down Expand Up @@ -436,9 +488,13 @@ def gsc_sign_image(args):
extract_user_from_image_config(unsigned_image.attrs['Config'], env)
env.globals['args'] = extract_define_args(args)
env.tests['trueish'] = test_trueish
distro = env.globals['Distro']

distro, _ = distro.split(':')
try:
distro = fetch_and_validate_distro_support(docker_socket, args.image, env)
except Exception as e:
print(e, file=sys.stderr)
sys.exit(1)

env.loader = jinja2.FileSystemLoader('templates/')
sign_template = env.get_template(f'{distro}/Dockerfile.sign.template')

Expand Down

0 comments on commit 03321cf

Please sign in to comment.