From ba95d9902b014a2699856678e44f1d3af27755a0 Mon Sep 17 00:00:00 2001 From: WangJL Date: Fri, 24 Jul 2020 00:45:11 +0800 Subject: [PATCH] Build multistage dockerfile by stage This commit adds two functions to implement building a multistage dockerfile to get its images by the stages. 1. get_multistage_image_dockerfiles() at tern\analyze\docker\dockerfile.py: This function splits a multistage dockerfile into dockerfiles by its stage for build. 2. build_multistage() at tern\analyze\docker\run.py: This functions builds a multistage dockerfile to get the images for analyze. So far we can build the dockerfile and the further jobs like analyze and clean up are implement by other commits. Works towards #612. Signed-off-by: WangJL --- tern/analyze/docker/dockerfile.py | 39 +++++++++++++++++++++++++++++++ tern/analyze/docker/run.py | 23 ++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/tern/analyze/docker/dockerfile.py b/tern/analyze/docker/dockerfile.py index dc46c9bc..f90530b3 100644 --- a/tern/analyze/docker/dockerfile.py +++ b/tern/analyze/docker/dockerfile.py @@ -11,6 +11,7 @@ import re import logging import copy +import os from tern.utils import general from tern.utils import constants @@ -363,3 +364,41 @@ def split_multistage_dockerfile(dfobj): # dfobj_temp not empty, append to the list dfobj_list.append(copy.copy(dfobj_temp)) return dfobj_list + + +def get_multistage_image_dockerfiles(dfobj_multi): + """Given a multistage dockerfile object, return a list of structures + for building image.""" + file_path_list = [] + structure = [] + idx = 0 + for command_dict in dfobj_multi.structure: + if command_dict['instruction'] == 'FROM': + if structure: + file_path = dfobj_multi.filepath + '_%d' % (idx) + # we make a new dir for the dockerfile of each stage. + if not os.path.isdir(file_path): + os.mkdir(file_path) + file_path += '/Dockerfile' + idx += 1 + write_dockerfile_by_structure(file_path, structure) + file_path_list.append(file_path) + structure.append(command_dict) + # append last file_path + if structure: + file_path = dfobj_multi.filepath + '_%d' % (idx) + # we make a new dir for the dockerfile of each stage. + if not os.path.isdir(file_path): + os.mkdir(file_path) + file_path += '/Dockerfile' + write_dockerfile_by_structure(file_path, structure) + file_path_list.append(file_path) + return file_path_list + + +def write_dockerfile_by_structure(file_name, structure): + """Given a dockerfile name and its structure, write the content into the + dockerfile.""" + with open(file_name, 'w') as f: + for st in structure: + f.write(st['content']) diff --git a/tern/analyze/docker/run.py b/tern/analyze/docker/run.py index 95093e04..459ad525 100644 --- a/tern/analyze/docker/run.py +++ b/tern/analyze/docker/run.py @@ -180,3 +180,26 @@ def execute_dockerfile(args): # noqa C901,R0912 if args.name == 'report': if not args.keep_wd: report.clean_working_dir() + + +def build_multistage(dockerfile_path): + """Given a multistage dockerfile, first get the dockerfiles for each + stage and then build the dockerfiles. + TO DO: + 1.Add image analyze + 2. Clean the temp dockerfiles""" + dfobj_multi = dockerfile.get_dockerfile_obj(dockerfile_path) + file_path_list = dockerfile.get_multistage_image_dockerfiles(dfobj_multi) + print(file_path_list) + for dfile in file_path_list: + dfobj = dockerfile.get_dockerfile_obj(dfile) + # expand potential ARG values so base image tag is correct + dockerfile.expand_arg(dfobj) + dockerfile.expand_vars(dfobj) + report.setup(dfobj=dfobj) + # attempt to build the image + logger.debug('Building Docker image...') + # placeholder to check if we can analyze the full image + build, _ = dhelper.is_build() + print(build) + # TO DO: Add image analyze here to analyze each image.