From 06ecdaa7b1fb9f70f1987764d2ed2a7b4042f3ff Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Tue, 19 May 2020 17:27:08 -0400 Subject: [PATCH] comment heavy playbook (#68981) * create examples/play.yml Co-authored-by: flowerysong --- examples/play.yml | 525 +++++++++++++++++++++++++ test/sanity/code-smell/package-data.py | 1 + test/sanity/ignore.txt | 1 + 3 files changed, 527 insertions(+) create mode 100644 examples/play.yml diff --git a/examples/play.yml b/examples/play.yml new file mode 100644 index 00000000000..0fb6a3e18f7 --- /dev/null +++ b/examples/play.yml @@ -0,0 +1,525 @@ +#!/usr/bin/env ansible-playbook +# ^ Trick: the above line can be used to make your play an executable +# you also must add 'x' permissions to the file +# +# this file is based on phred's 'pedantically commented playbook' +# https://github.com/phred/ansible-examples/blob/master/pedantically_commented_playbook.yml +# +--- +# ^^^ YAML documents can begin with the document separator "---" +# and end with the "...", neither is needed for Ansible # as it does not +# support multiple YAML documents per file, but some linters incorrectly insist +# you must have it .... +# +# The '#' is a comment character, so any line starting with it will be ignored by Ansible. + +# Blank lines are ignored, so can be used # to create spacing to your taste. + +# Note about YAML: like Python, cares about whitespace, it requires actual spaces, tabs won't work. +# Indent consistently throughout. +# Two-space or four space indents is what most users prefer, but do whatever you like. +# +# If you're new to YAML, keep in mind that YAML documents, like XML +# documents, represent a tree-like structure of nodes and text. More +# familiar with JSON? Think of YAML as a strict (with spaces) but also more flexible +# JSON (fewer significant characters, e.g., :, "", {}, [] and liberal quoting). +# Also, JSON is a subset of YAML, so most YAML parser can read JSON just the same. +# +# The curious may read more about YAML at: +# http://www.yaml.org/spec/1.1/current.html +# there is a 1.2, but Ansible uses pyyaml which is mostly 1.1 + +# the following line configures 'vim' to handle 2 space indents. +# vim:ff=unix ts=2 sw=2 ai expandtab + +### +# Notice the minus (-) on the line below, this is the start of a 'list' in YAML +# In Ansible this is the 'list of plays' which starts this playbook. +# Plays map the inventory hosts to the tasks, the most minimal play you can have +# just requires 'hosts' + +- hosts: all + ########### + # Play keyword: hosts + # Required: yes + # Description: + # The selection of hosts (or host) that the tasks in this play play should apply to. + # + ## Example values: + # hosts: all -- applies to all hosts + # hosts: host1 -- apply ONLY to the host that inventory defines as 'host1' + # hosts: group1 -- apply to all hosts in group1 + # hosts: group1,group2 -- apply to hosts in group1 & group2 + # hosts: group1,host1 -- hosts in group1 AND host + # + ## now using host patterns (TODO: url) + # hosts: group1,!group3 -- hosts in group1 that are not in group3 + # hosts: group1,&group3 -- hosts in group1 that are also in group3 + # hosts: group1:&group3 -- same as above, but using : instead of , as separator + # hosts: group1:!group2:&group3 -- hosts in group1 what are not in group2 but are also in group3 + # + ## Using a variable value for 'hosts' + # + # You can, in fact, set hosts to a variable, for example: + # + # hosts: '{{mygroups}}' -- apply to all hosts specified in the variable 'mygroups' + # + # This is handy for testing playbooks, running the same playbook against a + # staging environment before running it against production, occasional + # maintenance tasks, and other cases where you want to run the playbook + # against just a few systems rather than a whole group. + # Note that the variable cannot be set in inventory, since we need to know the hosts + # before we can use invenotry variables. So normally 'extra vars' are used, as you can + # see below. + # + # If you set hosts as shown above, then you can specify which hosts to + # apply the playbook to on each run as so: + # + # ansible-playbook playbook.yml --extra-vars="mygroups=staging" + # + # Use --extra-vars to set the variable to any combination of groups, hostnames, + # or host patterns just like the examples in the previous section. + # + + name: my heavily commented play + ########### + # Play keyword: name + # Default: play### + # Required: no + # Description: Just a description to document the play + + gather_facts: yes + ########### + # Play keyword: gather_facts + # Default: None + # Required: no + # Description: + # This controls if the play will trigger a 'fact gathering task' (aka 'gather_facts' or 'setup' action) to get information about the remote target. + # These facts normally provide useful variables on which to base decisions and task inputs. For example `ansible_os_distribution` can tell us if + # the target is a RHEL, Ubuntu or FreeBSD machine (among others), number of CPUs, RAM, etc. + # TODO: url to fact gathering + + remote_user: login_user + ########### + # Play keyword: user + # Default: depends on conneciton plugin, for ssh it is 'current user executing Ansible' + # Required: no + # Description: + # Remote user to login on remote targets and 'normally' execute the tasks as + + become: True + ########### + # Play keyword: become + # Default: False + # Required: no + # Description: + # If True, always use privilege escalationj to run tasks from this play, just like passing the + # --become flag to ansible or ansible-playbook. + + + become_user: root + ########### + # Play keyword: become_user + # Default: None + # Required: no + # Description: + # When using privilege escalation this is the user you 'become' after login with the remote_user + # for examplle you login to the remote as 'login_user' then you 'become' root to execute the tasks + + become_method: sudo + ########### + # Play keyword: become_method + # Default: sudo + # Required: no + # Description: + # When using privilege escalation this chooses the become plugin to use for privilege escalation. + # use `ansible-doc -t become -l` to list all the options. + + connection: ssh + ########### + # Play keyword: connection + # Default: ssh + # Required: no + # Description: + # This sets which connection plugin Asnible will use to try to communicate with the target host. + # notable options are paramiko (python implementation of ssh, mostly useful in corener cases in which the ssh cli + # does not work well with the target. Also 'local' which forces a 'local fork' to execute the task, but normally + # what you really want is `delegate_to: localhost` see examples below in 'Run things locally!' entry. + # use `ansible-doc -t connection -l` to list all the options. + + vars: + ########### + # Play keyword: vars + # Default: none + # Required: no + # Description: + # Mapping of variables defined for this play, normally for use in templates or as variables for tasks. + + # to get the value just use {{color}} to reference that value + color: brown + + # Mapping structures allow complex variables structures, to use you can reference + # the variable name with {{web['memcache']}} when using nested key value or {{web}} + # when using the whole structure.. + web: + memcache: 192.168.1.2 + httpd: apache + + # lists use a slightly different notation {{ mylist[1] }} to get 'b', they are 0 indexed. + mylist: + - a + - b + - c + + # Variables can be dynamically set via Jinja templates, to be filled when consumed. + # + # In this playbook, this will always evaluate to False, because 'color' + # is set to 'brown' above. + # + # When ansible interprets the following, it will first expand 'color' to + # 'brown' and then evaluate 'brown' == 'blue' as a Jinja expression. + is_color_blue: "{{ color == 'blue' }}" + + # TODO: (url variables) + + vars_files: + ########## + # Play keyword: vars_files + # Required: no + # Description: + # Specifies a list of YAML files to load variables from. + # + # Always evaluated after the 'vars' section, no matter which section + # occurs first in the playbook. Examples are below. + # + # Example YAML for a file to be included by vars_files: + # --- + # monitored_by: phobos.mars.nasa.gov + # fish_sticks: "good with custard" + # ... # (END OF DOCUMENT) + # + # Remove the indentation & comments of course, the '---' should be at + # the left margin in the variables file. + # + # Include a file from this absolute path + - /srv/ansible/vars/vars_file.yml + + # Include a file from a path relative to this playbook + - vars/vars_file.yml + + # By the way, variables set in 'vars' or as extra vars are available here. + - vars/{{something}}.yml + + # It's also possible to pass an array of files, in which case + # Ansible will loop over the array and include the first file that + # exists. If none exist, ansible-playbook will halt with an error. + # + # An excellent way to handle platform-specific differences. + - [ 'vars/{{platform}}.yml', vars/default.yml ] + + # Files in vars_files process in order, so later files can + # provide more specific configuration: + - [ 'vars/{{host}}.yml' ] + + # Hey, but if you're doing host-specific variable files, you might + # consider setting the variable for a group in your inventory and + # adding your host to that group. Just a thought. + + + vars_prompt: + ########## + # Play keyword: vars_prompt + # Required: no + # Description: + # A list of variables that Ansible will prompt for manual input each time this playbook + # runs. Used for sensitive data and also things like release numbers that + # vary on each deployment. + # + # Ansible won't prompts for this value if already provided, like when + # passed through --extra-vars, but not from inventory. + # + # Also it won't prompt if it detects that it is a non interactive session. + # For example, when called from cron. + # + - name: passphrase + prompt: "Please enter the passphrase for the SSL certificate" + private: yes + # The input won't be echoed back to the terminal when private (default yes) + + # Not sensitive, but something that should vary on each playbook run. + - name: release_version + prompt: "Please enter a release tag" + private: no + + # you can even have a default + - name: package_version + prompt: "Please enter a package version" + default: '1.0' + + # You can find more advanced features in https://docs.ansible.com/ansible/latest/user_guide/playbooks_prompts.html + + roles: + ########## + # Play keyword: roles + # Required: no + # Description: A list of roles to import and execute in this play. Executes AFTER pre_tasks and play fact gathering, but before 'tasks'. + # TODO url roles + url to 'play stages' + + tasks: + ########## + # Play keyword: tasks + # Required: no + # Description: A list of tasks to perform in this play. Executes AFTER roles and before post_tasks + + # A simple task + # Each task must have an action. 'name' is optional but very useful to document what the task does + - name: Check that the target can execute Ansible tasks + action: ping + + ########## + # Ansible modules do the work!, 'action' is not needed, you can use the 'action itself' as part of the task + - file: path=/tmp/secret mode=0600 owner=root group=root + # + # Format 'action' like above: + # : + # + # Test your parameters using: + # ansible -m -a "" + # + # Documentation for the stock modules: + # http://ansible.github.com/modules.html + + # normally most will want to use 'k: v' notation instead of 'k=v' used above (but useful for adhoc execution). + # while both formats are mostly interchangable, `k: v` is more explicit, 'type friendly' and simpler to escape. + - name: Ensure secret is locked down + file: + path: /tmp/secret + mode: '0600' + owner: root + group: root + + # note that 'action options' are indented inside the option, while 'task keywords' stay on the top level + + ########## + # Use variables in the task! It expands on use. + - name: Paint the server + command: echo {{color}} + + # you can also define variables at the task level + - name: Ensure secret is locked down + file: + path: '{{secret_file}}' + mode: '0600' + owner: root + group: root + vars: + secret_file: /tmp/secret + + ########## + # Trigger handlers when things change! + # + # Most Ansible actions can detect and report when something changed. + # Like if file permissions were not the same as requested, + # a file's content is different or a package was installed (or removed) + # When a change is reported, the task assumes the 'changed' status. + # Ansible can optionally notify one or more Handlers. + # Handlers are like normal tasks, the main difference is that they only + # run when notified. + # A common use is to restart a service after updating it's configuration file. + # https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#handlers-running-operations-on-change + + # TODO: explain handler per stage execution + + # This will call the "Restart Apache" handler whenever 'copy' alters + # the remote httpd.conf. + - name: Update the Apache config + copy: + src: httpd.conf + dest: /etc/httpd/httpd.conf + notify: Restart Apache + + # Here's how to specify more than one handler + - name: Update our app's configuration + copy: + src: myapp.conf + dest: /etc/myapp/production.conf + notify: + - Restart Apache + - Restart Redis + + ########## + # Include tasks from another file! + # + # Ansible can insert a list of tasks from another file. The file + # must represent a list of tasks, which is different than a play. + # + # Task list format: + # --- + # - name: create user + # user: name={{myuser}} color={{color}} + # + # - name: add user to group + # user: name={{myuser}} groups={{hisgroup}} append=true + # ... # (END OF DOCUMENT) + # + # A 'tasks' YAML file represents a list of tasks. Don't use playbook + # YAML for a 'tasks' file. + # + # Remove the indentation & comments of course, the '---' should be at + # the left margin in the variables file. + + # TODO: point at import_playbook, includes and roles + # In this example the user will be 'sklar' + # and 'color' will be 'red' inside new_user.yml + - import_tasks: tasks/new_user.yml + vars: + myuser: sklar + color: red + + # In this example the user will be 'mosh' + # and $color will be 'mauve' inside new_user.yml + - import_tasks: tasks/new_user.yml + vars: + myuser: mosh + color: mauve + + + ########## + # Run a task on each thing in a list! + # + # Ansible provides a simple loop facility. If 'loop' is provided for + # a task, then the task will be run once for each item in the provided + # list. Each iteration will create the 'item' variable with a different value. + - name: Create a file named via variable in /tmp + file: path=/tmp/{{item}} state=touched + loop: + - tangerine + - lemon + + - name: Loop using a variable + file: path=/tmp/{{item}} state=touched + loop: '{{mylist}}' + vars: + # defined here, but could be anywhere before the task runs + # also note that YAML lists can be flush with their key, + # we normally indent for clarity, but this form is also correct. + mylist: + - tangerine + - lemon + ########## + # Conditionally execute tasks! + # + # Sometimes you only want to run an action when a under certain conditions. + # Ansible supports using conditional Jinja expression, executing the task only when 'True'. + # + # If you're trying to run an task only when a value changes, + # consider rewriting the task as a handler and using 'notify' (see below). + # + - name: "shutdown all ubuntu" + command: /sbin/shutdown -t now + when: '{{is_ubuntu|bool}}' + + - name: "shutdown the if host is in the government" + command: /sbin/shutdown -t now + when: "{{inventory_hostname in groups['government']}}" + + # another way to write the same. + - name: "shutdown the if host is in the government" + command: /sbin/shutdown -t now + when: "{{'government' in group_names}}" + + # Ansible has some built in variables, you can check them here (TODO url) + # inventory_hostname is the name of the current host the task is executing for (derived from the hosts: keyword) + # group_names has the list of groups the current host (inventory_hostname) is part of + # groups is a mapping of the inventory groups with the list of hosts that belong to them + + ########## + # Run things as other users! + # + # Each task has optional keywords that control which + # user a task should run as and whether or not to use privilege escalation + # (like sudo or su) to switch to that user. + + - name: login in as postgres and dump all postgres databases + shell: pg_dumpall -w -f /tmp/backup.psql + remote_user: postgres + become: False + + - name: login normally, but sudo to postgres to dump all postgres databases + shell: pg_dumpall -w -f /tmp/backup.psql + become: true + become_user: postgres + become_method: sudo + + ########## + # Run things locally! + # + # Each task can also be delegated to the control host + - name: create tempfile + local_action: shell dd if=/dev/urandom of=/tmp/random.txt count=100 + + # which is equivalent to the following + - name: create tempfile + shell: dd if=/dev/urandom of=/tmp/random.txt count=100 + delegate_to: localhost + # delegate_to can use any target, but for the case above, it is the same as using local_action + # TODO url to delegation and implicit localhost + + handlers: + ########## + # Play keyword: handlers + # Required: no + # Description: + # Handlers are tasks that run when another task has changed something. + # See above for examples on how to trigger them. + # The format to define a handler is exactly the same as for tasks. + # Note that if multiple tasks notify the same handler in a playbook run + # that handler will only run once for that host. + # + # Handlers are referred to by name or using the listen keyword. + # They will be run in the order declared in the playbook. + # For example: if a task were to notify the handlers in reverse order like so: + # + # - task: ensure file does not exist + # file: + # name: /tmp/lock.txt + # state: absent + # notify: + # - Restart application + # - Restart nginx + # + # The "Restart nginx" handler will still run before the "Restart application" + # handler because it is declared first in this playbook. + + # this one can only be called by name + - name: Restart nginx + service: + name: nginx + state: restarted + + # this one can be called by name or via any entry in the listen keyword + - name: redis restarter + service: + name: redis + state: restarted + listen: + - Restart redis + + # Any module can be used for the handler action + # even though this can be triggered multiple ways and times + # it will only execute once per host + - name: restart application that should really be a service + command: /srv/myapp/restart.sh + listen: + - Restart application + - restart myapp + + # It's also possible to include handlers from another file. Structure is + # the same as a tasks file, see the tasks section above for an example. + - import_tasks: handlers/site.yml + + +# NOTE: this is not a complete list of all possible keywords in a play or task (TODO: url playbook object and keywords), just an example of very common options. + +# below is the "totally optional" YAML "End of document" marker. +... diff --git a/test/sanity/code-smell/package-data.py b/test/sanity/code-smell/package-data.py index 12f8d1f90d8..da02de95eed 100755 --- a/test/sanity/code-smell/package-data.py +++ b/test/sanity/code-smell/package-data.py @@ -56,6 +56,7 @@ def assemble_files_to_ship(complete_file_list): # Possibly should be included 'examples/scripts/uptime.py', 'examples/DOCUMENTATION.yml', + 'examples/play.yml', 'examples/hosts.yaml', 'examples/hosts.yml', 'examples/inventory_script_schema.json', diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index ac6a0f8872a..76c9b5348ce 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -7,6 +7,7 @@ docs/docsite/_themes/sphinx_rtd_theme/__init__.py metaclass-boilerplate docs/docsite/rst/conf.py future-import-boilerplate docs/docsite/rst/conf.py metaclass-boilerplate docs/docsite/rst/dev_guide/testing/sanity/no-smart-quotes.rst no-smart-quotes +examples/play.yml shebang examples/scripts/ConfigureRemotingForAnsible.ps1 pslint:PSCustomUseLiteralPath examples/scripts/upgrade_to_ps3.ps1 pslint:PSCustomUseLiteralPath examples/scripts/upgrade_to_ps3.ps1 pslint:PSUseApprovedVerbs