Last active
June 12, 2023 17:50
-
-
Save shantanoo-desai/bca38860af1e55d0323bbad9cb89fca1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- hosts: all | |
gather_facts: false | |
vars_files: | |
- defaults/main.yml | |
- vault.yml | |
tasks: | |
- name: Do Not Execute Playbook if No Flows File extra variable is mentioned | |
fail: | |
msg: > | |
Please specify the flows file to be deployed on the fleet using: | |
ansible-playbook deploy_flow.yml -e "flow=<name>.json" --ask-vault-pass | |
when: flow is not defined | |
- name: Checking if Flow File Exists in Directory | |
stat: | |
path: "{{flows_directory }}/{{ flow }}" | |
delegate_to: localhost | |
register: flow_file | |
- name: Flow File doesn't exist | |
fail: | |
msg: "The Flow File does not exist in Path: {{flows_directory}}" | |
when: not flow_file.stat.exists | |
- name: Reading the Flow File | |
set_fact: | |
flow_json: "{{ lookup('ansible.builtin.file', flows_directory + '/' + flow) | from_json }}" | |
when: flow_file.stat.exists | |
- name: Check for Existence of either InfluxDB, MySQL or MQTT nodes in Flows | |
set_fact: | |
nodes_exist: true | |
when: "flow_json | selectattr('type', 'equalto', node) | list | count > 0" | |
loop: | |
- influxdb | |
- mqtt-broker | |
- mysql | |
loop_control: | |
loop_var: node | |
- name: Dynamic Credential Insertion for InfluxDB, MySQL, or MQTT Configuration Nodes | |
# Use set_fact on `flow_json` variable because we want to update the same JSON structure | |
set_fact: | |
# Extract the dict and combine the credentials block in it | |
# and merge it back into the list and update `flow_json` fact | |
flow_json: "{{ flow_json[0:index] | |
+ [ node | combine(node_replacements[node.type]) ] | |
+ flow_json[index|int+1:] }}" | |
# Loop over the list of dictionaries and determine the index of the dict | |
# which has `type` value either `influxdb`, `mysql` or `mqtt` | |
when: (nodes_exist is not undefined) and (nodes_exist is true) | |
loop: "{{ query('ansible.utils.index_of', flow_json, 'in', matched_nodes) }}" | |
loop_control: | |
# Use `idx` as the variable name for the index obtained by loop | |
loop_var: index | |
# Observe the matches made on the Terminal as <node.id>, <node.type> for better clarity | |
label: "{{ node.id ~ ', ' ~ node.type }}" | |
# Variables needed for the loop | |
vars: | |
# call each indexed dictionary in the list as `node` | |
node: "{{ flow_json[index] }}" | |
# check if `type` is either `influxdb`, `mysql`, or `mqtt` and store it `matched_nodes` variable | |
matched_nodes: "{{ flow_json | selectattr('type', 'in', node_replacements.keys()) }}" | |
# The general Structure of the "credentials" block to be inserted if there are `matched_nodes` | |
# The username, password credentials will be extracted from Ansible-Vault | |
node_replacements: | |
influxdb: | |
credentials: | |
username: "{{ influxdb.username }}" | |
password: "{{ influxdb.password }}" | |
MySQLdatabase: | |
credentials: | |
user: "{{ mysql.username }}" | |
password: "{{ mysql.password }}" | |
mqtt-broker: | |
credentials: | |
username: "{{ mqtt.username }}" | |
password: "{{ mqtt.password }}" | |
- name: Preparing Flow File for Deployment | |
copy: | |
dest: "{{ flows_directory + '/.' + flow }}" | |
content: "{{ flow_json | to_nice_json }}" | |
delegate_to: localhost | |
- name: Obtain the Authentication Token for Node-RED Instance | |
ansible.builtin.uri: | |
# API: /auth/token HTTP POST | |
# Headers: Content-Type: application/json | |
# Body: | |
# client_id: node-red-admin | |
# grant_type: password | |
# scope: * | |
# username: Node-RED instance username | |
# password: Node-RED instance username | |
# Expected Response Code: 200 | |
# Response Body: | |
# { | |
# "access_token": "<token>", | |
# "expires_in": 604800, | |
# "token_type": "Bearer" | |
# } | |
url: "{{ nodered_base_url }}/auth/token" | |
method: POST | |
body: | |
client_id: node-red-admin | |
grant_type: password | |
scope: "*" | |
username: "{{ nodered.username }}" | |
password: "{{ nodered.password }}" | |
body_format: form-urlencoded | |
status_code: 200 | |
# Store the Response in a variable called `login` | |
register: login | |
when: flow is defined | |
- name: Upload Dedicated Flow on Each Node-RED Instance in Fleet | |
ansible.builtin.uri: | |
# API: /nodered/flows HTTP POST | |
# Headers: | |
# Authorization: Bearer <token> | |
# Content-Type: application/json | |
# Node-RED-API-Version: v1 | |
# Node-RED-Deployment-Type: full | |
# Body: | |
# <Flow JSON File> | |
# Expected Response Code: 204 | |
# Response body: none | |
url: "{{ nodered_base_url }}/flows" | |
method: POST | |
headers: | |
# Obtain the Token and its Type from the `login` variable from previous task | |
Authorization: "{{ login.json.token_type }} {{ login.json.access_token }}" | |
Content-Type: application/json | |
Node-RED-API-Version: v1 | |
Node-RED-Deployment-Type: full | |
# `flow` is a variable containing the <name>.json of the node-RED flow to be deployed | |
body: "{{ lookup('ansible.builtin.file', flows_directory + '/.' + flow) }}" | |
body_format: json | |
status_code: 204 | |
register: flow_upload_status | |
when: flow is defined | |
- name: Revoke the Authentication Token for Node-RED Instance | |
ansible.builtin.uri: | |
# API: /nodered/auth/revoke HTTP POST | |
# Headers: | |
# Authorization: Bearer <token> | |
# Content-Type: application/json | |
# Body: | |
# {"token": "<token>"} | |
# Expected Response Code: 200 | |
# Response body: none | |
url: "{{ nodered_base_url }}/auth/revoke" | |
method: POST | |
headers: | |
# Obtain the Token and its Type from the `login` variable from previous task | |
Authorization: "{{ login.json.token_type }} {{ login.json.access_token }}" | |
Content-Type: application/json | |
body: '{"token": "{{ login.json.access_token }}" }' | |
body_format: json | |
status_code: 200 | |
- name: Post Deployment Cleanup | |
file: | |
name: "{{ flows_directory + '/.' + flow }}" | |
state: absent | |
when: flow_upload_status.status == 204 | |
delegate_to: localhost |
Solution 1
from Kristianheljas on Ansible IRC Chat
kristianheljas 16:23:11
So, it is just looping trough indexes that match the items you find in �matched_nodes and replacing each index using list splicing to inject on into the correct place
- hosts: localhost
gather_facts: false
vars:
node_red_flows: [
{
"id": "one",
"type": "influxdb",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "test",
"name": "testfluxdb",
"usetls": false,
"tls": "",
"influxdbVersion": "1.x",
"url": "http://localhost:8086",
"rejectUnauthorized": true
},
{
"id": "f6f2187d.f17ca8",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": ""
},
{
"id": "two",
"type": "influxdb",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "test",
"name": "testfluxdb",
"usetls": false,
"tls": "",
"influxdbVersion": "1.x",
"url": "http://localhost:8086",
"rejectUnauthorized": true
},
{
"id": "ae7554e7968c9aa1",
"type": "influxdb in",
"z": "f6f2187d.f17ca8",
"influxdb": "398670264ec6e79f",
"name": "fluxtestdb",
"query": "",
"rawOutput": false,
"precision": "",
"retentionPolicy": "",
"org": "organisation",
"x": 460,
"y": 300,
"wires": [
[ ]
]
}
]
tasks:
- set_fact:
node_red_flows: "{{ node_red_flows[0:item] + [node_red_flows[item] | combine(combine_with)] + node_red_flows[item|int+1:] }}"
loop: "{{ query('ansible.utils.index_of', node_red_flows, 'in', matched_nodes) }}"
vars:
matched_nodes: "{{ node_red_flows | selectattr('type', '==', 'influxdb') }}"
combine_with:
credentials:
username: tester
password: tester
- debug:
msg: "{{ node_red_flows }}"
Solution
by kristian heljas
explanation
I added loop_control as well to provide nicer output for the item and more meaningul name to the index (opposed to item)
[4:58:27 PM] Using the label option, you can configure howok: [localhost] => (item=****THIS*****)
looks like in the ansible output
[4:59:12 PM] And �loop_var allows you to change the default variable name �item, which in this case might have been confusing
- hosts: localhost
gather_facts: false
vars:
node_red_flows: [
{
"id": "one",
"type": "mysql",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "test",
"name": "testfluxdb",
"usetls": false,
"tls": "",
"influxdbVersion": "1.x",
"url": "http://localhost:8086",
"rejectUnauthorized": true
},
{
"id": "f6f2187d.f17ca8",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": ""
},
{
"id": "two",
"type": "influxdb",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "test",
"name": "testfluxdb",
"usetls": false,
"tls": "",
"influxdbVersion": "1.x",
"url": "http://localhost:8086",
"rejectUnauthorized": true
},
{
"id": "ae7554e7968c9aa1",
"type": "influxdb in",
"z": "f6f2187d.f17ca8",
"influxdb": "398670264ec6e79f",
"name": "fluxtestdb",
"query": "",
"rawOutput": false,
"precision": "",
"retentionPolicy": "",
"org": "organisation",
"x": 460,
"y": 300,
"wires": [
[ ]
]
},
{
"id": "three",
"type": "influxdb",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "test",
"name": "testfluxdb",
"usetls": false,
"tls": "",
"influxdbVersion": "1.x",
"url": "http://localhost:8086",
"rejectUnauthorized": true
}
]
tasks:
- set_fact:
node_red_flows: "{{ node_red_flows[0:index] + [node | combine(node_replacements[node.type])] + node_red_flows[index|int+1:] }}"
loop: "{{ query('ansible.utils.index_of', node_red_flows, 'in', matched_nodes) }}"
loop_control:
loop_var: index
label: "{{ node.id ~ ', ' ~ node.type }}"
vars:
node: "{{ node_red_flows[index] }}"
matched_nodes: "{{ node_red_flows | selectattr('type', 'in', node_replacements.keys()) }}"
node_replacements:
influxdb:
credentials:
username: influxdb-tester
password: influxdb-tester
mysql:
credentials:
username: mysql-tester
password: mysql-tester
- debug:
msg: "{{ node_red_flows }}"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
InfluxDB Flow with multiple Influxdb in and Influxdb out nodes but same configuration
jq
commandeach
influxdb in
andinfluxdb out
node has ainfluxdb
key in them, upon querying they all end out having the same hash value that is theid
value of the node"type": "influxdb"