diff --git a/debops b/debops deleted file mode 100755 index 9e41312..0000000 --- a/debops +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -debops: run ansible-playbook with some customization -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - -from __future__ import print_function - -import sys -import os -import ConfigParser - -import ansible - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - - -PATHSEP = ':' - -ConfigFileHeader = """\ -# Ansible configuration file generated by DebOps, all changes will be lost. -# You can manipulate the contents of this file via `.debops.cfg`. -""" - -def write_config(filename, config): - cfgparser = ConfigParser.ConfigParser() - for section, pairs in config.items(): - cfgparser.add_section(section) - for option, value in pairs.items(): - cfgparser.set(section, option, value) - - with open(filename, "w") as fh: - print(ConfigFileHeader, file=fh) - cfgparser.write(fh) - - -def gen_ansible_cfg(filename, config, project_root, playbooks_path, - inventory_path): - # Generate Ansible configuration file - - def custom_paths(type): - if type in defaults: - # prepend value from .debops.cfg - yield defaults[type] - yield os.path.join(project_root, "ansible", type) - yield os.path.join(playbooks_path, type) - yield os.path.join("/usr/share/ansible/", type) - - # Add custom configuration options to ansible.cfg: Take values - # from [ansible ...] sections of the .debops.cfg file - # Note: To set debops default values, use debops.config.DEFAULTS - cfg = dict((sect.split(None, 1)[1], pairs) - for sect, pairs in config.items() if sect.startswith('ansible ')) - - defaults = cfg.setdefault('defaults', {}) - defaults['inventory'] = inventory_path - defaults['roles_path'] = PATHSEP.join(filter(None, ( - defaults.get('roles_path'), # value from .debops.cfg or None - os.path.join(project_root, "roles"), - os.path.join(project_root, "ansible", "roles"), - os.path.join(playbooks_path, "..", "roles"), - os.path.join(playbooks_path, "roles"), - "/etc/ansible/roles"))) - for plugin_type in ('action', 'callback', 'connection', - 'filter', 'lookup', 'vars'): - plugin_type = plugin_type+"_plugins" - defaults[plugin_type] = PATHSEP.join(custom_paths(plugin_type)) - - if ansible.__version__ >= "1.7": - # work around a bug obviously introduced in 1.7, see - # https://github.com/ansible/ansible/issues/8555 - if ' ' in defaults[plugin_type]: - defaults[plugin_type] = PATHSEP.join( - '"%s"' % p for p in defaults[plugin_type].split(PATHSEP)) - - defaults['library'] = PATHSEP.join(custom_paths('library')) - - write_config(filename, cfg) - - -def main(cmd_args): - project_root = find_debops_project(required=True) - config = read_config(project_root) - playbooks_path = find_playbookpath(config, project_root, required=True) - - # Make sure required commands are present - require_commands('ansible-playbook') - - def find_playbook(playbook): - tries = [ - (project_root, "playbooks", playbook), - (project_root, "ansible", "playbooks", playbook), - (playbooks_path, playbook), - ] - - if 'playbooks_path' in config['paths']: - tries += [(custom_path, playbook) for custom_path in - config['paths']['playbooks_path'].split(PATHSEP)] - - for parts in tries: - play = os.path.join(*parts) - if os.path.isfile(play): - return play - - # Check if user specified a potential playbook name as the first - # argument. If yes, use it as the playbook name and remove it from - # the argument list - play = None - if len(cmd_args) > 0: - maybe_play = cmd_args[0] - if os.path.isfile(maybe_play): - play = maybe_play - else: - play = find_playbook(maybe_play + ".yml") - if play: - cmd_args.pop(0) - del maybe_play - if not play: - play = find_playbook("site.yml") - - inventory_path = find_inventorypath(project_root) - os.environ['ANSIBLE_HOSTS'] = inventory_path - - ansible_config_file = os.path.join(project_root, ANSIBLE_CONFIG_FILE) - os.environ['ANSIBLE_CONFIG'] = os.path.abspath(ansible_config_file) - gen_ansible_cfg(ansible_config_file, config, project_root, playbooks_path, - inventory_path) - - # Allow insecure SSH connections if requested - if INSECURE: - os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' - - # Create path to EncFS encrypted directory, based on inventory name - encfs_encrypted = os.path.join(os.path.dirname(inventory_path), - ENCFS_PREFIX + SECRET_NAME) - - # Check if encrypted secret directory exists and use it - revert_unlock = padlock_unlock(encfs_encrypted) - try: - # Run ansible-playbook with custom environment - print("Running Ansible playbook from:") - print(play, "...") - return subprocess.call(['ansible-playbook', play] + cmd_args) - finally: - if revert_unlock: - padlock_lock(encfs_encrypted) - - -try: - sys.exit(main(sys.argv[1:])) -except KeyboardInterrupt: - raise SystemExit('... aborted') diff --git a/debops-defaults b/debops-defaults deleted file mode 100755 index 3e543ac..0000000 --- a/debops-defaults +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -debops-defaults: aggregate all defaults from Ansible roles into one stream -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - -from __future__ import print_function - -import os -import sys -import codecs -import subprocess -import glob -import argparse -import errno - -reload(sys) -sys.setdefaultencoding('utf-8') - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - -def cat(filename, outstream): - try: - fh = codecs.open(filename, encoding=sys.getdefaultencoding()) - except IOError, e: - # This should only happen if the user listed a unknown role. - outstream.write('%s: %s\n' % (e.strerror, e.filename)) - return - try: - # Read input file as Unicode object and pass it to outstream. - outstream.write(fh.read()) - finally: - fh.close() - -def aggregate_defaults(playbooks_path, role_list, outstream): - # Aggregate role/defaults/main.yml files from all roles into one stream - roles_path = os.path.normpath(os.path.join(playbooks_path, '..', 'roles')) - if role_list: - for role in role_list: - if not '.' in role: - role = ROLE_PREFIX + '.' + role - fn = os.path.join(roles_path, role, 'defaults', 'main.yml') - cat(fn, outstream=outstream) - else: - for fn in glob.glob(os.path.join(roles_path, - '*', 'defaults', 'main.yml')): - cat(fn, outstream=outstream) - -# ---- DebOps environment setup ---- - -def main(role_list): - project_root = find_debops_project(required=False) - config = read_config(project_root) - playbooks_path = find_playbookpath(config, project_root, required=True) - - # Make sure required commands are present - require_commands('view') - - if sys.stdout.isatty(): - # if script is run as standalone, redirect to view - view = subprocess.Popen(['view', '+set ft=yaml', '-'], - stdin=subprocess.PIPE) - try: - aggregate_defaults(playbooks_path, role_list, view.stdin) - except IOError, e: - if e.errno not in (errno.EPIPE, errno.EINVAL): - # "Invalid pipe" or "Invalid argument" - raise - finally: - view.communicate() - else: - # else, send everything to stdout - aggregate_defaults(playbooks_path, role_list, sys.stdout) - - -parser = argparse.ArgumentParser() -parser.add_argument('role', nargs='*') -args = parser.parse_args() - -try: - main(args.role) -except KeyboardInterrupt: - raise SystemExit('... aborted') diff --git a/debops-init b/debops-init deleted file mode 100755 index 46cc158..0000000 --- a/debops-init +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -debops-init: create a new DebOps project directory -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - -from __future__ import print_function - -import os -import codecs -import subprocess -import glob -import argparse - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - -SKEL_DIRS = ( - os.path.join("ansible", INVENTORY, "group_vars", "all"), - os.path.join("ansible", INVENTORY, "host_vars"), - os.path.join("ansible", "playbooks"), - os.path.join("ansible", "roles"), -) - -DEFAULT_DEBOPS_CONFIG = """ -# -*- conf -*- - -[paths] -;data-home: /opt/debops - -[ansible defaults] -display_skipped_hosts = False -retry_files_enabled = False -;callback_plugins = /my/plugins/callback -;roles_path = /my/roles - -[ansible paramiko] -;record_host_keys=True - -[ansible ssh_connection] -;ssh_args = -o ControlMaster=auto -o ControlPersist=60s -""" - -DEFAULT_GITIGNORE = """\ -ansible/{SECRET_NAME} -{SECRET_NAME} -{ENCFS_PREFIX}{SECRET_NAME} -ansible.cfg - -#-- python -*.py[co] - -#-- vim -[._]*.s[a-w][a-z] -[._]s[a-w][a-z] -*.un~ -Session.vim -.netrwhist -*~ - -#-- Emacs -\#*\# -/.emacs.desktop -/.emacs.desktop.lock -*.elc -auto-save-list -tramp -.\#* - -#-- SublimeText -*.sublime-workspace -#*.sublime-project - -#-- sftp configuration file -sftp-config.json -""" - -HOSTS_FILE_HEADER = """\ -# This is an Ansible inventory file in INI format. You can define a list of -# hosts and groups to be managed by this particular inventory. - -# Hosts listed under [debops_all_hosts] will have common DebOps plays -# ran against them. It will include services such as iptables, DNS, Postfix, -# sshd configuration and more. -# -# View the list here: -# https://github.com/debops/debops-playbooks/blob/master/playbooks/common.yml -# -# You should check Getting Started guide for useful suggestions: -# https://docs.debops.org/en/latest/debops-playbooks/docs/guides/getting-started.html -""" - -HOSTS_FILE_CONTENT_CONTROLER = """ -# Your host is eligible to be managed by DebOps' common playbook. If you want -# that functionality and more, then uncomment your hostname below. - -[debops_all_hosts] -#%s ansible_connection=local -""" % platform.node() - -HOSTS_FILE_CONTENT_NO_CONTROLER = """ -# Your host was not detected as compatible with DebOps playbooks, so you will -# not be able to leverage the above features on your current operating system. -# You can however use a virtual machine as the Ansible Controller. - -[debops_all_hosts] - -""" - - -def write_file(filename, *content): - """ - If file:`filename` does not exist, create it and write - var:`content` into it. - """ - if not os.path.exists(filename): - with open(filename, "w") as fh: - fh.writelines(content) - - -def write_config_files(project_root): - """ - Create the default debops-config files in the dir:`project_root` - directory. - """ - # Create .debops.cfg - write_file(os.path.join(project_root, DEBOPS_CONFIG), DEFAULT_DEBOPS_CONFIG) - # Create .gitignore - write_file(os.path.join(project_root, '.gitignore'), - DEFAULT_GITIGNORE.format(SECRET_NAME=SECRET_NAME, ENCFS_PREFIX=ENCFS_PREFIX)) - - hosts_filename = os.path.join(project_root, "ansible", INVENTORY, "hosts") - # Swap in different hosts file content depending on the host's OS/distro - if (platform.system() == "Linux" and - platform.linux_distribution()[0].lower() in ("debian", "ubuntu")): - write_file(hosts_filename, - HOSTS_FILE_HEADER, HOSTS_FILE_CONTENT_CONTROLER) - else: - write_file(hosts_filename, - HOSTS_FILE_HEADER, HOSTS_FILE_CONTENT_NO_CONTROLER) - - -def main(project_root): - orig_project_root = project_root - project_root = os.path.abspath(project_root) - - #-- Check for existing debops project directory - debops_project_root = find_debops_project(project_root, required=False) - - # Exit if DebOps configuration file has been found in project_dir - if os.path.exists(os.path.join(project_root, DEBOPS_CONFIG)): - error_msg("%s is already a DebOps project directory" % project_root) - - # Exit if we are in a DebOps project dir and nested project would be created - if debops_project_root: - error_msg("You are inside %s project already" % debops_project_root) - - #-- Main script - - print("Creating new DebOps project directory in", orig_project_root, "...") - - # Create base project directories - for skel_dir in SKEL_DIRS: - skel_dir = os.path.join(project_root, skel_dir) - if not os.path.isdir(skel_dir): - os.makedirs(skel_dir) - # Write default config files - write_config_files(project_root) - - -parser = argparse.ArgumentParser() -parser.add_argument('project_dir', default=os.curdir) -args = parser.parse_args() - -try: - main(args.project_dir) -except KeyboardInterrupt: - raise SystemExit('... aborted') diff --git a/debops-padlock b/debops-padlock deleted file mode 100755 index 7b7ceb6..0000000 --- a/debops-padlock +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -debops-padlock: encrypt secret directory with EncFS and GPG -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - -from __future__ import print_function - -import os -import shutil -import argparse -import itertools -import stat -import sys -import time -from pkg_resources import resource_filename - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - -def gen_pwd(): - from string import ascii_letters, digits, punctuation - import random - ALLCHARS = digits + ascii_letters + punctuation - ALLCHARS = digits + ascii_letters + '-_!@#$%^&*()_+{}|:<>?=' - pwd = ''.join(random.choice(ALLCHARS) for i in range(ENCFS_KEYFILE_LENGTH)) - return pwd - - -# Randomness source for EncFS keyfile generation -devrandom = os.environ.get('DEVRANDOM', "/dev/urandom") - -SCRIPT_FILENAME = 'padlock-script' - -# ---- DebOps environment setup ---- - -def main(subcommand_func, **kwargs): - project_root = find_debops_project(required=True) - # :todo: Source DebOps configuration file - #[ -r ${debops_config} ] && source ${debops_config} - - # ---- Main script ---- - - # Make sure required commands are present - require_commands('encfs', 'find', 'fusermount', 'gpg') - - inventory_path = find_inventorypath(project_root, required=False) - # If inventory hasn't been found automatically, assume it's the default - if not inventory_path: - inventory_path = os.path.join(project_root, 'ansible', INVENTORY) - - # Create names of EncFS encrypted and decrypted directories, based on - # inventory name (absolute paths are specified) - encfs_encrypted = os.path.join(os.path.dirname(inventory_path), - ENCFS_PREFIX + SECRET_NAME) - encfs_decrypted = os.path.join(os.path.dirname(inventory_path), - SECRET_NAME) - subcommand_func(encfs_decrypted, encfs_encrypted, **kwargs) - - -def init(encfs_decrypted, encfs_encrypted, recipients): - # EncFS cannot create encrypted directory if directory with - # decrypted data is not empty - if not os.path.exists(encfs_decrypted): - os.makedirs(encfs_decrypted) - elif os.listdir(encfs_decrypted): - error_msg("secret directory not empty") - - # Quit if encrypted directory already exists. - if os.path.isdir(encfs_encrypted): - error_msg("EncFS directory already exists") - os.makedirs(encfs_encrypted) - - encfs_keyfile = os.path.join(encfs_encrypted, ENCFS_KEYFILE) - encfs_configfile = os.path.join(encfs_encrypted, ENCFS_CONFIGFILE) - - # put a `-r` in front of each recipient for passing as args to gpg - recipients = list(itertools.chain.from_iterable(['-r', r] - for r in recipients)) - - # Generate a random password and encrypt it with GPG keys of recipients. - print("Generating a random", ENCFS_KEYFILE_LENGTH, "char password") - pwd = gen_pwd() - gpg = subprocess.Popen(['gpg', '--encrypt', '--armor', - '--output', encfs_keyfile] + recipients, - stdin=subprocess.PIPE) - gpg.communicate(pwd) - - # Mount the encfs to the config file will be written. Tell encfs - # it to ask gpg for the password. - # NB1: Alternativly we could use --stdinpass, but using --extpass makes - # the user check if she has the correct passphrase early. - # NB2: We can not use padlock_unlock here, because the config file - # does not yet exist. - encfs = subprocess.Popen([ - 'encfs', encfs_encrypted, encfs_decrypted, - '--extpass', 'gpg --no-mdc-warning --output - '+shquote(encfs_keyfile)], - stdin=subprocess.PIPE) - encfs.communicate('p\n'+pwd) - - # Create padlock-script - padlock_script = os.path.join(encfs_encrypted, PADLOCK_CMD) - - # :todo: use resource_stream - shutil.copy(resource_filename('debops', SCRIPT_FILENAME), padlock_script) - os.chmod(padlock_script, - os.stat(padlock_script).st_mode|stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH) - - # Lock the EncFS directory after creation - time.sleep(0.5) # :fixme: why sleeping here? - padlock_lock(encfs_encrypted) - - # Protect the EncFS configuration file by also encrypting it with - # the GPG keys of recipients. - subprocess.call(['gpg', '--encrypt', '--armor', - '--output', encfs_configfile+'.asc'] - + recipients + [encfs_configfile]) - os.remove(encfs_configfile) - - -def lock(encfs_decrypted, encfs_encrypted, verbose): - # Unmount the directory if it is mounted - if padlock_lock(encfs_encrypted): - if verbose: print("Locked!") - else: - if verbose: print("Is already locked.") - - -def unlock(encfs_decrypted, encfs_encrypted, verbose): - # Mount the directory it if it is unmounted - if padlock_unlock(encfs_encrypted): - if verbose: print("Unlocked!") - else: - if verbose: print("Is already unlocked.") - - -parser = argparse.ArgumentParser() -subparsers = parser.add_subparsers( - help='action to perform. Use `%(prog)s --help ` for further help.') - -p = subparsers.add_parser('init') -p.add_argument('recipients', nargs='*', - help=("GPG recipients for which the secret key should be " - "encrypted for (name, e-mail or key-id)")) -p.set_defaults(subcommand_func=init) - -p = subparsers.add_parser('unlock') -p.add_argument('-v', '--verbose', action='store_true', help="be verbose") -p.set_defaults(subcommand_func=unlock) - -p = subparsers.add_parser('lock') -p.add_argument('-v', '--verbose', action='store_true', help="be verbose") -p.set_defaults(subcommand_func=lock) - -args = parser.parse_args() - -try: - main(**vars(args)) -except KeyboardInterrupt: - raise SystemExit('... aborted') diff --git a/debops-task b/debops-task deleted file mode 100755 index f0aaff8..0000000 --- a/debops-task +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -debops-task: run ansible with some customization -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - - -from __future__ import print_function - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - - -DEBOPS_RESERVED_NAMES = ["task", "init", "update", "defaults", "padlock"] - -# ---- DebOps environment setup ---- - -# Find DebOps configuration file -project_root = find_debops_project(required=True) -# Source DebOps configuration file -###----- todo: need to decide on semantics! -#config = read_config(project_root) - - -# ---- Main script ---- - -# Make sure required commands are present -require_commands('ansible') - -ansible_inventory = find_inventorypath(project_root) - -# Get module name from the script name if script is named 'debops-*' -module_name = SCRIPT_NAME.rsplit('-', 1)[-1] -if module_name not in DEBOPS_RESERVED_NAMES: - module = ["-m", module_name] -else: - module = [] - -os.environ['ANSIBLE_HOSTS'] = os.path.abspath(ansible_inventory) - -# Allow insecure SSH connections if requested -if INSECURE: - os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' - -# Run ansible with custom environment -cmd = ['ansible'] + module + sys.argv[1:] -subprocess.call(cmd) diff --git a/debops-update b/debops-update deleted file mode 100755 index ab4ca84..0000000 --- a/debops-update +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -(""" -debops-update: install or update DebOps playbooks and roles -""" -# Copyright (C) 2014-2015 Hartmut Goebel -# Part of the DebOps - https://debops.org/ - - -# This program is free software; you can redistribute -# it and/or modify it under the terms of the -# GNU General Public License as published by the Free -# Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will -# be useful, but WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public -# License for more details. -# -# You should have received a copy of the GNU General -# Public License along with this program; if not, -# write to the Free Software Foundation, Inc., 59 -# Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# An on-line copy of the GNU General Public License can -# be downloaded from the FSF web page at: -# https://www.gnu.org/copyleft/gpl.html - -""" -This script can be used to install or update installed DebOps playbooks and -roles to current or specified version. By default it works on the installed -playbook in users $HOME/.local/share/debops directory, but it can also be -used on locally installed playbooks and roles in current directory. - -Short usage guide: - -- 'debops-update' will check if we are in DebOps project directory - ('.debops.cfg' exists) - * if yes, it will check if 'debops-playbooks/playbooks/site.yml' exists - * if yes, update playbooks and roles in $PWD - * if no, check if DebOps playbooks are installed in known places, - like ~/.local/share/debops - * if yes, update playbooks in a place that they are installed at - * if no, install DebOps playbooks in - ~/.local/share/debops/debops-playbooks - -- 'debops-update path/to/dir' will check if specified directory exists - * if no, create it - * if yes, check if DebOps playbooks are installed at $path/debops-playbooks - * if yes, update them - * if no, install DebOps playbooks at $path/debops-playbooks - -""") - -from __future__ import print_function - -import os -import subprocess -import argparse - -from debops import * -from debops.cmds import * - -__author__ = "Hartmut Goebel " -__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " -__licence__ = "GNU General Public License version 3 (GPL v3) or later" - - -# ---- Configuration variables ---- - -# Default URI of DebOps (user https for server authentication) -GIT_URI = "https://github.com/debops" - -# Default git sources for debops-playbooks -PLAYBOOKS_GIT_URI = GIT_URI + "/debops-playbooks" - -# Default slug prefix for roles -GIT_ROLE_PREFIX = "ansible-" - -# Ansible Galaxy requirements file to use by default to download or update -GALAXY_REQUIREMENTS = "galaxy/requirements.txt" - -# Default Ansible Galaxy user account name -GALAXY_ACCOUNT = "debops" - - -# ---- Functions ---- - -def fetch_or_clone_roles(roles_path, requirements_file, dry_run=False): - """ - Efficiently fetch or clone a role - """ - with open(requirements_file) as fh: - requirements = [r.strip().split() for r in fh.readlines()] - num_roles = len(requirements) - - for cnt, role_name in enumerate(requirements, 1): - # Parse the requirements.txt file to extract the role name and version - try: - role_name, role_version = role_name[:2] - except: - role_name = role_name[0] - role_version = 'master' - - # :todo: rethink if we really want this - if role_name.startswith(GALAXY_ACCOUNT + '.'): - galaxy_name = role_name - role_name = role_name.split('.', 1)[1] - else: - galaxy_name = GALAXY_ACCOUNT + '.' + role_name - - remote_uri = GIT_URI + '/' + GIT_ROLE_PREFIX + role_name - destination_dir = os.path.join(roles_path, galaxy_name) - progress_label="[{role_version}] ({cnt}/{num_roles})".format(**locals()) - - # Either update or clone the role - if os.path.exists(destination_dir): - print("Updating", remote_uri, progress_label) - update_git_repository(destination_dir, dry_run, remote_uri) - else: - print() - print("Installing", remote_uri, progress_label) - clone_git_repository(remote_uri, role_version, destination_dir, dry_run) - print() - - -def clone_git_repository(repo_uri, branch, destination, dry_run=False): - if dry_run: - print("Cloning '%s' to %s..." % (repo_uri, destination)) - else: - subprocess.call(['git', 'clone', '--quiet', '--branch', branch, - repo_uri, destination]) - -def update_git_repository(path, dry_run=False, remote_uri=False): - """ - Update an exiting git repository. - - To get nice output, merge only of origin as updates. - """ - # Move into the role's directory - old_pwd = os.getcwd() - os.chdir(path) - - if dry_run: - subprocess.call(['git', 'fetch']) - subprocess.call(['git', 'diff', 'HEAD', 'origin', '--stat']) - else: - # Get the current sha of the head branch - current_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() - - # Fetch it silently and store the new sha - subprocess.call(['git', 'fetch', '--quiet']) - fetch_sha = subprocess.check_output(['git', 'rev-parse', 'FETCH_HEAD']).strip() - - if current_sha != fetch_sha: - print() - print('--') - subprocess.call(['git', 'merge', fetch_sha]) - - if remote_uri: - compare_uri = remote_uri + '/compare/' + current_sha[:7] + '...' + fetch_sha[:7] - print() - print("Compare:", compare_uri) - - print('--') - print() - - # Move back to the initial directory - os.chdir(old_pwd) - - -# ---- Main script ---- - -def main(project_dir=None, dry_run=False): - - # Check if user specified a directory as a parameter, if yes, use it as - # a project directory and clone DebOps playbooks inside - if project_dir: - # If it's a new project, create the directory for it - if not os.path.exists(project_dir): - print ("Creating project directory in", project_dir) - if not dry_run: - os.makedirs(project_dir) - - # Make sure that playbooks and roles will be installed in project - # directory if it's specified - install_path = os.path.join(project_dir, "debops-playbooks") - - # If playbooks already are installed in specified location, set them as - # currently used for eventual update - if os.path.isfile(os.path.join(install_path, DEBOPS_SITE_PLAYBOOK)): - playbooks_path = install_path - else: - playbooks_path = None - - else: - # If there's no project specified, look for playbooks in known locations - project_root = find_debops_project(required=False) - config = read_config(project_root) - playbooks_path = find_playbookpath(config, project_root, required=False) - if playbooks_path: - install_path = os.path.dirname(playbooks_path) - else: - install_path = config['paths']['install-path'] - - roles_path = os.path.join(install_path, 'roles') - - # ---- Create or update the playbooks and roles ---- - - # Playbooks have not been found, at this point assume playbooks are not - # installed. Install them in user home directory - if not playbooks_path: - if dry_run: - raise SystemExit("--dry-run requires DebOps playbooks.\n" \ - "Run debops-update without --dry-run first.") - - # Download/clone main debops-playbooks repository - print("DebOps playbooks have not been found, installing into", - install_path) - print() - - clone_git_repository(PLAYBOOKS_GIT_URI, 'master', install_path, dry_run) - os.chdir(install_path) - os.makedirs(roles_path) - else: - # Update found debops-playbooks repository - print("DebOps playbooks have been found in", install_path) - update_git_repository(install_path, dry_run) - print() - os.chdir(install_path) - - # Now install or update the roles into roles_path - fetch_or_clone_roles(roles_path, GALAXY_REQUIREMENTS, dry_run) - - -parser = argparse.ArgumentParser() -parser.add_argument('--dry-run', action='store_true', - help='perform a trial run with no changes made') -parser.add_argument('project_dir', nargs='?') -args = parser.parse_args() - -try: - main(args.project_dir, args.dry_run) -except KeyboardInterrupt: - raise SystemExit('... aborted')