Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create content_view_rollback role #1217

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
70 changes: 70 additions & 0 deletions roles/content_view_rollback/README.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions roles/content_view_rollback/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
# defaults file for content_view_promotion_rollback_publish
FOREMAN_VALIDATE_CERTS: true
92 changes: 92 additions & 0 deletions roles/content_view_rollback/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions roles/content_view_rollback/tasks/version.yml
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +4 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Maybe this can be turned into a filter plugin.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which part do you think would lend itself best to being a filter plugin?

My main concern here, is that we are pretty much handling this with 3 lines right now, and (at least after my cursory glance) adding a filter plugin would also require more code and perhaps make this a bit more confusing.

However, filters for something like 'get_next_lowest' integer in a list and 'get_next_highest' integer in a list might be good ideas for core contributions.

If that's of interest I can always make a PR over there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking exactly about that kind of arithmetic. It was my first thought when reading the task name, and was reinforced by seeing set_fact. I'm fine if we say, that's a task for another PR on a another day.