Skip to content
This repository was archived by the owner on Oct 8, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions roles/asg_management/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# AWS ASG variables to allow for the suspension of autoscaling during a code deployment.
asg_management:
name: "" # if the deploy is on an ASG put the name here
#target_group_name: "example" # matches the ASG name by default, specify if your TargetGroup name is different (for example due to the 32-char name length limit in AWS)
refresh_asg_instances: true # runs only if squashFS image unmount failed and this set to true.
#profile: "example" # optional, the boto profile name to use if not the system default
region: "eu-west-1"
suspend_processes: "Launch Terminate HealthCheck" # space separated string, see https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html
Expand Down
2 changes: 1 addition & 1 deletion roles/asg_management/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
region: "{{ asg_management.region }}"
profile: "{{ asg_management.profile | default(omit) }}"
names:
- "{{ asg_management.name }}"
- "{{ asg_management.target_group_name | default(asg_management.name) }}"
register: _target_group

- name: Loop over target instances until they are all 'healthy'.
Expand Down
212 changes: 133 additions & 79 deletions roles/deploy_code/tasks/cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,93 +150,147 @@
failed_when: false
register: _deploy_code_mount_check

- name: Get the current pts session.
ansible.builtin.shell:
cmd: "tty | sed 's#/dev/##'"
register: deploy_pts

- name: "Check for active sessions in {{ deploy_base_path }}."
ansible.builtin.shell:
cmd: "ps -eo pid,tty | awk '{print $1}' | xargs -n 1 pwdx 2>&1 | grep -v 'No such process' | grep {{ deploy_base_path }} | cut -d: -f1 | xargs -n 1 ps -o tty= -p | sort | uniq"
register: sessions_in_deploy_path
become: true

- name: Display active sessions.
ansible.builtin.debug:
msg: >
Deploy session: {{ deploy_pts.stdout | default('Unknown') }}.
Active sessions in {{ deploy_base_path }}: {{ sessions_in_deploy_path.stdout_lines | default([]) | join(', ') | default('None') }}.

- name: Kill sessions except the current one.
- name: Mount SquashFS image if there is no mounted one.
ansible.builtin.command:
cmd: "pkill -9 -t {{ item }}"
loop: "{{ sessions_in_deploy_path.stdout_lines }}"
when:
- "item != deploy_pts.stdout"
- "item is match('^pts/\\d+$')"
failed_when: false
register: kill_sessions_result
become: true

- name: Display killed sessions.
ansible.builtin.debug:
msg: >
Sessions terminated: {{ kill_sessions_result.results | selectattr('rc', 'defined') | selectattr('rc', 'equalto', 0) | map(attribute='item') | list | join(', ') | default('None') }}.

- name: Reload any services that might be keeping the loop device busy.
ansible.builtin.service:
name: "{{ www_service }}"
state: reloaded
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- _deploy_code_mount_check.rc == 0
- deploy_code.service_action == "reload"
- deploy_code.services | length > 0

- name: Stop any services that might be keeping the loop device busy.
ansible.builtin.service:
name: "{{ www_service }}"
state: stopped
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
cmd: "mount {{ build_base_path }}/deploy.sqsh {{ deploy_base_path }} -t squashfs -o loop"
become: true
when:
- _deploy_code_mount_check.rc == 0
- deploy_code.service_action == "stop"
- deploy_code.services | length > 0
- _deploy_code_mount_check.rc != 0

- name: Unmount existing SquashFS image.
ansible.builtin.command:
cmd: "umount {{ deploy_base_path }}"
become: true
- name: Mount new SquashFS image instead of the already mounted.
when:
- _deploy_code_mount_check.rc == 0
register: task_result
retries: "{{ deploy_code.unmount_retries }}"
delay: "{{ deploy_code.unmount_delay }}"
until: task_result.rc == 0
block:
- name: Get the current pts session.
ansible.builtin.shell:
cmd: "tty | sed 's#/dev/##'"
register: deploy_pts

- name: Mount new SquashFS image.
ansible.builtin.command:
cmd: "mount {{ build_base_path }}/deploy.sqsh {{ deploy_base_path }} -t squashfs -o loop"
become: true
- name: "Check for active sessions in {{ deploy_base_path }}."
ansible.builtin.shell:
cmd: "ps -eo pid,tty | awk '{print $1}' | xargs -n 1 pwdx 2>&1 | grep -v 'No such process' | grep {{ deploy_base_path }} | cut -d: -f1 | xargs -n 1 ps -o tty= -p | sort | uniq"
register: sessions_in_deploy_path
become: true

- name: Start any services we stopped.
ansible.builtin.service:
name: "{{ www_service }}"
state: started
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- _deploy_code_mount_check.rc == 0
- deploy_code.service_action == "stop"
- deploy_code.services | length > 0
- name: Display active sessions.
ansible.builtin.debug:
msg: >
Deploy session: {{ deploy_pts.stdout | default('Unknown') }}.
Active sessions in {{ deploy_base_path }}: {{ sessions_in_deploy_path.stdout_lines | default([]) | join(', ') | default('None') }}.

- name: Kill sessions except the current one.
ansible.builtin.command:
cmd: "pkill -9 -t {{ item }}"
loop: "{{ sessions_in_deploy_path.stdout_lines }}"
when:
- "item != deploy_pts.stdout"
- "item is match('^pts/\\d+$')"
failed_when: false
register: kill_sessions_result
become: true

- name: Display killed sessions.
ansible.builtin.debug:
msg: >
Sessions terminated: {{ kill_sessions_result.results | selectattr('rc', 'defined') | selectattr('rc', 'equalto', 0) | map(attribute='item') | list | join(', ') | default('None') }}.

- name: Reload any services that might be keeping the loop device busy.
ansible.builtin.service:
name: "{{ www_service }}"
state: reloaded
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- deploy_code.service_action == "reload"
- deploy_code.services | length > 0

- name: Stop any services that might be keeping the loop device busy.
ansible.builtin.service:
name: "{{ www_service }}"
state: stopped
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- deploy_code.service_action == "stop"
- deploy_code.services | length > 0

- name: Unmount existing SquashFS image.
ansible.builtin.command:
cmd: "umount {{ deploy_base_path }}"
become: true
register: task_result
retries: "{{ deploy_code.unmount_retries }}"
delay: "{{ deploy_code.unmount_delay }}"
until: task_result.rc == 0
failed_when: false

- name: If current image unmount succeeded.
when: task_result.rc == 0
block:
- name: Mount new SquashFS image.
ansible.builtin.command:
cmd: "mount {{ build_base_path }}/deploy.sqsh {{ deploy_base_path }} -t squashfs -o loop"
become: true

- name: Start any services that we stopped if the image re-mounting was successful.
ansible.builtin.service:
name: "{{ www_service }}"
state: started
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- deploy_code.service_action == "stop"
- deploy_code.services | length > 0

- name: If current image unmount failed.
when: task_result.rc != 0
block:
- name: Resume all autoscale processes on ASG.
amazon.aws.autoscaling_group:
name: "{{ asg_management.name }}"
region: "{{ asg_management.region }}"
profile: "{{ asg_management.profile | default(omit) }}"
suspended_processes: []
delegate_to: localhost
run_once: true
when:
- asg_management.name | length > 0

- name: Run ASG instance refresh.
amazon.aws.autoscaling_instance_refresh:
name: "{{ asg_management.name }}"
region: "{{ asg_management.region }}"
profile: "{{ asg_management.profile | default(omit) }}"
strategy: Rolling
preferences:
min_healthy_percentage: 51
instance_warmup: 100
skip_matching: false
state: started
delegate_to: localhost
run_once: true
when:
- asg_management.name | length > 0
- asg_management.refresh_asg_instances

- name: Start any services we stopped if the image re-mounting failed and ASG management is disabled.
ansible.builtin.service:
name: "{{ www_service }}"
state: started
with_items: "{{ deploy_code.services }}"
loop_control:
loop_var: www_service
become: true
when:
- deploy_code.service_action == "stop"
- deploy_code.services | length > 0
- not asg_management.refresh_asg_instances or (asg_management.name | length) == 0
# End of the squashFS block.

- name: Trigger an infrastructure rebuild.
Expand Down