Places: provisioning hosts

The opslib.places module defines components for provisioning resources inside hosts. They are used to set up configuration files, scripts, and run commands.

Hosts

LocalHost represents the local host where Opslib is run. SshHost represents a host that can be accessed over SSH. Both inherit from BaseHost.

The host components don’t actually deploy anything. They are needed by other components to tell them where to operate, and provide handy factory methods to create them.

from opslib import LocalHost, Stack

stack = Stack(__name__)
stack.host = LocalHost()

Directories and Files

Directory represents a directory in a particular host. During deployment it gets created and optionally a permissions mode is set.

from pathlib import Path
from opslib import Directory

stack.directory = Directory(
    host=stack.host,
    path=Path("/opt/opslib"),
)

Using the shorthand directory() method, this is equivalent:

stack.directory = stack.host.directory("/opt/opslib")

The directory also has a convenience subdir() method to create a subdirectory, which is also aliased to the / operator:

stack.appdir = stack.directory.subdir("app")
stack.appdir = stack.directory / "app"

File represents a file on a particular host.

stack.hello_txt = File(
    host=stack.host,
    path=Path("/opt/opslib/app/hello.txt"),
    content="Hello World!\n",
)

The BaseHost.file() and Directory.file() methods can be used as shorthand; the latter in particular makes for terse code that doesn’t repeat directory paths much:

stack.hello_txt = stack.host.file(
    path=Path("/opt/opslib/app/hello.txt"),
    content="Hello World!\n",
)

stack.hello_txt = stack.appdir.file(
    name="hello.txt",
    content="Hello World!\n",
)

Commands

Command will run a command on a particular host upon deployment.

from textwrap import dedent
from opslib import LocalHost, Stack

stack = Stack(__name__)
stack.host = LocalHost()
stack.directory = stack.host.directory("/opt/opslib/app")
stack.compose_file = stack.directory.file(
    name="docker-compose.yml",
    content=dedent(
        """\
        version: "3"
        services:
          app:
            image: nginx
            ports:
              - 8080:80
        """
    ),
)

compose_args = ["docker", "compose", "--project-directory", stack.directory.path]
stack.compose_up = stack.host.command(
    args=[*self.compose_args, "up", "-d", "-t1"],
)

This will run docker compose up -d, in the directory /opt/opslib/app, on each deployment.

If the run_after prop is set, the command will only run after one of the listed components is deployed and reports that it changed:

stack.compose_up = stack.host.command(
    args=[*self.compose_args, "up", "-d", "-t1"],
    run_after=[stack.compose_file],
)

Defined like this, the compose_up command will only run after docker-compose.yml is changed.