shantanoo-desai / komponist

A Composer for your favorite IoT/ IIoT container stacks with Ansible + Jinja2 + Docker Compose v2
GNU Affero General Public License v3.0
25 stars 2 forks source link

[node-red] determine external node-red nodes and download them as tarballs for offline usage #110

Closed shantanoo-desai closed 11 months ago

shantanoo-desai commented 1 year ago

Description

while working on #109 it is possible to extract information of node-red nodes that are externally installed via the HTTP GET /nodes API. Upon inspecting the documentation further each entry in the array is a type of Node Set.

Logic

In order determine which nodes are externally installed, the value of module in the Node Set needs to be observed. If the value of the module parameter in the object is not node-red it is an external module.

Using the name and the version of the nodes one can download the respective tarballs from the NPM registry. These tarballs can then be uploaded to offline instances using the HTTP POST /nodes API along side the Flows + Credentials upload.

shantanoo-desai commented 11 months ago

Initial Draft for playbook

# SPDX-License-Identifier: AGPL-3.0-only
#
# Komponist - Generate Your Favourite Compose Stack With the Least Effort
#
# Copyright (C) 2023  Shantanoo "Shan" Desai <sdes.softdev@gmail.com>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published
#   by the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# export_nodes_nodered.yml: Ansible Playbook that exports any external Node-RED nodes
# for offline installation
---
- name: Export Node-RED Nodes from Node-RED for Offline Installation
  hosts: localhost
  gather_facts: true
  vars_files:
    - vars/config.yml
    - vars/creds.yml
  module_defaults:
    ansible.builtin.uri:
      method: POST
      headers:
        Content-Type: application/json

  tasks:
    - name: Get Node-RED user with all Privileges
      ansible.builtin.set_fact:
        nodered_creds: "{{ item }}"
      when: "item.permissions is defined and item['permissions'] == '*'"
      loop: "{{ credentials.nodered.users }}"
      no_log: true

    - name: Obtain Authentication Token
      ansible.builtin.uri:
        url: "http://localhost/nodered/auth/token"
        body:
          client_id: node-red-admin
          grant_type: password
          scope: "{{ nodered_creds.permissions }}"
          username: "{{ nodered_creds.username }}"
          password: "{{ nodered_creds.password }}"
        body_format: json
        status_code: 200
      register: auth_token
      when: nodered_creds is defined

    - name: Lookup Nodes from Node-RED instance
      ansible.builtin.set_fact:
        node_set: "{{ lookup('ansible.builtin.url', 'http://localhost/nodered/nodes', headers=headers) }}"
      vars:
        headers:
          Accept: application/json
          Authorization: "{{ auth_token.json.token_type }} {{ auth_token.json.access_token }}"

    - name: External Nodes in Node-RED instance
      ansible.builtin.get_url:
        url: "https://registry.npmjs.org/{{ item.module }}/-/{{ item.module }}-{{ item.version }}.tgz"
        dest: "{{ komponist.deploy_dir }}/nodered/"
        mode: "0640"
      loop: "{{ node_set }}"
      when: item.module != 'node-red'

    - name: Revoke Authentication Token
      ansible.builtin.uri:
        url: http://localhost/nodered/auth/revoke
        headers:
          Authorization: "{{ auth_token.json.token_type }} {{ auth_token.json.access_token }}"
        body:
          token: "{{ auth_token.json.access_token }}"
        body_format: json
        status_code: 200