Esempio n. 1
0
def parse_action(action, parsed):
    """ Parse the action to execute. """
    if action == 'deploy':
        deploy(parsed.configuration)

    elif action == 'list':
        list_configs()

    elif action == 'new':
        new_config(parsed.name)

    elif action == 'prepare':
        deploy(parsed.configuration, prepare=True)

    elif action == 'remove':
        remove_config(parsed.name)

    else:
        util.cprint(m.FUMI_UNKNOWN)
Esempio n. 2
0
def remove_config(name):
    """Remove a configuration from the fumi.yml file.

    Arguments:
        name (str): Name of the configuration to remove.
    """
    status, content = util.read_yaml(FUMI_YML)

    if not status:
        sys.exit(-1)

    if not content:
        util.cprint(m.NO_YML, 'red')
        sys.exit(-1)

    if name not in content.keys():
        util.cprint(m.CONF_NAME_NOT_FOUND % name, 'red')
        sys.exit(-1)

    # Remove section
    del content[name]

    status = util.write_yaml(FUMI_YML, content)
    if not status:
        sys.exit(-1)

    util.cprint(m.CONF_REMOVED % name, 'green')
Esempio n. 3
0
def new_config(name):
    """Create new basic configuration in fumi.yml file.

    Arguments:
        name (str): Name for the new configuration.
    """
    status, content = util.read_yaml(FUMI_YML)

    if not status:
        sys.exit(-1)

    if not content:
        # Starting from scratch
        content = {}

    if name in content.keys():
        # Do not overwrite configuration
        util.cprint(m.CONF_EXISTS % name, 'red')
        sys.exit(-1)

    content[name] = {
        'source-type': '',
        'source-path': '',
        'predep': [],
        'postdep': [],
        'host': '',
        'user': '',
        'use-password': False,
        'password': '',
        'deploy-path': '',
    }

    status = util.write_yaml(FUMI_YML, content)
    if not status:
        sys.exit(-1)

    util.cprint(m.CREATED_BLANK % name)
Esempio n. 4
0
def build_deployer(config):
    """Build a Deployer object.

    Arguments:
        config (dict): Parsed section of the YAML configuration file.

    Returns:
        Boolean indicating result and ``Deployer`` instance or ``None``.
    """
    try:
        deployer = Deployer(**config)

    except KeyError as e:
        # Missing required parameter
        key = e.args[0]
        cprint(m.DEP_MISSING_PARAM + '\n' % key, 'red')
        return False, None

    # Determine deployment function to use
    if deployer.source_type == 'local':
        cprint(m.DEP_LOCAL)
        deployer.deploy = types.MethodType(deployments.deploy_local, deployer)

    elif deployer.source_type == 'git':
        cprint(m.DEP_GIT)
        deployer.deploy = types.MethodType(deployments.deploy_git, deployer)

    else:
        # Unknown deployment type
        cprint(m.DEP_UNKNOWN % deployer.source_type, 'red')
        return False, None

    # Additional method for preparing/testing the deployment
    deployer.prepare = types.MethodType(deployments.prepare, deployer)

    return True, deployer
Esempio n. 5
0
def list_configs():
    """List the configurations present in the fumi.yml file."""
    status, content = util.read_yaml(FUMI_YML)

    if not status:
        sys.exit(-1)

    if not content:
        util.cprint(m.NO_YML, 'red')
        sys.exit(-1)

    for conf in content.keys():
        is_default = content[conf].get('default', False)

        if is_default:
            util.cprint(m.LIST_DEFAULT % conf)

        else:
            util.cprint('- %s' % conf)
Esempio n. 6
0
def deploy(deployer):
    """Git based deployment.

    Arguments:
        deployer (``Deployer``): Deployer instance.

    Returns:
        Boolean indicating result of the deployment.
    """
    # SSH connection
    util.cprint('> ' + m.DEP_CONNECTING % (deployer.host, deployer.user),
                'cyan')

    status, ssh = util.connect(deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.DEP_CONNECTED + '\n', 'green')

    # Predeployment commands
    status, util.run_commands(ssh, deployer.predep)
    if not status:
        ssh.close()
        return False

    # Directory structures
    util.cprint('> ' + m.DEP_CHECK_REMOTE, 'cyan')

    status = util.check_dirs(ssh, deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.CORRECT + '\n', 'green')

    # Clone source
    timestamp = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
    util.cprint(m.DEP_PREPARE_REV % timestamp, 'white')

    util.cprint('> ' + m.DEP_GIT_CLONE, 'cyan')

    rev_path = os.path.join(deployer.deploy_path, 'rev')
    current_rev = os.path.join(deployer.deploy_path, 'rev', timestamp)

    clone = 'git clone %s %s' % (deployer.source_path, current_rev)

    stdin, stdout, stderr = ssh.exec_command(clone)
    status = stdout.channel.recv_exit_status()

    if status == 127:
        util.cprint(m.DEP_MISSING_PARAM)
        ssh.close()
        return False
    # TODO: check git exit codes

    util.cprint(m.DONE + '\n', 'green')

    # Link directory
    status = util.symlink(ssh, deployer, rev_path, timestamp)
    if not status:
        util.rollback(ssh, deployer, timestamp, 3)
        ssh.close()
        return False

    # Link shared paths
    util.symlink_shared(ssh, deployer)

    # Run post-deployment commands
    status = util.run_commands(ssh, deployer.postdep,
                               os.path.join(deployer.deploy_path, 'current'))

    if not status:
        util.rollback(ssh, deployer, timestamp, 3)
        ssh.close()
        return False

    # Clean revisions
    if deployer.keep_max:
        status = util.clean_revisions(ssh, deployer.keep_max, rev_path)

    util.cprint(m.DEP_COMPLETE, 'green')

    # Close SSH connection
    ssh.close()

    return True
Esempio n. 7
0
def prepare(deployer):
    """Test connection and prepare directories.

    Arguments:
        deployer (``Deployer``): Deployer instance.

    Returns:
        Boolean indicating result of the preparation.
    """
    # SSH connection
    util.cprint('> ' + m.DEP_CONNECTING % (deployer.host, deployer.user),
                'cyan')

    status, ssh = util.connect(deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.DEP_CONNECTED + '\n', 'green')

    # Directory structures
    util.cprint('> ' + m.DEP_CREATEDIR, 'cyan')

    status = util.create_dirs(ssh, deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.CORRECT + '\n', 'green')

    util.cprint(m.DEP_PREPARE_COMPLETE, 'green')
    util.cprint(m.DEP_PREPARE_NOTICE, 'white')

    # Close SSH connection
    ssh.close()

    return True
Esempio n. 8
0
def deploy(conf_name, prepare=False):
    """Deploy using given configuration.

    Arguments:
        conf_name (str): Name of the configuration to use in the deployment.
        prepare (bool): Whether or not this is a preparation deployment.
            A preparation simply checks connection and creates the remote
            directory tree.
    """
    status, content = util.read_yaml(FUMI_YML)

    if not status:
        sys.exit(-1)

    if not content:
        util.cprint(m.NO_YML, 'red')
        sys.exit(-1)

    if not conf_name:
        # Find default configuration
        default = None

        for k in content.keys():
            if content[k].get('default'):
                # Found default configuration
                default = k
                util.cprint(m.USE_DEFAULT_CONF % default)
                break

        if not default:
            # Default not found
            if len(content.keys()) == 0:
                util.cprint(m.NO_CONFS, 'red')
                sys.exit(-1)

            # Ask for default
            util.cprint(m.CONFS_FOUND)

            for k in content.keys():
                util.cprint('- %s' % k)

            default = six.moves.input(m.CONF_SET_DEF)

            if default in content.keys():
                content[default]['default'] = True
                util.write_yaml(FUMI_YML, content)

            else:
                # Welp...
                util.cprint(m.CONF_NOT_FOUND, 'red')
                sys.exit(-1)

        conf_name = default

    elif conf_name not in content.keys():
        util.cprint(m.CONF_NAME_NOT_FOUND % conf_name, 'red')
        sys.exit(-1)

    # Build deployer
    status, deployer = build_deployer(content[conf_name])

    if not status:
        sys.exit(-1)

    if prepare:
        # Preparation
        result = deployer.prepare()

    else:
        # Deploy!
        result = deployer.deploy()

    if not result:
        sys.exit(-1)
Esempio n. 9
0
def deploy(deployer):
    """Local based deployment.

    Arguments:
        deployer (``Deployer``): Deployer instance.

    Returns:
        Boolean indicating result of the deployment.
    """
    # SSH connection
    util.cprint('> ' + m.DEP_CONNECTING % (deployer.host, deployer.user),
                'cyan')

    status, ssh = util.connect(deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.DEP_CONNECTED + '\n', 'green')

    # Predeployment commands
    status, util.run_commands(ssh, deployer.predep)
    if not status:
        ssh.close()
        return False

    # Directory structures
    util.cprint('> ' + m.DEP_CHECK_REMOTE, 'cyan')

    status = util.check_dirs(ssh, deployer)
    if not status:
        ssh.close()
        return False

    util.cprint(m.CORRECT + '\n', 'green')

    # Compress source to temporary directory
    timestamp = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
    util.cprint(m.DEP_PREPARE_REV % timestamp, 'white')

    compressed_file = timestamp + '.tar.gz'
    tmp_local = os.path.join('/tmp', compressed_file)

    if deployer.local_ignore:
        cnt_list = list(
            set(os.listdir(deployer.source_path)) ^ set(deployer.local_ignore))

    else:
        cnt_list = os.listdir(deployer.source_path)

    util.cprint('> ' + m.DEP_LOCAL_COMPRESS % tmp_local, 'cyan')

    with tarfile.open(tmp_local, "w:gz") as tar:
        for item in cnt_list:
            path = os.path.join(deployer.source_path, item)

            if os.path.exists(path):
                # Compress
                tar.add(path, arcname=timestamp + '/' + item)

            else:
                # Ignore
                util.cprint(m.DEP_LOCAL_PATHNOEXIST % path, 'white')

    util.cprint(m.DONE + '\n', 'green')

    # Upload compressed source
    util.cprint('> ' + m.DEP_LOCAL_UPLOAD % compressed_file, 'cyan')

    try:
        uload = scp.SCPClient(ssh.get_transport(),
                              buff_size=deployer.buffer_size)

    except:
        # Failed to initiate SCP
        util.cprint(m.DEP_LOCAL_SCPFAIL, 'red')
        status = util.rollback(ssh, deployer, timestamp, 1)
        ssh.close()
        return False

    uload_tmp = deployer.host_tmp or '/tmp'
    uload_path = os.path.join(uload_tmp, compressed_file)

    try:
        uload.put(tmp_local, uload_path)

    except scp.SCPException as e:
        util.cprint(m.DEP_LOCAL_UPLOADERR + '\n' % e, 'red')
        status = util.rollback(ssh, deployer, timestamp, 3)
        ssh.close()
        return False

    util.cprint(m.DONE + '\n', 'green')

    # Uncompress source to deploy_path/rev
    util.cprint('> ' + m.DEP_LOCAL_UNCOMPRESS, 'cyan')

    rev_path = os.path.join(deployer.deploy_path, 'rev')
    untar = 'tar -C %s -zxvf %s' % (rev_path, uload_path)

    stdin, stdout, stderr = ssh.exec_command(untar)
    status = stdout.channel.recv_exit_status()

    if status == 127:
        util.cprint(m.DEP_LOCAL_ERR127 + '\n', 'red')

        status = util.rollback(ssh, deployer, timestamp, 1)
        ssh.close()
        return False

    elif status == 1:
        util.cprint(m.DEP_LOCAL_ERR1 + '\n', 'red')
        util.cprint(*stderr.readlines())

        status = util.rollback(ssh, deployer, timestamp, 2)
        ssh.close()
        return False

    elif status == 2:
        util.cprint(m.DEP_LOCAL_ERR2 + '\n', 'red')
        util.cprint(*stderr.readlines())

        status = util.rollback(ssh, deployer, timestamp, 2)
        ssh.close()
        return False

    util.cprint(m.DONE + '\n', 'green')

    # Link directory
    status = util.symlink(ssh, deployer, rev_path, timestamp)
    if not status:
        util.rollback(ssh, deployer, timestamp, 3)
        ssh.close()
        return False

    # Link shared paths
    util.symlink_shared(ssh, deployer)

    # Run post-deployment commands
    status = util.run_commands(ssh, deployer.postdep,
                               os.path.join(deployer.deploy_path, 'current'))

    if not status:
        util.rollback(ssh, deployer, timestamp, 3)
        ssh.close()
        return False

    # Clean revisions
    if deployer.keep_max:
        status = util.clean_revisions(ssh, deployer.keep_max, rev_path)

    # Cleanup temporary files
    util.cprint('> ' + m.DEP_LOCAL_CLEAN, 'cyan')

    util.remove_local(tmp_local)
    util.remove_remote(ssh, uload_path)

    util.cprint(m.DONE + '\n', 'green')

    util.cprint(m.DEP_COMPLETE, 'green')

    # Close SSH connection
    ssh.close()

    return True