Ansible Refactoring Part 3 - First Refactor
Part 3 - First Refactor
This is Part 3 of an Ansible Refactoring Series - Start Here
In our first pass through the project we will just focus on breaking it up into more modular components, breaking apart the variables from the code, and cleaning up the base directory by moving out any files or templates into sub-directories. If you are Jeff Geerling and very comfortable with writing roles and collections then this part can be skipped!
-
Break
main.ymlinto 3 purpose based playbooks -
Create a
site.ymlwrapper to allow a full deploy with 1 run ofansible-playbook -
Break out the variables into separate files
-
Clean up the project directory
-
Move any templates into a
./templatesdirectory and update playbooks -
Move any files (e.g. used by
copymodule or similar) into a./filesdirectory and update playbooks
-
Plan of Attack
-
Make a refactor
gitbranchEven if you don’t use
gitor other SCM (Source Code Management) tool it is a very good place to start. This will give you safety, and confidence, to make major changes with little to no risk. We’ll introduce basic git commands as we go along.git checkout -b refactor-pass-01Sample OutputSwitched to a new branch 'refactor-pass-01'cp main.yml provision_database_tier.yml cp main.yml provision_app_tier.yml cp main.yml provision_load_balancer_tier.yml -
Edit each file so it contains only the appropriate play
Using the editor of your choice,
vimis installed, delete the redundant extra plays from each of your new files. Your objective is to have 3 focussed playbooks which do 1 tier each.You can verify you haven’t introduced syntax errors by checking with
ansible-playbook --syntax-checkansible-playbook --syntax-check *.ymlSample Outputplaybook: main.yml playbook: provision_app_tier.yml playbook: provision_database_tier.yml playbook: provision_load_balancer_tier.yml playbook: teardown-app.ymlNoteLinters such as
yamllint,ansible-lint, andansible-revieware outside the scope of this particular stage If you are curious to try then installpython3and the usepip3to installansible-reviewsudo yum install python3 -y sudo pip3 install ansible-reviewOutput omitted for brevity and yes using
sudoforpip3is a bad practice but a useful shortcut for now (use virtualenvs or link to/usr/local/binwould be better but a few more steps)Try it on your new playbooksansible-review provision*.ymlSample OutputWARN: Best practice "Playbooks should not contain logic (vars, tasks, handlers)" not met: provision_database_tier.yml:2: [EXTRA0008] tasks should not be required in a play WARN: Best practice "Playbooks should not contain logic (vars, tasks, handlers)" not met: ....Lots of warnings but no errors - lets return to those warnings later.
-
Wrap your 3 playbooks in a
site.ymlusingimport_playbookcat site.ymlSample Output- import_playbook: provision_database_tier.yml - import_playbook: provision_app_tier.yml - import_playbook: provision_load_balancer_tier.yml -
Verify your new
site.yml(if necessary runansible-playbook teardown.ymlfirst to delete the install)ansible-playbook site.yml
Success (hopefully) - now cleanup and move on to the next stage
A git Digression
Now we have made some changes, added some files/playbooks, amd main.yml is redundant it would be nice to snapshot our progress in git.
This allows us to move forward confidently, and yet revert or recover back to a known good state
-
Configure some
gitglobal variablesFeel free to use whatever values you want below
git config --global user.email "tok@example.com" git config --global user.name "tok" git config -lSample Outputuser.email=tok@example.com (1) user.name=tok (2) core.repositoryformatversion=0 core.filemode=true core.bare=false core.logallrefupdates=true remote.origin.url=https://github.com/tonykay/ansible_flask_app_loader_all_in_one.git remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* branch.main.remote=origin branch.main.merge=refs/heads/mainYour changes have been applied
-
Your email
-
Your name
-
-
Now cleanup your repo and commit your changes
git rm main.yml git add --all git commit -m "Refactored and removed main.yml to site.yml"Sample Output[refactor-pass-01 a61fb5c] Refactored and removed main.yml to site.yml 5 files changed, 300 insertions(+), 296 deletions(-) delete mode 100644 main.yml create mode 100644 provision_app_tier.yml create mode 100644 provision_database_tier.yml create mode 100644 provision_load_balancer_tier.yml create mode 100644 site.ymlTipYou can check on your changes and state with git statusand view the commit history withgit log
Refactoring the Variables
It is, generally, a bad practice to store code and configuration together, and your 3 playbooks are full of variables. Variables, or vars, can change frequently and being able to modify these or supply alternatives simply is very powerful. In a mature codebase the playbooks, roles, and collections may become predominately read-only in day to day use with the var or inputs changing far more frequently.
-
Break each set of vars out into separate "var files".
There are a number of places we could put them and many ways we can read them back into our playbooks. However in this case the simplest and easiest option is to move them into files in a
group_varsdirectory. Each file will take the name of itsgrouppostfixed by.ymland Ansible will automatically include it at run time.-
make the
group_varsdirectorymkdir group_vars -
Remind yourself of your
groupnamesansible-inventory --graphSample Output@all: |--@app_servers: | |--app1.fe87.internal | |--app2.fe87.internal |--@database_servers: | |--appdb1.fe87.internal |--@load_balancers: | |--frontend1.fe87.internal |--@ungrouped: -
Copy your playbooks into
group_varsusing thegroupnames above postfixed with.ymlcp provision_database_tier.yml group_vars/database_servers.yml cp provision_app_tier.yml group_vars/app_servers.yml cp provision_load_balancer_tier.yml group_vars/load_balancers.yml -
Cleanup each new variable file
-
Delete all non variable lines including
vars: -
Fix the indentation, aligning the vars with column 1
Tipvimis extremely good at these types of operationsTable 1. vim command modeoptionsCommand Function nddDelete n lines (
exmode is even more powerful)n<<allows you to change indentation levels over n multiple lines
For example your files should look like this:
head group_vars/database_servers.ymlSample Outputpostgres_rhel7_repo: "https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm" postgres_packages: - postgresql10 - postgresql10-server - postgresql10-contrib - postgresql10-libs postgres_library: python-psycopg2 postgres_10_data_dir: /var/lib/pgsql/10/data postgres_10_bin_path: /usr/pgsql-10/bin
-
-
Now remove the vars from each of your playbooks
Edit each playbook removing the
vars:section completely
-
-
Test your changes remembering to run
ansible-playbook teardown.ymlfirst if necessaryansible-playbook site.ymlYour, slowly getting better,
site.ymlshould run successfully. If not debug, fix, until successful.TipYAML at first appears very fussy about indentation etc but soon this becomes natural. Adopt a consistent style as when creating lists for example you have 2 indentation styles to chose from. ansible-playbook <playbook-name> --syntax-checkus useful andpip3can also installyamllint -
Finally
commityour changesgit add --all git commit -m "Refactored all vars to group_vars"Sample Output[refactor-pass-01 9399a27] Refactored all vars to group_vars 6 files changed, 53 insertions(+), 54 deletions(-) create mode 100644 group_vars/app_servers.yml create mode 100644 group_vars/database_servers.yml create mode 100644 group_vars/load_balancers.yml -
Examine your
githistory withgit loggit logSample Outputcommit 9399a277637e74cc9ccb167daa464d6b813dd552 Author: tok <tok@example.com> Date: Thu Jul 23 17:58:42 2020 +0000 Refactored all vars to group_vars commit a61fb5ce457e88759a8a63cdf4b938c9df73581e Author: tok <tok@example.com> Date: Thu Jul 23 17:10:22 2020 +0000 Refactored and removed main.yml to site.yml commit 531439bee9f84c1068be761e5c10fa65ad4abb7a Author: Tony <tony.g.kay@gmail.com> Date: Tue Jul 21 13:30:19 2020 -0600 ....Notice you also see my own earlier commits in the history prior to your own work
Clean Up your Templates
The root directory of your project is a bit cluttered, including several template files.
-
Make a
templatessub-directorymkdir templates -
Move all the jinja template files (ending
.j2)mv *.j2 templates -
All your playbooks now have an incorrect path
|
Tip
|
Sample Output
|
-
Fix each of the
src:lines above to include thetemplatessub-directory in the path -
Validate your work by running
ansible-playbook teardown-app.ymland thenansible-playbook site.yml -
Before committing your changes use
git statusto see the changes.git diffwill show the details of your editsgit statusSample Output# On branch refactor-pass-01 # Changes not staged for commit: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: flask_service.j2 # deleted: haproxy.cfg.j2 # deleted: launch_resource_hub.j2 # deleted: pg_hba.conf.j2 # modified: provision_app_tier.yml # modified: provision_database_tier.yml # modified: provision_load_balancer_tier.yml # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # templates/ no changes added to commit (use "git add" and/or "git commit -a") -
Save your changes with
git addandgit commitgit add --all git commit -am "Cleaned up jinja templates to templates directory"Sample Output[refactor-pass-01 7e0d63a] Cleaned up jinja templates to templates directory 7 files changed, 4 insertions(+), 4 deletions(-) rename flask_service.j2 => templates/flask_service.j2 (100%) rename haproxy.cfg.j2 => templates/haproxy.cfg.j2 (100%) rename launch_resource_hub.j2 => templates/launch_resource_hub.j2 (100%) rename pg_hba.conf.j2 => templates/pg_hba.conf.j2 (100%) -
Finally
mergeyou changes into yourmainbranchgit checkout main git merge refactor-pass-01Sample OutputUpdating 531439b..7e0d63a Fast-forward group_vars/app_servers.yml | 26 ++++++++++++ group_vars/database_servers.yml | 24 +++++++++++ group_vars/load_balancers.yml | 3 ++ main.yml | 296 --------------------------------------------------------------------------------------------------------- provision_app_tier.yml | 77 +++++++++++++++++++++++++++++++++++ provision_database_tier.yml | 85 +++++++++++++++++++++++++++++++++++++++ provision_load_balancer_tier.yml | 81 +++++++++++++++++++++++++++++++++++++ site.yml | 3 ++ flask_service.j2 => templates/flask_service.j2 | 0 haproxy.cfg.j2 => templates/haproxy.cfg.j2 | 0 launch_resource_hub.j2 => templates/launch_resource_hub.j2 | 0 pg_hba.conf.j2 => templates/pg_hba.conf.j2 | 0 12 files changed, 299 insertions(+), 296 deletions(-) create mode 100644 group_vars/app_servers.yml create mode 100644 group_vars/database_servers.yml create mode 100644 group_vars/load_balancers.yml delete mode 100644 main.yml create mode 100644 provision_app_tier.yml create mode 100644 provision_database_tier.yml create mode 100644 provision_load_balancer_tier.yml create mode 100644 site.yml rename flask_service.j2 => templates/flask_service.j2 (100%) rename haproxy.cfg.j2 => templates/haproxy.cfg.j2 (100%) rename launch_resource_hub.j2 => templates/launch_resource_hub.j2 (100%) rename pg_hba.conf.j2 => templates/pg_hba.conf.j2 (100%)
Solution
I’ve deliberately created a second repo with a solution, to avoid the temptation of just checking out the relevant commit/tag/branch (more on them later). Meanwhile it can be found here
Next Steps
Congratulations, you know have a cleaner codebase that is more modular and easier to maintain. However it is still a bit "clunky" and it would be awkward for another team to "borrow" say your Postgres playbook.
In Part 4 we will look more closely at roles and both convert the bulk of provision_database_tier.yml into a role and also look to see if someone else has already written a good HAProxy role on Ansible Galaxy