Wolfgang Kulhanek
2020-03-16 96f73bbfe948e2a2789c7f609bfac6308926eaa3
Update documentation in example workload with dictionary approach (#1342)

* Update documentation in example workload with dictionary approach

* Added AgnosticV example
6 files modified
327 ■■■■ changed files
ansible/roles/ocp-workload-example/defaults/main.yml 25 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/readme.adoc 239 ●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/tasks/post_workload.yml 5 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/tasks/pre_workload.yml 5 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/tasks/remove_workload.yml 14 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/tasks/workload.yml 39 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp-workload-example/defaults/main.yml
@@ -1,4 +1,27 @@
---
become_override: False
ocp_username: wkulhane-redhat.com
silent: False
silent: False
# If your workload needs customization options provide variables
# in a dictionary. The dictionary should be named "<role name>_defaults".
# So because this example workload is called "ocp-workload-example" the dictionary
# should be named "ocp_workload_example_defaults". Note that the dictionary can not
# include "-" characters, so replace all "-" with "_" instead.
#
# You can override the defaults as parameters to the Ansible run that runs
# your workload. The overrides should be in a dictionary called
# "<role name>_vars".
#
# If there are any secrets that need to be passed (data that should not be in a
# public repo somewhere) you can specify another dictionary "<role name>_secrets".
#
# When the workload is executed the dictionary "<role name>" will be constructed from
# "<role name>_defaults" plus "<role name>_vars" (if defined) and "<role name>_secrets"
# (if defined).
#
# The logic in the workload should only use the dictionary "<role name>".
ocp_workload_example_defaults:
  variable_1: "value 1"
  variable_2: "value 2"
  variable_secret: ""
ansible/roles/ocp-workload-example/readme.adoc
@@ -8,7 +8,7 @@
*** Debug task will print out: `pre_workload Tasks completed successfully.`
** Playbook: link:./tasks/workload.yml[workload.yml] - Used to deploy the actual
 workload, i.e, 3scale, Mobile or some Demo
 workload, i.e, 3scale, Mobile, some Demo or OpenShift customization
*** This role only prints the current username for which this role is provisioning.
*** Debug task will print out: `workload Tasks completed successfully.`
@@ -28,31 +28,134 @@
* The variable *ocp_username* is mandatory to assign the workload to the correct OpenShift user.
* A variable *silent=True* can be passed to suppress debug messages.
* You can modify any of these default values by adding `-e "variable_name=variable_value"` to the command line
* Add a dictionary for workload parameters.
== Understand how paremeters to your workload should be specified
=== Overview
A `dictionary` approach should be used in order to neatly encapsulate parameters to roles.
The role itself defines a dictionary `<role name>_defaults` in the file link:./defaults/main.yml[./defaults/main.yml]. Because this example role is `ocp-workload-example` the dictionary is called `ocp_workload_example_defaults`. Note that `_` is used instead of `-` in the dictionary name to create a valid Ansible dictionary name.
When deploying a workload you can specify another dictionary with the specific parameters for this deployment. This dictionary should be called `<role name>_vars`. In our example this dictionary would be called `ocp_workload_example_vars`.
=== Handling secret parameters
In case you need to pass a secret parameter (e.g. the Bind Password for an LDAP server) you may define another dictionary, `<role name>_secrets`, and pass that as well.
[TIP]
When setting default values for secrets in the *defaults* dictionary be careful what you set. `""` is probably a good choice. Some workloads (ocp4-workload-authentication for example) generate passwords if a password is not being specified. If you set a default password in the defaults dictionary this logic will never be executed.
For deployment via AgnosticD/AgnosticV the contents of this secrets dictionary need to be placed on the provisioning host and referenced in the AgnosticV configuration.
=== Combined dictionary
When running your workload the first task in link:./tasks/workload.yml[./tasks/workload.yml] sets up the combined dictionary with name `<role name>`. The defaults are combined with vars and secrets. In that order. This means that values in *defaults* can be overridden with values in *vars* and *secrets*.
When adapting this example role for your particular workload make sure to update the dictionary names in both link:./tasks/workload.yml[./tasks/workload.yml] and link:./tasks/remove_workload.yml[./tasks/remove_workload.yml] files.
=== Deploy a Workload with the `ocp-workload` playbook [Mostly for testing]
. If your workload uses parameters create a `<role name>_vars.yaml` input file.
+
.ocp_workload_example_vars.yaml
[source,yaml]
----
TARGET_HOST="bastion.na39.openshift.opentlc.com"
OCP_USERNAME="shacharb-redhat.com"
# You can set any variable, not just the dictionary
silent: true
# Set up the `vars` dictionary
ocp_workload_example_vars:
  variable_2: "My variable 2"
----
. If your workload uses secrets create a `<role name>_secrets.yaml` input file.
+
.ocp_workload_example_secrets.yaml
[source,yaml]
----
# Set up the `secrets` dictionary
ocp_workload_example_secrets:
  variable_secret: "top secret data"
----
. Set up Environment Variables for the bastion you want to run this role on.
+
[source,yaml]
----
TARGET_HOST="bastion.dev.openshift.opentlc.com"
OCP_USERNAME="wkulhane-redhat.com"
ANSIBLE_USER="ec2-user"
WORKLOAD="ocp-workload-example"
GUID=1001
----
. Finally run the workload passing the input files as parameters:
+
[source,sh]
----
# a TARGET_HOST is specified in the command line, without using an inventory file
ansible-playbook -i ${TARGET_HOST}, ./configs/ocp-workloads/ocp-workload.yml \
    -e"ansible_ssh_private_key_file=~/.ssh/keytoyourhost.pem" \
    -e"ansible_user=ec2-user" \
    -e"ansible_user=${ANSIBLE_USER}" \
    -e"ocp_username=${OCP_USERNAME}" \
    -e"ocp_workload=${WORKLOAD}" \
    -e"silent=False" \
    -e"guid=${GUID}" \
    -e"ACTION=create"
    -e"ACTION=create" \
    -e @./ocp_workload_example_vars.yaml \
    -e @./ocp_workload_example_secrets.yaml
----
+
Note how the dictionary got set up:
* *Variable 1: value 1* This value comes from the *defaults* dictionary. It did not get overwritten anywhere
* *Variable 2: My variable 2* This value comes from the *vars* dictionary that was passed as a parameter
* *Variable Secret: top secret data* This value comes from the *secrets* dictionary that was passed as a parameter
+
.Example output when using the examples above:
[source,text,options="nowrap"]
----
[...]
TASK [ocp-workload-example : Running Pre Workload Tasks] *****************************************************************************************************************************************************************
Monday 16 March 2020  15:27:10 -0400 (0:00:00.070)       0:00:05.383 **********
included: /Users/wkulhane/Development/agnosticd/ansible/roles/ocp-workload-example/tasks/./pre_workload.yml for bastion.dev4.openshift.opentlc.com
TASK [ocp-workload-example : Set up ocp4_workload_example combined dictionary] *******************************************************************************************************************************************
Monday 16 March 2020  15:27:10 -0400 (0:00:00.051)       0:00:05.434 **********
ok: [bastion.dev4.openshift.opentlc.com]
[...]
TASK [ocp-workload-example : Setting up workload for user] ***************************************************************************************************************************************************************
Monday 16 March 2020  15:27:10 -0400 (0:00:00.047)       0:00:05.625 **********
ok: [bastion.dev4.openshift.opentlc.com] => {
    "msg": "Setting up workload for user ocp_username = wkulhane-redhat.com"
}
TASK [ocp-workload-example : Print Example Variables] ********************************************************************************************************************************************************************
Monday 16 March 2020  15:27:10 -0400 (0:00:00.032)       0:00:05.658 **********
ok: [bastion.dev4.openshift.opentlc.com] => (item=Variable 1: value 1.) => {
    "msg": "Variable 1: value 1."
}
ok: [bastion.dev4.openshift.opentlc.com] => (item=Variable 2: My variable 2.) => {
    "msg": "Variable 2: My variable 2."
}
ok: [bastion.dev4.openshift.opentlc.com] => (item=Variable Secret: top secret data) => {
    "msg": "Variable Secret: top secret data"
}
[...]
----
=== To Delete an environment
----
TARGET_HOST="bastion.na39.openshift.opentlc.com"
OCP_USERNAME="ankay-redhat.com"
TARGET_HOST="bastion.dev.openshift.opentlc.com"
OCP_USERNAME="wkulhane-redhat.com"
ANSIBLE_USER="ec2-user"
WORKLOAD="ocp-workload-example"
GUID=1002
@@ -63,55 +166,97 @@
    -e"ocp_username=${OCP_USERNAME}" \
    -e"ocp_workload=${WORKLOAD}" \
    -e"guid=${GUID}" \
    -e"ACTION=remove"
    -e"ACTION=remove" \
    -e @./ocp_workload_example_vars.yaml \
    -e @./ocp_workload_example_secrets.yaml
----
== Deploying a Workload with AgnosticV
== Other related information:
When creating a configuration in AgnosticV that includes the deployment of the workload you can specify the dictionary straight in the AgnosticV config. Because AgnosticV configs are usually created by combining a `common.yaml` file with either `dev.yaml`, `test.yaml` or `prod.yaml` you can specify parts of the dictionary in each of these files. For example you could have common values defined in the `common.yaml` file and then specific values for development or production environments in `dev.yaml` or `prod.yaml`.
=== Deploy Workload on OpenShift Cluster from an existing playbook:
AgnosticV merges the definition files starting with `common.yaml` and then adding/overwriting what comes from either `dev.yaml` or `prod.yaml`.
Example of a simple AgnosticV config:
.common.yaml
[source,yaml]
----
- name: Deploy a workload role on a master host
  hosts: all
  become: true
  gather_facts: False
  tags:
    - step007
  roles:
    - { role: "{{ocp_workload}}", when: 'ocp_workload is defined' }
# --- Quay Shared Workload Deployment for RPDS
# --- System: RHPDS
# --- Catalog: OpenShift Demos
# --- Catalog Item: Quay 3 on OpenShift 4
# --- Platform
platform: rhpds
# --- Cloud Provider
cloud_provider: none
# --- Config
env_type: ocp-workloads
ocp_workload: ocp4-workload-quay-operator
# This workload must be run as ec2-user (or cloud-user on OpenStack)
# because it has tasks requiring sudo.
ansible_user: ec2-user
ansible_ssh_private_key_file: /home/opentlc-mgr/.ssh/opentlc_admin_backdoor.pem
# --- Ensure the workload prints the correct statements for CloudForms to realize it finished
workload_shared_deployment: true
# --- Workload Configuration
ocp4_workload_quay_operator_vars:
  project: "quay-{{ guid }}"
# --- AgnosticV Meta variables
agnosticv_meta:
  params_to_variables:
    user: ocp_username
  secrets:
  # This secret file holds the token to pull the Quay image
  - ocp4_workload_quay_secrets
----
NOTE: You might want to change `hosts: all` to fit your requirements
=== Set up your Ansible inventory file
* You can create an Ansible inventory file to define your connection method to your host (Master/Bastion with `oc` command)
* You can also use the command line to define the hosts directly if your `ssh` configuration is set to connect to the host correctly
* You can also use the command line to use localhost or if your cluster is already authenticated and configured in your `oc` configuration
.Example inventory file
[source, ini]
.dev.yaml
[source,yaml]
----
[gptehosts:vars]
ansible_ssh_private_key_file=~/.ssh/keytoyourhost.pem
ansible_user=ec2-user
purpose: development
[gptehosts:children]
openshift
# --- Use specific variable values for Development
target_host: bastion.dev4.openshift.opentlc.com
[openshift]
bastion.cluster1.openshift.opentlc.com
bastion.cluster2.openshift.opentlc.com
bastion.cluster3.openshift.opentlc.com
bastion.cluster4.openshift.opentlc.com
[dev]
bastion.cluster1.openshift.opentlc.com
bastion.cluster2.openshift.opentlc.com
[prod]
bastion.cluster3.openshift.opentlc.com
bastion.cluster4.openshift.opentlc.com
# --- Workload Configuration Overrides
# Deploy Quay v3.2.0
ocp4_workload_quay_operator_vars:
  quay_image_tag: "v3.2.0"
  clair_image_tag: "v3.2.0"
----
.prod.yaml
[source,yaml]
----
---
purpose: production
# --- Use specific variable values for Production
target_host: bastion.rhpds.openshift.opentlc.com
# --- Workload Configuration Overrides
ocp4_workload_quay_operator_vars:
  quay_image_tag: "v3.1.3"
  clair_image_tag: "v3.1.3"
# --- AgnosticV Meta variables
agnosticv_meta:
  agnosticd_git_tag_prefix: ocp4-workload-quay-rhpds-prod
----
== Complex Examples
If you want to see more examples of how this works in a real world workload the following workloads already use this approach:
* ocp4-workload-authentication
* ocp4-workload-machinesets
* ocp4-workload-logging
* ocp4-workload-quay-operator
ansible/roles/ocp-workload-example/tasks/post_workload.yml
@@ -1,7 +1,12 @@
---
# Implement your Post Workload deployment tasks here
# --------------------------------------------------
# Leave these as the last tasks in the playbook
# ---------------------------------------------
# For deployment onto a dedicated cluster (as part of the
# cluster deployment) set workload_shared_deployment to False
ansible/roles/ocp-workload-example/tasks/pre_workload.yml
@@ -1,7 +1,12 @@
---
# Implement your Pre Workload deployment tasks here
# -------------------------------------------------
# Leave these as the last tasks in the playbook
# ---------------------------------------------
# For deployment onto a dedicated cluster (as part of the
# cluster deployment) set workload_shared_deployment to False
ansible/roles/ocp-workload-example/tasks/remove_workload.yml
@@ -1,8 +1,22 @@
---
# Set up the combined dictionary for the workload
#
# To Do: Adjust the names of your dictionaries here
- name: Set up ocp4_workload_example combined dictionary
  set_fact:
    ocp_workload_example: >-
      {{ ocp_workload_example_defaults
       | combine(ocp_workload_example_vars    | default( {} ),
                 ocp_workload_example_secrets | default( {} ), recursive=true )
      }}
# Implement your Workload removal tasks here
# ------------------------------------------
# Leave this as the last task in the playbook.
# --------------------------------------------
- name: remove_workload tasks complete
  debug:
    msg: "Remove Workload tasks completed successfully."
ansible/roles/ocp-workload-example/tasks/workload.yml
@@ -1,12 +1,47 @@
---
# Implement your Workload deployment tasks here
- name: Setting up workload for user
  debug:
    msg: "Setting up workload for user ocp_username = {{ ocp_username }}"
# Set up the combined dictionary for the workload
#
# To Do: Adjust the names of your dictionaries here
- name: Set up ocp4_workload_example combined dictionary
  set_fact:
    ocp_workload_example: >-
      {{ ocp_workload_example_defaults
       | combine(ocp_workload_example_vars    | default( {} ),
                 ocp_workload_example_secrets | default( {} ), recursive=true )
      }}
# Because now secrets are part of the combined dictionary use
# verbosity 2 to prevent printing the dictionary during every run.
- name: Print combined role variables
  debug:
    var: ocp4_workload_example
    verbosity: 2
# To Do: Implement your Workload deployment tasks here
# If you have parameters for your workload use the "<role name> dictionary"
# -------------------------------------------------------------------------
- name: Example Workload, print dictionary values
  debug:
    msg: "{{ item }}"
  loop:
  - "Variable 1:      {{ ocp_workload_example.variable_1 }}"
  - "Variable 2:      {{ ocp_workload_example.variable_2 }}"
  - "Variable Secret: {{ ocp_workload_example.variable_secret }}"
# Leave this as the last task in the playbook.
# --------------------------------------------
- name: workload tasks complete
  debug:
    msg: "Workload Tasks completed successfully."