community.general.deploy_helper (8.5.0) — module

Manages some of the steps common in deploying projects

Authors: Ramon de la Fuente (@ramondelafuente)

Install collection

Install with ansible-galaxy collection install community.general:==8.5.0


Add to requirements.yml

  collections:
    - name: community.general
      version: 8.5.0

Description

The Deploy Helper manages some of the steps common in deploying software. It creates a folder structure, manages a symlink for the current release and cleans up old releases.

Running it with the O(state=query) or O(state=present) will return the C(deploy_helper) fact. C(project_path), whatever you set in the O(path) parameter, C(current_path), the path to the symlink that points to the active release, C(releases_path), the path to the folder to keep releases in, C(shared_path), the path to the folder to keep shared resources in, C(unfinished_filename), the file to check for to recognize unfinished builds, C(previous_release), the release the 'current' symlink is pointing to, C(previous_release_path), the full path to the 'current' symlink target, C(new_release), either the 'release' parameter or a generated timestamp, C(new_release_path), the path to the new release folder (not created by the module).

Usage examples

  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.

# General explanation, starting with an example folder structure for a project:

# root:
#     releases:
#         - 20140415234508
#         - 20140415235146
#         - 20140416082818
#
#     shared:
#         - sessions
#         - uploads
#
#     current: releases/20140416082818


# The 'releases' folder holds all the available releases. A release is a complete build of the application being
# deployed. This can be a clone of a repository for example, or a sync of a local folder on your filesystem.
# Having timestamped folders is one way of having distinct releases, but you could choose your own strategy like
# git tags or commit hashes.
#
# During a deploy, a new folder should be created in the releases folder and any build steps required should be
# performed. Once the new build is ready, the deploy procedure is 'finalized' by replacing the 'current' symlink
# with a link to this build.
#
# The 'shared' folder holds any resource that is shared between releases. Examples of this are web-server
# session files, or files uploaded by users of your application. It's quite common to have symlinks from a release
# folder pointing to a shared/subfolder, and creating these links would be automated as part of the build steps.
#
# The 'current' symlink points to one of the releases. Probably the latest one, unless a deploy is in progress.
# The web-server's root for the project will go through this symlink, so the 'downtime' when switching to a new
# release is reduced to the time it takes to switch the link.
#
# To distinguish between successful builds and unfinished ones, a file can be placed in the folder of the release
# that is currently in progress. The existence of this file will mark it as unfinished, and allow an automated
# procedure to remove it during cleanup.


# Typical usage
- name: Initialize the deploy root and gather facts
  community.general.deploy_helper:
    path: /path/to/root
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Clone the project to the new release folder
  ansible.builtin.git:
    repo: ansible.builtin.git://foosball.example.org/path/to/repo.git
    dest: '{{ deploy_helper.new_release_path }}'
    version: v1.1.1
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Add an unfinished file, to allow cleanup on successful finalize
  ansible.builtin.file:
    path: '{{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }}'
    state: touch
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Perform some build steps, like running your dependency manager for example
  composer:
    command: install
    working_dir: '{{ deploy_helper.new_release_path }}'
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Create some folders in the shared folder
  ansible.builtin.file:
    path: '{{ deploy_helper.shared_path }}/{{ item }}'
    state: directory
  with_items:
    - sessions
    - uploads
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Add symlinks from the new release to the shared folder
  ansible.builtin.file:
    path: '{{ deploy_helper.new_release_path }}/{{ item.path }}'
    src: '{{ deploy_helper.shared_path }}/{{ item.src }}'
    state: link
  with_items:
      - path: app/sessions
        src: sessions
      - path: web/uploads
        src: uploads
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- name: Finalize the deploy, removing the unfinished file and switching the symlink
  community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Retrieving facts before running a deploy
- name: Run 'state=query' to gather facts without changing anything
  community.general.deploy_helper:
    path: /path/to/root
    state: query
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Remember to set the 'release' parameter when you actually call 'state=present' later
- name: Initialize the deploy root
  community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: present
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# all paths can be absolute or relative (to the 'path' parameter)
- community.general.deploy_helper:
    path: /path/to/root
    releases_path: /var/www/project/releases
    shared_path: /var/www/shared
    current_path: /var/www/active
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Using your own naming strategy for releases (a version tag in this case):
- community.general.deploy_helper:
    path: /path/to/root
    release: v1.1.1
    state: present
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Using a different unfinished_filename:
- community.general.deploy_helper:
    path: /path/to/root
    unfinished_filename: README.md
    release: '{{ deploy_helper.new_release }}'
    state: finalize
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Postponing the cleanup of older builds:
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
    clean: false
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Or running the cleanup ahead of the new deploy
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- community.general.deploy_helper:
    path: /path/to/root
    state: present
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Keeping more old releases:
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
    keep_releases: 10
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Or, if you use 'clean=false' on finalize:
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
    keep_releases: 10
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Removing the entire project root folder
- community.general.deploy_helper:
    path: /path/to/root
    state: absent
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
# Debugging the facts returned by the module
- community.general.deploy_helper:
    path: /path/to/root
  • Success
    Steampunk Spotter scan finished with no errors, warnings or hints.
- ansible.builtin.debug:
    var: deploy_helper

Inputs

    
mode:
    description:
    - The permissions the resulting filesystem object should have.
    - For those used to I(/usr/bin/chmod) remember that modes are actually octal numbers.
      You must give Ansible enough information to parse them correctly. For consistent
      results, quote octal numbers (for example, V('644') or V('1777')) so Ansible receives
      a string and can do its own conversion from string into number. Adding a leading
      zero (for example, V(0755)) works sometimes, but can fail in loops and some other
      circumstances.
    - Giving Ansible a number without following either of these rules will end up with
      a decimal number which will have unexpected results.
    - As of Ansible 1.8, the mode may be specified as a symbolic mode (for example, V(u+rwx)
      or V(u=rw,g=r,o=r)).
    - If O(mode) is not specified and the destination filesystem object B(does not) exist,
      the default C(umask) on the system will be used when setting the mode for the newly
      created filesystem object.
    - If O(mode) is not specified and the destination filesystem object B(does) exist,
      the mode of the existing filesystem object will be used.
    - Specifying O(mode) is the best way to ensure filesystem objects are created with
      the correct permissions. See CVE-2020-1736 for further details.
    type: raw

path:
    aliases:
    - dest
    description:
    - The root path of the project. Returned in the C(deploy_helper.project_path) fact.
    required: true
    type: path

clean:
    default: true
    description:
    - Whether to run the clean procedure in case of O(state=finalize).
    type: bool

group:
    description:
    - Name of the group that should own the filesystem object, as would be fed to I(chown).
    - When left unspecified, it uses the current group of the current user unless you
      are root, in which case it can preserve the previous ownership.
    type: str

owner:
    description:
    - Name of the user that should own the filesystem object, as would be fed to I(chown).
    - When left unspecified, it uses the current user unless you are root, in which case
      it can preserve the previous ownership.
    - Specifying a numeric username will be assumed to be a user ID and not a username.
      Avoid numeric usernames to avoid this confusion.
    type: str

state:
    choices:
    - present
    - finalize
    - absent
    - clean
    - query
    default: present
    description:
    - The state of the project.
    - V(query) will only gather facts.
    - V(present) will create the project C(root) folder, and in it the C(releases) and
      C(shared) folders.
    - V(finalize) will remove the unfinished_filename file, create a symlink to the newly
      deployed release and optionally clean old releases.
    - V(clean) will remove failed & old releases.
    - V(absent) will remove the project folder (synonymous to the M(ansible.builtin.file)
      module with O(state=absent)).
    type: str

serole:
    description:
    - The role part of the SELinux filesystem object context.
    - When set to V(_default), it will use the C(role) portion of the policy if available.
    type: str

setype:
    description:
    - The type part of the SELinux filesystem object context.
    - When set to V(_default), it will use the C(type) portion of the policy if available.
    type: str

seuser:
    description:
    - The user part of the SELinux filesystem object context.
    - By default it uses the V(system) policy, where applicable.
    - When set to V(_default), it will use the C(user) portion of the policy if available.
    type: str

release:
    description:
    - The release version that is being deployed. Defaults to a timestamp format C(%Y%m%d%H%M%S)
      (for example V(20141119223359)). This parameter is optional during O(state=present),
      but needs to be set explicitly for O(state=finalize). You can use the generated
      fact C(release={{ deploy_helper.new_release }}).
    type: str

selevel:
    description:
    - The level part of the SELinux filesystem object context.
    - This is the MLS/MCS attribute, sometimes known as the C(range).
    - When set to V(_default), it will use the C(level) portion of the policy if available.
    type: str

attributes:
    aliases:
    - attr
    description:
    - The attributes the resulting filesystem object should have.
    - To get supported flags look at the man page for I(chattr) on the target system.
    - This string should contain the attributes in the same order as the one displayed
      by I(lsattr).
    - The C(=) operator is assumed as default, otherwise C(+) or C(-) operators need to
      be included in the string.
    type: str
    version_added: '2.3'
    version_added_collection: ansible.builtin

shared_path:
    default: shared
    description:
    - The name of the folder that will hold the shared resources. This can be relative
      to O(path) or absolute. If this is set to an empty string, no shared folder will
      be created. Returned in the C(deploy_helper.shared_path) fact.
    type: path

current_path:
    default: current
    description:
    - The name of the symlink that is created when the deploy is finalized. Used in O(state=finalize)
      and O(state=clean). Returned in the C(deploy_helper.current_path) fact.
    type: path

keep_releases:
    default: 5
    description:
    - The number of old releases to keep when cleaning. Used in O(state=finalize) and
      O(state=clean). Any unfinished builds will be deleted first, so only correct releases
      will count. The current version will not count.
    type: int

releases_path:
    default: releases
    description:
    - The name of the folder that will hold the releases. This can be relative to O(path)
      or absolute. Returned in the C(deploy_helper.releases_path) fact.
    type: str

unsafe_writes:
    default: false
    description:
    - Influence when to use atomic operation to prevent data corruption or inconsistent
      reads from the target filesystem object.
    - By default this module uses atomic operations to prevent data corruption or inconsistent
      reads from the target filesystem objects, but sometimes systems are configured or
      just broken in ways that prevent this. One example is docker mounted filesystem
      objects, which cannot be updated atomically from inside the container and can only
      be written in an unsafe manner.
    - This option allows Ansible to fall back to unsafe methods of updating filesystem
      objects when atomic operations fail (however, it doesn't force Ansible to perform
      unsafe writes).
    - IMPORTANT! Unsafe writes are subject to race conditions and can lead to data corruption.
    type: bool
    version_added: '2.2'
    version_added_collection: ansible.builtin

unfinished_filename:
    default: DEPLOY_UNFINISHED
    description:
    - The name of the file that indicates a deploy has not finished. All folders in the
      O(releases_path) that contain this file will be deleted on O(state=finalize) with
      O(clean=true), or O(state=clean). This file is automatically deleted from the C(new_release_path)
      during O(state=finalize).
    type: str