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

Run command in container #83

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
51 changes: 50 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import requests
from flask import Flask, jsonify, request
from scripts.git_repo import git_pull, git_repo, GIT_YML_PATH
from scripts.bridge import ps_, get_project, get_container_from_id, get_yml_path, containers, project_config, info
from scripts.bridge import ps_, get_project, get_container_from_id, get_yml_path, containers, project_config, info, client
from scripts.find_files import find_yml_files, get_readme_file, get_logo_file
from scripts.requires_auth import requires_auth, authentication_enabled, \
disable_authentication, set_authentication
Expand Down Expand Up @@ -79,6 +79,55 @@ def project_containers(name):
project = get_project_with_name(name)
return jsonify(containers=ps_(project))

@app.route(API_V1 + "exec/<container_id>", methods=['POST'])
@requires_auth
def run_exec(container_id):
"""
Run a one-off exec command in a specific container specified by the
"container_id" param.
"""

json = loads(request.data)

if 'command' not in json:
raise Exception('run_exec expects command to be set in JSON body')

command = json['command']
container = get_container_from_id(client(), container_id)

r = container.create_exec(command)

if 'Id' not in r:
raise Exception('Unable to create exec for command "%s"' % command)

container.start_exec(r['Id'], detach=True)

return jsonify(\
id=r['Id'], \
command=command, \
container=container.name, \
container_id=container.id \
)

@app.route(API_V1 + "exec/<container_id>/<exec_id>", methods=['GET'])
@requires_auth
def inspect_exec(container_id, exec_id):
"""
Inspect a one-off exec command ran with `run_exec`.
"""

container = get_container_from_id(client(), container_id)
r = client().exec_inspect(exec_id)

return jsonify(\
id=exec_id, \
running=r.get('Running'), \
code=r.get('ExitCode'), \
pid=r.get('Pid'), \
container_id=r.get('ContainerID') \
)


@app.route(API_V1 + "projects/<project>/<service_id>", methods=['POST'])
@requires_auth
def run_service(project, service_id):
Expand Down
42 changes: 40 additions & 2 deletions static/scripts/directives/project-detail.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

angular.module('composeUiApp')
.directive('projectDetail', function($resource, $log, projectService, $window, $location){
.directive('projectDetail', function($resource, $log, projectService, $window, $location, $timeout){
return {
restrict: 'E',
scope: {
Expand All @@ -19,7 +19,6 @@ 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');
Expand Down Expand Up @@ -58,6 +57,45 @@ angular.module('composeUiApp')
});
};

var Exec = $resource('api/v1/exec/:container/:id');

$scope.showRunCommand = function (container) {
$scope.container = container;
$scope.showRunDialog = true;
};
$scope.runCommand = function (container_id, command) {
Exec.save({
container: container_id
}, {
command: command
}, function(r) {
alertify.success('`' + r.command + '` running in ' + r.container + '.');

// repeatedly check exec command exit code
var checkExec = function() {
// check for successful exit code
Exec.get({
container: container_id,
id: r.id
}, function (exec) {
if (exec.running) {
// keep checking until the command is finished
return $timeout(checkExec, 5000);
}

if (exec.code === 0) {
alertify.success('`' + r.command + '` successfully completed in ' + r.container + '.');
} else {
alertify.error('`' + r.command + '` exited with non-zero exit code');
}
});
};
checkExec();
}, function() {
alertify.error('Error running `' + command + '`');
});
};

$scope.rebuild = function(serviceName) {
$scope.working = true;
Project.save({id: $scope.projectId, service_names: [serviceName], do_build: true},
Expand Down
21 changes: 21 additions & 0 deletions static/views/project-detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ <h5>
<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>
<button class="btn btn-xs btn-default" ng-click="showRunCommand(container)">run</button>
</div>
<div class="panel-body">

Expand Down Expand Up @@ -103,6 +104,26 @@ <h4 class="modal-title">{{containerLogs}} logs</h4>
</div>
</div>
</div>
<div modal-show="showRunDialog" class="modal fade" tabindex="-1">
<form class="form-horizontal" ng-submit="runCommand(container.name, command)">
<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">Run in {{container.name}}</h4>
</div>
<div class="modal-body">
<div class="form-group">
<input type="text" ng-model="command" class="form-control" id="command" placeholder="Command to run..." required>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Run</button>
</div>
</div>
</div>
</form>
</div>
<span ng-show="isEmpty(services)">no containers found</span>
</div>

Expand Down