Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let users save logs #1040

Merged
merged 1 commit into from
Dec 15, 2016
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: 2 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"Clipboard": false,
"$": false,
"_" : false,
"URITemplate": false
"URITemplate": false,
"saveAs": false
}
}
3 changes: 3 additions & 0 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ <h1>JavaScript Required</h1>
<script src="bower_components/js-yaml/dist/js-yaml.js"></script>
<script src="bower_components/angular-moment/angular-moment.js"></script>
<script src="bower_components/angular-utf8-base64/angular-utf8-base64.js"></script>
<script src="bower_components/file-saver/FileSaver.js"></script>
<!-- endbower -->
<!-- endbuild -->

Expand Down Expand Up @@ -209,6 +210,7 @@ <h1>JavaScript Required</h1>
<script src="scripts/services/labels.js"></script>
<script src="scripts/services/catalog.js"></script>
<script src="scripts/services/modals.js"></script>
<script src="scripts/services/cliHelp.js"></script>
<script src="scripts/controllers/projects.js"></script>
<script src="scripts/controllers/pods.js"></script>
<script src="scripts/controllers/pod.js"></script>
Expand Down Expand Up @@ -269,6 +271,7 @@ <h1>JavaScript Required</h1>
<script src="scripts/controllers/modals/createSecretModal.js"></script>
<script src="scripts/controllers/modals/confirmModal.js"></script>
<script src="scripts/controllers/modals/confirmScale.js"></script>
<script src="scripts/controllers/modals/confirmSaveLog.js"></script>
<script src="scripts/controllers/modals/deleteModal.js"></script>
<script src="scripts/controllers/modals/debugTerminal.js"></script>
<script src="scripts/controllers/modals/confirmReplaceModal.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion app/scripts/controllers/modals/confirmModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ angular.module('openshiftConsole')
// content supplied in the following forms:
// heading: modalConfig.message
// content: modalConfig.details (plain text ONLY, no user imput)
// content: modalConfig.detailsHtml (pre-sanitized, see _.escape() or _.template('<%- %>') )
// content: modalConfig.detailsMarkup (pre-sanitized, see _.escape() or _.template('<%- %>') )
_.extend($scope, modalConfig);

$scope.confirm = function() {
Expand Down
28 changes: 28 additions & 0 deletions app/scripts/controllers/modals/confirmSaveLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

/**
* @ngdoc controller
* @name openshiftConsole.controller:ConfirmSaveLogController
* @description
*
* Shows a confirmation dialog before saving a partial log for a pod,
* replication controller, or build. The dialog contains the CLI command to get
* the full log.
*/
angular.module('openshiftConsole')
.controller('ConfirmSaveLogController',
function($scope,
$uibModalInstance,
object,
CLIHelp) {
$scope.object = object;
$scope.command = CLIHelp.getLogsCommand(object);

$scope.save = function() {
$uibModalInstance.close('save');
};

$scope.cancel = function() {
$uibModalInstance.dismiss('cancel');
};
});
32 changes: 29 additions & 3 deletions app/scripts/directives/logViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ angular.module('openshiftConsole')
'APIService',
'APIDiscovery',
'DataService',
'ModalsService',
'logLinks',
'BREAKPOINTS',
function($sce, $timeout, $window, $filter, AuthService, APIService, APIDiscovery, DataService, logLinks, BREAKPOINTS) {
function($sce, $timeout, $window, $filter, AuthService, APIService, APIDiscovery, DataService, ModalsService, logLinks, BREAKPOINTS) {
// cache the jQuery win, but not clobber angular's $window
var $win = $(window);

Expand Down Expand Up @@ -295,7 +296,11 @@ angular.module('openshiftConsole')
var options = angular.extend({
follow: true,
tailLines: 5000,
limitBytes: 10 * 1024 * 1024 // Limit log size to 10 MiB
// Limit log size to 10 MiB. Note: This can't be more than 500 MiB,
// otherwise save log will break because we'll exceed the max Blob
// size for some browsers.
// https://github.com/eligrey/FileSaver.js#supported-browsers
limitBytes: 10 * 1024 * 1024
}, $scope.options);

streamer = DataService.createStream(logSubresource, name, $scope.context, options);
Expand Down Expand Up @@ -385,7 +390,6 @@ angular.module('openshiftConsole')
streamer.start();
};


// Kibana archives -------------------------------------------------

APIDiscovery
Expand Down Expand Up @@ -505,6 +509,28 @@ angular.module('openshiftConsole')
ctrl.cacheAffixable(document.getElementById($scope.logViewerID+'-affixedFollow'));
ctrl.start();
}, 0);

var saveLog = function() {
var text = $($elem).find('.log-line-text').text();
var filename = _.get($scope, 'object.metadata.name', 'openshift') + '.log';
var blob = new Blob([text], { type: "text/plain;charset=utf-8" });
saveAs(blob, filename);
};

// Detect if we can save files.
// https://github.com/eligrey/FileSaver.js#supported-browsers
$scope.canSave = !!new Blob();

$scope.saveLog = function() {
// Save without confirmation if we're showing the complete log.
if (!$scope.largeLog) {
saveLog();
return;
}

// Prompt if this is a partial log.
ModalsService.confirmSaveLog($scope.object).then(saveLog);
};
}
};
}
Expand Down
56 changes: 56 additions & 0 deletions app/scripts/services/cliHelp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

angular.module("openshiftConsole")
.factory("CLIHelp", function($filter) {
var annotation = $filter('annotation');

// Gets the logs command for an API object.
//
// object - a pod, deployment config, replication controller, build config, or build
// containerName - container name for the pod (optional)
var getLogsCommand = function(object, /* optional */ containerName) {
if (!object) {
return null;
}

var command, name, version;
switch (object.kind) {
case 'Pod':
command = 'oc logs ' + object.metadata.name;
if (containerName) {
command += ' -c ' + containerName;
}
break;
case 'DeploymentConfig':
command = 'oc logs dc/' + object.metadata.name;
break;
case 'ReplicationController':
name = annotation(object, 'deploymentConfig');
version = annotation(object, 'deploymentVersion');
if (name && version) {
command = 'oc logs --version ' + version + ' dc/' + name;
} else {
command = 'oc logs rc/' + object.metadata.name;
}
break;
case 'BuildConfig':
command = 'oc logs bc/' + object.metadata.name;
break;
case 'Build':
name = annotation(object, 'buildConfig');
version = annotation(object, 'buildNumber');
command = 'oc logs --version ' + version + ' bc/' + name;
break;
default:
return null;
}
command += ' -n ' + object.metadata.namespace;

return command;
};

return {
getLogsCommand: getLogsCommand
};
});

13 changes: 13 additions & 0 deletions app/scripts/services/modals.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ angular.module("openshiftConsole")
return modalInstance.result;
},

confirmSaveLog: function(object) {
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'views/modals/confirm-save-log.html',
controller: 'ConfirmSaveLogController',
resolve: {
object: object
}
});

return modalInstance.result;
},

showJenkinsfileExamples: function() {
$uibModal.open({
animation: true,
Expand Down
13 changes: 10 additions & 3 deletions app/views/directives/logs/_log-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@
</form>
<span ng-if="state && state !== 'empty'" class="action-divider">|</span>
</span>
<span ng-if="canSave && state && state !== 'empty'">
<a href=""
ng-click="saveLog()"
role="button">
Save
<i class="fa fa-download"></i></a>
<span ng-if="state && state !== 'empty'" class="action-divider">|</span>
</span>
<a ng-if="state && state !== 'empty'"
href=""
ng-click="goChromeless(options, fullLogUrl)"
role="button">
Expand Log
<i class="fa fa-external-link"></i>
</a>
Expand
<i class="fa fa-external-link"></i></a>
</div>
</div>

Expand Down
17 changes: 17 additions & 0 deletions app/views/modals/confirm-save-log.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="modal-resource-action">
<div class="modal-body">
<h1>Save partial log for <strong>{{object.metadata.name}}</strong>?</h1>
<div class="mar-bottom-xl">
The log might not be complete. Continuing will save only the content currently displayed.
<span ng-if="command">To get the complete log, run the command</span>
</div>
<copy-to-clipboard ng-if="command" display-wide="true" clipboard-text="command"></copy-to-clipboard>
<div class="mar-top-xl">
Learn more about the <a href="command-line" target="_blank">command line tools</a>.
</div>
</div>
<div class="modal-footer">
<button class="btn btn-lg btn-primary" type="button" ng-click="save()">Save</button>
<button class="btn btn-lg btn-default" type="button" ng-click="cancel()">Cancel</button>
</div>
</div>
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"angular-inview": "1.5.7",
"js-yaml": "3.6.1",
"angular-moment": "1.0.0",
"angular-utf8-base64": "0.0.5"
"angular-utf8-base64": "0.0.5",
"file-saver": "1.3.3"
},
"devDependencies": {
"angular-mocks": "1.3.20",
Expand Down
Loading