From 536349ae0441d17a080a4f2edb436f59a50e5dde Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Thu, 14 May 2015 10:45:35 -0400 Subject: [PATCH] Move the Atomic class into python library directory. I am beginning to work on dbus bindings, this will allow the atomic cli and the atomic_dbus_server app to share the same code. Renamed atomicpulp to Atomic in the python library. --- .gitignore | 1 + {atomicpulp => Atomic}/__init__.py | 3 +- Atomic/atomic.py | 550 +++++++++++++++++++++++++++++ {atomicpulp => Atomic}/config.py | 0 {atomicpulp => Atomic}/pulp.py | 2 +- atomic | 540 +--------------------------- setup.py | 2 +- 7 files changed, 558 insertions(+), 540 deletions(-) create mode 100644 .gitignore rename {atomicpulp => Atomic}/__init__.py (98%) create mode 100644 Atomic/atomic.py rename {atomicpulp => Atomic}/config.py (100%) rename {atomicpulp => Atomic}/pulp.py (98%) mode change 100755 => 100644 atomic diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/atomicpulp/__init__.py b/Atomic/__init__.py similarity index 98% rename from atomicpulp/__init__.py rename to Atomic/__init__.py index 7213e36..f94e54b 100644 --- a/atomicpulp/__init__.py +++ b/Atomic/__init__.py @@ -1,6 +1,7 @@ import sys, os from .pulp import PulpServer from .config import PulpConfig +from .atomic import Atomic def writeOut(output, lf="\n"): sys.stdout.flush() @@ -33,7 +34,7 @@ def push_image_to_pulp(image, server_url, username, password, verify_ssl, docker pulp.create_repo(image, repo) except Exception as e: raise IOError('Failed to create Pulp repository: {0}'.format(e)) - + try: writeOut('Uploading image "{0}" to pulp server "{1}"'.format(image, server_url)) pulp.upload_docker_image(image, repo) diff --git a/Atomic/atomic.py b/Atomic/atomic.py new file mode 100644 index 0000000..ad8d214 --- /dev/null +++ b/Atomic/atomic.py @@ -0,0 +1,550 @@ +import sys, os +import argparse +import gettext +import docker +import json +import subprocess +import getpass +import requests +try: + from subprocess import DEVNULL # pylint: disable=no-name-in-module +except ImportError: + import os + DEVNULL = open(os.devnull, 'wb') + +images=[] +def findRepoTag(d, id): + global images + if len(images) == 0: + images = d.images() + for image in images: + if id == image["Id"]: + return image["RepoTags"][0] + return "" + +class Atomic(object): + INSTALL_ARGS = ["/usr/bin/docker", "run", + "-t", + "-i", + "--rm", + "--privileged", + "-v", "/:/host", + "--net=host", + "--ipc=host", + "--pid=host", + "-e", "HOST=/host", + "-e", "NAME=NAME", + "-e", "IMAGE=IMAGE", + "-v", "${CONFDIR}:/etc/NAME", + "-v", "${LOGDIR}:/var/log/NAME", + "-v", "${DATADIR}:/var/lib/NAME", + "-e", "CONFDIR=${CONFDIR}", + "-e", "LOGDIR=${LOGDIR}", + "-e", "DATADIR=${DATADIR}", + "--name", "NAME", + "IMAGE"] + + SPC_ARGS = ["/usr/bin/docker", "run", + "-t", + "-i", + "--rm", + "--privileged", + "-v", "/:/host", + "-v", "/run:/run", + "-v", "/etc/localtime:/etc/localtime", + "--net=host", + "--ipc=host", + "--pid=host", + "-e", "HOST=/host", + "-e", "NAME=NAME", + "-e", "IMAGE=IMAGE", + "IMAGE" ] + + RUN_ARGS = ["/usr/bin/docker", "create", + "-t", + "-i", + "--name", "NAME", + "IMAGE" ] + + def __init__(self): + self.d = docker.Client() + self.name = None + self.image = None + self.spc = False + self.inspect = None + self.force = False + self.images = [] + + def writeOut(self, output, lf="\n"): + sys.stdout.flush() + sys.stdout.write(output + lf) + + def get_label(self, label, image=None): + inspect = self._inspect_image(image) + cfg = inspect.get("Config", None) + if cfg: + labels = cfg.get("Labels", []) + if labels and label in labels: + return labels[label] + return "" + + def force_delete_containers(self): + if self._inspect_image(): + image = self.image + if self.image.find(":") == -1: + image += ":latest" + for c in self.d.containers(all=True): + if c["Image"] == image: + self.d.remove_container(c["Id"], force=True) + + def update(self): + if self.force: + self.force_delete_containers() + return subprocess.check_call(["/usr/bin/docker", "pull", self.image]) + + def pull(self): + prevstatus = "" + prev = "" + for line in self.d.pull(self.image, stream=True): + bar = json.loads(line) + status = bar['status'] + if prevstatus != status: + self.writeOut(status, "") + if 'id' not in bar: + continue + if status == "Downloading": + self.writeOut(bar['progress'] + " ") + elif status == "Extracting": + self.writeOut("Extracting: " + bar['id']) + elif status == "Pull complete": + pass + elif status.startswith("Pulling"): + self.writeOut("Pulling: " + bar['id']) + + prevstatus = status + self.writeOut("") + + def upload(self): + prevstatus = "" + if not self.args.username: + self.args.username = raw_input("Registry Username: ") + if not self.args.password: + self.args.password = getpass.getpass("Registry Password: ") + + if self.args.pulp: + return push_image_to_pulp(self.image, self.args.url, self.args.username, self.args.password, self.args.verify_ssl, self.d) + else: + self.d.login(self.args.username, self.args.password) + for line in self.d.push(self.image, stream=True): + bar = json.loads(line) + status = bar['status'] + if prevstatus != status: + self.writeOut(status, "") + if 'id' not in bar: + continue + if status == "Uploading": + self.writeOut(bar['progress'] + " ") + elif status == "Push complete": + pass + elif status.startswith("Pushing"): + self.writeOut("Pushing: " + bar['id']) + + prevstatus = status + + def set_args(self, args): + self.args=args + try: + self.image = args.image + except: + pass + try: + self.command = args.command + except: + self.command = None + + try: + self.spc = args.spc + except: + self.spc = False + + try: + self.name = args.name + except: + pass + + try: + self.force = args.force + except: + pass + + if not self.name and self.image is not None: + self.name = self.image.split("/")[-1].split(":")[0] + if self.spc: + self.name = self.name + "-spc" + + + def _getconfig(self, key, default=None): + assert self.inspect is not None + cfg = self.inspect.get("Config") + if cfg is None: + return default + val = cfg.get(key, default) + if val is None: + return default + return val + + def _get_cmd(self): + return self._getconfig("Cmd", [ "/bin/sh" ]) + + def _get_labels(self): + return self._getconfig("Labels", []) + + def _interactive(self): + return (self._getconfig("AttachStdin", False) and + self._getconfig("AttachStdout", False) and + self._getconfig("AttachStderr", False)) + + def _running(self): + if self._interactive(): + cmd = ["/usr/bin/docker", "exec", "-t", "-i", self.name] + if self.command: + cmd += self.command + else: + cmd += self._get_cmd() + return subprocess.check_call(cmd, stderr=DEVNULL) + else: + if self.command: + return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command, + stderr=DEVNULL) + else: + self.writeOut("Container is running") + + def _start(self): + if self._interactive(): + if self.command: + subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) + return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command) + else: + return subprocess.check_call(["/usr/bin/docker", "start", "-i", "-a", self.name], stderr=DEVNULL) + else: + if self.command: + subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) + return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command) + else: + return subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) + + def _inspect_image(self, image=None): + try: + if image: + return self.d.inspect_image(image) + return self.d.inspect_image(self.image) + except docker.errors.APIError: + pass + except requests.exceptions.ConnectionError as e: + raise IOError("Unable to communicate with docker daemon: %s\n" % str(e)) + return None + + def _inspect_container(self): + try: + return self.d.inspect_container(self.name) + except docker.errors.APIError: + pass + except requests.exceptions.ConnectionError as e: + raise IOError("Unable to communicate with docker daemon: %s\n" % str(e)) + return None + + def _get_args(self, label): + labels = self._get_labels() + if label in labels: + return labels[label].split() + return None + + def _check_latest(self): + inspect = self._inspect_image() + if inspect and inspect["Id"] != self.inspect["Image"]: + response = "" + sys.stdout.write("""The '%(name)s' container is using an older version of the installed +'%(image)s' container image. If you wish to use the newer image, +you must either create a new container with a new name or +uninstall the '%(name)s' container. + +# atomic uninstall --name %(name)s %(image)s + +and create new container on the '%(image)s' image. + +atomic update --force %(image)s + +removes all containers based on an image. + +""" % { "name" : self.name, "image" : self.image} ) + + def container_run_command(self): + command = "%s run " % sys.argv[0] + if self.spc: + command += "--SPC " + + if self.name != self.image: + command += "--name %s " % self.name + command += self.image + return command + + def run(self): + self.inspect = self._inspect_container() + + if self.inspect: + self._check_latest() + # Container exists + if self.inspect["State"]["Running"]: + return self._running() + else: + return self._start() + else: + if self.command: + raise ValueError("Container '%s' must be running before executing a command into it.\nExecute the following to create the container:\n%s" % (self.name, self.container_run_command())) + + # Container does not exist + self.inspect = self._inspect_image() + if not self.inspect: + self.update() + self.inspect = self._inspect_image() + + if self.spc: + if self.command: + args = self.SPC_ARGS + self.command + else: + args = self.SPC_ARGS + self._get_cmd() + + cmd = self.gen_cmd(args) + self.writeOut(cmd) + else: + missing_RUN = False + args = self._get_args("RUN") + if not args: + missing_RUN = True + args = self.RUN_ARGS + self._get_cmd() + + cmd = self.gen_cmd(args) + self.writeOut(cmd) + + if missing_RUN: + subprocess.check_call(cmd, env={ + "CONFDIR": "/etc/%s" % self.name, + "LOGDIR": "/var/log/%s" % self.name, + "DATADIR":"/var/lib/%s" % self.name}, shell=True, stderr=DEVNULL, stdout=DEVNULL) + return self._start() + + subprocess.check_call(cmd, env={ + "CONFDIR": "/etc/%s" % self.name, + "LOGDIR": "/var/log/%s" % self.name, + "DATADIR":"/var/lib/%s" % self.name}, shell=True) + + + def stop(self): + self.inspect = self._inspect_container() + args = self._get_args("STOP") + if args: + cmd = self.gen_cmd(args) + self.writeOut(cmd) + + subprocess.check_call(cmd, env={ + "CONFDIR": "/etc/%s" % self.name, + "LOGDIR": "/var/log/%s" % self.name, + "DATADIR":"/var/lib/%s" % self.name}, shell=True) + + + # Container exists + if self.inspect["State"]["Running"]: + self.d.stop(self.name) + + def _rpmostree(self, *args): + os.execl("/usr/bin/rpm-ostree", "rpm-ostree", *args) + + def host_status(self): + self._rpmostree("status") + + def host_upgrade(self): + argv = ["upgrade"] + if self.args.reboot: + argv.append("--reboot") + self._rpmostree(*argv) + + def host_rollback(self): + argv = ["rollback"] + if self.args.reboot: + argv.append("--reboot") + self._rpmostree(*argv) + + def uninstall(self): + self.inspect = self._inspect_container() + + if self.force: + self.force_delete_containers() + + if self.name != self.image: + try: + # Attempt to remove container, if it exists just return + self.d.stop(self.name) + self.d.remove_container(self.name) + return + except: + # On exception attempt to remove image + pass + + try: + self.d.stop(self.image) + self.d.remove_container(self.image) + except docker.errors.APIError: + pass + self.inspect = self._inspect_image() + args = self._get_args("UNINSTALL") + if args: + cmd = self.gen_cmd(args) + self.writeOut(cmd) + subprocess.check_call(cmd, env={ + "CONFDIR": "/etc/%s" % self.name, + "LOGDIR": "/var/log/%s" % self.name, + "DATADIR":"/var/lib/%s" % self.name}, shell=True) + subprocess.check_call(["/usr/bin/docker", "rmi", self.image]) + + def gen_cmd(self,cargs): + args = [] + for c in cargs: + if c == "IMAGE": + args.append(self.image) + continue + if c == "IMAGE=IMAGE": + args.append("IMAGE=%s" % self.image) + continue + if c == "NAME=NAME": + args.append("NAME=%s" % self.name) + continue + if c == "NAME": + args.append(self.name) + continue + args.append(c) + return " ".join(args) + + def info(self): + self.inspect = self._inspect_image() + if not self.inspect: + self.update() + self.inspect = self._inspect_image() + + labels = self._get_labels() + for label in labels: + self.writeOut("%-13s: %s" % (label, labels[label])) + + def install(self): + self.inspect = self._inspect_image() + if not self.inspect: + self.update() + self.inspect = self._inspect_image() + + args = self._get_args("INSTALL") + if args: + cmd = self.gen_cmd(args) + self.writeOut(cmd) + + return(subprocess.check_call(cmd, env={ + "CONFDIR": "/etc/%s" % self.name, + "LOGDIR": "/var/log/%s" % self.name, + "DATADIR":"/var/lib/%s" % self.name}, shell=True)) + + def help(self): + if os.path.exists("/usr/bin/rpm-ostree"): + return _('Atomic Management Tool') + else: + return _('Atomic Container Tool') + + def print_spc(self): + return " ".join(self.SPC_ARGS) + + def print_run(self): + return " ".join(self.RUN_ARGS) + + def print_install(self): + return " ".join(self.INSTALL_ARGS) + " /usr/bin/INSTALLCMD" + + def print_uninstall(self): + return " ".join(self.INSTALL_ARGS) + " /usr/bin/UNINSTALLCMD" + + def _get_layer(self, image): + def get_label(label): + return self.get_label(label, image["Id"]) + image = self._inspect_image(image) + if not image: + raise ValueError("Image '%s' does not exist" % self.image) + return({ "Id": image['Id'], "Name": get_label("Name"), "Version" : ("%s:%s:%s" % (get_label("Name"),get_label("Version"),get_label("Release"))).strip(":"), "Tag": findRepoTag(self.d, image['Id']), "Parent": image['Parent']}) + + def get_layers(self): + layers = [] + layer = self._get_layer(self.image) + layers.append(layer) + while layer["Parent"] != "": + layer = self._get_layer(layer["Parent"]) + layers.append(layer) + return layers + + def _get_image(self, image): + def get_label(label): + return self.get_label(label, image["Id"]) + + return { "Id": image['Id'], "Name": get_label("Name"), "Version" : ("%s:%s:%s" % (get_label("Name"),get_label("Version"),get_label("Release"))).strip(":"), "Tag": image["RepoTags"][0]} + + def get_images(self): + if len(self.images) > 0: + return self.images + + images = self.d.images() + for image in images: + self.images.append(self._get_image(image)) + + return self.images + + def verify(self): + def get_label(label): + val = self._get_args(label) + if val: + return val[0] + return "" + self.inspect = self._inspect_image() + current_name = get_label("Name") + version = "" + if current_name: + version = "%s:%s:%s" % (current_name,get_label("Version"),get_label("Release")) + + prev = "" + name = None + for layer in self.get_layers(): + if name == layer["Name"]: + continue + name = layer["Name"] + if len(name)>0: + for i in self.get_images(): + if i["Name"] == name: + if i["Version"] > layer["Version"]: + self.writeOut("Image '%s' contains a layer '%s' that is out of date image version '%s' is available." % (self.image, layer["Version"], i["Version"])) + self.writeOut("You should rebuild the '%s' image useing docker build, it could be vulnerable." % (self.image)) + break + + def version(self): + def get_label(label): + val = self._get_args(label) + if val: + return val[0] + return "" + + try: + self.inspect = self.d.inspect_image(self.image) + except docker.errors.APIError: + self.update() + self.inspect = self.d.inspect_image(self.image) + + if self.args.recurse: + for layer in self.get_layers(): + self.writeOut("%s %s %s" % (layer["Id"],layer["Version"],layer["Tag"])) + else: + layer = self._get_layer(self.image) + self.writeOut("%s %s %s" % (layer["Id"],layer["Version"],layer["Tag"])) + + diff --git a/atomicpulp/config.py b/Atomic/config.py similarity index 100% rename from atomicpulp/config.py rename to Atomic/config.py diff --git a/atomicpulp/pulp.py b/Atomic/pulp.py similarity index 98% rename from atomicpulp/pulp.py rename to Atomic/pulp.py index 51bbf3d..c950908 100644 --- a/atomicpulp/pulp.py +++ b/Atomic/pulp.py @@ -46,7 +46,7 @@ class PulpServer(object): #print('Pulp JSON response:\n{0}'.format(json.dumps(r_json, indent=2))) if 'error_message' in r_json: - sys.stderror.write('Error messages from Pulp response:\n{0}'.format(r_json['error_message'])) + sys.stderr.write('Error messages from Pulp response:\n{0}'.format(r_json['error_message'])) if 'spawned_tasks' in r_json: for task in r_json['spawned_tasks']: diff --git a/atomic b/atomic old mode 100755 new mode 100644 index 587ca1e..67ae9c1 --- a/atomic +++ b/atomic @@ -25,16 +25,8 @@ import sys, os import argparse import gettext import docker -import json import subprocess -import atomicpulp -import getpass -try: - from subprocess import DEVNULL # pylint: disable=no-name-in-module -except ImportError: - import os - DEVNULL = open(os.devnull, 'wb') -import requests +import Atomic PROGNAME="atomic" gettext.bindtextdomain(PROGNAME, "/usr/share/locale") @@ -51,532 +43,6 @@ except IOError: import builtins builtins.__dict__['_'] = str -images=[] -def findRepoTag(d, id): - global images - if len(images) == 0: - images = d.images() - for image in images: - if id == image["Id"]: - return image["RepoTags"][0] - return "" - -class Atomic(object): - INSTALL_ARGS = ["/usr/bin/docker", "run", - "-t", - "-i", - "--rm", - "--privileged", - "-v", "/:/host", - "--net=host", - "--ipc=host", - "--pid=host", - "-e", "HOST=/host", - "-e", "NAME=NAME", - "-e", "IMAGE=IMAGE", - "-v", "${CONFDIR}:/etc/NAME", - "-v", "${LOGDIR}:/var/log/NAME", - "-v", "${DATADIR}:/var/lib/NAME", - "-e", "CONFDIR=${CONFDIR}", - "-e", "LOGDIR=${LOGDIR}", - "-e", "DATADIR=${DATADIR}", - "--name", "NAME", - "IMAGE"] - - SPC_ARGS = ["/usr/bin/docker", "run", - "-t", - "-i", - "--rm", - "--privileged", - "-v", "/:/host", - "-v", "/run:/run", - "-v", "/etc/localtime:/etc/localtime", - "--net=host", - "--ipc=host", - "--pid=host", - "-e", "HOST=/host", - "-e", "NAME=NAME", - "-e", "IMAGE=IMAGE", - "IMAGE" ] - - RUN_ARGS = ["/usr/bin/docker", "create", - "-t", - "-i", - "--name", "NAME", - "IMAGE" ] - - def __init__(self): - self.d = docker.Client() - self.name = None - self.image = None - self.spc = False - self.inspect = None - self.force = False - self.images = [] - - def writeOut(self, output, lf="\n"): - sys.stdout.flush() - sys.stdout.write(output + lf) - - def get_label(self, label, image=None): - inspect = self._inspect_image(image) - cfg = inspect.get("Config", None) - if cfg: - labels = cfg.get("Labels", []) - if labels and label in labels: - return labels[label] - return "" - - def force_delete_containers(self): - if self._inspect_image(): - image = self.image - if self.image.find(":") == -1: - image += ":latest" - for c in self.d.containers(all=True): - if c["Image"] == image: - self.d.remove_container(c["Id"], force=True) - - def update(self): - if self.force: - self.force_delete_containers() - return subprocess.check_call(["/usr/bin/docker", "pull", self.image]) - - def pull(self): - prevstatus = "" - prev = "" - for line in self.d.pull(self.image, stream=True): - bar = json.loads(line) - status = bar['status'] - if prevstatus != status: - self.writeOut(status, "") - if 'id' not in bar: - continue - if status == "Downloading": - self.writeOut(bar['progress'] + " ") - elif status == "Extracting": - self.writeOut("Extracting: " + bar['id']) - elif status == "Pull complete": - pass - elif status.startswith("Pulling"): - self.writeOut("Pulling: " + bar['id']) - - prevstatus = status - self.writeOut("") - - def upload(self): - prevstatus = "" - if not self.args.username: - self.args.username = raw_input("Registry Username: ") - if not self.args.password: - self.args.password = getpass.getpass("Registry Password: ") - - if self.args.pulp: - return atomicpulp.push_image_to_pulp(self.image, self.args.url, self.args.username, self.args.password, self.args.verify_ssl, self.d) - else: - self.d.login(self.args.username, self.args.password) - for line in self.d.push(self.image, stream=True): - bar = json.loads(line) - status = bar['status'] - if prevstatus != status: - self.writeOut(status, "") - if 'id' not in bar: - continue - if status == "Uploading": - self.writeOut(bar['progress'] + " ") - elif status == "Push complete": - pass - elif status.startswith("Pushing"): - self.writeOut("Pushing: " + bar['id']) - - prevstatus = status - - def set_args(self, args): - self.args=args - try: - self.image = args.image - except: - pass - try: - self.command = args.command - except: - self.command = None - - try: - self.spc = args.spc - except: - self.spc = False - - try: - self.name = args.name - except: - pass - - try: - self.force = args.force - except: - pass - - if not self.name and self.image is not None: - self.name = self.image.split("/")[-1].split(":")[0] - if self.spc: - self.name = self.name + "-spc" - - - def _getconfig(self, key, default=None): - assert self.inspect is not None - cfg = self.inspect.get("Config") - if cfg is None: - return default - val = cfg.get(key, default) - if val is None: - return default - return val - - def _get_cmd(self): - return self._getconfig("Cmd", [ "/bin/sh" ]) - - def _get_labels(self): - return self._getconfig("Labels", []) - - def _interactive(self): - return (self._getconfig("AttachStdin", False) and - self._getconfig("AttachStdout", False) and - self._getconfig("AttachStderr", False)) - - def _running(self): - if self._interactive(): - cmd = ["/usr/bin/docker", "exec", "-t", "-i", self.name] - if self.command: - cmd += self.command - else: - cmd += self._get_cmd() - return subprocess.check_call(cmd, stderr=DEVNULL) - else: - if self.command: - return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command, stderr=DEVNULL) - else: - self.writeOut("Container is running") - - def _start(self): - if self._interactive(): - if self.command: - subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) - return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command) - else: - return subprocess.check_call(["/usr/bin/docker", "start", "-i", "-a", self.name], stderr=DEVNULL) - else: - if self.command: - subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) - return subprocess.check_call(["/usr/bin/docker", "exec", "-t", "-i", self.name] + self.command) - else: - return subprocess.check_call(["/usr/bin/docker", "start", self.name], stderr=DEVNULL) - - def _inspect_image(self, image=None): - try: - if image: - return self.d.inspect_image(image) - return self.d.inspect_image(self.image) - except docker.errors.APIError: - pass - except requests.exceptions.ConnectionError as e: - raise IOError("Unable to communicate with docker daemon: %s\n" % str(e)) - return None - - def _inspect_container(self): - try: - return self.d.inspect_container(self.name) - except docker.errors.APIError: - pass - except requests.exceptions.ConnectionError as e: - raise IOError("Unable to communicate with docker daemon: %s\n" % str(e)) - return None - - def _get_args(self, label): - labels = self._get_labels() - if label in labels: - return labels[label].split() - return None - - def _check_latest(self): - inspect = self._inspect_image() - if inspect and inspect["Id"] != self.inspect["Image"]: - response = "" - sys.stdout.write("""The '%(name)s' container is using an older version of the installed -'%(image)s' container image. If you wish to use the newer image, -you must either create a new container with a new name or -uninstall the '%(name)s' container. - -# atomic uninstall --name %(name)s %(image)s - -and create new container on the '%(image)s' image. - -atomic update --force %(image)s - -removes all containers based on an image. - -""" % { "name" : self.name, "image" : self.image} ) - - def container_run_command(self): - command = "%s run " % sys.argv[0] - if self.spc: - command += "--SPC " - - if self.name != self.image: - command += "--name %s " % self.name - command += self.image - return command - - def _new_env(self): - env = dict(os.environ) - env["CONFDIR"] = "/etc/%s" % self.name - env["LOGDIR"] = "/var/log/%s" % self.name - env["DATADIR"] = "/var/lib/%s" % self.name - return env - - def run(self): - self.inspect = self._inspect_container() - - if self.inspect: - self._check_latest() - # Container exists - if self.inspect["State"]["Running"]: - return self._running() - else: - return self._start() - else: - if self.command: - raise ValueError("Container '%s' must be running before executing a command into it.\nExecute the following to create the container:\n%s" % (self.name, self.container_run_command())) - - # Container does not exist - self.inspect = self._inspect_image() - if not self.inspect: - self.update() - self.inspect = self._inspect_image() - - if self.spc: - if self.command: - args = self.SPC_ARGS + self.command - else: - args = self.SPC_ARGS + self._get_cmd() - - cmd = self.gen_cmd(args) - self.writeOut(cmd) - else: - missing_RUN = False - args = self._get_args("RUN") - if not args: - missing_RUN = True - args = self.RUN_ARGS + self._get_cmd() - - cmd = self.gen_cmd(args) - self.writeOut(cmd) - - if missing_RUN: - subprocess.check_call(cmd, env=self._new_env(), shell=True, stderr=DEVNULL, stdout=DEVNULL) - return self._start() - - subprocess.check_call(cmd, env=self._new_env(), shell=True) - - - def stop(self): - self.inspect = self._inspect_container() - args = self._get_args("STOP") - if args: - cmd = self.gen_cmd(args) - self.writeOut(cmd) - - subprocess.check_call(cmd, env=self._new_env(), shell=True) - - # Container exists - if self.inspect["State"]["Running"]: - self.d.stop(self.name) - - def _rpmostree(self, *args): - os.execl("/usr/bin/rpm-ostree", "rpm-ostree", *args) - - def host_status(self): - self._rpmostree("status") - - def host_upgrade(self): - argv = ["upgrade"] - if self.args.reboot: - argv.append("--reboot") - self._rpmostree(*argv) - - def host_rollback(self): - argv = ["rollback"] - if self.args.reboot: - argv.append("--reboot") - self._rpmostree(*argv) - - def uninstall(self): - self.inspect = self._inspect_container() - - if self.force: - self.force_delete_containers() - - if self.name != self.image: - try: - # Attempt to remove container, if it exists just return - self.d.stop(self.name) - self.d.remove_container(self.name) - return - except: - # On exception attempt to remove image - pass - - try: - self.d.stop(self.image) - self.d.remove_container(self.image) - except docker.errors.APIError: - pass - self.inspect = self._inspect_image() - args = self._get_args("UNINSTALL") - if args: - cmd = self.gen_cmd(args) - self.writeOut(cmd) - subprocess.check_call(cmd, env=self._new_env(), shell=True) - subprocess.check_call(["/usr/bin/docker", "rmi", self.image]) - - def gen_cmd(self,cargs): - args = [] - for c in cargs: - if c == "IMAGE": - args.append(self.image) - continue - if c == "IMAGE=IMAGE": - args.append("IMAGE=%s" % self.image) - continue - if c == "NAME=NAME": - args.append("NAME=%s" % self.name) - continue - if c == "NAME": - args.append(self.name) - continue - args.append(c) - return " ".join(args) - - def info(self): - self.inspect = self._inspect_image() - if not self.inspect: - self.update() - self.inspect = self._inspect_image() - - labels = self._get_labels() - for label in labels: - self.writeOut("%-13s: %s" % (label, labels[label])) - - def install(self): - self.inspect = self._inspect_image() - if not self.inspect: - self.update() - self.inspect = self._inspect_image() - - args = self._get_args("INSTALL") - if args: - cmd = self.gen_cmd(args) - self.writeOut(cmd) - - return subprocess.check_call(cmd, env=self._new_env(), shell=True) - - def help(self): - if os.path.exists("/usr/bin/rpm-ostree"): - return _('Atomic Management Tool') - else: - return _('Atomic Container Tool') - - def print_spc(self): - return " ".join(self.SPC_ARGS) - - def print_run(self): - return " ".join(self.RUN_ARGS) - - def print_install(self): - return " ".join(self.INSTALL_ARGS) + " /usr/bin/INSTALLCMD" - - def print_uninstall(self): - return " ".join(self.INSTALL_ARGS) + " /usr/bin/UNINSTALLCMD" - - def _get_layer(self, image): - def get_label(label): - return self.get_label(label, image["Id"]) - image = self._inspect_image(image) - if not image: - raise ValueError("Image '%s' does not exist" % self.image) - return({ "Id": image['Id'], "Name": get_label("Name"), "Version" : ("%s:%s:%s" % (get_label("Name"),get_label("Version"),get_label("Release"))).strip(":"), "Tag": findRepoTag(self.d, image['Id']), "Parent": image['Parent']}) - - def get_layers(self): - layers = [] - layer = self._get_layer(self.image) - layers.append(layer) - while layer["Parent"] != "": - layer = self._get_layer(layer["Parent"]) - layers.append(layer) - return layers - - def _get_image(self, image): - def get_label(label): - return self.get_label(label, image["Id"]) - - return { "Id": image['Id'], "Name": get_label("Name"), "Version" : ("%s:%s:%s" % (get_label("Name"),get_label("Version"),get_label("Release"))).strip(":"), "Tag": image["RepoTags"][0]} - - def get_images(self): - if len(self.images) > 0: - return self.images - - images = self.d.images() - for image in images: - self.images.append(self._get_image(image)) - - return self.images - - def verify(self): - def get_label(label): - val = self._get_args(label) - if val: - return val[0] - return "" - self.inspect = self._inspect_image() - current_name = get_label("Name") - version = "" - if current_name: - version = "%s:%s:%s" % (current_name,get_label("Version"),get_label("Release")) - - prev = "" - name = None - for layer in self.get_layers(): - if name == layer["Name"]: - continue - name = layer["Name"] - if len(name)>0: - for i in self.get_images(): - if i["Name"] == name: - if i["Version"] > layer["Version"]: - self.writeOut("Image '%s' contains a layer '%s' that is out of date image version '%s' is available." % (self.image, layer["Version"], i["Version"])) - self.writeOut("You should rebuild the '%s' image useing docker build, it could be vulnerable." % (self.image)) - break - - def version(self): - def get_label(label): - val = self._get_args(label) - if val: - return val[0] - return "" - - try: - self.inspect = self.d.inspect_image(self.image) - except docker.errors.APIError: - self.update() - self.inspect = self.d.inspect_image(self.image) - - if self.args.recurse: - for layer in self.get_layers(): - self.writeOut("%s %s %s" % (layer["Id"],layer["Version"],layer["Tag"])) - else: - layer = self._get_layer(self.image) - self.writeOut("%s %s %s" % (layer["Id"],layer["Version"],layer["Tag"])) - - def SetFunc(function): class customAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): @@ -590,7 +56,7 @@ class HelpByDefaultArgumentParser(argparse.ArgumentParser): sys.exit(2) if __name__ == '__main__': - atomic=Atomic() + atomic=Atomic.Atomic() parser = HelpByDefaultArgumentParser(description=atomic.help()) subparser = parser.add_subparsers(help=_("commands")) @@ -684,7 +150,7 @@ if __name__ == '__main__': default=False, action="store_true", help=_("upload image using pulp")) - config = atomicpulp.PulpConfig().config() + config = Atomic.PulpConfig().config() uploadp.add_argument("--verify_ssl", default=config["verify_ssl"], action="store_true", diff --git a/setup.py b/setup.py index 1c046d2..85efbf3 100644 --- a/setup.py +++ b/setup.py @@ -4,4 +4,4 @@ import os from distutils.core import setup -setup(name = "atomic", scripts=["atomic"], version="1.0", description="Atomic Management Tool", author="Daniel Walsh", author_email="dwalsh@redhat.com", packages=["atomicpulp"]) +setup(name = "atomic", scripts=["atomic"], version="1.0", description="Atomic Management Tool", author="Daniel Walsh", author_email="dwalsh@redhat.com", packages=["Atomic"])