How to use Fabric with Salt-Stack Posted on 2015-09-20 in dev I like to use Fabric in combination with Salt-Stack. My current setup looks like: from fabric.api import sudo, task, env, local, settings from fabric.contrib.files import upload_template from fabric.contrib.project import upload_project import os salt_task = hosts('@'.join([os.environ['AS_MASTER_USER'], os.environ['AS_MASTER_HOST']])) """Decorator to specify Salt-Stack master user and host.""" @task def bootstrap_master(): """Use insecure one-liner to install Salt-Stack master.""" # XXX: Fails on Docker, see # https://github.com/saltstack/salt-bootstrap/issues/394 with settings(warn_only=True): sudo('curl -L http://bootstrap.saltstack.org | sudo sh -s -- -M -N') upload_template( filename='master.template', destination='/etc/salt/master', template_dir='config', use_sudo=True, use_jinja=True, ) sudo('service salt-master restart') @task def bootstrap(master_hostname, hostname): """Setup salt minion. $ fab -H bootstrap:, """ with settings(warn_only=True): sudo('curl -L http://bootstrap.saltstack.org | sudo sh') context = { 'master_hostname': master_hostname, 'id': hostname, } upload_template( filename='minion.template', destination='/etc/salt/minion', template_dir='config', context=context, use_sudo=True, use_jinja=True, ) sudo('service salt-minion restart') @task def sync_states(): """Sync Salt-Stack state files.""" upload_project(local_dir='config/salt', remote_dir='/srv', use_sudo=True) upload_project(local_dir='config/pillar', remote_dir='/srv', use_sudo=True) @task def master(): """Run in production.""" env.user = os.environ['AS_MASTER_USER'] env.hosts = os.environ['AS_MASTER_HOST'] @task def vagrant(): """Run tasks on vagrant instance.""" env.user = 'vagrant' env.hosts = ['127.0.0.1:2222'] result = local('vagrant ssh-config | grep IdentityFile', capture=True) env.key_filename = result.split()[1] @salt_task @task def nginx_reload(*servername_seq): """Restart Nginx service.""" salt('service.relaod nginx', *servername_seq) def salt(cmd, *servername_seq): """Run salt command on given hosts.""" args = '-L' if '*' not in servername_seq else '' cmd = cmd if cmd else 'cmd.run "uptime"' env.output_prefix = False sudo('salt {args} "{servername_csv}" {cmd}'.format( args=args, servername_csv=','.join(servername_seq), cmd=cmd, )) Bootstrap master: fab master bootstrap_master To add new minion run: fab -H <minion_host> -u <minion_user> bootstrap:<master_ip>,<human_readable_minion_hostname> Project layout: ├── config │ ├── master.template │ ├── minion.template │ ├── pillar │ │ ├── base.sls │ │ ├── homepage.sls │ │ └── top.sls │ └── salt │ ├── _modules │ ├── _states │ ├── apps │ ├── firewall │ ├── nginx │ ├── python │ ├── supervisor │ └── top.sls ├── env │ ├── AS_MASTER_HOST │ └── AS_MASTER_USER ├── fabfile.py └── requirements.txt Keep configuration outside code with envdir. $AS_MASTER_USER, $AS_MASTER_HOST environment variables contains master host details. Explicitly copy state files to master. Allows to cheat and check states without committing to configuration repository. Python Fabric Salt-Stack envdir