def test_constructor_cache(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) df1 = DockerfileParser(tmpdir_path) df1.lines = ["From fedora:latest\n", "LABEL a b\n"] df2 = DockerfileParser(tmpdir_path, True) assert df2.cached_content
def test_constructor_cache(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) df1 = DockerfileParser(tmpdir_path) df1.lines = ["From fedora:latest\n", "LABEL a b\n"] df2 = DockerfileParser(tmpdir_path, True) assert df2.cached_content
def run(self): """ run the plugin """ # dict comprehension is syntax error on 2.6 yum_repos = {} for key, value in self.workflow.files.items(): if key.startswith(YUM_REPOS_DIR): yum_repos[key] = value if self.wrap_commands: wrap_yum_commands(yum_repos, self.workflow.builder.df_path) else: if not yum_repos: return # absolute path in containers -> relative path within context repos_host_cont_mapping = {} host_repos_path = os.path.join(self.workflow.builder.df_dir, RELATIVE_REPOS_PATH) self.log.info("creating directory for yum repos: %s", host_repos_path) os.mkdir(host_repos_path) for repo, repo_content in self.workflow.files.items(): repo_basename = os.path.basename(repo) repo_relative_path = os.path.join(RELATIVE_REPOS_PATH, repo_basename) repo_host_path = os.path.join(host_repos_path, repo_basename) self.log.info("writing repo to '%s'", repo_host_path) with open(repo_host_path, "wb") as fp: fp.write(repo_content.encode("utf-8")) repos_host_cont_mapping[repo] = repo_relative_path # Find out the USER inherited from the base image inspect = self.workflow.builder.inspect_base_image() inherited_user = inspect['Config'].get('User', '') df = DockerfileParser(self.workflow.builder.df_path) df.lines = add_yum_repos_to_dockerfile(repos_host_cont_mapping, df, inherited_user)
def run(self): """ run the plugin """ dockerfile = DockerfileParser(self.workflow.builder.df_path) lines = dockerfile.lines # correct syntax is: # LABEL "key"="value" "key2"="value2" # Make sure to escape '\' and '"' characters. try: # py3 env_trans = str.maketrans({'\\': '\\\\', '"': '\\"'}) except AttributeError: # py2 env_trans = None def escape(s): if env_trans: return s.translate(env_trans) return s.replace('\\', '\\\\').replace('"', '\\"') labels = [] for key, value in self.labels.items(): try: base_image_value = self.workflow.base_image_inspect["Config"][ "Labels"][key] except KeyError: self.log.info("label %s not present in base image", repr(key)) except (AttributeError, TypeError): self.log.warning("base image was not inspected") break else: if base_image_value == value: self.log.info("label %s is already set to %s", repr(key), repr(value)) continue else: self.log.info("base image has label %s set to %s", repr(key), repr(base_image_value)) if key in self.dont_overwrite: self.log.info("denying overwrite of label %s", repr(key)) continue label = '"%s"="%s"' % (escape(key), escape(value)) self.log.info("setting label %s", label) labels.append(label) content = "" if labels: content = 'LABEL ' + " ".join(labels) # put it before last instruction lines.insert(-1, content + '\n') dockerfile.lines = lines return content
def test_get_parent_env_from_scratch(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) p_env = {"bar": "baz"} df1 = DockerfileParser(tmpdir_path, env_replace=True, parent_env=p_env) df1.lines = [ "FROM scratch\n", ] assert not df1.envs
def test_get_parent_env_from_scratch(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) p_env = {"bar": "baz"} df1 = DockerfileParser(tmpdir_path, env_replace=True, parent_env=p_env) df1.lines = [ "FROM scratch\n", ] assert not df1.envs
def run(self): """ run the plugin """ dockerfile = DockerfileParser(self.workflow.builder.df_path) lines = dockerfile.lines # correct syntax is: # LABEL "key"="value" "key2"="value2" # Make sure to escape '\' and '"' characters. try: # py3 env_trans = str.maketrans({'\\': '\\\\', '"': '\\"'}) except AttributeError: # py2 env_trans = None def escape(s): if env_trans: return s.translate(env_trans) return s.replace('\\', '\\\\').replace('"', '\\"') labels = [] for key, value in self.labels.items(): try: base_image_value = self.workflow.base_image_inspect["Config"]["Labels"][key] except KeyError: self.log.info("label %s not present in base image", repr(key)) except (AttributeError, TypeError): self.log.warning("base image was not inspected") break else: if base_image_value == value: self.log.info("label %s is already set to %s", repr(key), repr(value)) continue else: self.log.info("base image has label %s set to %s", repr(key), repr(base_image_value)) if key in self.dont_overwrite: self.log.info("denying overwrite of label %s", repr(key)) continue label = '"%s"="%s"' % (escape(key), escape(value)) self.log.info("setting label %s", label) labels.append(label) content = "" if labels: content = 'LABEL ' + " ".join(labels) # put it before last instruction lines.insert(-1, content + '\n') dockerfile.lines = lines return content
def test_multiple_repourls(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) filename1 = 'myrepo.repo' filename2 = 'repo-2.repo' repo_path1 = os.path.join(YUM_REPOS_DIR, filename1) repo_path2 = os.path.join(YUM_REPOS_DIR, filename2) workflow.files[repo_path1] = repocontent workflow.files[repo_path2] = repocontent runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, 'args': { 'wrap_commands': False } }]) runner.run() # Remove the repos/ directory. repos_dir = os.path.join(str(tmpdir), RELATIVE_REPOS_PATH) for repofile in [filename1, filename2]: os.remove(os.path.join(repos_dir, repofile)) os.rmdir(repos_dir) # Examine the Dockerfile. newdf = df.lines before_add = len(df_content.lines_before_add) before_remove = len(df_content.lines_before_remove) # Start of file should be unchanged. assert newdf[:before_add] == df_content.lines_before_add # Should see a single add line. after_add = before_add + 1 assert (newdf[before_add:after_add] == [ "ADD %s* '/etc/yum.repos.d/'\n" % RELATIVE_REPOS_PATH ]) # Lines from there up to the remove line should be unchanged. before_remove = after_add + len(df_content.lines_before_remove) assert ( newdf[after_add:before_remove] == df_content.lines_before_remove) # For the 'rm' line, they could be in either order remove = newdf[before_remove:] assert remove_lines_match(remove, df_content.remove_lines, [filename1, filename2])
def test_single_repourl(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) filename = 'test.repo' repo_path = os.path.join(YUM_REPOS_DIR, filename) workflow.files[repo_path] = repocontent runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, 'args': { 'wrap_commands': False } }]) runner.run() # Was it written correctly? repos_dir = os.path.join(str(tmpdir), RELATIVE_REPOS_PATH) repofile = os.path.join(repos_dir, filename) with open(repofile, "r") as fp: assert fp.read() == repocontent # Remove the repos/ directory. os.remove(repofile) os.rmdir(repos_dir) # Examine the Dockerfile. newdf = df.lines before_add = len(df_content.lines_before_add) before_remove = len(df_content.lines_before_remove) # Start of file should be unchanged. assert newdf[:before_add] == df_content.lines_before_add # Should see a single add line. after_add = before_add + 1 assert (newdf[before_add:after_add] == [ "ADD %s* '/etc/yum.repos.d/'\n" % RELATIVE_REPOS_PATH ]) # Lines from there up to the remove line should be unchanged. before_remove = after_add + len(df_content.lines_before_remove) assert ( newdf[after_add:before_remove] == df_content.lines_before_remove) # The 'rm' lines should match # There should be a final 'rm' remove = newdf[before_remove:] assert remove_lines_match(remove, df_content.remove_lines, [filename])
def test_get_parent_env(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) p_env = {"bar": "baz"} df1 = DockerfileParser(tmpdir_path, env_replace=True, parent_env=p_env) df1.lines = [ "FROM parent\n", "ENV foo=\"$bar\"\n", "LABEL label=\"$foo $bar\"\n" ] # Even though we inherit an ENV, this .envs count should only be for the # ENVs defined in *this* Dockerfile as we're parsing the Dockerfile and # the parent_env is only to satisfy use of inhereted ENVs. assert len(df1.envs) == 1 assert df1.envs.get('foo') == 'baz' assert len(df1.labels) == 1 assert df1.labels.get('label') == 'baz baz'
def test_single_repourl(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) filename = 'test.repo' repo_path = os.path.join(YUM_REPOS_DIR, filename) workflow.files[repo_path] = repocontent runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, 'args': {'wrap_commands': False}}]) runner.run() # Was it written correctly? repos_dir = os.path.join(str(tmpdir), RELATIVE_REPOS_PATH) repofile = os.path.join(repos_dir, filename) with open(repofile, "r") as fp: assert fp.read() == repocontent # Remove the repos/ directory. os.remove(repofile) os.rmdir(repos_dir) # Examine the Dockerfile. newdf = df.lines before_add = len(df_content.lines_before_add) before_remove = len(df_content.lines_before_remove) # Start of file should be unchanged. assert newdf[:before_add] == df_content.lines_before_add # Should see a single add line. after_add = before_add + 1 assert (newdf[before_add:after_add] == ["ADD %s* '/etc/yum.repos.d/'\n" % RELATIVE_REPOS_PATH]) # Lines from there up to the remove line should be unchanged. before_remove = after_add + len(df_content.lines_before_remove) assert (newdf[after_add:before_remove] == df_content.lines_before_remove) # The 'rm' lines should match # There should be a final 'rm' remove = newdf[before_remove:] assert remove_lines_match(remove, df_content.remove_lines, [filename])
def test_multiple_repourls(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) filename1 = 'myrepo.repo' filename2 = 'repo-2.repo' repo_path1 = os.path.join(YUM_REPOS_DIR, filename1) repo_path2 = os.path.join(YUM_REPOS_DIR, filename2) workflow.files[repo_path1] = repocontent workflow.files[repo_path2] = repocontent runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, 'args': {'wrap_commands': False}}]) runner.run() # Remove the repos/ directory. repos_dir = os.path.join(str(tmpdir), RELATIVE_REPOS_PATH) for repofile in [filename1, filename2]: os.remove(os.path.join(repos_dir, repofile)) os.rmdir(repos_dir) # Examine the Dockerfile. newdf = df.lines before_add = len(df_content.lines_before_add) before_remove = len(df_content.lines_before_remove) # Start of file should be unchanged. assert newdf[:before_add] == df_content.lines_before_add # Should see a single add line. after_add = before_add + 1 assert (newdf[before_add:after_add] == ["ADD %s* '/etc/yum.repos.d/'\n" % RELATIVE_REPOS_PATH]) # Lines from there up to the remove line should be unchanged. before_remove = after_add + len(df_content.lines_before_remove) assert (newdf[after_add:before_remove] == df_content.lines_before_remove) # For the 'rm' line, they could be in either order remove = newdf[before_remove:] assert remove_lines_match(remove, df_content.remove_lines, [filename1, filename2])
def test_no_repourls(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, }]) runner.run() assert InjectYumRepoPlugin.key is not None # Should be unchanged. assert df.lines == df_content.lines_before_add + \ df_content.lines_before_remove
def test_get_parent_env(self, tmpdir): tmpdir_path = str(tmpdir.realpath()) p_env = {"bar": "baz"} df1 = DockerfileParser(tmpdir_path, env_replace=True, parent_env=p_env) df1.lines = [ "FROM parent\n", "ENV foo=\"$bar\"\n", "LABEL label=\"$foo $bar\"\n" ] # Even though we inherit an ENV, this .envs count should only be for the # ENVs defined in *this* Dockerfile as we're parsing the Dockerfile and # the parent_env is only to satisfy use of inherited ENVs. assert len(df1.envs) == 1 assert df1.envs.get('foo') == 'baz' assert len(df1.labels) == 1 assert df1.labels.get('label') == 'baz baz'
def test_no_repourls(tmpdir): for df_content in DOCKERFILES.values(): df = DockerfileParser(str(tmpdir)) df.lines = df_content.lines_before_add + \ df_content.lines_before_remove tasker, workflow = prepare(df.dockerfile_path, df_content.inherited_user) runner = PreBuildPluginsRunner(tasker, workflow, [{ 'name': InjectYumRepoPlugin.key, }]) runner.run() assert InjectYumRepoPlugin.key is not None # Should be unchanged. assert df.lines == df_content.lines_before_add + \ df_content.lines_before_remove
def create_context_archive(context, docker_file, base_image): original_dockerfile = open(context + '/' + docker_file, 'r') dfp = DockerfileParser() dfp.lines = original_dockerfile.readlines() original_dockerfile.close() dfp.baseimage = base_image new_file = tempfile.NamedTemporaryFile() new_file.write(dfp.content.encode('utf-8')) new_file.flush() context_archive = docker.utils.tar(context, dockerfile=docker_file) t = tarfile.open(name=context_archive.name, mode='a') t.add(new_file.name, arcname=docker_file) t.close() return context_archive
def run(self): """ run the plugin """ dockerfile = DockerfileParser(self.workflow.builder.df_path) lines = dockerfile.lines # when using final dockerfile, we should use DOCKERFILE_FILENAME # otherwise we should use the copied version if self.use_final_dockerfile: content = 'ADD {0} {1}'.format(DOCKERFILE_FILENAME, self.df_path) else: content = 'ADD {0} {1}'.format(self.df_name, self.df_path) # put it before last instruction lines.insert(-1, content + '\n') dockerfile.lines = lines self.log.info("added %s", self.df_path) return content
def run(self): """ run the plugin """ try: config = self.workflow.base_image_inspect[INSPECT_CONFIG] except (AttributeError, TypeError): message = "base image was not inspected" self.log.error(message) raise RuntimeError(message) else: base_image_labels = config["Labels"] or {} dockerfile = DockerfileParser(self.workflow.builder.df_path) lines = dockerfile.lines # changing dockerfile.labels writes out modified Dockerfile - err on # the safe side and make a copy self.add_aliases(base_image_labels.copy(), dockerfile.labels.copy(), self.labels.copy()) # correct syntax is: # LABEL "key"="value" "key2"="value2" # Make sure to escape '\' and '"' characters. try: # py3 env_trans = str.maketrans({'\\': '\\\\', '"': '\\"'}) except AttributeError: # py2 env_trans = None def escape(s): if env_trans: return s.translate(env_trans) return s.replace('\\', '\\\\').replace('"', '\\"') labels = [] for key, value in self.labels.items(): try: base_image_value = base_image_labels[key] except KeyError: self.log.info("label %r not present in base image", key) else: if base_image_value == value: self.log.info("label %r is already set to %r", key, value) continue else: self.log.info("base image has label %r set to %r", key, base_image_value) if key in self.dont_overwrite: self.log.info("denying overwrite of label %r", key) continue label = '"%s"="%s"' % (escape(key), escape(value)) self.log.info("setting label %r", label) labels.append(label) content = "" if labels: content = 'LABEL ' + " ".join(labels) # put labels at the end of dockerfile (since they change metadata and do not interact # with FS, this should cause no harm) lines.append('\n' + content + '\n') dockerfile.lines = lines return content
def run(self): """ run the plugin """ try: config = self.workflow.base_image_inspect[INSPECT_CONFIG] except (AttributeError, TypeError): message = "base image was not inspected" self.log.error(message) raise RuntimeError(message) else: base_image_labels = config["Labels"] or {} dockerfile = DockerfileParser(self.workflow.builder.df_path) lines = dockerfile.lines # changing dockerfile.labels writes out modified Dockerfile - err on # the safe side and make a copy self.add_aliases(base_image_labels.copy(), dockerfile.labels.copy(), self.labels.copy()) # correct syntax is: # LABEL "key"="value" "key2"="value2" # Make sure to escape '\' and '"' characters. try: # py3 env_trans = str.maketrans({'\\': '\\\\', '"': '\\"'}) except AttributeError: # py2 env_trans = None def escape(s): if env_trans: return s.translate(env_trans) return s.replace('\\', '\\\\').replace('"', '\\"') labels = [] for key, value in self.labels.items(): try: base_image_value = base_image_labels[key] except KeyError: self.log.info("label %r not present in base image", key) else: if base_image_value == value: self.log.info("label %r is already set to %r", key, value) continue else: self.log.info("base image has label %r set to %r", key, base_image_value) if key in self.dont_overwrite: self.log.info("denying overwrite of label %r", key) continue label = '"%s"="%s"' % (escape(key), escape(value)) self.log.info("setting label %r", label) labels.append(label) content = "" if labels: content = 'LABEL ' + " ".join(labels) # put labels at the end of dockerfile (since they change metadata and do not interact # with FS, this should cause no harm) lines.append('\n' + content + '\n') dockerfile.lines = lines return content