Permalink
Browse files

Improved documentation.

  • Loading branch information...
1 parent 4e66841 commit 98c76251bea2cf64934a341a8967fade153194dd @jonathanslenders committed Feb 27, 2014
View
@@ -71,8 +71,8 @@ def __exit__(context, *args):
def cd(self, path, expand=False):
"""
- Execute commands in this directory.
- Nesting of cd-statements is allowed.
+ Execute commands in this directory. Nesting of cd-statements is
+ allowed.
::
View
@@ -1,5 +1,5 @@
from contextlib import nested
-from deployer.host import Host, HostContext
+from deployer.host import Host
from deployer.exceptions import ExecCommandFailed
from deployer.utils import isclass, esc1
from functools import wraps
@@ -9,18 +9,21 @@
class HostsContainer(object):
"""
- Facade to the host instances.
- if you have a role, name 'www' inside the service webserver, you can do:
-
- - webserver.hosts.run(...)
- - webserver.hosts.www.run(...)
- - webserver.hosts[0].run(...)
- - webserver.hosts.www[0].run(...)
- - webserver.hosts.filter('www')[0].run(...)
-
- The host container also keeps track of HostStatus. So, if we fork a new
- thread, and the HostStatus object gets modified in either thread. Clone
- this HostsContainer first.
+ Proxy to a group of :class:`~deployer.host.base.Host` instances.
+
+ For instance, if you have a role, name 'www' inside the container, you
+ could do:
+
+ ::
+
+ host_container.run(...)
+ host_container[0].run(...)
+ host_container.filter('www')[0].run(...)
+
+ Typically, you get a :class:`~deployer.host_container.HostsContainer` class
+ by accessing the :attr:`~deployer.node.base.Env.hosts` property of an
+ :class:`~deployer.node.base.Env` (:class:`~deployer.node.base.Node`
+ wrapper.)
"""
def __init__(self, hosts, pty=None, logger=None, is_sandbox=False):
# the hosts parameter is a dictionary, mapping roles to <Host> instances, or lists
@@ -99,7 +102,7 @@ def get_hosts_as_dict(self):
Return a dictionary which maps all the roles to the set of
:class:`deployer.host.Host` classes for each role.
"""
- return { k: { h.__class__ for h in l } for k,l in self._hosts.items() }
+ return { k: { h.__class__ for h in l } for k, l in self._hosts.items() }
def __repr__(self):
return ('<%s\n' % self.__class__.__name__ +
@@ -194,10 +197,12 @@ def __iter__(self):
def expand_path(self, path):
return [ h.expand_path(path) for h in self._all ]
- @wraps(Host.run)
def run(self, *a, **kw):
- """
- Call ``run`` with this parameters on every host.
+ """run(command, sandbox=False, interactive=True, user=None, ignore_exit_status=False, initial_input=None)
+
+ Call :func:`~deployer.host.base.Host.run` with this parameters on every
+ :class:`~deployer.host.base.Host` in this container. It can be executed
+ in parallel when we have multiple hosts.
:returns: An array of all the results.
"""
@@ -235,40 +240,64 @@ def call(pty):
else:
return [ c(self._pty) for c in callables ]
- @wraps(Host.sudo)
def sudo(self, *args, **kwargs):
- """
- Call ``sudo`` with this parameters on every host.
+ """sudo(command, sandbox=False, interactive=True, user=None, ignore_exit_status=False, initial_input=None)
+
+ Call :func:`~deployer.host.base.Host.sudo` with this parameters on every
+ :class:`~deployer.host.base.Host` in this container. It can be executed
+ in parallel when we have multiple hosts.
+
+ :returns: An array of all the results.
"""
kwargs['use_sudo'] = True
return HostsContainer.run(self, *args, **kwargs)
# NOTE: here we use HostsContainer instead of self, to be
# sure that we don't call te overriden method in
# HostContainer.
- @wraps(HostContext.prefix)
- def prefix(self, *a, **kw):
+ def prefix(self, command):
"""
- Call 'prefix' with this parameters on every host.
+ Call :func:`~deployer.host.base.HostContext.prefix` on the
+ :class:`~deployer.host.base.HostContext` of every host.
+
+ ::
+
+ with host.prefix('workon environment'):
+ host.run('./manage.py migrate')
"""
- return nested(* [ h.host_context.prefix(*a, **kw) for h in self._all ])
+ return nested(* [ h.host_context.prefix(command) for h in self._all ])
- @wraps(HostContext.cd)
- def cd(self, *a, **kw):
+ def cd(self, path, expand=False):
"""
- Call 'cd' with this parameters on every host.
+ Execute commands in this directory. Nesting of cd-statements is
+ allowed.
+
+ Call :func:`~deployer.host.base.HostContext.cd` on the
+ :class:`~deployer.host.base.HostContext` of every host.
+
+ ::
+
+ with host_container.cd('directory'):
+ host_container.run('ls')
"""
- return nested(* [ h.host_context.cd(*a, **kw) for h in self._all ])
+ return nested(* [ h.host_context.cd(path, expand=expand) for h in self._all ])
- @wraps(HostContext.env)
- def env(self, *a, **kw):
+ def env(self, variable, value, escape=True):
"""
- Call 'env' with this parameters on every host.
+ Sets an environment variable.
+
+ This calls :func:`~deployer.host.base.HostContext.env` on the
+ :class:`~deployer.host.base.HostContext` of every host.
+
+ ::
+
+ with host_container.cd('VAR', 'my-value'):
+ host_container.run('echo $VAR')
"""
- return nested(* [ h.host_context.env(*a, **kw) for h in self._all ])
+ return nested(* [ h.host_context.env(variable, value, escape=escape) for h in self._all ])
def getcwd(self):
- """ Call getcwd() for every host """
+ """ Calls :func:`~deployer.host.base.Host.getcwd` for every host and return the result as an array. """
return [ h._host.getcwd() for h in self ]
#
@@ -277,7 +306,8 @@ def getcwd(self):
#
def exists(self, filename, use_sudo=True):
"""
- Returns ``True`` when this file exists on the hosts.
+ Returns an array of boolean values that represent whether this a file
+ with this name exist for each host.
"""
def on_host(container):
return container._host.exists(filename, use_sudo=use_sudo)
@@ -311,7 +341,8 @@ def is_64_bit(self): # TODO: deprecate!!!
class HostContainer(HostsContainer):
"""
- Similar to hostsContainer, but wraps only around exactly one host.
+ Similar to :class:`~deployer.host_container.HostsContainer`, but wraps only
+ around exactly one :class:`~deployer.host.base.Host`.
"""
@property
def _host(self):
@@ -326,7 +357,7 @@ def slug(self):
return self._host.slug
@wraps(Host.get_file)
- def get_file(self, *args,**kwargs):
+ def get_file(self, *args, **kwargs):
kwargs['sandbox'] = self._sandbox
return self._host.get_file(*args, **kwargs)
@@ -369,10 +400,15 @@ def __getattr__(self, name):
def expand_path(self, *a, **kw):
return HostsContainer.expand_path(self, *a, **kw)[0]
- @wraps(HostsContainer.exists)
- def exists(self, *a, **kw):
- return HostsContainer.exists(self, *a, **kw)[0]
+ def exists(self, filename, use_sudo=True):
+ """
+ Returns ``True`` when this file exists on the hosts.
+ """
+ return HostsContainer.exists(self, filename, use_sudo=use_sudo)[0]
- @wraps(HostsContainer.has_command)
- def has_command(self, *a, **kw):
- return HostsContainer.has_command(self, *a, **kw)[0]
+ def has_command(self, command, use_sudo=False):
+ """
+ Test whether this command can be found in the bash shell, by executing
+ a ``which`` Returns ``True`` when the command exists.
+ """
+ return HostsContainer.has_command(self, command, use_sudo=use_sudo)[0]
View
@@ -205,7 +205,9 @@ def default_from_node(cls, node):
Create a default environment for this node to run.
It will be attached to stdin/stdout and commands will be logged to
- stdout. The is the most obvious default.
+ stdout. The is the most obvious default to create an ``Env`` instance.
+
+ :param node: :class:`~deployer.node.base.Node` instance
"""
from deployer.pseudo_terminal import Pty
from deployer.loggers import LoggerInterface
@@ -647,7 +649,10 @@ def __new__(cls, parent=None):
@_internal
def __init__(self, parent=None):
+ #: Reference to the parent :class:`~deployer.node.base.Node`.
+ #: (This is always assigned in the constructor. You should never override it.)
self.parent = parent
+
if self._node_type in (NodeTypes.SIMPLE_ARRAY, NodeTypes.SIMPLE_ONE) and not parent:
raise Exception('Cannot initialize a node of type %s without a parent' % self._node_type)
@@ -801,12 +806,20 @@ class ParallelNode(Node):
Multiple hosts can be given for this role, but all of them will be isolated,
during execution. This allows parallel executing of functions on each 'cell'.
+ If you call a method on a ``ParallelNode``, it will be called one for every
+ host, which can be accessed through the ``host`` property.
+
:note: This was called `SimpleNode` before.
"""
__metaclass__ = ParallelNodeBase
_node_type = NodeTypes.SIMPLE
def host(self):
+ """
+ This is the proxy to the active host.
+
+ :returns: :class:`~deployer.host_container.HostContainer` instance.
+ """
if self._node_is_isolated:
host = self.hosts.filter('host')
if len(host) != 1:
View
@@ -27,12 +27,12 @@ meant to replace anything, it's another tool for your toolbox.
.. _Fabric: http://docs.fabfile.org/
.. _Saltstack: http://saltstack.com
-Questions? Just `create a ticket <create-ticket>`_ in Github for now:
+Questions? Just `create a ticket`_ in Github for now:
-.. _create-ticket: https://github.com/jonathanslenders/python-deployer/issues?state=open
+.. _create a ticket: https://github.com/jonathanslenders/python-deployer/issues?state=open
- - Read the tutorials: :ref:`Hello world <getting-started>` and :ref:`Django
- tutorial <django-tutorial>`
+ - Read the tutorials: :ref:`Hello world <hello-world>` and :ref:`Deploying an
+ application <django-tutorial>`
- Find the source code at `github`_.
.. _github: https://github.com/jonathanslenders/python-deployer
@@ -45,8 +45,8 @@ Table of contents
.. toctree::
:maxdepth: 3
- pages/getting_started
- examples/django-deployment
+ pages/hello_world
+ pages/django-deployment
pages/architecture_of_roles_and_nodes
pages/interactive_shell
@@ -123,9 +123,9 @@ the repository. You can add the ``install_git``, ``git_clone`` and
with self.host.cd(self.project_directory, expand=True):
self.host.run("git checkout '%s'" % esc1(commit))
-Probably obvious, we have a clone and checkout function that are meant to move
-to a certain directory on the server and run a shell command in there. Some
-points worth noting:
+Probably obvious, we have a clone and checkout function that are meant to go
+to a certain directory on the server and run a shell command in there through
+:func:`~deployer.host.base.Host.run`. Some points worth noting:
- ``expand=True``: this means that we should do tilde-expension. You want the
tilde to be replaced with the home directory. If you have an absolute path,
@@ -254,6 +254,7 @@ Anyway, suppose that you have a configuration that you want to upload to
"""
class DjangoDeployment(Node):
+ ...
def upload_django_settings(self):
""" Upload the content of the variable 'local_settings' in the
local_settings.py file. """
@@ -858,4 +859,4 @@ would overwrite it with.
Also, learn about :ref:`query expressions <query-expressions>` and the
-``parent`` variable which are very powerful.
+:attr:`~deployer.node.base.Node.parent` variable which are very powerful.
@@ -1,4 +1,4 @@
-.. _getting-started:
+.. _hello-world:
Tutorial: Hello world
=====================
@@ -1,13 +1,14 @@
host_container
==============
-Access to hosts from within a ``Node`` class happens through a
-``HostsContainer`` proxy. This container object has also methods for reducing
-the amount of hosts on which commands are executed, by filtering according to
-conditions.
+Access to hosts from within a :class:`~deployer.node.base.Node` class happens
+through a :class:`~deployer.host_container.HostsContainer` proxy. This
+container object has also methods for reducing the amount of hosts on which
+commands are executed, by filtering according to conditions.
-The ``hosts`` property of a node instance returns such a ``HostsContainer``
-object.
+The :attr:`~deployer.node.base.Env.hosts` property of
+:class:`~deployer.node.base.Env` wrapper around a node instance returns such a
+:class:`~deployer.host_container.HostsContainer` object.
::
@@ -17,12 +18,14 @@ object.
caching_servers = Host3
def do_something(self):
- # self.hosts here, is a HostsContainer instance.
+ # ``self.hosts`` here is a HostsContainer instance.
self.hosts.filter('caching_servers').run('echo hello')
Reference
---------
-.. automodule:: deployer.host_container
+.. autoclass:: deployer.host_container.HostsContainer
:members:
+.. autoclass:: deployer.host_container.HostContainer
+ :members:
@@ -5,8 +5,9 @@ The interactive shell
=====================
It's very easy to create an interactive command line shell from a node tree.
-Suppose that you have a `deployer.node.Node` called ``MyRootNode``, then you
-can create a shell by making an executable file like this:
+Suppose that you have a :class:`~deployer.node.base.Node` called
+``MyRootNode``, then you can create a shell by making an executable file like
+this:
::
@@ -51,8 +52,10 @@ If you save this as ``client.py`` and call it by typing ``python ./client.py
--version : Show version information.
There are several options to start such a shell. It can be multi or single
-threaded, or you can run it as a telnet-server. Normally, you just type the
-following to get the interactive prompt:
+threaded, or you can run it as a telnet-server. Assuming you made the file also
+executable using ``chmod +x client.py``, you just type the following to get the
+interactive prompt:
+
::
Oops, something went wrong.

0 comments on commit 98c7625

Please sign in to comment.