Skip to content
This repository has been archived by the owner on Nov 20, 2020. It is now read-only.

Support for external web console #91

Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ target/

# Visual Studio code
.vscode/

# Intellij
.idea
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ RUN pip install virtualenv

RUN apk add -U --no-cache git

COPY . /app
COPY ./requirements.txt /app/requirements.txt
RUN virtualenv /env && /env/bin/pip install --no-cache-dir -r /app/requirements.txt

COPY . /app

VOLUME ["/opt/docker-compose-projects"]

COPY demo-projects /opt/docker-compose-projects
Expand Down
24 changes: 24 additions & 0 deletions Dockerfile-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# https://github.com/francescou/docker-compose-ui
# DOCKER-VERSION 1.12.3
FROM python:2.7-alpine
MAINTAINER Francesco Uliana <[email protected]>

RUN pip install virtualenv

RUN apk add -U --no-cache git

COPY ./requirements.txt /app/requirements.txt
RUN virtualenv /env && /env/bin/pip install --no-cache-dir -r /app/requirements.txt

VOLUME /app

VOLUME ["/opt/docker-compose-projects"]

COPY demo-projects /opt/docker-compose-projects

EXPOSE 5000

CMD []
ENTRYPOINT ["/env/bin/python", "/app/main.py"]

WORKDIR /opt/docker-compose-projects/
6 changes: 6 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,12 @@ def host():

return jsonify(host=host_value, workdir=os.getcwd() if YML_PATH == '.' else YML_PATH)

@app.route(API_V1 + "web_console_pattern", methods=['GET'])
def get_web_console_pattern():
"""
forward WEB_CONSOLE_PATTERN env var from server to spa
"""
return jsonify(web_console_pattern=os.getenv('WEB_CONSOLE_PATTERN'))

@app.route(API_V1 + "health", methods=['GET'])
def health():
Expand Down
1 change: 1 addition & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ <h3>Projects</h3>
<script src="scripts/directives/actions.js"></script>
<script src="scripts/directives/project-detail.js"></script>
<script src="scripts/directives/modal.js"></script>
<script src="scripts/directives/console-modal.js"></script>
<script src="scripts/services/project.js"></script>
<script src="scripts/services/logs.js"></script>
<script async defer src="https://buttons.github.io/buttons.js"></script>
Expand Down
34 changes: 34 additions & 0 deletions static/scripts/directives/console-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

angular.module('composeUiApp')
.directive('consoleModalShow', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {

//Hide or show the modal
scope.showConsoleModal = function (visible, elem) {
if (!elem)
elem = element;

if (visible)
$(elem).modal('show');
else
$(elem).modal('hide');
};

//Watch for changes to the modal-visible attribute
scope.$watch(attrs.consoleModalShow, function (newValue) {
scope.showConsoleModal(newValue, attrs.$$element);
});

//Update the visible value when the dialog is closed through UI actions (Ok, cancel, etc.)
$(element).bind('hide.bs.modal', function () {
$parse(attrs.consoleModalShow).assign(scope, false);
if (!scope.$$phase && !scope.$root.$$phase)
scope.$apply();
});
}

};
});
36 changes: 34 additions & 2 deletions static/scripts/directives/project-detail.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
'use strict';

angular.module('composeUiApp')
.config(function($sceDelegateProvider) {
$.get('/api/v1/web_console_pattern', (response) => {
if (response.web_console_pattern) {
var parser = document.createElement('a'),
whitelistUrlPattern;

parser.href = response.web_console_pattern;
whitelistUrlPattern = parser.protocol + '//' + parser.host + '/**';

$sceDelegateProvider.resourceUrlWhitelist([
'self',
whitelistUrlPattern
]);
}
});
})
.directive('projectDetail', function($resource, $log, projectService, $window, $location){
return {
restrict: 'E',
Expand All @@ -18,11 +34,10 @@ angular.module('composeUiApp')
}
});



var Host = $resource('api/v1/host');
var Yml = $resource('api/v1/projects/yml/:id');
var Readme = $resource('api/v1/projects/readme/:id');
var WebConsolePattern = $resource('api/v1/web_console_pattern');

$scope.$watch('projectId', function (val) {
if (val) {
Expand Down Expand Up @@ -57,7 +72,24 @@ angular.module('composeUiApp')
$scope.logs = data.logs;
});
};

$scope.containerConsolePattern = null;
WebConsolePattern.get(function(data) {
if (data.web_console_pattern) {
$scope.containerConsolePattern = data.web_console_pattern;
}
});

$scope.openConsole = function(containerName, shell) {
if ($scope.containerConsolePattern) {
console.log('Opening console for ' + containerName + ' with shell ' + shell);

$scope.containerConsoleUrl = $scope.containerConsolePattern.replace('{containerName}', containerName).replace('{command}', shell);
$scope.containerName = containerName;
$scope.showConsoleDialog = true;
}
};

$scope.rebuild = function(serviceName) {
$scope.working = true;
Project.save({id: $scope.projectId, service_names: [serviceName], do_build: true},
Expand Down
4 changes: 4 additions & 0 deletions static/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,8 @@ header {

.btn {
padding: 6px 9px;
}

.shell-selector-list a {
cursor: pointer;
}
39 changes: 36 additions & 3 deletions static/views/project-detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,28 @@ <h5>

<div class="panel" ng-class="{'active': container.is_running, 'off': !container.is_running}">
<div class="panel-heading">

<span ng-class="{'text-muted': !container.is_running}">
{{container.name_without_project}}
</span>
<button class="btn btn-xs btn-default" ng-click="displayLogs(container.name)">logs</button>
<a class="btn btn-xs btn-default" ng-href="#/project/{{projectId}}/{{container.name}}">details</a>
<button class="btn btn-xs btn-default" ng-click="rebuild(id)">rebuild</button>
<br/>

<div class="btn-group" role="group" >
<button class="btn btn-xs btn-default" ng-click="displayLogs(container.name)">logs</button>
<a class="btn btn-xs btn-default" ng-href="#/project/{{projectId}}/{{container.name}}">details</a>
<button class="btn btn-xs btn-default" ng-click="rebuild(id)">rebuild</button>

<div class="btn-group" role="group" ng-show="containerConsolePattern">
<button class="btn btn-xs btn-default dropdown-toggle" type="button" id="shellSelector" data-toggle="dropdown">
console <span class="caret"></span>
</button>
<ul class="dropdown-menu shell-selector-list">
<li><a ng-click="openConsole(container.name, '/bin/bash')">/bin/bash</a></li>
<li><a ng-click="openConsole(container.name, '/bin/sh')">/bin/sh</a></li>
</ul>
</div>
</div>

</div>
<div class="panel-body">

Expand Down Expand Up @@ -103,6 +119,23 @@ <h4 class="modal-title">{{containerLogs}} logs</h4>
</div>
</div>
</div>

<div console-modal-show="showConsoleDialog" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{containerName}} console</h4>
</div>
<div class="modal-body">
<iframe ng-if="containerConsoleUrl" src="{{containerConsoleUrl}}" style="width: 800px; height: 450px; border: none;"></iframe>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>

<span ng-show="isEmpty(services)">no containers found</span>
</div>

Expand Down