Skip to content

Commit

Permalink
Change -i option to use OCIImage
Browse files Browse the repository at this point in the history
This is work towards #948

This commit makes changes to hook up the CLI option for container
image to use the OCIImage class rather than the DockerImage class.
The argument for the -i option is now --image.

- If the raw_image option is given, we assume that this is the
  output of "docker save" rather than some other tool.
- In the load_full_image function, we provide an "image_type"
  string which should either be "oci" or "docker", "oci" being the
  default. Based on this string, we either return an instance of
  OCIImage or DockerImage.
- For the Dockerfile or Lock features, we explicitly create
  DockerImage objects.

Signed-off-by: Nisha K <nishak@vmware.com>
  • Loading branch information
Nisha K committed Dec 15, 2021
1 parent 1644456 commit 0891287
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 47 deletions.
20 changes: 9 additions & 11 deletions tern/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def check_image_input(options):
logger.critical(errors.incorrect_raw_option)
sys.exit(1)
# Check if the image string has the right format
if options.docker_image:
if not check_image_string(options.docker_image):
if options.image:
if not check_image_string(options.image):
logger.critical(errors.incorrect_image_string_format)
sys.exit(1)

Expand Down Expand Up @@ -108,9 +108,9 @@ def do_main(args):
drun.execute_dockerfile(args)
else:
logger.critical("Currently --layer/-y can only be used with"
" --docker-image/-i")
" --image/-i")
sys.exit(1)
elif args.docker_image or args.raw_image:
elif args.image or args.raw_image:
check_image_input(args)
# If the checks are OK, execute for docker image
crun.execute_image(args)
Expand Down Expand Up @@ -167,15 +167,13 @@ def main():
parser_report.add_argument('-d', '--dockerfile', type=check_file_existence,
help="Dockerfile used to build the Docker"
" image")
parser_report.add_argument('-i', '--docker-image',
help="Docker image that exists locally -"
" image:tag"
" The option can be used to pull docker"
" images by digest as well -"
" <repo>@<digest-type>:<digest>")
parser_report.add_argument('-i', '--image',
help="A container image referred either by "
" repo:tag or repo@digest-type:digest")
parser_report.add_argument('-w', '--raw-image', metavar='FILE',
help="Raw container image that exists locally "
"in the form of a tar archive.")
"in the form of a tar archive. Only the output"
"of 'docker save' is supported")
parser_report.add_argument('-y', '--layer', metavar='LAYER_NUMBER',
const=1, action='store',
dest='load_until_layer',
Expand Down
22 changes: 14 additions & 8 deletions tern/analyze/default/container/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from tern.classes.notice import Notice
from tern.classes.docker_image import DockerImage
from tern.classes.oci_image import OCIImage
from tern.utils import constants
from tern.analyze import passthrough
from tern.analyze.default.container import single_layer
Expand All @@ -23,24 +24,29 @@
logger = logging.getLogger(constants.logger_name)


def load_full_image(image_tag_string, load_until_layer=0):
'''Create image object from image name and tag and return the object.
Loads only as many layers as needed.'''
test_image = DockerImage(image_tag_string)
def load_full_image(image_tag_string, image_type='oci', load_until_layer=0):
"""Create image object from image name and tag and return the object.
The kind of image object is created based on the image_type.
image_type = oci OR docker
Loads only as many layers as needed."""
if image_type == 'oci':
image = OCIImage(image_tag_string)
elif image_type == 'docker':
image = DockerImage(image_tag_string)
failure_origin = formats.image_load_failure.format(
testimage=test_image.repotag)
testimage=image.repotag)
try:
test_image.load_image(load_until_layer)
image.load_image(load_until_layer)
except (NameError,
subprocess.CalledProcessError,
IOError,
docker.errors.APIError,
ValueError,
EOFError) as error:
logger.warning('Error in loading image: %s', str(error))
test_image.origins.add_notice_to_origins(
image.origins.add_notice_to_origins(
failure_origin, Notice(str(error), 'error'))
return test_image
return image


def default_analyze(image_obj, options):
Expand Down
34 changes: 13 additions & 21 deletions tern/analyze/default/container/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from tern.report import report
from tern.report import formats
from tern import prep
from tern.load import docker_api
from tern.load import skopeo
from tern.analyze import common
from tern.analyze.default.container import image as cimage

Expand All @@ -27,28 +27,20 @@ def extract_image(args):
"""The image can either be downloaded from a container registry or provided
as an image tarball. Extract the image into a working directory accordingly
Return an image name and tag and an image digest if it exists"""
if args.docker_image:
# extract the docker image
image_attrs = docker_api.dump_docker_image(args.docker_image)
if image_attrs:
# repo name and digest is preferred, but if that doesn't exist
# the repo name and tag will do. If neither exist use repo Id.
if image_attrs['Id']:
image_string = image_attrs['Id']
if image_attrs['RepoTags']:
image_string = image_attrs['RepoTags'][0]
if image_attrs['RepoDigests']:
image_string = image_attrs['RepoDigests'][0]
return image_string
logger.critical("Cannot extract Docker image")
if args.image:
# download the image
result = skopeo.pull_image(args.image)
if result:
return 'oci', args.image
logger.critical("Cannot download Container image: \"%s\"", args.image)
if args.raw_image:
# for now we assume that the raw image tarball is always
# the product of "docker save", hence it will be in
# the docker style layout
if rootfs.extract_tarfile(args.raw_image, rootfs.get_working_dir()):
return args.raw_image
logger.critical("Cannot extract raw image")
return None
return 'docker', args.raw_image
logger.critical("Cannot extract raw Docker image")
return None, None


def setup(image_obj):
Expand All @@ -72,11 +64,11 @@ def teardown(image_obj):
def execute_image(args):
"""Execution path for container images"""
logger.debug('Starting analysis...')
image_string = extract_image(args)
image_type, image_string = extract_image(args)
# If the image has been extracted, load the metadata
if image_string:
if image_type and image_string:
full_image = cimage.load_full_image(
image_string, args.load_until_layer)
image_string, image_type, args.load_until_layer)
# check if the image was loaded successfully
if full_image.origins.is_empty():
# Add an image origin here
Expand Down
10 changes: 5 additions & 5 deletions tern/analyze/default/debug/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
from tern.analyze.default.container import multi_layer


def check_image_obj(image_string):
def check_image_obj(image_string, image_type):
"""Return the image object and if it was loaded successfully"""
if image_string:
full_image = cimage.load_full_image(image_string)
full_image = cimage.load_full_image(image_string, image_type)
if full_image.origins.is_empty():
return full_image, True
print("Something went wrong in loading the image")
Expand Down Expand Up @@ -190,9 +190,9 @@ def recover():

def execute_debug(args):
"""Debug container images"""
if args.docker_image:
image_string = run.extract_image(args)
full_image, success = check_image_obj(image_string)
if args.image:
image_type, image_string = run.extract_image(args)
full_image, success = check_image_obj(image_string, image_type)
if success:
if args.keys:
# we have an image to mount and some scripts to invoke
Expand Down
4 changes: 2 additions & 2 deletions tern/analyze/default/dockerfile/run.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019-2020 VMware, Inc. All Rights Reserved.
# Copyright (c) 2019-2021 VMware, Inc. All Rights Reserved.
# SPDX-License-Identifier: BSD-2-Clause

"""
Expand Down Expand Up @@ -123,7 +123,7 @@ def full_image_analysis(dfile, options):
"""This subroutine is executed when a Dockerfile is successfully built"""
image_list = []
# attempt to load the built image metadata
full_image = cimage.load_full_image(dfile)
full_image = cimage.load_full_image(dfile, image_type='docker')
if full_image.origins.is_empty():
# Add an image origin here
full_image.origins.add_notice_origin(
Expand Down

0 comments on commit 0891287

Please sign in to comment.