From bce4da585bceaa95c0158aaa1efcbeb06e84569d Mon Sep 17 00:00:00 2001 From: yuqi-zhang Date: Wed, 9 Nov 2016 17:04:14 +0000 Subject: [PATCH] syscontainers: environment variable detection Add functionality to detect environment variables in template files. The variables that can be set by the user through --set is displayed on 'atomic images info'. The variables with overridable default values will either have their default value displayed, or shown as {SET_BY_OS} if atomic sets them. Variables without default value must be set by the user and is listed separately. Currently install --system already checks for variables that have no value after install, and will error out. Signed-off-by: Yu Qi Zhang Closes: #752 Approved by: giuseppe --- Atomic/info.py | 20 +++++++++++++ Atomic/syscontainers.py | 63 +++++++++++++++++++++++++++++++++++++++++ docs/atomic-images.1.md | 2 ++ 3 files changed, 85 insertions(+) diff --git a/Atomic/info.py b/Atomic/info.py index b879ca9..099842e 100644 --- a/Atomic/info.py +++ b/Atomic/info.py @@ -120,6 +120,16 @@ class Info(Atomic): labels = manifest["Labels"] for label in labels: buf += ('\n{0}: {1}'.format(label, labels[label])) + + template_variables, template_variables_to_set = self.syscontainers.get_template_variables(self.image) + buf += ("\n\nEnvironment variables with default value, but overridable with --set:") + for variable in template_variables.keys(): + buf += ('\n{}: {}'.format(variable, template_variables.get(variable))) + + if template_variables_to_set: + buf += ("\n\nEnvironment variables that has no default value, and must be set with --set:") + for variable in template_variables_to_set.keys(): + buf += ('\n{}: {}'.format(variable, template_variables_to_set.get(variable))) return buf # Check if the input is an image id associated with more than one # repotag. If so, error out. @@ -141,6 +151,16 @@ class Info(Atomic): labels = manifest["Labels"] for label in labels: buf += ('\n{0}: {1}'.format(label, labels[label])) + + template_variables, template_variables_to_set = self.syscontainers.get_template_variables(self.image) + buf += ("\n\nEnvironment variables with default value, but overridable with --set:") + for variable in template_variables.keys(): + buf += ('\n{}: {}'.format(variable, template_variables.get(variable))) + + if template_variables_to_set: + buf += ("\n\nEnvironment variables that has no default value, and must be set with --set:") + for variable in template_variables_to_set.keys(): + buf += ('\n{}: {}'.format(variable, template_variables_to_set.get(variable))) return buf else: self._no_such_image() diff --git a/Atomic/syscontainers.py b/Atomic/syscontainers.py index a4f59fc..154cbce 100644 --- a/Atomic/syscontainers.py +++ b/Atomic/syscontainers.py @@ -52,6 +52,9 @@ WorkingDirectory=$DESTDIR [Install] WantedBy=multi-user.target """ +TEMPLATE_FORCED_VARIABLES = ["DESTDIR", "NAME", "EXEC_START", "EXEC_STOP", + "HOST_UID", "HOST_GID"] +TEMPLATE_OVERRIDABLE_VARIABLES = ["RUN_DIRECTORY", "STATE_DIRECTORY"] class SystemContainers(object): @@ -624,6 +627,66 @@ class SystemContainers(object): ret.append(container) return ret + def get_template_variables(self, image): + repo = self._get_ostree_repo() + _, commit_rev = self._resolve_image(repo, image) + if not commit_rev: + return + + manifest = self._image_manifest(repo, commit_rev) + layers = SystemContainers.get_layers_from_manifest(json.loads(manifest)) + templates = {} + manifest_template = None + for i in layers: + layer = i.replace("sha256:", "") + commit = repo.read_commit(repo.resolve_rev("%s%s" % (OSTREE_OCIIMAGE_PREFIX, layer), True)[1])[1] + exports = commit.get_root().get_child("exports") + if not exports.query_exists(): + continue + + children = exports.enumerate_children("", Gio.FileQueryInfoFlags.NONE, None) + for child in reversed(list(children)): + name = child.get_name() + if name == "manifest.json": + manifest_template = exports.get_child(name).read() + + if name.endswith(".template"): + if name.startswith(".wh"): + name = name[4:] + templates.pop(name, None) + else: + templates[name] = exports.get_child(name).read() + + variables = {} + for v in templates.values(): + fd = v.get_fd() + with os.fdopen(fd) as f: + data = f.read() + template = Template(data) + for variable in ["".join(x) for x in template.pattern.findall(data)]: # pylint: disable=no-member + if variable not in TEMPLATE_FORCED_VARIABLES: + variables[variable] = variable + + variables_with_default = {} + if manifest_template: + fd = manifest_template.get_fd() + with os.fdopen(fd) as f: + data = json.loads(f.read()) + for variable in data['defaultValues']: + variables_with_default[variable] = data['defaultValues'][variable] + + # Also include variables that are set by the OS + # but can be overriden by --set + for variable in TEMPLATE_OVERRIDABLE_VARIABLES: + variables_with_default[variable] = "{SET_BY_OS}" + + variables_to_set = {} + for variable in variables: + if variable not in variables_with_default: + variables_to_set[variable] = "{DEF_VALUE}" + + return variables_with_default, variables_to_set + def delete_image(self, image): repo = self._get_ostree_repo() if not repo: diff --git a/docs/atomic-images.1.md b/docs/atomic-images.1.md index bcb4c07..de2ad39 100644 --- a/docs/atomic-images.1.md +++ b/docs/atomic-images.1.md @@ -26,6 +26,8 @@ atomic images allows the user to view and operate on container images in a docke Displays the LABEL fields within an image. By default, it will check first for a local image and then all configured registries. + For a system container image, this will also display the environment variables a user can set. + **help** Displays a help file associated with a container or image.