Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Docs to go with the 0.1.0 release #14

Merged
merged 6 commits into from
Mar 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
PY := .venv/bin/python

.PHONY: clean
clean:
find . -name '*.pyc' -delete
find . -name '*.bak' -delete
find . -name __pycache__ -delete
rm -f .coverage

# DEPLOY
.PHONY: docs
docs:
pip3 list | grep Sphinx || pip3 install -U sphinx
cd docs && make html && cd -

.PHONY: dist
dist: docs
$(PY) setup.py sdist

.PHONY: publish
publish: docs
$(PY) setup.py sdist upload_docs --upload-dir=docs/build/html

1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
91 changes: 4 additions & 87 deletions charms/docker/__init__.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,4 @@
import os
import subprocess

from shlex import split

from .workspace import Workspace


class Docker:
'''
Wrapper class to communicate with the Docker daemon on behalf of
a charmer. Provides stateless operations of a running docker daemon
'''

def __init__(self, socket="unix:///var/run/docker.sock", workspace=None):
'''
@param socket - URI to the Docker daemon socket
default: unix://var/run/docker.sock

@param workspace - Path to directory containing a Dockerfile
default: None
'''
self.socket = socket
if workspace:
self.workspace = Workspace(workspace)

def running(self):
'''
Predicate method to determine if the daemon we are talking to is
actually online and recieving events.

ex: bootstrap = Docker(socket="unix://var/run/docker-boostrap.sock")
bootstrap.running()
> True
'''
# TODO: Add TCP:// support for running check
return os.path.isfile(self.socket)

def run(self, image, options=[], commands=[], arg=[]):
'''
Docker Run exposed as a method. This wont be as natural as the
command line docker experience.

Docker CLI output example:
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

@param image - string of the container to pull from the registry,
eg: ubuntu:latest
@param options - array of string options, eg: ['-d', '-v /tmp:/tmp']
@param commands - array of string commands, eg: ['ls']
@param arg - array of string command args, eg: ['-al']
'''
options = ' '.join(options)
command = ' '.join(commands)
args = ' '.join(arg)
cmd = "docker run {0} {1} {2} {3}".format(
options, image, command, args)

try:
subprocess.check_output(split(cmd))
except subprocess.CalledProcessError as expect:
print("Error: ", expect.returncode, expect.output)

def login(self, user, password, email):
'''
Docker login exposed as a method.

@param user - Username in the registry
@param password - Password for the registry
@param email - Email address on account (dockerhub)
'''
cmd = ['docker', 'login', '-u', user, '-p', password, '-e', email]
subprocess.check_call(cmd)

def ps(self):
'''
return a string of docker status output
'''
cmd = ['docker', 'ps']
return subprocess.check_output(cmd)

def pull(self, image):
'''
Pull an image from the docker hub
'''
cmd = ['docker', 'pull', image]
return subprocess.check_output(cmd)
from .docker import Docker # noqa
from .compose import Compose # noqa
from .dockeropts import DockerOpts # noqa
from .workspace import Workspace # noqa
124 changes: 85 additions & 39 deletions charms/docker/compose.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import os

from contextlib import contextmanager
from shlex import split
from subprocess import check_output
from .runner import run
from .workspace import Workspace


Expand All @@ -13,75 +9,125 @@ def __init__(self, workspace, strict=True):
a natural language for performing common tasks with docker in
juju charms.

@param workspace - Define the CWD for docker-compose execution
:param workspace: Define the CWD for docker-compose execution

@param strict - Enable/disable workspace validation
:param strict: - Enable/disable workspace validation
'''
self.workspace = Workspace(workspace)
if strict:
self.workspace.validate()

def up(self, service=None):
def build(self, service=None, force_rm=True, no_cache=False, pull=False):
'''
Convenience method that wraps `docker-compose up`
Build or rebuild services.

usage: c.up('nginx') to start the 'nginx' service from the
defined `docker-compose.yml` as a daemon
Services are built once and then tagged as `project_service`. If you
change a service's Dockerfile or the contents of its build directory
you can invoke this method to rebuild it.

:param service: if provided will rebuild scoped to that service
:param force_rm: Always remove intermediate containers.
:param no_cache: Do not use cache when building the image
:param pull: Always attempt to pull a newer version of the image
'''
cmd = "docker-compose build"

if force_rm:
cmd = "{} --force-rm".format(cmd)
if no_cache:
cmd = "{} --no-cache".format(cmd)
if pull:
cmd = "{} --pull".format(cmd)
if service:
cmd = "docker-compose up -d {}".format(service)
else:
cmd = "docker-compose up -d"
self.run(cmd)
cmd = "{} {}".format(cmd, service)

run(cmd, self.workspace)

def kill(self, service=None):
'''
Convenience method that wraps `docker-compose kill`

usage: c.kill('nginx') to kill the 'nginx' service from the
defined `docker-compose.yml`
:param service: if defined will only kill that service.
'''
if service:
cmd = "docker-compose kill {}".format(service)
else:
cmd = "docker-compose kill"
self.run(cmd)
run(cmd, self.workspace)

def pull(self, service=None):
'''
Pulls service images

:param service: if defined, only pulls the image for specified service.
'''
if service:
cmd = "docker-compose pull {}".format(service)
else:
cmd = "docker-compose pull"
run(cmd, self.workspace)

def restart(self, service=None):
'''
Restart services

:param service: if defined, only restarts the specified service.
'''
if service:
cmd = "docker-compose restart {}".format(service)
else:
cmd = "docker-compose restart"
run(cmd, self.workspace)

def rm(self, service=None):
'''
Convenience method that wraps `docker-compose rm`

usage: c.rm('nginx') to remove the 'nginx' service from the
defined `docker-compose.yml`
:param service: if defined only the specified service.
'''
if service:
cmd = "docker-compose rm -f {}".format(service)
else:
cmd = "docker-compose rm -f"
self.run(cmd)
run(cmd, self.workspace)

def run(self, cmd):
def scale(self, service, count):
'''
chdir sets working context on the workspace
Set number of containers to run for a service.

@param: cmd - String of the command to run. eg: echo "hello world"
the string is passed through shlex.parse() for convenience.
:param service: Service to scale as defined in docker-compose.yml
:param count: number of containers to scale
'''
cmd = "docker-compose scale {}={}".format(service, count)
run(cmd, self.workspace)

returns STDOUT of command execution
def start(self, service):
'''
Start existing containers

usage: c.run('docker-compose ps')
:param service: Service to start
'''
with chdir("{}".format(self.workspace)):
out = check_output(split(cmd))
return out
cmd = "docker-compose start {}".format(service)
run(cmd, self.workspace)

def stop(self, service, timeout=10):
'''
Stop running containers without removing them.

:param service: Service to stop.
:param timeout: specify a shutdown timeout in seconds.
'''
cmd = "docker-compose stop -t {} {}".format(timeout, service)
run(cmd, self.workspace)

# This is helpful for setting working directory context
@contextmanager
def chdir(path):
'''Change the current working directory to a different directory to run
commands and return to the previous directory after the command is done.'''
old_dir = os.getcwd()
os.chdir(path)
yield
os.chdir(old_dir)
def up(self, service=None):
'''
Convenience method that wraps `docker-compose up`

:param service: if defined only launches the specified service
'''
if service:
cmd = "docker-compose up -d {}".format(service)
else:
cmd = "docker-compose up -d"
run(cmd, self.workspace)
87 changes: 87 additions & 0 deletions charms/docker/docker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import os
import subprocess

from shlex import split

from .workspace import Workspace


class Docker:
'''
Wrapper class to communicate with the Docker daemon on behalf of
a charmer. Provides stateless operations of a running docker daemon
'''

def __init__(self, socket="unix:///var/run/docker.sock", workspace=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many slashes here. I don't think you were intending for unix colon slash slash slash

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? :) See the daemon options, specifically the -H option: https://docs.docker.com/engine/quickstart/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I see the issue, it is using the third slash as the root dir. If this is correct, you should fix all other uses that do not have three slashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and done. Thanks for pointing that out, I completely missed it even after you called out the funky one that was indeed correct. #whatamidoingwithmylife

'''
:param socket: URI to the Docker daemon socket
default: unix:///var/run/docker.sock

:param workspace: Path to directory containing a Dockerfile
default: None
'''
self.socket = socket
if workspace:
self.workspace = Workspace(workspace)

def running(self):
'''
Predicate method to determine if the daemon we are talking to is
actually online and recieving events.

ex: bootstrap = Docker(socket="unix:///var/run/docker-boostrap.sock")
bootstrap.running()
> True
'''
# TODO: Add TCP:// support for running check
return os.path.isfile(self.socket)

def run(self, image, options=[], commands=[], arg=[]):
'''
Docker Run exposed as a method. This wont be as natural as the
command line docker experience.

Docker CLI output example:
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

:param image: string of the container to pull from the registry,
eg: ubuntu:latest
:param options: array of string options, eg: ['-d', '-v /tmp:/tmp']
:param commands: array of string commands, eg: ['ls']
:param arg: array of string command args, eg: ['-al']
'''
options = ' '.join(options)
command = ' '.join(commands)
args = ' '.join(arg)
cmd = "docker run {0} {1} {2} {3}".format(
options, image, command, args)

try:
subprocess.check_output(split(cmd))
except subprocess.CalledProcessError as expect:
print("Error: ", expect.returncode, expect.output)

def login(self, user, password, email):
'''
Docker login exposed as a method.

:param user: Username in the registry
:param password: - Password for the registry
:param email: - Email address on account (dockerhub)
'''
cmd = ['docker', 'login', '-u', user, '-p', password, '-e', email]
subprocess.check_call(cmd)

def ps(self):
'''
return a string of docker status output
'''
cmd = ['docker', 'ps']
return subprocess.check_output(cmd)

def pull(self, image):
'''
Pull an image from the docker hub
'''
cmd = ['docker', 'pull', image]
return subprocess.check_output(cmd)