Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions doc/source/developer/docker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Labels are defined with the prefix namespace::

eu.simphony-project.docker

The following labels are currently defined.
The following labels are currently defined.
Their definition can be found in ``remoteappmanager.docker.docker_labels``

- ``ui_name``: the UI visible name of the image
Expand All @@ -21,12 +21,12 @@ Their definition can be found in ``remoteappmanager.docker.docker_labels``
the original base image (vncapp or webapp)
- ``env``: subnamespace for accepted environment variables. See below.

The env is a subnamespace defining the environment variables the image internals
The env is a subnamespace defining the environment variables the image internals
can understand. This does not mean that they are the only ones that will be
passed to the image.
passed to the image.

The naming strategy works around the `docker label restrictions
<https://docs.docker.com/engine/userguide/labels-custom-metadata/#/label-keys-namespaces>`_
The naming strategy works around the `docker label restrictions
<https://docs.docker.com/engine/userguide/labels-custom-metadata/#/label-keys-namespaces>`_
of having `kebab case <http://c2.com/cgi/wiki?KebabCase>`_ vs envvars that are
traditionally MACRO_CASE. Additionally, it allows new variables to be added
by layers without having to know the variables understood by the base layer.
Expand All @@ -39,23 +39,25 @@ and dashes converted to underscores. For example:
the value of the label is currently unused, and should be left empty.

If your application uses variables with a different convention, or uses double underscores,
you will have to define an auxilliary variable and transfer the value in the image
you will have to define an auxilliary variable and transfer the value in the image
startup scripts.

Currently reserved env keys:

- ``x11-width``: for the VNC images, the X11 width
- ``x11-height``: for the VNC images, the X11 height
- ``x11-depth``: for the VNC images, the X11 depth (currently unused, fixed at 16)
- ``srdata``: this variable can be set to a file that will be loaded by an application
upon startup

Container Labels
''''''''''''''''

When a container is started, the following labels will be added:

- ``url_id``: unique identifier that ends up in the URL when the
- ``url_id``: unique identifier that ends up in the URL when the
user is redirected
- ``mapping_id``: a unique key identifying the combination of image
- ``mapping_id``: a unique key identifying the combination of image
and policy used to start the container.
- ``user``: the user that started the container

Expand Down
3 changes: 2 additions & 1 deletion frontend/admin/vue-components/AccountingView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@
item.allow_home,
item.volume_source,
item.volume_target,
item.volume_mode === "ro"
item.volume_mode === "ro",
item.allow_startup_data
]);
});
})
Expand Down
12 changes: 10 additions & 2 deletions frontend/admin/vue-components/accounting/NewAccountingDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
</label>
</div>

<div class="form-group">
<label>
<input type="checkbox" id="allow_startup_data" v-model="model.allow_startup_data"/> Allow providing a startup file
</label>
</div>

<p v-if="crossValidationError" class="text-danger">Both Volume Source and Target must be defined</p>
<div class="modal-footer">
<div class="alert alert-danger" v-if="communicationError">
Expand Down Expand Up @@ -83,7 +89,8 @@
volume_source: '',
volume_target: '',
volume_readonly: false,
volume_source_target: []
volume_source_target: [],
allow_startup_data: false
},
imageNames: []
};
Expand Down Expand Up @@ -136,7 +143,8 @@
allow_home: this.model.allow_home,
volume_source: this.model.volume_source,
volume_target: this.model.volume_target,
volume_mode: (model.volume_readonly ? "ro" : "rw")
volume_mode: (model.volume_readonly ? "ro" : "rw"),
allow_startup_data: this.model.allow_startup_data
};

resources.Accounting.create(rep)
Expand Down
26 changes: 26 additions & 0 deletions frontend/user/configurables/StartupData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<div class="form-group">
<label>Startup file</label><br>
<input v-model="startupdata">
</div>
</template>

<script>
let Vue = require('vue');

module.exports = Vue.component('startupdata-component', {
props: ['configDict'],

data: function () {
return {
startupdata: this.configDict.startupdata
};
},

watch: {
configDict: function () { this.startupdata = this.configDict.startupdata; },
startupdata: function () { this.$emit('update:configDict', { startupdata: this.startupdata }); }
}
});

</script>
15 changes: 15 additions & 0 deletions frontend/user/configurables/configurables.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
let utils = require("utils");

require("./Resolution");
require("./StartupData");


// Your configurable class must implement a tag and default configDict
Expand All @@ -22,9 +23,23 @@ class ResolutionModel {
}
}

class StartupDataModel {
constructor() {
this.tag = "startupdata";
this.configDict = { startupdata: '' };
}

asConfigDict() {
let startupdata = this.configDict.startupdata;

return { startupdata: startupdata };
}
}

let outputConfigurables = {};

// Export all your configurable models here respecting the tag (here resolution)
outputConfigurables.resolution = ResolutionModel;
outputConfigurables.startupdata = StartupDataModel;

module.exports = outputConfigurables;
16 changes: 12 additions & 4 deletions frontend/user/vue-components/ApplicationView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,18 @@
<form class="configuration">
<fieldset v-if="currentApp.configurables.length === 0">No configurable options for this image</fieldset>
<fieldset v-else :disabled="!currentApp.isStopped()">
<component v-for="configurable in currentApp.configurables"
:key="configurable.tag"
:is="configurable.tag + '-component'"
:configDict.sync="configurable.configDict"></component>
<fieldset v-if="appPolicy.allow_startup_data">
<component v-for="configurable in currentApp.configurables"
:key="configurable.tag"
:is="configurable.tag + '-component'"
:configDict.sync="configurable.configDict"></component>
</fieldset>
<fieldset v-else>
<component v-for="configurable in currentApp.configurables" v-if="configurable.tag !== 'srdata'"
:key="configurable.tag"
:is="configurable.tag + '-component'"
:configDict.sync="configurable.configDict"></component>
</fieldset>
</fieldset>
</form>
</div>
Expand Down
28 changes: 22 additions & 6 deletions remoteappmanager/cli/remoteappdb/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ def list(ctx, no_decoration, show_apps):
headers = ["ID", "Name"]
if show_apps:
headers += ["App", "License", "Home", "View", "Common",
"Vol. Source", "Vol. Target", "Vol. Mode"]
"Vol. Source", "Vol. Target", "Vol. Mode",
"Allow Startup Data"]

session = ctx.obj.session

Expand All @@ -240,7 +241,8 @@ def list(ctx, no_decoration, show_apps):
entry.application_policy.allow_common,
entry.application_policy.volume_source,
entry.application_policy.volume_target,
entry.application_policy.volume_mode]
entry.application_policy.volume_mode,
entry.application_policy.allow_startup_data]
for entry in orm.accounting_for_user(session, user)]

if len(apps) == 0:
Expand Down Expand Up @@ -352,8 +354,13 @@ def list(ctx, no_decoration):
@click.option("--volume", type=click.STRING,
help="Application data volume, format=SOURCE:TARGET:MODE, "
"where mode is 'ro' or 'rw'.")
@click.option("--allow-startup-data",
is_flag=True,
help="Allow user to provide a file for the container to load"
"at startup.")
@click.pass_context
def grant(ctx, image, user, app_license, allow_home, allow_view, volume):
def grant(ctx, image, user, app_license, allow_home, allow_view, volume,
allow_startup_data):
"""Grants access to application identified by IMAGE to a specific
user USER and specified access policy."""
allow_common = False
Expand Down Expand Up @@ -389,7 +396,9 @@ def grant(ctx, image, user, app_license, allow_home, allow_view, volume):
orm.ApplicationPolicy.allow_view == allow_view,
orm.ApplicationPolicy.volume_source == source,
orm.ApplicationPolicy.volume_target == target,
orm.ApplicationPolicy.volume_mode == mode).one_or_none()
orm.ApplicationPolicy.volume_mode == mode,
orm.ApplicationPolicy.allow_startup_data == allow_startup_data
).one_or_none()

if orm_policy is None:
orm_policy = orm.ApplicationPolicy(
Expand All @@ -400,6 +409,7 @@ def grant(ctx, image, user, app_license, allow_home, allow_view, volume):
volume_source=source,
volume_target=target,
volume_mode=mode,
allow_startup_data=allow_startup_data,
)
session.add(orm_policy)

Expand Down Expand Up @@ -440,10 +450,14 @@ def grant(ctx, image, user, app_license, allow_home, allow_view, volume):
@click.option("--volume", type=click.STRING,
help="Application data volume, format=SOURCE:TARGET:MODE, "
"where mode is 'ro' or 'rw'.")
@click.option("--allow-startup-data",
is_flag=True,
help="Allow user to provide a file for the container to load"
"at startup.")
@click.pass_context
def revoke(
ctx, image, user, revoke_all, app_license,
allow_home, allow_view, volume):
allow_home, allow_view, volume, allow_startup_data):
"""Revokes access to application identified by IMAGE to a specific
user USER and specified parameters."""
allow_common = False
Expand Down Expand Up @@ -478,7 +492,9 @@ def revoke(
orm.ApplicationPolicy.allow_view == allow_view,
orm.ApplicationPolicy.volume_source == source,
orm.ApplicationPolicy.volume_target == target,
orm.ApplicationPolicy.volume_mode == mode).one()
orm.ApplicationPolicy.volume_mode == mode,
orm.ApplicationPolicy.allow_startup_data == allow_startup_data
).one()

session.query(orm.Accounting).filter(
orm.Accounting.application == orm_app,
Expand Down
6 changes: 4 additions & 2 deletions remoteappmanager/cli/tests/test_remoteappdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,14 @@ def test_app_grant(self):
self._remoteappdb("app grant myapp user")
self._remoteappdb("app grant myapp user "
"--allow-view "
"--volume=frobniz:froble:ro")
"--volume=frobniz:froble:ro "
"--allow-startup-data")
_, out = self._remoteappdb("user list --show-apps")
self.assertIn("myapp", out)
self.assertIn("frobniz", out)
self.assertIn("froble", out)
self.assertIn(" ro\n", out)
self.assertIn(" ro", out)
self.assertIn("1\n", out)

def test_app_revoke(self):
self._remoteappdb("app create myapp --no-verify")
Expand Down
16 changes: 11 additions & 5 deletions remoteappmanager/db/csv_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def __init__(self, id, name):
'policy.allow_common',
'policy.volume_source',
'policy.volume_target',
'policy.volume_mode')
'policy.volume_mode',
'policy.allow_startup_data')


@mergedocs(ABCDatabase)
Expand Down Expand Up @@ -158,6 +159,9 @@ def __init__(self, csv_file_path, **kwargs):
None)
volume_mode = (record[indices['policy.volume_mode']] or
None)
allow_startup_data = record[
indices['policy.allow_startup_data']
] == '1'

application_policy = self.application_policies.setdefault(
(app_license,
Expand All @@ -166,15 +170,17 @@ def __init__(self, csv_file_path, **kwargs):
allow_common,
volume_source,
volume_target,
volume_mode),
volume_mode,
allow_startup_data),
CSVApplicationPolicy(
app_license=app_license,
allow_home=allow_home,
allow_view=allow_view,
allow_common=allow_common,
volume_source=volume_source,
volume_target=volume_target,
volume_mode=volume_mode))
volume_mode=volume_mode,
allow_startup_data=allow_startup_data))

# Save the configuration
# Note that we don't filter existing duplicate entry
Expand Down Expand Up @@ -222,11 +228,11 @@ def list_applications(self):
return list(self.applications.values())

def grant_access(self, app_name, user_name, app_license,
allow_home, allow_view, volume):
allow_home, allow_view, volume, allow_startup_data):
raise UnsupportedOperation()

def revoke_access(self, app_name, user_name, app_license,
allow_home, allow_view, volume):
allow_home, allow_view, volume, allow_startup_data):
raise UnsupportedOperation()

def revoke_access_by_id(self, mapping_id):
Expand Down
15 changes: 12 additions & 3 deletions remoteappmanager/db/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ABCApplicationPolicy(metaclass=ABCMeta):

def __init__(self, app_license=None, allow_home=False, allow_view=False,
allow_common=False, volume_source=None, volume_target=None,
volume_mode=None):
volume_mode=None, allow_startup_data=False):

#: Application License (if specified)
self.app_license = app_license
Expand All @@ -50,6 +50,9 @@ def __init__(self, app_license=None, allow_home=False, allow_view=False,
#: Mode for read-write access (ro = Read-only. rw = Read-write)
self.volume_mode = volume_mode

#: Allow user to provide a file for the container to load at startup
self.allow_startup_data = allow_startup_data

def __repr__(self):
args = _inspect.getargs(
ABCApplicationPolicy.__init__.__code__).args[1:]
Expand Down Expand Up @@ -203,7 +206,7 @@ def list_applications(self):

@abstractmethod
def grant_access(self, app_name, user_name, app_license,
allow_home, allow_view, volume):
allow_home, allow_view, volume, allow_startup_data):
"""Grant access for user to application.

Parameters
Expand All @@ -228,6 +231,9 @@ def grant_access(self, app_name, user_name, app_license,
mode being "ro" or "rw".
(e.g. "/host/path:/container/path:ro").

allow_startup_data : bool
If the user can provide a file for the container to load at startup

Raises
------
exception.NotFound:
Expand All @@ -243,7 +249,7 @@ def grant_access(self, app_name, user_name, app_license,

@abstractmethod
def revoke_access(self, app_name, user_name, app_license,
allow_home, allow_view, volume):
allow_home, allow_view, volume, allow_startup_data):
"""Revoke access for user to application.

Parameters
Expand All @@ -268,6 +274,9 @@ def revoke_access(self, app_name, user_name, app_license,
mode being "ro" or "rw".
(e.g. "/host/path:/container/path:ro").

allow_startup_data : bool
If the user can provide a file for the container to load at startup

Raises
------
exception.NotFound:
Expand Down
Loading