diff --git a/roles/content_view_rollback/README.md b/roles/content_view_rollback/README.md new file mode 100644 index 0000000000..d2ee73625b --- /dev/null +++ b/roles/content_view_rollback/README.md @@ -0,0 +1,70 @@ +theforeman.foreman.content_view_rollback +========= + +A role for automating the rollback of Content-Views. + +Requirements +------------ + +This role requires the theforeman.foreman module collection. + +Role Variables +-------------- + +This role requires most of the common foreman variable more noteably: + +`foreman_organization`: The Organization that the Content-View belongs to. + +`foreman_username`: A foreman user that has access rights to publish new Content-View versions in the aforementioned Organization. + +`foreman_password`: The password for the user. foreman_server_url: The URL used to access foreman. + + +As well as two additional variables: + +`foreman_content_view`: The name of the Content-View which should have Lifecycle Environments promoted. + +`foreman_lifecycle_environments`: A list of Lifecycle Environments that should be promtoed. + + + +Dependencies +------------ + +You need a Foreman user with admin access to the Organization, Lifecycle_Environment, and Content_View you wish to interact with. + +By default, the role will require a valid SSL certificate installed on your Foreman server that the ansible client can trace trust to. To disable that update the 'FOREMAN_VALIDATE_CERTS' variable in defaults/main.yml. + +For example, to disable certificate checking you would update the variable as such: +``` +FOREMAN_VALIDATE_CERTS: false +``` + +Example Playbook +---------------- + +The role can be instantiated quite simply, all of the decision making is handled by the variables previously set: + +``` +--- +- name: "Run the content_view_rollback Role" + hosts: all + tasks: + - name: "Run the content_view_rollback Role" + include_role: + name: theforeman.foreman.content_view_rollback +``` +For example: + +Rolling back Lifecycle Environments inside their respective Content-Views to the previous version: +``` +foreman_organization: "Org1" +foreman_content_view: "content-view1" +foreman_lifecycle_environments: + - "Dev" + - "QA" + - "Prod" + +``` + +The role would take the Dev, QA and Prod Lifecycle Environments to Content-View version N-1. If, prior to role runtime, the versions were: Prod=10, QA=11, and Dev=12, the result at the end of the run would be: Prod=9, QA=10, and Dev=11. If that Content-View version does not exist it will select the next lowest Content-View version. If there are none lower, it will exit with a message saying such. diff --git a/roles/content_view_rollback/defaults/main.yml b/roles/content_view_rollback/defaults/main.yml new file mode 100644 index 0000000000..60001f2a0b --- /dev/null +++ b/roles/content_view_rollback/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for content_view_promotion_rollback_publish +FOREMAN_VALIDATE_CERTS: true diff --git a/roles/content_view_rollback/tasks/main.yml b/roles/content_view_rollback/tasks/main.yml new file mode 100644 index 0000000000..5b26b3eac2 --- /dev/null +++ b/roles/content_view_rollback/tasks/main.yml @@ -0,0 +1,92 @@ +--- +# get data on the current content-view +- name: "Gather Data For Current Content-View From Foreman" + theforeman.foreman.resource_info: + username: "{{ foreman_user }}" + password: "{{ foreman_password }}" + server_url: "{{ foreman_server_url }}" + organization: "{{ foreman_organization }}" + validate_certs: "{{ foreman_validate_certs }}" + resource: content_views + search: name = "{{ foreman_content_view }}" + register: content_view_data + +# get data on the current content-view version +- name: "Gather Data For Current Content-View Versions From Foreman" + theforeman.foreman.resource_info: + username: "{{ foreman_user }}" + password: "{{ foreman_password }}" + server_url: "{{ foreman_server_url }}" + organization: "{{ foreman_organization }}" + validate_certs: "{{ foreman_validate_certs }}" + resource: content_view_versions + params: + content_view_id: "{{ content_view_data.resources[0].id }}" + register: version_information + +# creates a dictionary with data formatted as such {'Prod':'11.0'} +- name: "Build Dictionary With Lifecycle Envrionment And Version Number" + set_fact: + environments: "{{ environments | default({}) | combine ({item[1].name : item[0].name.split()[-1]}) }}" + with_subelements: + - "{{ version_information.resources }}" + - environments + +# create list of content-view versions +- name: "Build List of All Versions of Content-View" + set_fact: + cv_versions: "{{ cv_versions | default([]) + [item.major] }}" + with_items: "{{ version_information.resources }}" + +# set the highest version to zero so that we don't use previous Content-View settings +- name: "Set Lowest Version to 0" + set_fact: + lowest_version: 0 + +# set highest number +- name: "Set the Lowest Version of the Content-View Currently Available" + set_fact: + lowest_version: "{{ cv_versions | min }}" + +# add one to each of the version numbers +- name: "Update Facts With Decremented Content-View Version Numbers" + set_fact: + new_environments: "{{ new_environments | default({}) | combine({item.key: item.value|int - 1.0 }) }}" + with_dict: "{{ environments }}" + +# check if each of the N-1 versions exist +- name: "Check to make sure previous version exists" + set_fact: + non_environments: "{{ non_environments | default({}) | combine({item.key: item.value }) }}" + with_dict: "{{ new_environments }}" + when: item.value not in cv_versions + +- name: "Check for next closest, lower version" + include_tasks: version.yml + with_dict: "{{ non_environments | default({}) }}" + loop_control: + loop_var: version + +# check if each of the N-1 versions exist +- name: "Check to make sure previous version exists" + fail: + msg: "There is no version lower than {{ item.value }} to roll back to for Content-View {{ item.key }}. Stopping execution." + with_dict: "{{ new_environments }}" + when: item.value not in cv_versions + +# only promote environments defined in the vars +- name: "Rollback Environments to Version N-1" + theforeman.foreman.content_view_version: + username: "{{ foreman_user }}" + password: "{{ foreman_password }}" + server_url: "{{ foreman_server_url }}" + organization: "{{ foreman_organization }}" + validate_certs: "{{ foreman_validate_certs }}" + content_view: "{{ foreman_content_view }}" + # dictionaries aren't ordered and Foreman doesn't want you promoting things out of order + # but we're promoting them all so we just override that behavior + force_promote: true + lifecycle_environments: "{{ item.key }}" + version: "{{ item.value }}" + with_dict: "{{ new_environments }}" + when: item.key in foreman_lifecycle_environments diff --git a/roles/content_view_rollback/tasks/version.yml b/roles/content_view_rollback/tasks/version.yml new file mode 100644 index 0000000000..da1baa33c8 --- /dev/null +++ b/roles/content_view_rollback/tasks/version.yml @@ -0,0 +1,8 @@ +--- +# reset the var value to the next lowest value +# reversing the list and iterating downwards until first match would be more efficient +- name: "Get closest lower version" + set_fact: + new_environments: "{{ new_environments | combine({version.key: item}) }}" + with_items: "{{ cv_versions | sort | list }}" + when: item | int < version.value | int