mirror of
https://github.com/portainer/portainer.git
synced 2025-08-07 23:05:26 +02:00
feat(schedules): add the ability to list tasks from snapshots (#2458)
* feat(schedules): add the ability to list tasks from snapshots * feat(schedules): update schedules * refactor(schedules): fix linting issue
This commit is contained in:
parent
a2d9f591a7
commit
64c29f7402
22 changed files with 440 additions and 149 deletions
|
@ -0,0 +1,97 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="datatable">
|
||||
<rd-widget>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="toolBar">
|
||||
<div class="toolBarTitle">
|
||||
<i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
{{ $ctrl.titleText }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input type="text" class="searchInput" ng-model="$ctrl.state.textFilter" placeholder="Search..." auto-focus>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-filters nowrap-cells">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Endpoint')">
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Endpoint' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Endpoint' && $ctrl.state.reverseOrder"></i>
|
||||
Endpoint
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Id')">
|
||||
Id
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Status')">
|
||||
State
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Status' && $ctrl.state.reverseOrder"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ng-click="$ctrl.changeOrderBy('Created')">
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Created' && !$ctrl.state.reverseOrder"></i>
|
||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Created' && $ctrl.state.reverseOrder"></i>
|
||||
Created
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))">
|
||||
<td>
|
||||
{{ item.Endpoint.Name }}
|
||||
</td>
|
||||
<td>
|
||||
<a ng-click="$ctrl.goToContainerLogs(item.EndpointId, item.Id)">{{ item.Id | truncate: 32 }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{{ item.Status|containerstatusbadge }}">{{ item.Status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{ item.Created | getisodatefromtimestamp}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="9" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
|
||||
<td colspan="9" class="text-center text-muted">No tasks available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer" ng-if="$ctrl.dataset">
|
||||
<div class="paginationControls">
|
||||
<form class="form-inline">
|
||||
<span class="limitSelector">
|
||||
<span style="margin-right: 5px;">
|
||||
Items per page
|
||||
</span>
|
||||
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</span>
|
||||
<dir-pagination-controls max-size="5"></dir-pagination-controls>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
angular.module('portainer.docker').component('scheduleTasksDatatable', {
|
||||
templateUrl: 'app/portainer/components/datatables/schedule-tasks-datatable/scheduleTasksDatatable.html',
|
||||
controller: 'GenericDatatableController',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
goToContainerLogs: '<'
|
||||
}
|
||||
});
|
|
@ -33,6 +33,13 @@ function ScriptExecutionJobModel(data) {
|
|||
this.RetryInterval = data.RetryInterval;
|
||||
}
|
||||
|
||||
function ScriptExecutionTaskModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.EndpointId = data.EndpointId;
|
||||
this.Status = createStatus(data.Status);
|
||||
this.Created = data.Created;
|
||||
}
|
||||
|
||||
function ScheduleCreateRequest(model) {
|
||||
this.Name = model.Name;
|
||||
this.CronExpression = model.CronExpression;
|
||||
|
|
|
@ -8,6 +8,7 @@ function SchedulesFactory($resource, API_ENDPOINT_SCHEDULES) {
|
|||
get: { method: 'GET', params: { id: '@id' } },
|
||||
update: { method: 'PUT', params: { id: '@id' } },
|
||||
remove: { method: 'DELETE', params: { id: '@id'} },
|
||||
file: { method: 'GET', params: { id : '@id', action: 'file' } }
|
||||
file: { method: 'GET', params: { id : '@id', action: 'file' } },
|
||||
tasks: { method: 'GET', isArray: true, params: { id : '@id', action: 'tasks' } }
|
||||
});
|
||||
}]);
|
||||
|
|
|
@ -36,6 +36,23 @@ function ScheduleService($q, Schedules, FileUploadService) {
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.scriptExecutionTasks = function(scheduleId) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
Schedules.tasks({ id: scheduleId }).$promise
|
||||
.then(function success(data) {
|
||||
var tasks = data.map(function (item) {
|
||||
return new ScriptExecutionTaskModel(item);
|
||||
});
|
||||
deferred.resolve(tasks);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to retrieve tasks associated to the schedule', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.createScheduleFromFileContent = function(model) {
|
||||
var payload = new ScheduleCreateRequest(model);
|
||||
return Schedules.create({ method: 'string' }, payload).$promise;
|
||||
|
|
|
@ -13,15 +13,53 @@
|
|||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<schedule-form
|
||||
ng-if="schedule"
|
||||
model="schedule"
|
||||
endpoints="endpoints"
|
||||
groups="groups"
|
||||
form-action="update"
|
||||
form-action-label="Update schedule"
|
||||
action-in-progress="state.actionInProgress"
|
||||
></schedule-form>
|
||||
|
||||
<uib-tabset active="state.activeTab">
|
||||
<uib-tab index="0">
|
||||
<uib-tab-heading>
|
||||
<i class="fa fa-wrench" aria-hidden="true"></i> Configuration
|
||||
</uib-tab-heading>
|
||||
|
||||
<schedule-form
|
||||
ng-if="schedule"
|
||||
model="schedule"
|
||||
endpoints="endpoints"
|
||||
groups="groups"
|
||||
form-action="update"
|
||||
form-action-label="Update schedule"
|
||||
action-in-progress="state.actionInProgress"
|
||||
></schedule-form>
|
||||
</uib-tab>
|
||||
|
||||
|
||||
<uib-tab index="1">
|
||||
<uib-tab-heading>
|
||||
<i class="fa fa-tasks" aria-hidden="true"></i> Tasks
|
||||
</uib-tab-heading>
|
||||
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Tasks are retrieved across all endpoints via snapshots. Data available in this view might not be up-to-date.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 form-section-title" style="margin-bottom: 20px;">
|
||||
Tasks
|
||||
</div>
|
||||
<schedule-tasks-datatable
|
||||
ng-if="tasks"
|
||||
title-text="Tasks" title-icon="fa-tasks"
|
||||
dataset="tasks"
|
||||
table-key="schedule-tasks"
|
||||
order-by="Status" reverse-order="true"
|
||||
go-to-container-logs="goToContainerLogs"
|
||||
></schedule-tasks-datatable>
|
||||
</uib-tab>
|
||||
|
||||
</uib-tabset>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
angular.module('portainer.app')
|
||||
.controller('ScheduleController', ['$q', '$scope', '$transition$', '$state', 'Notifications', 'EndpointService', 'GroupService', 'ScheduleService',
|
||||
function ($q, $scope, $transition$, $state, Notifications, EndpointService, GroupService, ScheduleService) {
|
||||
.controller('ScheduleController', ['$q', '$scope', '$transition$', '$state', 'Notifications', 'EndpointService', 'GroupService', 'ScheduleService', 'EndpointProvider',
|
||||
function ($q, $scope, $transition$, $state, Notifications, EndpointService, GroupService, ScheduleService, EndpointProvider) {
|
||||
|
||||
$scope.state = {
|
||||
actionInProgress: false
|
||||
};
|
||||
|
||||
$scope.update = update;
|
||||
$scope.goToContainerLogs = goToContainerLogs;
|
||||
|
||||
function update() {
|
||||
var model = $scope.schedule;
|
||||
|
@ -25,25 +26,48 @@ function ($q, $scope, $transition$, $state, Notifications, EndpointService, Grou
|
|||
});
|
||||
}
|
||||
|
||||
function goToContainerLogs(endpointId, containerId) {
|
||||
EndpointProvider.setEndpointID(endpointId);
|
||||
$state.go('docker.containers.container.logs', { id: containerId });
|
||||
}
|
||||
|
||||
function associateEndpointsToTasks(tasks, endpoints) {
|
||||
for (var i = 0; i < tasks.length; i++) {
|
||||
var task = tasks[i];
|
||||
|
||||
for (var j = 0; j < endpoints.length; j++) {
|
||||
var endpoint = endpoints[j];
|
||||
|
||||
if (task.EndpointId === endpoint.Id) {
|
||||
task.Endpoint = endpoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initView() {
|
||||
var id = $transition$.params().id;
|
||||
var schedule = null;
|
||||
|
||||
$q.all({
|
||||
schedule: ScheduleService.schedule(id),
|
||||
file: ScheduleService.getScriptFile(id),
|
||||
tasks: ScheduleService.scriptExecutionTasks(id),
|
||||
endpoints: EndpointService.endpoints(),
|
||||
groups: GroupService.groups()
|
||||
})
|
||||
.then(function success(data) {
|
||||
schedule = data.schedule;
|
||||
var schedule = data.schedule;
|
||||
schedule.Job.FileContent = data.file.ScheduleFileContent;
|
||||
|
||||
var endpoints = data.endpoints;
|
||||
var tasks = data.tasks;
|
||||
associateEndpointsToTasks(tasks, endpoints);
|
||||
|
||||
$scope.schedule = schedule;
|
||||
$scope.tasks = data.tasks;
|
||||
$scope.endpoints = data.endpoints;
|
||||
$scope.groups = data.groups;
|
||||
|
||||
return ScheduleService.getScriptFile(schedule.Id);
|
||||
})
|
||||
.then(function success(data) {
|
||||
schedule.Job.FileContent = data.ScheduleFileContent;
|
||||
$scope.schedule = schedule;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve endpoint list');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue