From 797feebd07fcf2625d84df7519ba218eb15925d6 Mon Sep 17 00:00:00 2001
From: Stefano Alberto Russo <stefano.russo@gmail.com>
Date: Wed, 24 Nov 2021 01:26:58 +0100
Subject: [PATCH] Added support for extra supported archs, improved arch
 checks, minor imporvements.

---
 .../0027_computing_supported_archs.py         | 19 ++++++++++++
 .../webapp/code/rosetta/core_app/models.py    |  9 ++++--
 .../core_app/templates/components/task.html   | 10 ++++---
 .../rosetta/core_app/templates/new_task.html  |  6 ++++
 .../webapp/code/rosetta/core_app/views.py     | 30 ++++++++-----------
 5 files changed, 49 insertions(+), 25 deletions(-)
 create mode 100644 services/webapp/code/rosetta/core_app/migrations/0027_computing_supported_archs.py

diff --git a/services/webapp/code/rosetta/core_app/migrations/0027_computing_supported_archs.py b/services/webapp/code/rosetta/core_app/migrations/0027_computing_supported_archs.py
new file mode 100644
index 0000000..cb6ee53
--- /dev/null
+++ b/services/webapp/code/rosetta/core_app/migrations/0027_computing_supported_archs.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.2.1 on 2021-11-24 00:25
+
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core_app', '0026_auto_20211123_0037'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='computing',
+            name='supported_archs',
+            field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True, verbose_name='Supported architectures'),
+        ),
+    ]
diff --git a/services/webapp/code/rosetta/core_app/models.py b/services/webapp/code/rosetta/core_app/models.py
index e675c1d..346c996 100644
--- a/services/webapp/code/rosetta/core_app/models.py
+++ b/services/webapp/code/rosetta/core_app/models.py
@@ -122,7 +122,7 @@ class Container(models.Model):
     image_name = models.CharField('Image', max_length=255, blank=False, null=False)
     
     # Image identifiers
-    image_tag  = models.CharField('Tag', max_length=255, blank=True, null=True, default='latest')
+    image_tag  = models.CharField('Tag', max_length=255, blank=False, null=False, default='latest')
     image_arch = models.CharField('Architecture', max_length=36, blank=True, null=True)
     image_os   = models.CharField('Operating system', max_length=36, blank=True, null=True)
     # -- OR --
@@ -183,7 +183,7 @@ class Computing(models.Model):
     name        = models.CharField('Name', max_length=255, blank=False, null=False)
     description = models.TextField('Description', blank=True, null=True)
 
-    # Type (standalone / cluster) and arch
+    # Type (standalone / cluster) and arch (i.e. amd64)
     type = models.CharField('Type', max_length=255, blank=False, null=False)
     arch = models.CharField('Architecture', max_length=255, blank=False, null=False)
 
@@ -196,7 +196,10 @@ class Computing(models.Model):
     container_runtimes = JSONField('Container runtimes', blank=False, null=False)
     #container_runtime = models.CharField('Container runtimes', max_length=256, blank=False, null=False)
  
-    # Emulated architectures, by container runtime {'docker': ['arm64', 'amd']    
+    # Supported architectures (i.e. 386 for amd64), as list: ['386']
+    supported_archs = JSONField('Supported architectures', blank=True, null=True) 
+
+    # Emulated architectures, by container runtime: {'docker': ['arm64/v7', 'arm64/v8']    
     emulated_archs = JSONField('Emulated architectures', blank=True, null=True) 
 
     # Conf
diff --git a/services/webapp/code/rosetta/core_app/templates/components/task.html b/services/webapp/code/rosetta/core_app/templates/components/task.html
index 35ef478..effe0f1 100644
--- a/services/webapp/code/rosetta/core_app/templates/components/task.html
+++ b/services/webapp/code/rosetta/core_app/templates/components/task.html
@@ -5,6 +5,8 @@
             <div style="margin-top:5px; padding:10px; text-align:center; border-bottom: #f8f8f8 solid 1px; ">
             Task <b>{{data.task.name}}</b> summary
             </div>
+            <hr style="margin:0">
+            
           {% else %}
           <div style="width:350px; float:left; border: #e0e0e0 solid 1px; margin:10px; background:#f8f8f8; margin-bottom:20px">
             
@@ -27,18 +29,18 @@
             </div> -->
 
             
-            <div style="padding:10px;">
+            <div style="padding:10px; padding-left:15px; padding-right:15px; text-align:left;">
             <!-- <b>Container:</b> {{ task.container.name }} <span style="font-size:14px; background-color:{{task.container.color}}">&nbsp;&nbsp;&nbsp;&nbsp;</span> <br/>
             <b>Computing:</b> {{ task.computing.name }} <span style="font-size:14px; background-color:{{task.computing.color}}">&nbsp;&nbsp;&nbsp;&nbsp;</span><br/> -->
 
-            <b>Software:</b>
-            <a href="/software/?uuid={{ task.container.uuid }}" style="color:{{task.container.color}}">{{ task.container.name }}</a>
+            <b>Software container:</b>
+            <a href="/software/?container_uuid={{ task.container.uuid }}" style="color:{{task.container.color}}">{{ task.container.name }}</a>
             {% if task.container.type == 'docker' %}<img src="/static/img/docker-logo.svg" style="height:18px; width:18px; margin-bottom:4px" />{% endif %}
             {% if task.container.type == 'singularity' %}<img src="/static/img/singularity-logo.svg" style="height:18px; width:18px; margin-bottom:4px" />{% endif %}
 
             <br/>
             
-            <b>Computing:</b> <a href="/computing/?uuid={{ task.computing.uuid }}">{{ task.computing.name }}</a>
+            <b>Computing resource:</b> <a href="/computing/?uuid={{ task.computing.uuid }}">{{ task.computing.name }}</a>
             <!-- <a href="/computing/?uuid={{ task.computing.uuid }}" no_style="color:{{task.computing.color}}"><i class="fa fa-external-link" ></i></a><br/> -->           
             
             <div style="margin-top:2px">
diff --git a/services/webapp/code/rosetta/core_app/templates/new_task.html b/services/webapp/code/rosetta/core_app/templates/new_task.html
index c8d9b21..807e554 100644
--- a/services/webapp/code/rosetta/core_app/templates/new_task.html
+++ b/services/webapp/code/rosetta/core_app/templates/new_task.html
@@ -172,6 +172,12 @@
           <i class="fa fa-exclamation-triangle" style="color:orange"></i> The selected software container architecture ({{ data.task_container_arch}}) can only be emulated on this computing resource, meaning the it will probably run quite slow.
           </p></div>
           {% endif %}
+
+          {% if data.arch_auto_selection %}
+          <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px; margin-left:5px">
+          <i class="fa fa-exclamation-triangle" style="color:orange"></i> The selected software container does not specify any architecture. This will leave to the container runtime either to auto-select the right image architecture on the registry, or to fallback on emulation if not found. Beware of potential incompatibilities or slowdowns.
+          </p></div>
+          {% endif %}
           
           <!-- {% if data.task_container.interface_port and not data.task_container.supports_interface_auth %}
           <div> <p style="font-size:15px; max-width:700px; margin-bottom:20px; margin-left:5px">
diff --git a/services/webapp/code/rosetta/core_app/views.py b/services/webapp/code/rosetta/core_app/views.py
index bd1bd89..8af2c5a 100644
--- a/services/webapp/code/rosetta/core_app/views.py
+++ b/services/webapp/code/rosetta/core_app/views.py
@@ -464,14 +464,6 @@ def new_task(request):
                 raise Exception('Consistency error, container with uuid "{}" does not exists or user "{}" does not have access rights'.format(task_container_uuid, request.user.email))
         return task_container
 
-    # Get task container arch helper function
-    def get_task_container_arch(request):
-        container_arch = request.POST.get('task_container_arch', None)
-        if not container_arch:
-            # At the second step the task uuid is set via a GET request 
-            container_arch = request.GET.get('task_container_arch', None)
-        return container_arch
-
     # Get task computing helper function
     def get_task_computing(request):
         task_computing_uuid = request.POST.get('task_computing_uuid', None)
@@ -523,24 +515,27 @@ def new_task(request):
         data['task_computing'] = get_task_computing(request)
         
         # Check that container required architecture is compatible with the computing resource
+        # TODO: support setting the container runtime when creating the task
+        # TODO: refactor and unroll this code
         if data['task_container'].image_arch:
-            if data['task_container'].image_arch != data['task_computing'].arch:
-                # TODO: support setting the container runtime when creating the task
-                # TODO: refactor and unroll this code
-                # TODO: add support for setting the container runtime
+            if (data['task_container'].image_arch != data['task_computing'].arch) and (data['task_container'].image_arch not in data['task_computing'].supported_archs):
+
                 if data['task_computing'].emulated_archs:
                     container_runtime = data['task_computing'].container_runtimes[0]
+                    
                     if container_runtime in data['task_computing'].emulated_archs and data['task_container'].image_arch in data['task_computing'].emulated_archs[container_runtime]:
                         data['arch_emulation'] = True
+                        
                     else:
-                        raise ErrorMessage('This computing resource does not support architecture "{}" nor as native or emulated'.format(data['task_container'].image_arch))
+                        raise ErrorMessage('This computing resource does not support architecture \'{}\' nor as native or emulated'.format(data['task_container'].image_arch))
+                
                 else:
-                    raise ErrorMessage('This computing resource does not support architecture "{}" nor as native or emulated'.format(data['task_container'].image_arch))
+                    raise ErrorMessage('This computing resource does not support architecture \'{}\' nor as native or emulated'.format(data['task_container'].image_arch))
 
         else:
-            raise ErrorMessage('Auto detecting architectures is not supported yet')
-            
-        
+            data['arch_auto_selection'] = True
+            #raise ErrorMessage('Auto selecting architectures is not supported yet')
+
         # Generate random auth token        
         data['task_auth_token'] = str(uuid.uuid4())
 
@@ -553,7 +548,6 @@ def new_task(request):
 
         # Get software container and arch
         data['task_container'] = get_task_container(request)
-        data['task_container_arch'] = get_task_container_arch(request)
 
         # Get computing resource
         data['task_computing'] = get_task_computing(request)
-- 
GitLab