From d58fd11546cd378ff4eba6227adc10f8c06c386a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Isma=C3=ABl=20Bouya?= Date: Fri, 25 Feb 2022 18:14:29 +0100 Subject: [PATCH] Add an option to remove existing keys that are not declared in ansible --- README.md | 4 ++++ defaults/main.yml | 1 + dhall/package.dhall | 2 ++ files/fetch_keys.sh | 22 ++++++++++++++++++++++ tasks/key.yml | 30 ++++++++++++++++++++++++++++++ tasks/keys.yml | 43 ++++++++++++++++++++++--------------------- tasks/main.yml | 3 +-- 7 files changed, 82 insertions(+), 23 deletions(-) create mode 100755 files/fetch_keys.sh create mode 100644 tasks/key.yml diff --git a/README.md b/README.md index 8a5f903..9962610 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ Role Variables * `rundeck_remove_missing` Whether to delete jobs present in rundeck and not in file. Defaults to true. * `rundeck_jobs_group` the group of job to check for removal * `rundeck_ignore_creation_errors` whether to ignore job creation error. Default to true to follow the 200 status given by rundeck API +* `rundeck_jobs_keys` a list of keys to import in rundeck. Each key is a dict with a `path`, a `value` and a `type` as declared in [https://docs.rundeck.com/3.0.x/api/index.html#upload-keys](). +* `rundeck_keys_scoped_by_project` scope each key by project (In a project/ProjectName subdirectory) +* `rundeck_keys_scoped_by_group` scope each key by group. Defaults to true if the group is defined, false otherwise +* `rundeck_remove_missing_keys` remove keys that are not declared in ansible (possibly restrained to the scope defined above) A [dhall](https://dhall-lang.org/) Type representing the roles' variables is available in the `./dhall/Config.dhall` file to help you configure your projects with some type checking. diff --git a/defaults/main.yml b/defaults/main.yml index dc73d56..c3f0967 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,6 +1,7 @@ --- rundeck_api_version: 26 rundeck_remove_missing: true +rundeck_remove_missing_keys: false rundeck_ignore_creation_error: true rundeck_keys_scoped_by_project: true rundeck_jobs_keys: [] diff --git a/dhall/package.dhall b/dhall/package.dhall index 7ada0dc..4e3b668 100644 --- a/dhall/package.dhall +++ b/dhall/package.dhall @@ -10,6 +10,7 @@ let Config = , rundeck_api_token : Text , rundeck_api_version : Optional Natural , rundeck_remove_missing : Optional Bool + , rundeck_remove_missing_keys : Optional Bool , rundeck_ignore_creation_error : Optional Bool , rundeck_jobs_group : Optional Text , rundeck_jobs_keys : List Key @@ -19,6 +20,7 @@ let Config = , default = { rundeck_api_version = Some 26 , rundeck_remove_missing = Some True + , rundeck_remove_missing_keys = Some False , rundeck_ignore_creation_error = Some True , rundeck_jobs_group = None Text , rundeck_jobs_keys = [] : List Key diff --git a/files/fetch_keys.sh b/files/fetch_keys.sh new file mode 100755 index 0000000..e15dc12 --- /dev/null +++ b/files/fetch_keys.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BASE_URL="$1" +TOKEN="$2" +BASE_PATH="$3" + +list_path_rec() { + path="$1" + result=$(curl -ks "$BASE_URL/storage/$path?authtoken=$TOKEN") + + case "$(echo "$result" | jq -r .type)" in + "file") echo "$result" | jq -r .path | sed -e "s@^$BASE_PATH/@@" + ;; + "directory") + echo "$result" | jq -r ".resources[]|.path" | while read p; do list_path_rec "$p"; done + ;; + esac +} + +list_path_rec "$BASE_PATH" diff --git a/tasks/key.yml b/tasks/key.yml new file mode 100644 index 0000000..aa2b2d9 --- /dev/null +++ b/tasks/key.yml @@ -0,0 +1,30 @@ +--- +- name: Build scoped path + set_fact: + rundeck_key_full_path: "{{ rundeck_keys_base_path }}/{{ item.path }}" + +- name: Check key existence + uri: + url: "{{ rundeck_api_url }}/{{ rundeck_api_version }}/storage/keys/{{ rundeck_key_full_path }}" + method: GET + headers: + Accept: application/json + X-Rundeck-Auth-Token: "{{ rundeck_api_token }}" + status_code: [200, 404] + register: rundeck_existing_key + +- name: Set method + set_fact: + rundeck_key_uri_method: "{{ (rundeck_existing_key.status == 404) | ternary('POST', 'PUT') }}" + +- name: Import key + uri: + url: "{{ rundeck_api_url }}/{{ rundeck_api_version }}/storage/keys/{{ rundeck_key_full_path }}" + method: "{{ rundeck_key_uri_method }}" + headers: + Accept: application/json + Content-Type: "{{ item.type }}" + X-Rundeck-Auth-Token: "{{ rundeck_api_token }}" + status_code: [200, 201] + body: "{{ item.value }}" + body_format: raw diff --git a/tasks/keys.yml b/tasks/keys.yml index 98c6136..7ca0904 100644 --- a/tasks/keys.yml +++ b/tasks/keys.yml @@ -1,33 +1,34 @@ --- -- name: Build scoped path +- name: Set scope variables set_fact: - rundeck_key_full_path: "{{ rundeck_keys_scoped_by_project | default(true) | ternary('project/' + rundeck_project + '/' + key_group_path, key_group_path) }}" + rundeck_keys_base_path: "{{ rundeck_keys_scoped_by_project | default(true) | ternary('project/' + rundeck_project + '/' + rundeck_keys_group_path, rundeck_keys_group_path) }}" vars: group_name: "{{ rundeck_jobs_group | default('') }}" - key_group_path: "{{ rundeck_keys_scoped_by_group | default((group_name|length) > 0) | ternary(group_name + '/' + item.path, item.path) }}" + rundeck_keys_group_path: "{{ rundeck_keys_scoped_by_group | default((group_name|length) > 0) | ternary(group_name, '') }}" -- name: Check key existence - uri: - url: "{{ rundeck_api_url }}/{{ rundeck_api_version }}/storage/keys/{{ rundeck_key_full_path }}" - method: GET - headers: - Accept: application/json - X-Rundeck-Auth-Token: "{{ rundeck_api_token }}" - status_code: [200, 404] - register: rundeck_existing_key +- name: Include rundeck key + include_tasks: key.yml + with_items: "{{ rundeck_jobs_keys }}" + +- name: Get all stored keys + script: + cmd: "{{ role_path }}/files/fetch_keys.sh {{ rundeck_api_url }}/{{ rundeck_api_version }} {{ rundeck_api_token }} keys/{{ rundeck_keys_base_path }}" + register: rundeck_existing_keys + when: rundeck_remove_missing_keys -- name: Set method +- name: "Prepare list of keys to remove" set_fact: - rundeck_key_uri_method: "{{ (rundeck_existing_key.status == 404) | ternary('POST', 'PUT') }}" + rundeck_existing_keys: "{{ rundeck_existing_keys.stdout_lines | list }}" + rundeck_known_keys: "{{ rundeck_jobs_keys | map(attribute='path') | list }}" + when: rundeck_remove_missing_keys -- name: Import key +- name: "Remove jobs not declared" uri: - url: "{{ rundeck_api_url }}/{{ rundeck_api_version }}/storage/keys/{{ rundeck_key_full_path }}" - method: "{{ rundeck_key_uri_method }}" + url: "{{ rundeck_api_url }}/{{ rundeck_api_version }}/storage/keys/{{ rundeck_keys_base_path }}/{{ item }}" + method: DELETE headers: Accept: application/json - Content-Type: "{{ item.type }}" X-Rundeck-Auth-Token: "{{ rundeck_api_token }}" - status_code: [200, 201] - body: "{{ item.value }}" - body_format: raw + status_code: [204, 404] + with_items: "{{ rundeck_existing_keys | difference(rundeck_known_keys) }}" + when: rundeck_remove_missing_keys diff --git a/tasks/main.yml b/tasks/main.yml index 644fef0..955d0a9 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -4,7 +4,6 @@ tags: - rundeck-jobs - name: Include rundeck keys - include_tasks: keys.yml tags: - rundeck-keys - with_items: "{{ rundeck_jobs_keys }}" + include_tasks: keys.yml -- 2.41.0