Welcome to Refit’s documentation!¶
Introduction¶
Welcome to Refit - simple remote server configuration, using asyncio.
Installation¶
Make sure Python 3.7 or above is installed, then install the following (preferably in a virtualenv):
pip install refit
Creating a project¶
For the purposes of the documentation, we’ll assume we’re creating a pet shop web app.
refit scaffold pet_shop
This will create a deployments
folder in the current directory, with a
pet_shop
folder inside, containing a hosts.py
and tasks.py
file.
You can create as many different deployments as you like - each one represents a collection of tasks which achieve some objective. For example deploying a web app, or configuring a database server.
Hosts¶
In the hosts file, we define the remote machines we want to connect to.
It’s important that each Host
gets registered with the HostRegistry
.
The recommended way of doing this is using the decorator syntax.
# Generated by Refit version: 0.1.0
from refit.host import Host
from refit.registry import HostRegistry
host_registry = HostRegistry()
@host_registry.register(environment="production")
class PetShopProduction(Host):
address = "127.0.0.1"
environment_vars = {}
@host_registry.register(environment="test")
class PetShopTest(Host):
address = "127.0.0.1"
environment_vars = {}
Hint
Refit uses SSH to communicate with remote servers. In order to access
your remote servers, make sure your SSH public key is present in the
known_hosts
file on each remote server. For example,
/home/my_user/.ssh/known_hosts
.
Tasks¶
Tasks are what get run on hosts. Examples are uploading files, or running a bash command.
Similarly to Host
, it’s important that each Task
gets registered with
the TaskRegistry
, otherwise it won’t get run.
# Generated by Refit version: 0.1.0
from refit.task import Task
from refit.registry import TaskRegistry
task_registry = TaskRegistry()
@task_registry.register
class PetShop(Task):
"""
Provision PetShop.
"""
async def run(self):
path = "/tmp/hello_world"
if not await self.path_exists(path):
await self.create_folder(path)
await self.raw('touch /tmp/hello_world/greetings.txt')
The order in which a Task
is added to the registry determines the order
in which it runs.
run¶
Each Task has an async run
method, which performs the actual work.
You can do whatever you like within this method, but a lot of the time you’ll be calling other Task methods, which implement the bulk of Refit’s functionality. Under the hood, these are implemented as Mixins.
Running tasks¶
Once you have defined your hosts and tasks, you run them with the following command:
refit deploy --environment=test pet_shop
You’ll notice in your hosts file that there’s multiple Host
subclasses,
one for each environment. You need to specify which environment you want to
deploy to when running your tasks.
Mixins¶
The Task
class inherits from many mixins, which provide a lot of useful
utilities for performing common server admin tasks.
AptMixin¶
DockerMixin¶
FileMixin¶
-
class
refit.mixins.file.
FileMixin
[source]¶ -
-
async
create_folder
(path, owner='root', group='root', permissions='755')[source]¶ Creates folder, and all intermediate directories.
Only changes the group and owner of the deepest directory. If each folder in the chain needs certain permissions, call this function repeatedly for each folder.
- Return type
None
-
async
PathMixin¶
PythonMixin¶
SystemdMixin¶
TemplateMixin¶
Custom Mixins¶
There’s nothing magical about the builtin mixins - you can develop your own, and inherit from them.
from refit.task import Task
class MyMixin():
def hello_world(self):
print('hello world')
class MyTask(Task, MyMixin):
async def run(self):
self.hello_world()
Task Sequencing¶
You can use Refit to execute commands sequentially on a single server. However, much of it’s power is in running several commands at the same time - either on a single machine, or across multiple machines.
TaskRegistry¶
Instead of adding your Task
to the TaskRegistry
using the decorator
syntax, you can also use register
or gather
.
register¶
This tells the task registry to execute the given tasks sequentially.
from ..shared.tasks import AddKeysTask, CreateDatabaseTask
task_registry = TaskRegistry()
task_registry.register(AddKeysTask, CreateDatabaseTask)
gather¶
This tells the task registry to execute the given tasks concurrently.
from ..shared.tasks import AddKeysTask, CreateDatabaseTask
task_registry = TaskRegistry()
task_registry.gather(AddKeysTask, CreateDatabaseTask)
Contributing¶
Mixins¶
The easiest way to contribute is to add new Mixins, or extend existing ones, with common functionality.
Style Guide¶
The Black formatter is used, with a line length of 79 to make it consistent with PEP8.
Tests¶
Some tests require Docker to be running, so Refit can actually SSH onto a server to execute commands.
Modules¶
task¶
-
class
refit.task.
Concurrent
(host_class)[source]¶ Bases:
refit.task.Task
Bundles several tasks to be run concurrently.
-
class
refit.task.
Task
(host_class)[source]¶ Bases:
refit.mixins.apt.AptMixin
,refit.mixins.docker.DockerMixin
,refit.mixins.file.FileMixin
,refit.mixins.path.PathMixin
,refit.mixins.python.PythonMixin
,refit.mixins.template.TemplateMixin
-
classmethod
create
(host_registry, environment)[source]¶ Creates and runs a task for all matching hosts.
- Return type
None
-
abstract async
run
()[source]¶ Override in subclasses. This is what does the actual work in the task, and is awaited when the Task is run.
- Return type
None
-
sub_tasks
= []¶
-
classmethod
-
refit.task.
new_gathered_task
(tasks)[source]¶ Task definitions are classes, not instances, hence why we require this.
- Parameters
tasks (
Iterable
[Type
[Task
]]) – A list of Task classes to execute.- Return type
Type
[Concurrent
]
registry¶
-
class
refit.registry.
HostRegistry
[source]¶ -
get_host_classes
(environment, tags)[source]¶ Returns hosts matching the given tags.
If no tags are given, all hosts match.
- Return type
Sequence
[Type
[Host
]]
-