1
0
mirror of https://github.com/projectatomic/atomic.git synced 2026-02-05 18:45:01 +01:00

Atomic/help.py: Display man-like help for an image

Images or containers can now have an associated
man-like help page to help users understand more
about the image.  Typical information included
are things like a longer description, if the image
needs to be installed, security implications, steps
to upgrade, etc.

The default behavior is for atomic to display
a file called help.1 (in man format) located in
the / of the docker object.  This default
can be overriden with the HELP LABEL.  The
HELP LABEL needs to be a fully qualified
command to work correctly.
This commit is contained in:
Brent Baude
2016-01-09 09:39:14 -06:00
parent aa9d08349b
commit 70427a5159
12 changed files with 251 additions and 6 deletions

95
Atomic/help.py Normal file
View File

@@ -0,0 +1,95 @@
from . import Atomic
import subprocess
from pydoc import pager
import os
from . import mount
import sys
from . import util
class AtomicHelp(Atomic):
def __init__(self):
super(AtomicHelp, self).__init__()
self.mount_location = '/run/atomic'
self.help_file_name = 'help.1'
self.docker_object = None
self.is_container = True
self.use_pager = True
self.alt_help_cmd = None
def help(self):
"""
Displays help text for a container.
:return: None
"""
self.docker_object = self.args.image
docker_id = self.get_input_id(self.docker_object)
self.inspect = self._inspect_container(docker_id)
if self.inspect is None: # docker_id is an image
self.inspect = self._inspect_image(docker_id)
self.is_container = False
else:
# The docker object is a container, need to set
# its image
self.image = self.inspect['Image']
# Check if an alternate help command is provided
labels = self._get_labels()
self.alt_help_cmd = None if len(labels) == 0 else labels.get('HELP')
if self.alt_help_cmd is not None:
self.display_alt_help()
else:
self.display_man_help(docker_id)
def display_man_help(self, docker_id):
"""
Display the help for a container or image using the default
method of displaying a man formatted page
:param docker_id: docker object to get help for
:return: None
"""
if not os.path.exists(self.mount_location):
os.mkdir(self.mount_location)
# Set the pager to less -R
enc = sys.getdefaultencoding()
if sys.stdout.isatty():
os.environ['PAGER'] = '/usr/bin/less -R'
else:
# There is no tty
self.use_pager = False
dm = mount.DockerMount(self.mount_location, mnt_mkdir=True)
mnt_path = dm.mount(docker_id)
try:
help_file = open(os.path.join(mnt_path, self.help_file_name))
except IOError:
pass
try:
help_file = open(os.path.join(mnt_path, 'rootfs', self.help_file_name))
except IOError:
dm.unmount(path=mnt_path)
raise ValueError("Unable to find help file for {}".format(self.docker_object))
cmd2 = ['groff', '-man', '-Tascii']
c2 = subprocess.Popen(cmd2, stdin=help_file, stdout=subprocess.PIPE)
result = c2.communicate()[0].decode(enc)
help_file.close()
if not self.use_pager:
util.writeOut("\n{}\n".format(result))
else:
# Call the pager
pager(result)
# Clean up
dm.unmount(path=mnt_path)
def display_alt_help(self):
"""
Displays help when the HELP LABEL override is being used.
:return: None
"""
cmd = self.gen_cmd(self.alt_help_cmd.split(" "))
self.display(cmd)
subprocess.check_call(cmd, env=self.cmd_env, shell=True)

View File

@@ -208,12 +208,18 @@ class DockerMount(Mount):
except docker.errors.APIError as ex:
raise MountError('Error creating temporary container:\n%s' % str(ex))
def _clone(self, cid):
def _clone(self, cid, image_only=False):
"""
Create a temporary image snapshot from a given cid.
Create a temporary image snapshot from a given cid and then
create temporary container from the temporary image.
Temporary image snapshots are marked with a sentinel label
so that they can be cleaned on unmount.
image_only: Create the image from the container only
Return: the id of the temporary container unless image_only=True
in which case it returns the image cloned image id.
"""
try:
iid = self.client.commit(
@@ -227,7 +233,10 @@ class DockerMount(Mount):
except docker.errors.APIError as ex:
raise MountError(str(ex))
self.tmp_image = iid
return self._create_temp_container(iid)
if image_only:
return iid
else:
return self._create_temp_container(iid)
def _is_container_running(self, cid):
cinfo = self.client.inspect_container(cid)
@@ -469,14 +478,17 @@ class DockerMount(Mount):
if self.tmp_image is not None:
self.client.remove_image(self.tmp_image, noprune=True)
def unmount(self):
def unmount(self, path=None):
"""
Unmounts and cleans-up after a previous mount().
"""
driver = self.client.info()['Driver']
driver_unmount_fn = getattr(self, "_unmount_" + driver,
self._unsupported_backend)
driver_unmount_fn()
if path is not None:
driver_unmount_fn(path=path)
else:
driver_unmount_fn()
def _get_all_cids(self):
'''

8
atomic
View File

@@ -32,6 +32,7 @@ import Atomic
from Atomic.diff import Diff
from Atomic.top import Top
from Atomic.verify import Verify
from Atomic.help import AtomicHelp
PROGNAME = "atomic"
gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
@@ -129,6 +130,13 @@ if __name__ == '__main__':
diffp.add_argument("-v", "--verbose", default=False, action='store_true',
help=_("Show verbose output, listing all RPMs"))
#atomic help
helpp = subparser.add_parser(
"help", help=_("Display help associated with the image"),
epilog="atomic help 'image'")
helpp.set_defaults(_class=AtomicHelp, func='help')
helpp.add_argument("image", help=_("Image ID or name"))
if os.path.exists("/usr/bin/rpm-ostree"):
# atomic host
hostp = subparser.add_parser("host", help=_("execute Atomic host "

View File

@@ -525,6 +525,27 @@ _atomic_host_host() {
esac
}
_atomic_help() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
;;
*)
local counter=$( __atomic_pos_first_nonflag $( __atomic_to_alternatives "$options_with_args" ) )
if [ $cword -eq $counter ]; then
__atomic_containers_and_images
return 0
fi
COMPREPLY=( $( compgen -d "$cur" ) )
return 0
;;
esac
return 0
}
_atomic_host() {
local commands=(
deploy

32
docs/atomic-help.1.md Normal file
View File

@@ -0,0 +1,32 @@
% ATOMIC(1) Atomic Man Pages
% Brent Baude
% January 2016
# NAME
atomic-help - Display help associated with a container or image
# SYNOPSIS
**atomic help**
[**-h**|**--help**]
IMAGE|CONTAINER
# DESCRIPTION
**Atomic help** displays a help file associated with a container or image.
If a container or image has a help file (in man format) embedded in itself, atomic help will display
the help file in a pager similar to man. The default location for a help file is /image_help.1 but
the location of the help can be overridden with the HELP LABEL. If you choose to override the default
location, ensure the path provided is a fully-qualified path that includes the help file itself.
The help file can be written using the middleman markup and the converted using the go-md2man utility
as follows:
```
go-md2man -in image_help.1.md -out image_help.1
```
You can also use any of the many options to create the help file including using native man tagging.
# OPTIONS
**-h** **--help**
Print usage statement
# HISTORY
January 2016, Originally written by Brent Baude (bbaude at redhat dot com)

View File

@@ -20,6 +20,9 @@ Atomic Management Tool
**atomic-diff(1)**
show the differences between two images|containers' RPMs
**atomic-help(1)**
show help associated with a container or image
**atomic-host(1)**
execute Atomic commands

11
test.sh
View File

@@ -60,7 +60,6 @@ make_docker_images () {
chksum=$(_checksum ${df})
IFS=$'.' read -a split <<< "${BASE_NAME}"
iname="atomic-test-${split[1]}"
# If there is a matching Dockerfile.X.d, then include its contents
# in the checksum data.
chksum="${chksum}$(_checksum ${df}.d)"
@@ -82,6 +81,16 @@ make_docker_images () {
cp ${df} ${df_cp}
printf "\nLABEL \"Checksum\"=\"${chksum}" >> ${df_cp}
# Copy help.1 into atomic-test-1
if [[ ${iname} = "atomic-test-1" ]]; then
cp ./tests/test-images/help.1 ${WORK_DIR}
fi
# Copy help.sh into atomic-test-3
if [[ ${iname} = "atomic-test-3" ]]; then
cp ./tests/test-images/help.sh ${WORK_DIR}
fi
# Remove the old image... Though there may not be one.
set +e
${DOCKER} rmi ${iname} &>> ${LOG}

32
tests/integration/test_help.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash -x
set -euo pipefail
IFS=$'\n\t'
# Test scripts run with PWD=tests/..
# The test harness exports some variables into the environment during
# testing: PYTHONPATH (python module import path
# WORK_DIR (a directory that is safe to modify)
# DOCKER (the docker executable location)
# ATOMIC (an invocation of 'atomic' which measures code coverage)
# SECRET (a generated sha256 hash inserted into test containers)
# In addition, the test harness creates some images for use in testing.
# See tests/test-images/
OUTPUT=$(/bin/true)
# Test standard help in man format
${ATOMIC} help --no_pager atomic-test-1 1>/dev/null
# Test override label
${ATOMIC} help atomic-test-3 1>/dev/null
rc=0
${ATOMIC} help centos:latest 1>/dev/null || rc=$?
if [[ ${rc} != 1 ]]; then
# Test failed
echo "This test should result in a return code of 1"
exit 1
fi

View File

@@ -7,3 +7,5 @@ LABEL "Name"="atomic-test-1"
LABEL RUN "/usr/bin/docker run -t --user \${SUDO_UID}:\${SUDO_GID} \${OPT1} -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the run label."
LABEL INSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the install label."
COPY help.1 /

View File

@@ -0,0 +1,12 @@
FROM centos
MAINTAINER "Sally O'Malley <somalley at redhat dot com>
ENV container docker
LABEL "Name"="atomic-test-3"
LABEL RUN "/usr/bin/docker run -t --user \${SUDO_UID}:\${SUDO_GID} \${OPT1} -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the run label."
LABEL INSTALL "/usr/bin/docker \${OPT1} run -v /etc/\${NAME}:/etc -v /var/log/\${NAME}:/var/log -v /var/lib/\${NAME}:/var/lib \$OPT2 --name \${NAME} \${IMAGE} \$OPT3 echo I am the install label."
LABEL HELP "docker run --rm IMAGE /usr/bin/bash /help.sh"
COPY help.sh /

18
tests/test-images/help.1 Normal file
View File

@@ -0,0 +1,18 @@
.TH "ATOMIC" "1" " Atomic Man Pages" "Brent Baude" "December 2015" ""
.SH DESCRIPTION
.PP
Simple \ftest\fP case
.SH USAGE
.PP
test
.PP
.RS
.SH HISTORY
.PP
December 2015, Originally written by Brent Baude (bbaude at redhat dot com)

View File

@@ -0,0 +1 @@
echo "Testing help"