[CORD-869] Building and deploying UI Extensions in platform install (separate playbook)

Change-Id: I30166503fbe23e0864029df5fb8d051e586a7881
diff --git a/deploy-xos-gui-extensions-playbook.yml b/deploy-xos-gui-extensions-playbook.yml
new file mode 100644
index 0000000..0275611
--- /dev/null
+++ b/deploy-xos-gui-extensions-playbook.yml
@@ -0,0 +1,24 @@
+---
+# deploy-xos-gui-extensions-playbook.yml
+
+- name: Include vars
+  hosts: all
+  tasks:
+    - name: Include variables
+      include_vars: "{{ item }}"
+      with_items:
+        - "profile_manifests/{{ cord_profile }}.yml"
+        - profile_manifests/local_vars.yml
+
+- name: Build GUI Extensions docker images
+  hosts: head
+  roles:
+    - xos-gui-extension-build
+
+- include: add-bootstrap-containers-playbook.yml
+
+- name: Onboard GUI Extensions
+  hosts: xos_bootstrap_ui
+  connection: docker
+  roles:
+    - xos-gui-extension-onboard
\ No newline at end of file
diff --git a/profile_manifests/mock-rcord.yml b/profile_manifests/mock-rcord.yml
index ef2dbc5..1c2bcc7 100644
--- a/profile_manifests/mock-rcord.yml
+++ b/profile_manifests/mock-rcord.yml
@@ -41,9 +41,6 @@
     state: "xos.core.node"
   - label: "Instances"
     state: "xos.core.instance"
-  - label: "AAAAAAA"
-    state: "xos.core.instance"
-    parent: "xos.core"
 
 # GUI branding [OLD GUI to be removed]
 gui_branding_name: "CORD"
diff --git a/roles/cord-profile/tasks/main.yml b/roles/cord-profile/tasks/main.yml
index 2f3fd95..de66651 100644
--- a/roles/cord-profile/tasks/main.yml
+++ b/roles/cord-profile/tasks/main.yml
@@ -89,6 +89,7 @@
     - xos.yaml
     - xos-bootstrap-docker-compose.yaml
     - onboard-chameleon.yaml
+    - onboard-gui-extensions-store.yaml
     - onboard-xos-gui.yaml
     - onboard-xos-rest-gw.yaml
     - gateway-config.yml
diff --git a/roles/cord-profile/templates/onboard-gui-extensions-store.yaml.j2 b/roles/cord-profile/templates/onboard-gui-extensions-store.yaml.j2
new file mode 100644
index 0000000..0e83718
--- /dev/null
+++ b/roles/cord-profile/templates/onboard-gui-extensions-store.yaml.j2
@@ -0,0 +1,26 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # UI Extension Store
+    gui-extensions-store:
+      type: tosca.nodes.Component
+      properties:
+        image: node:argon
+        command: /bin/true
+
+    /var/www/dist/extensions:
+      type: tosca.nodes.ComponentVolume
+      properties:
+          host_path: ""
+          read_only: false
+      requirements:
+          - xos:
+             node: gui-extensions-store
+             relationship: tosca.relationships.VolumeOfComponent
\ No newline at end of file
diff --git a/roles/cord-profile/templates/onboard-xos-gui.yaml.j2 b/roles/cord-profile/templates/onboard-xos-gui.yaml.j2
index e2dd0d3..bcaf3c3 100644
--- a/roles/cord-profile/templates/onboard-xos-gui.yaml.j2
+++ b/roles/cord-profile/templates/onboard-xos-gui.yaml.j2
@@ -37,6 +37,16 @@
              node: xos-spa-gui
              relationship: tosca.relationships.VolumeOfComponent
 
+    # Mounting volume from volume container
+    gui-extensions-store:
+      type: tosca.nodes.ComponentVolumeContainer
+      properties:
+          container: gui-extensions-store
+      requirements:
+          - xos:
+             node: xos-spa-gui
+             relationship: tosca.relationships.VolumeContainerOfComponent
+
     gui-to-chameleon:
       type: tosca.nodes.ComponentLink
       properties:
diff --git a/roles/cord-profile/templates/style.config.js.j2 b/roles/cord-profile/templates/style.config.js.j2
index 01a7624..976d88d 100644
--- a/roles/cord-profile/templates/style.config.js.j2
+++ b/roles/cord-profile/templates/style.config.js.j2
@@ -6,14 +6,14 @@
     payoff: '{{ gui_payoff }}',
     logo: '{{ gui_logo }}',
     routes: [
-    {% for route in gui_routes %}
-    {
+{% for route in gui_routes %}
+        {
             label: '{{ route.label }}',
             state: '{{ route.state }}',
-            {% if route.parent is defined %}
+{% if route.parent is defined %}
             parent: '{{ route.parent }}'
-            {% endif %}
-    },
-    {% endfor %}
+{% endif %}
+        },
+{% endfor %}
     ]
 });
\ No newline at end of file
diff --git a/roles/xos-bootstrap/tasks/main.yml b/roles/xos-bootstrap/tasks/main.yml
index 109d9ee..d962165 100644
--- a/roles/xos-bootstrap/tasks/main.yml
+++ b/roles/xos-bootstrap/tasks/main.yml
@@ -12,4 +12,3 @@
     project_src: "{{ cord_profile_dir }}"
     files: "xos-bootstrap-docker-compose.yaml"
   register: xos_bootstrap_out
-
diff --git a/roles/xos-gui-extension-build/defaults/main.yml b/roles/xos-gui-extension-build/defaults/main.yml
new file mode 100644
index 0000000..715410c
--- /dev/null
+++ b/roles/xos-gui-extension-build/defaults/main.yml
@@ -0,0 +1,12 @@
+---
+# xos-gui-extension-build/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+enabled_gui_extensions:
+  - name: sample
+    path: orchestration/xos-sample-gui-extension
+  - name: other-sample
+    path: orchestration/xos-sample-gui-extension
+
diff --git a/roles/xos-gui-extension-build/tasks/main.yml b/roles/xos-gui-extension-build/tasks/main.yml
new file mode 100644
index 0000000..51be926
--- /dev/null
+++ b/roles/xos-gui-extension-build/tasks/main.yml
@@ -0,0 +1,36 @@
+# xos-gui-extension-build/tasks/main.yml
+# Dockerfile.xos-gui-extension-builder
+
+# Create a base image with installed deps (to speedup the process)
+- name: Build xos-gui-extension-builder image
+  docker_image:
+    name: "xosproject/xos-gui-extension-builder"
+    path: "{{ cord_dir }}/orchestration/xos-gui/"
+    dockerfile: "Dockerfile.xos-gui-extension-builder"
+  register: "xos-gui-extension-builder"
+
+# Build extensions images
+- name: Build xos-gui-extensions docker images
+  docker_image:
+    name: "xosproject/gui-extension-{{ item.name }}"
+    path: "{{ cord_dir }}/{{ item.path }}"
+    pull: false
+  with_items: "{{ enabled_gui_extensions }}"
+
+# Compile the TOSCA to onboard extensions
+- name: Create templated TOSCA to onboard extensions
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+    mode: 0644
+  with_items:
+    - xos-gui-extensions-onboard.yml
+
+# Compile the TOSCA to persist extensions
+- name: Create templated TOSCA to persist extensions
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+    mode: 0644
+  with_items:
+    - xos-gui-extensions.yml
diff --git a/roles/xos-gui-extension-build/templates/xos-gui-extensions-onboard.yml.j2 b/roles/xos-gui-extension-build/templates/xos-gui-extensions-onboard.yml.j2
new file mode 100644
index 0000000..435daa3
--- /dev/null
+++ b/roles/xos-gui-extension-build/templates/xos-gui-extensions-onboard.yml.j2
@@ -0,0 +1,28 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard xos-sample-gui-extension
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # UI Extensions
+{% for ext in enabled_gui_extensions %}
+    gui-extensions-{{ext.name}}:
+      type: tosca.nodes.Component
+      properties:
+        image: xosproject/gui-extension-{{ ext.name }}
+        command: npm run build
+
+    gui-extensions-store-{{ ext.name }}:
+      type: tosca.nodes.ComponentVolumeContainer
+      properties:
+          container: gui-extensions-store
+      requirements:
+          - xos:
+             node: gui-extensions-{{ext.name}}
+             relationship: tosca.relationships.VolumeContainerOfComponent
+{% endfor %}
+
diff --git a/roles/xos-gui-extension-build/templates/xos-gui-extensions.yml.j2 b/roles/xos-gui-extension-build/templates/xos-gui-extensions.yml.j2
new file mode 100644
index 0000000..6b5be5e
--- /dev/null
+++ b/roles/xos-gui-extension-build/templates/xos-gui-extensions.yml.j2
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Persist xos-sample-gui-extension
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # UI Extension
+{% for ext in enabled_gui_extensions %}
+    {{ext.name}}:
+      type: tosca.nodes.XOSGuiExtension
+      properties:
+        files: /spa/extensions/{{ext.name}}/vendor.js, /spa/extensions/{{ext.name}}/app.js
+{% endfor %}
diff --git a/roles/xos-gui-extension-onboard/defaults/main.yml b/roles/xos-gui-extension-onboard/defaults/main.yml
new file mode 100644
index 0000000..450ea3d
--- /dev/null
+++ b/roles/xos-gui-extension-onboard/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# xos-gui-extension-onboard/defaults/main.yml
+
+xos_admin_user: "xosadmin@opencord.org"
+
diff --git a/roles/xos-gui-extension-onboard/tasks/main.yaml b/roles/xos-gui-extension-onboard/tasks/main.yaml
new file mode 100644
index 0000000..5ea8897
--- /dev/null
+++ b/roles/xos-gui-extension-onboard/tasks/main.yaml
@@ -0,0 +1,12 @@
+# xos-gui-extension-onboard/tasks/main.yml
+
+- name: Execute xos-gui-extensions onboard TOSCA recipe
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/xos-gui-extensions-onboard.yml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Execute xos-gui-extensions persist TOSCA recipe
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/xos-gui-extensions.yml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
diff --git a/roles/xos-onboarding/defaults/main.yml b/roles/xos-onboarding/defaults/main.yml
index 76ed617..3167ed5 100644
--- a/roles/xos-onboarding/defaults/main.yml
+++ b/roles/xos-onboarding/defaults/main.yml
@@ -9,6 +9,8 @@
   - "ng-xos-lib"
 
 xos_components:
+  - name: gui-extensions-store
+    recipe_filename: onboard-gui-extensions-store.yaml
   - name: Chameleon
     recipe_filename: onboard-chameleon.yaml
   - name: xos-rest-gw