Commit 49191098 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Added support for editing containers. Minor fixes.

parent 08649091
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -153,10 +153,22 @@ class Container(models.Model):
        return str('Container "{}" of user "{}" with image name "{}" and image tag "{}" on registry "{}" '.format(self.name, user_str, self.image_name, self.image_tag, self.registry))

    def save(self, *args, **kwargs):
        # Check that digest starts with sha256:

        if self.image_tag is None:
            raise ValueError('The image tag field must be always set (e.g. using "latest")')
        if not self.image_arch:
            self.image_arch = None
        if not self.image_os:
            raise ValueError('The image must be always set')
        if self.image_digest and not self.image_digest.startswith('sha256:'):
            raise ValueError('The digest field must start with "sha256:"')

        if not self.interface_port:
            self.interface_port = None
        if not self.interface_protocol:
            self.interface_protocol = None
        if not self.interface_transport:
            self.interface_transport = None
        super(Container, self).save(*args, **kwargs)

    @property
+11 −9
Original line number Diff line number Diff line


{% if details %}

<div style="width:400px; float:left; border: #e0e0e0 solid 1px; margin:10px; background:#f8f8f8; margin-bottom:15px">
@@ -107,7 +105,7 @@

       <tr>
        <td colspan=2>
         <b>Disable HTTP auth embedding</b> &nbsp;
         <b>Disable HTTP auth embedding {{user.is_admin }}</b> &nbsp;
         {% if container.disable_http_basicauth_embedding %}
           <input type="checkbox" name="disable_http_basicauth_embedding" checked disabled/>
         {% else %}
@@ -123,10 +121,14 @@
       </tr>
       {% endif %}

       {% if container.user %}
       {% if container.user or user.is_staff %}
       <tr>
        <td><b>Operations</b></td>
        <td><a href="?action=delete&container_uuid={{ container.uuid }}">Delete</a></td>
        <td>
          <a href="?action=delete&container_uuid={{ container.uuid }}">Delete</a>
          &nbsp;|&nbsp;
          <a href="/edit_software?container_uuid={{ container.uuid }}">Edit</a>
        </td>
       </tr>
       {% endif %}
      </table>
+158 −0
Original line number Diff line number Diff line
{% load static %}
{% include "header.html" %}
{% include "navigation.html" %}
{% include "logo.html" %}

<div class="container">
  <div class="dashboard">
    <div class="span8 offset2">
      <h1><a href="/software">Software containers</a> <span style="font-size:18px"> / <a href="/software/?container_family_id={{data.container.family_id}}&details=True">{{ data.container.name }}</a> / {{ data.container.image_tag}} / edit</span></h1>
      <hr>

      {% if data.error %}
        <div class="alert alert-danger">{{ data.error }}</div>
      {% endif %}
      {% if data.edited %}
        <div class="alert alert-success">Container updated successfully.</div>
      {% endif %}

      <form action="#" method="POST">
      {% csrf_token %}
      <table class="dashboard" style="width:400px; margin-bottom:25px">
        <tr>
          <td><b>Name</b></td>
          <td><input type="text" name="container_name" value="{{ data.container.name }}" size="23" required /></td>
        </tr>
        <tr>
          <td><b>Description</b></td>
          <td><textarea name="container_description" rows="3" cols="22">{{ data.container.description }}</textarea></td>
        </tr>
        <tr>
          <td><b>Registry</b></td>
          <td><input type="text" name="container_registry" value="{{ data.container.registry }}" size="23" required /></td>
        </tr>
        <tr>
          <td><b>Image&nbsp;name</b></td>
          <td><input type="text" name="container_image_name" value="{{ data.container.image_name }}" size="23" required /></td>
        </tr>
        <tr>
          <td><b>Image tag</b></td>
          <td><input type="text" name="container_image_tag" value="{{ data.container.image_tag }}" size="23" required /></td>
        </tr>
      </table>

      <h4>Interface</h4>
      <table class="dashboard" style="width:400px; margin-bottom:25px">
        <tr>
          <td><b>Interface port</b></td>
          {% if data.container.interface_port %}
          <td><input type="text" name="container_interface_port" value="{{ data.container.interface_port }}" size="5" /></td>
          {% else %}
          <td><input type="text" name="container_interface_port" value="" size="5" /></td>
          {% endif %}
        </tr>
        <tr>
          <td><b>Interface protocol</b></td>
          <td>
            {% if request.user.profile.is_power_user %}
              <input type="text" name="container_interface_protocol" value="{{ data.container.interface_protocol }}" size="5" />
            {% else %}
              <select name="container_interface_protocol">
                <option value="http" {% if data.container.interface_protocol == 'http' %}selected{% endif %}>http</option>
                <option value="https" {% if data.container.interface_protocol == 'https' %}selected{% endif %}>https</option>
              </select>
            {% endif %}
          </td>
        </tr>
      </table>

      <a href="javascript:void(0);" id="show_button" onclick="toggle_visibility('advanced_div')">Advanced...</a>
      <div id="advanced_div" style="display:none; width:400px;">
        <h4>Advanced <font size=-1>| <a href="javascript:void(0);" id="hide_button" onclick="toggle_visibility('advanced_div')" style="display:none">hide</a></font></h4>
        <table class="dashboard" style="width:400px; margin-bottom:25px">
          <tr>
            <td><b>Image arch</b></td>
            {% if data.container.image_arch %}
            <td><input type="text" name="container_image_arch" value="{{ data.container.image_arch }}" size="5" /></td>
            {% else %}
            <td><input type="text" name="container_image_arch" value="" size="5" /></td>
            {% endif %}
          </tr>
          <tr>
            <td><b>Image OS</b></td>
            <td>
              <select name="container_image_os">
                <option value="linux" {% if data.container.image_os == 'linux' %}selected{% endif %}>linux</option>
              </select>
            </td>
          </tr>
          <tr>
            <td><b>Image digest</b></td>
            {% if data.container.image_digest %}
            <td><input type="text" name="container_image_digest" value="{{ data.container.image_digest }}" size="15" /></td>
            {% else %}
            <td><input type="text" name="container_image_digest" value="" size="15" /></td>
            {% endif %}
          </tr>
          <tr>
            <td><b>Interface transport</b></td>
            <td>
              <select name="container_interface_transport">
                <option value="tcp/ip" {% if data.container.interface_transport == 'tcp/ip' %}selected{% endif %}>tcp/ip</option>
              </select>
            </td>
          </tr>
          <tr>
            <td colspan=2>
              <b>Supports custom interface port</b> &nbsp;
              <input type="checkbox" name="container_supports_custom_interface_port" value="True" {% if data.container.supports_custom_interface_port %}checked{% endif %} />
            </td>
          </tr>
          <tr>
            <td colspan=2>
              <b>Supports interface auth</b> &nbsp;
              <input type="checkbox" name="container_supports_interface_auth" value="True" {% if data.container.supports_interface_auth %}checked{% endif %} />
            </td>
          </tr>
          <tr>
            <td colspan=2>
              <b>Disable HTTP auth embedding</b> &nbsp;
              <input type="checkbox" name="container_disable_http_basicauth_embedding" value="True" {% if data.container.disable_http_basicauth_embedding %}checked{% endif %} />
            </td>
          </tr>
          <tr>
            <td><b>Environment variables</b></td>
            <td><textarea name="container_env_vars" rows="2" cols="22">{{ data.container.env_vars|default_if_none:'' }}</textarea></td>
          </tr>
        </table>
      </div>
      <table style="width:400px; border:0; background:#ffffff; margin-top:20px">
        <tr><td align="center">
          <a href="/software/?container_family_id={{data.container.family_id}}&details=True" class="btn btn-primary" style="margin-left:10px">Back</a>
          &nbsp;
          <input type="submit" value="Save changes" class="btn btn-primary">
        </td></tr>
      </table>
      </form>
    </div>
  </div>
</div>

{% include "footer.html" %}

<script>
function toggle_visibility(id) {
  var e = document.getElementById(id);
  var showBtn = document.getElementById('show_button');
  var hideBtn = document.getElementById('hide_button');
  if(e.style.display == 'block') {
    e.style.display = 'none';
    if (showBtn) showBtn.style.display = 'inline';
    if (hideBtn) hideBtn.style.display = 'none';
  } else {
    e.style.display = 'block';
    if (showBtn) showBtn.style.display = 'none';
    if (hideBtn) hideBtn.style.display = 'inline';
  }
}
</script> 
 No newline at end of file
+3 −3
Original line number Diff line number Diff line
@@ -72,15 +72,15 @@

      <div class="row" style="padding:5px">
      {% if data.container %}
      {% include "components/container.html" with container=data.container details=True %}
      {% include "components/container.html" with container=data.container user=data.user details=True %}
      {% else %}
      {% if data.container_families %}
      {% for container_family_id, container_family in data.container_families.items %}
      {% include "components/container_family.html" with container_family=container_family container_family_id=container_family_id%}
      {% include "components/container_family.html" with container_family=container_family container_family_id=container_family_id user=data.user%}
      {% endfor %}
      {% else %}
      {% for container in data.containers %}
      {% include "components/container.html" with container=container %}
      {% include "components/container.html" with container=container user=data.user %}
      {% endfor %}
      {% endif  %}
      {% endif %}
+87 −5
Original line number Diff line number Diff line
@@ -865,15 +865,21 @@ def software(request):
            def add(self, container):
                self.members.append(container)

                container_image_arch = container.image_arch

                # Handle None arch
                if container_image_arch is None:
                    container_image_arch = ''

                if not self.description:
                    self.description = container.description

                if not container.image_arch in self.all_archs:
                    self.all_archs.append(container.image_arch)
                if not container_image_arch in self.all_archs:
                    self.all_archs.append(container_image_arch)

                if not container.image_arch in self.container_by_tags_by_arch:
                    self.container_by_tags_by_arch[container.image_arch]={}
                self.container_by_tags_by_arch[container.image_arch][container.image_tag] = container
                if not container_image_arch in self.container_by_tags_by_arch:
                    self.container_by_tags_by_arch[container_image_arch]={}
                self.container_by_tags_by_arch[container_image_arch][container.image_tag] = container


            def finalize(self, desc=True):
@@ -1047,6 +1053,82 @@ def add_software(request):
    return render(request, 'add_software.html', {'data': data})


@private_view
def edit_software(request):
    data = {}
    data['user'] = request.user

    container_uuid = request.GET.get('container_uuid', None)
    if not container_uuid:
        data['error'] = 'No container specified.'
        return render(request, 'error.html', {'data': data})

    try:
        container = Container.objects.get(uuid=container_uuid)
    except Container.DoesNotExist:
        data['error'] = 'Container does not exist.'
        return render(request, 'error.html', {'data': data})

    # Only allow editing if user owns the container or is admin
    if not (container.user == request.user or request.user.is_superuser):
        data['error'] = 'You do not have permission to edit this container.'
        return render(request, 'error.html', {'data': data})

    data['container'] = container
    data['edited'] = False

    if request.method == 'POST':
        # Get all fields from POST, fallback to current values
        container.name = request.POST.get('container_name', container.name)
        container.description = request.POST.get('container_description', container.description)
        container.registry = request.POST.get('container_registry', container.registry)
        container.image_name = request.POST.get('container_image_name', container.image_name)
        container.image_tag = request.POST.get('container_image_tag', container.image_tag)
        container.image_arch = request.POST.get('container_image_arch', container.image_arch)
        container.image_os = request.POST.get('container_image_os', container.image_os)
        container.image_digest = request.POST.get('container_image_digest', container.image_digest)

        interface_port = request.POST.get('container_interface_port', None)
        if interface_port:
            try:
                container.interface_port = int(interface_port)
            except Exception:
                data['error'] = 'Invalid container port.'
                return render(request, 'edit_software.html', {'data': data})
        else:
            container.interface_port = None

        container.interface_protocol = request.POST.get('container_interface_protocol', container.interface_protocol)
        container.interface_transport = request.POST.get('container_interface_transport', container.interface_transport)

        supports_custom_interface_port = request.POST.get('container_supports_custom_interface_port', None)
        container.supports_custom_interface_port = supports_custom_interface_port == 'True'

        supports_interface_auth = request.POST.get('container_supports_interface_auth', None)
        container.supports_interface_auth = supports_interface_auth == 'True'

        disable_http_basicauth_embedding = request.POST.get('container_disable_http_basicauth_embedding', None)
        container.disable_http_basicauth_embedding = disable_http_basicauth_embedding == 'True'

        container_env_vars = request.POST.get('container_env_vars', None)
        if container_env_vars:
            try:
                container.env_vars = sanitize_container_env_vars(json.loads(container_env_vars))
            except Exception:
                data['error'] = 'Invalid environment variables format.'
                return render(request, 'edit_software.html', {'data': data})
        else:
            container.env_vars = None

        try:
            container.save()
            data['edited'] = True
        except Exception as e:
            data['error'] = f'Error saving container: {e}'
            return render(request, 'edit_software.html', {'data': data})

    return render(request, 'edit_software.html', {'data': data})


#=========================
#  Computing resources
Loading