diff --git a/.ansible-lint b/.ansible-lint index d59c24a..7294916 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -11,6 +11,7 @@ exclude_paths: # This makes linter to fully ignore rules/tags listed below skip_list: - 'fqcn-builtins' + - 'fqcn' - 'role-name' # List of additional kind:pattern to be added at the top of the default diff --git a/.github/actions/pr-lock/action.yml b/.github/actions/pr-lock/action.yml new file mode 100644 index 0000000..fd55cfd --- /dev/null +++ b/.github/actions/pr-lock/action.yml @@ -0,0 +1,44 @@ +name: 'PR Lock' +description: 'Ensure that only one PR can deploy at a time using a lock file mechanism.' +runs: + using: 'composite' + steps: + - name: Create or checkout lock branch + shell: bash + run: | + git config user.name github-actions + git config user.email github-actions@github.com + # Fetch the lock-branch if it exists, otherwise handle the failure + if git fetch origin lock-branch; then + echo "Lock branch exists. Checking out." + git checkout lock-branch + else + echo "Lock branch does not exist. Creating a new one." + git checkout --orphan lock-branch + git rm -rf . + git clean -fdx + git commit --allow-empty -m "Initialize lock branch" + fi + + - name: Check for lock file + id: check-lock + shell: bash + run: | + if [ -f ".deploy-lock" ]; then + # Read the PR number from the lock file + LOCKED_PR=$(cat .deploy-lock) + + # Compare it with the current PR number + if [ "$LOCKED_PR" != "${{ github.event.pull_request.number }}" ]; then + echo "Another deployment is in progress by PR #$LOCKED_PR. Exiting..." + exit 1 + else + echo "Current PR #${{ github.event.pull_request.number }} already holds the lock. Proceeding with deployment." + fi + else + echo "No lock file found. Creating lock file.." + echo "${{ github.event.pull_request.number }}" > .deploy-lock + git add .deploy-lock + git commit -m "Lock deployment for PR #${{ github.event.pull_request.number }}" + git push origin lock-branch + fi diff --git a/.github/actions/pr-unlock/action.yml b/.github/actions/pr-unlock/action.yml new file mode 100644 index 0000000..3dd93fe --- /dev/null +++ b/.github/actions/pr-unlock/action.yml @@ -0,0 +1,24 @@ +name: 'PR Lock Remove' +description: 'Remove the deployment lock file after deployment on the main branch.' +runs: + using: 'composite' + steps: + - name: Checkout lock branch + shell: bash + run: | + git fetch origin lock-branch + git checkout lock-branch || git checkout --orphan lock-branch + + - name: Remove lock file + id: remove-lock + shell: bash + run: | + if [ -f ".deploy-lock" ]; then + echo "Removing lock file..." + git rm .deploy-lock + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git commit -m "Unlock deployment" + git push origin lock-branch + else + echo "No lock file found." diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9678987..032a7a3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,10 @@ version: 2 updates: - package-ecosystem: "terraform" - directory: "/erraform" # Location of package manifests + directory: "/terraform" schedule: interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/ansible.yml b/.github/workflows/ansible.yml index 0044d8e..fe7a93e 100644 --- a/.github/workflows/ansible.yml +++ b/.github/workflows/ansible.yml @@ -15,6 +15,7 @@ on: - synchronize paths: - 'ansible/**' + - requirements.txt # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -29,30 +30,45 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run ansible-lint - # replace `main` with any valid ref, or tags like `v6` - uses: ansible-community/ansible-lint-action@v6.0.2 + uses: ansible/ansible-lint@v24.7.0 # or version tag instead of 'main' with: args: "ansible" + lock: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + # Lock if this is a PR so two PR cannot deploy at the same time + - name: PR Lock + if: github.event_name == 'pull_request' + uses: ./.github/actions/pr-lock + + - name: Clear PR Lock + if: github.ref == 'refs/heads/main' + uses: ./.github/actions/pr-unlock + run-playbook: # The type of runner that the job will run on runs-on: ubuntu-latest # The validate Job need to be sucessfull - needs: [ validate ] + needs: [ validate, lock ] # The deployement environnement thus provides the secrets for that env environment: digitalocean steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Set up Python 3.9 + # Setup the environment + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.12" cache: 'pip' # caching pip dependencies - name: Install dependencies Including Ansible @@ -60,6 +76,7 @@ jobs: python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi + if [ -f requirements.yml ]; then ansible-galaxy install -r requirements.yml; fi - name: write inventory to file env: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f598e4a..0f9b188 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,4 +2,12 @@ repos: - repo: https://github.com/adrienverge/yamllint.git rev: v1.29.0 hooks: - - id: yamllint \ No newline at end of file + - id: yamllint + - repo: https://github.com/ansible/ansible-lint + rev: v24.7.0 + hooks: + - id: ansible-lint + args: + - "ansible" + additional_dependencies: + - ansible \ No newline at end of file diff --git a/Readme.md b/README.md similarity index 100% rename from Readme.md rename to README.md diff --git a/ansible/docker-swarm-portainer-caddy.yml b/ansible/docker-swarm-portainer-caddy.yml index b8f4658..d339a2b 100644 --- a/ansible/docker-swarm-portainer-caddy.yml +++ b/ansible/docker-swarm-portainer-caddy.yml @@ -5,13 +5,16 @@ roles: - utils-affected-roles +# BASE - name: This is the base requirement for all nodes hosts: all become: true + roles: - - name: docker + - role: docker when: "'docker' in hostvars['localhost']['roles_with_changes']" +# SWARM - name: This setup the Docker Swarm Manager hosts: managers gather_facts: true @@ -21,26 +24,27 @@ # NOTE: One node requires python and extra tools to setup the swarm, I call it the controller. # I case we have an issue all master are setup as potential controller # this role is for the host running ansible to manage the swarm - - name: docker-swarm-controller + - role: docker-swarm-controller when: "'docker-swarm-controller' in hostvars['localhost']['roles_with_changes']" # this role is for creating the swarm and adding host as manager - - name: docker-swarm-manager + - role: docker-swarm-manager when: "'docker-swarm-manager' in hostvars['localhost']['roles_with_changes']" - name: This setup nodes to join the Swarm hosts: nodes roles: - - name: docker-swarm-node # this role is for host to join the swarm + - role: docker-swarm-node # this role is for host to join the swarm when: "'docker-swarm-node' in hostvars['localhost']['roles_with_changes']" +# APPS - name: This install Caddy and Portainer in the Swarm hosts: managers[0] # Only one manager need to be hit become: true roles: - - name: docker-swarm-app-caddy + - role: docker-swarm-app-caddy when: "'docker-swarm-app-caddy' in hostvars['localhost']['roles_with_changes']" - - name: docker-swarm-app-portainer + - role: docker-swarm-app-portainer caddy: true when: "'docker-swarm-app-portainer' in hostvars['localhost']['roles_with_changes']" diff --git a/ansible/docker-swarm-portainer.yml b/ansible/docker-swarm-portainer.yml index 15a88ca..d728e59 100644 --- a/ansible/docker-swarm-portainer.yml +++ b/ansible/docker-swarm-portainer.yml @@ -1,17 +1,19 @@ - name: This is the base requirement for all nodes hosts: all + become: true roles: - - {name: docker, become: true} + - docker - name: This setup the Docker Swarm Manager hosts: managers + become: true roles: # NOTE: One node requires python and extra tools to setup the swarm, I call it the controller. # I case we have an issue all master are setup as potential controller - - {name: docker-swarm-controller, become: true} # this role is for the host running ansible to manage the swarm - - {name: docker-swarm-manager, become: true} # this role is for creating the swarm and adding host as manager + - docker-swarm-controller # this role is for the host running ansible to manage the swarm + - docker-swarm-manager # this role is for creating the swarm and adding host as manager - name: This setup nodes to join the Swarm hosts: nodes @@ -21,6 +23,7 @@ - name: This install Portainer in the Swarm hosts: managers[0] # Only one manager need to be hit + become: true roles: - - {name: docker-swarm-app-portainer, become: true} + - docker-swarm-app-portainer diff --git a/ansible/docker-swarm.yml b/ansible/docker-swarm.yml index 07660c6..acb82d5 100644 --- a/ansible/docker-swarm.yml +++ b/ansible/docker-swarm.yml @@ -1,14 +1,16 @@ - name: This is the base requirement for all nodes hosts: all + become: true roles: - - {name: docker, become: true} + - docker - name: This setup the Docker Swarm Manager hosts: managers + become: true roles: - - {name: docker-swarm-manager, become: true} # this role is for creating the swarm and adding host as manager + - docker-swarm-manager # this role is for creating the swarm and adding host as manager - name: This setup nodes to join the Swarm hosts: nodes diff --git a/ansible/docker.yml b/ansible/docker.yml index 72001b1..7a32e68 100644 --- a/ansible/docker.yml +++ b/ansible/docker.yml @@ -9,4 +9,4 @@ copy: content: hello-world dest: /tmp/testfile.txt - mode: 0644 + mode: "0644" diff --git a/ansible/hello-world.yml b/ansible/hello-world.yml index 824d58a..ec561b1 100644 --- a/ansible/hello-world.yml +++ b/ansible/hello-world.yml @@ -6,4 +6,4 @@ copy: content: hello-world dest: /tmp/testfile.txt - mode: 0644 + mode: "0644" diff --git a/ansible/roles/docker-swarm-controller/tasks/main.yaml b/ansible/roles/docker-swarm-controller/tasks/main.yaml index 1bd6664..90234cf 100644 --- a/ansible/roles/docker-swarm-controller/tasks/main.yaml +++ b/ansible/roles/docker-swarm-controller/tasks/main.yaml @@ -10,10 +10,13 @@ # GENERAL Setup ### - name: Install required system packages - apt: name={{ item }} state=present update_cache=yes + apt: + name: "{{ item }}" + state: present + update_cache: true loop: ['python3-pip', 'virtualenv', 'python3-setuptools'] - name: Install python stuff required pip: - executable: pip3 - name: [jsondiff, passlib, docker] + executable: pip3 + name: [jsondiff, passlib, docker] diff --git a/ansible/roles/docker/tasks/main.yaml b/ansible/roles/docker/tasks/main.yaml index 2c176c6..13d9af6 100644 --- a/ansible/roles/docker/tasks/main.yaml +++ b/ansible/roles/docker/tasks/main.yaml @@ -7,8 +7,10 @@ # GENERAL Setup ### - name: Install required system packages - become_user: root - apt: name={{ item }} state=present update_cache=yes + apt: + name: "{{ item }}" + state: present + update_cache: true loop: ['apt-transport-https', 'ca-certificates', 'software-properties-common'] - name: Add Docker GPG apt Key @@ -22,7 +24,10 @@ state: present - name: Update apt and install docker-ce - apt: name={{ item }} state=present update_cache=yes + apt: + name: "{{ item }}" + state: present + update_cache: true loop: ['docker-ce', 'docker-ce-cli', 'docker-compose', 'containerd.io'] - name: Ensure docker users are added to the docker group. @@ -43,17 +48,17 @@ # Pull, start, stop a hello-world container ######## - name: Pull default Docker image for testing - docker_image: + community.docker.docker_image: name: "hello-world" source: pull - name: Create default containers - docker_container: + community.docker.docker_container: name: "hello-world" image: "hello-world" state: present - name: Stop a container - docker_container: + community.docker.docker_container: name: "hello-world" state: stopped diff --git a/ansible/roles/utils-affected-roles/tasks/main.yaml b/ansible/roles/utils-affected-roles/tasks/main.yaml index 0d7a356..6e78a99 100644 --- a/ansible/roles/utils-affected-roles/tasks/main.yaml +++ b/ansible/roles/utils-affected-roles/tasks/main.yaml @@ -23,9 +23,11 @@ - name: Extract folders from the diff set_fact: - changed_folders: "{{ diff.stdout_lines + changed_folders: "{{ + diff.stdout_lines | map('regex_replace', '^(.*/).*$' , '\\1') - | unique }}" + | unique + }}" when: branch.stdout != default_branch - name: Filter folders within the roles directory diff --git a/ansible/roles/utils-rotate-docker-secrets/handlers/main.yaml b/ansible/roles/utils-rotate-docker-secrets/handlers/main.yaml index 40b8f0b..cee7413 100644 --- a/ansible/roles/utils-rotate-docker-secrets/handlers/main.yaml +++ b/ansible/roles/utils-rotate-docker-secrets/handlers/main.yaml @@ -1,6 +1,7 @@ - name: List all Docker secrets managed by this role command: docker secret ls --filter label=managed_by=rotate_docker_secrets --format "{{ '{{ .Name }}' }}" register: existing_secrets + changed_when: false - name: Identify secrets to keep set_fact: @@ -14,3 +15,4 @@ when: item not in secrets_to_keep loop: "{{ existing_secrets.stdout_lines }}" ignore_errors: true + register: ignore_errors_register diff --git a/ansible/roles/utils-rotate-docker-secrets/tasks/main.yaml b/ansible/roles/utils-rotate-docker-secrets/tasks/main.yaml index 7632197..30b80df 100644 --- a/ansible/roles/utils-rotate-docker-secrets/tasks/main.yaml +++ b/ansible/roles/utils-rotate-docker-secrets/tasks/main.yaml @@ -22,11 +22,15 @@ set_fact: previous_checksums: "{{ previous_checksums | default({}) - | combine({ item.name: - lookup('ansible.builtin.ini', - 'checksum', - section=item.name, - file=checksum_directory + '/secrets.ini') }) }}" + | combine({ + item.name: lookup( + 'ansible.builtin.ini', + 'checksum', + section=item.name, + file=checksum_directory + '/secrets.ini' + ) + }) + }}" loop: "{{ secrets }}" when: previous_checksums_file.stat.exists diff --git a/requirements.txt b/requirements.txt index 58c543d..739dd15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -ansible==2.10.7 -ansible-lint==6.0.2 +ansible-compat==24.6.1 +ansible-core==2.17.1 jsondiff==2.0.0 passlib==1.7.4 -PyYAML==6.0 \ No newline at end of file +pyyaml==6.0.1 \ No newline at end of file diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..3f789ab --- /dev/null +++ b/requirements.yml @@ -0,0 +1,2 @@ +collections: +- community.docker \ No newline at end of file