I have an ansible playbook, where I'd like a variable I register on one machine to be available on another.
In my case, I'd like to run a command on localhost
, in this case git rev-parse --abbrev-ref HEAD
, so I can make a note of the current git branch, and sha1, and register this output, so I can refer to it later when working any machine in the main
group, in the second play.
However, it's not clear to me how I register a variable on localhost, so I can access it from main. When I try to access the variable in the second play I get this message:
TASK: [debug msg={{ app_git_sha1.stdout }}] *********************************** fatal: [main] => One or more undefined variables: 'app_git_sha1' is undefined
Here's the play I'm using. Is there anything obvious I should be doing?
--- - hosts: localhost connection: local gather_facts: no tasks: - name: register current branch command: git rev-parse --abbrev-ref HEAD register: git_branch sudo: no when: vagrant tags: - debugsha - debug: msg={{ git_branch.stdout }} tags: - debugsha - name: register the SHA1 of the branch being deployed command: git rev-parse origin/{{ git_branch.stdout }} register: app_git_sha1 sudo: no tags: - slack - debugsha - debug: msg={{ app_git_sha1.stdout }} tags: - debugsha - hosts: main sudo: yes roles: - role: productscience.deploy_user # TODO reprovision using these roles, for consistency # - role: app.essentials # - role: zenoamaro.postgresql - role: productscience.papertrailapp - role: jdauphant.nginx tasks: - include: setup.yml # - include: db.yml - name: checkout source control when deploying to remote servers include: source.yml when: not vagrant tags: - deploy - include: django.yml tags: - deploy - name: include vagrant specific dependencies for local development include: vagrant.yml when: vagrant handlers: - name: restart postgres sudo: yes service: name=postgresql state=restarted - name: start restart uwsgi sudo: yes service: name={{ app }} state=restarted - hosts: localhost connection: local gather_facts: no tasks: - name: register the SHA1 of the branch being deployed when: not vagrant command: git rev-parse origin/{{ git_branch }} register: git_sha tags: - slack - name: Send notification message via Slack all options when: not vagrant tags: - slack local_action: module: slack token: "{{ wof_slack_token }}" msg: "Deployment of `{{ git_branch }}` to {{ app_url }} completed with sha `{{ git_sha.stdout }}`" channel: "#wof" username: "Ansible deploy-o-tron"
Ansible registers are used when you want to capture the output of a task to a variable. You can then use the value of these registers for different scenarios like a conditional statement, logging etc. The variables will contain the value returned by the task. The common return values are documented in Ansible docs.
register − The output of the action is registered using the register keyword and Output is the variable name which holds the action output. always − Again a Ansible keyword , it states that below will always be executed. msg − Displays the message.
Ansible has a precedence for loading variable data, and thus it has an order and a definition to decide which variable will win. Variable value overriding is an advanced usage of Ansible, so it is important to fully understand the semantics before attempting such a scenario.
The problem you're running into is that you're trying to reference facts/variables of one host from those of another host. You need to keep in mind that in Ansible, the variable app_git_sha1
assigned to the host localhost
is distinct from the variable app_git_sha1
assigned to the host main
or any other host. If you want to access one hosts facts/variables from another host then you need to explicitly reference it via the hostvars
variable. There's a bit more of a discussion on this in this question.
Suppose you have a playbook like this:
- hosts: localhost tasks: - command: /bin/echo "this is a test" register: foo - hosts: localhost tasks: - debug: var=foo
This will work because you're referencing the host localhost
and localhosts
's instance of the variable foo
in both plays. The output of this playbook is something like this:
PLAY [localhost] ************************************************************** GATHERING FACTS *************************************************************** ok: [localhost] TASK: [command /bin/echo "this is a test"] ************************************ changed: [localhost] PLAY [localhost] ************************************************************** GATHERING FACTS *************************************************************** ok: [localhost] TASK: [debug var=foo] ********************************************************* ok: [localhost] => { "var": { "foo": { "changed": true, "cmd": [ "/bin/echo", "this is a test" ], "delta": "0:00:00.004585", "end": "2015-11-24 20:49:27.462609", "invocation": { "module_args": "/bin/echo \"this is a test\"", "module_complex_args": {}, "module_name": "command" }, "rc": 0, "start": "2015-11-24 20:49:27.458024", "stderr": "", "stdout": "this is a test", "stdout_lines": [ "this is a test" ], "warnings": [] } } }
If you modify this playbook slightly to run the first play on one host and the second play on a different host, you'll get the error that you encountered. The solution is to use Ansible's built-in hostvars
variable to have the second host explicitly reference the first hosts variable. So modify the first example like this:
- hosts: localhost tasks: - command: /bin/echo "this is a test" register: foo - hosts: anotherhost tasks: - debug: var=foo when: foo is defined - debug: var=hostvars['localhost']['foo'] when: hostvars['localhost']['foo'] is defined
The output of this playbook shows that the first task is skipped because foo
is not defined by the host anotherhost
. But the second task succeeds because it's explicitly referencing localhosts
's instance of the variable foo
:
TASK: [debug var=foo] ********************************************************* skipping: [anotherhost] TASK: [debug var=hostvars['localhost']['foo']] ************************** ok: ['anotherhost'] => { "var": { "hostvars['localhost']['foo']": { "changed": true, "cmd": [ "/bin/echo", "this is a test" ], "delta": "0:00:00.005950", "end": "2015-11-24 20:54:04.319147", "invocation": { "module_args": "/bin/echo \"this is a test\"", "module_complex_args": {}, "module_name": "command" }, "rc": 0, "start": "2015-11-24 20:54:04.313197", "stderr": "", "stdout": "this is a test", "stdout_lines": [ "this is a test" ], "warnings": [] } } }
So, in a nutshell, you want to modify the variable references in your main
playbook to reference the localhost
variables in this manner:
{{ hostvars['localhost']['app_git_sha1'] }}
For example to pass K8S token and hash from the master to the workers.
- name: "Cluster token" shell: kubeadm token list | cut -d ' ' -f1 | sed -n '2p' register: K8S_TOKEN - name: "CA Hash" shell: openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //' register: K8S_MASTER_CA_HASH - name: "Add K8S Token and Hash to dummy host" add_host: name: "K8S_TOKEN_HOLDER" token: "{{ K8S_TOKEN.stdout }}" hash: "{{ K8S_MASTER_CA_HASH.stdout }}" - name: debug: msg: "[Master] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}" - name: debug: msg: "[Master] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}"
- name: debug: msg: "[Worker] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}" - name: debug: msg: "[Worker] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" - name: "Kubeadmn join" shell: > kubeadm join --token={{ hostvars['K8S_TOKEN_HOLDER']['token'] }} --discovery-token-ca-cert-hash sha256:{{ hostvars['K8S_TOKEN_HOLDER']['hash'] }} {{K8S_MASTER_NODE_IP}}:{{K8S_API_SERCURE_PORT}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With