mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
fix(service): service related UI issues [EE-4062] (#7943)
This commit is contained in:
parent
93866644c6
commit
a2f734051c
18 changed files with 136 additions and 98 deletions
|
@ -8,7 +8,7 @@
|
|||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="service_name" class="col-sm-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" ng-model="formValues.Name" id="service_name" placeholder="e.g. myService" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,8 +18,8 @@
|
|||
<por-image-registry
|
||||
model="formValues.RegistryModel"
|
||||
auto-complete="true"
|
||||
label-class="col-sm-1"
|
||||
input-class="col-sm-11"
|
||||
label-class="col-sm-2"
|
||||
input-class="col-sm-8"
|
||||
endpoint="endpoint"
|
||||
is-admin="isAdmin"
|
||||
check-rate-limits="true"
|
||||
|
@ -30,29 +30,29 @@
|
|||
<div class="col-sm-12 form-section-title"> Scheduling </div>
|
||||
<!-- scheduling-mode -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label text-left"> Scheduling mode </label>
|
||||
<div class="btn-group btn-group-sm" style="margin-left: 20px">
|
||||
<div>
|
||||
<label class="control-label col-sm-2 text-left"> Scheduling mode </label>
|
||||
<div class="btn-group btn-group-sm col-sm-8">
|
||||
<label class="btn btn-light" ng-model="formValues.Mode" uib-btn-radio="'global'">Global</label>
|
||||
<label class="btn btn-light" ng-model="formValues.Mode" uib-btn-radio="'replicated'">Replicated</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-inline" ng-if="formValues.Mode === 'replicated'">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label text-left"> Replicas </label>
|
||||
<input type="number" class="form-control" ng-model="formValues.Replicas" id="replicas" placeholder="e.g. 3" style="margin-left: 20px" />
|
||||
<div>
|
||||
<label class="control-label col-sm-2 text-left"> Replicas </label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" class="form-control" ng-model="formValues.Replicas" id="replicas" placeholder="e.g. 3" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !scheduling-mode -->
|
||||
<div class="col-sm-12 form-section-title"> Ports configuration </div>
|
||||
<!-- port-mapping -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Port mapping</label>
|
||||
<span class="label label-default interactive vertical-center" style="margin-left: 10px" ng-click="addPortBinding()">
|
||||
<pr-icon icon="'plus'" mode="'alt'"></pr-icon> map additional port
|
||||
</span>
|
||||
<label class="control-label col-sm-2 text-left">Port mapping</label>
|
||||
<div class="col-sm-8 pt-2">
|
||||
<span class="label label-default interactive vertical-center" ng-click="addPortBinding()"> <pr-icon icon="'plus'" mode="'alt'"></pr-icon> map additional port </span>
|
||||
</div>
|
||||
<div class="col-sm-12 form-inline mt-2">
|
||||
<div ng-repeat="portBinding in formValues.Ports" class="mt-1">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<rd-widget-body classes="no-padding">
|
||||
<div class="form-inline" style="padding: 10px" authorization="DockerServiceUpdate">
|
||||
Add a config:
|
||||
<select class="form-control" ng-options="config.Name for config in configs | orderBy: 'Name'" ng-model="newConfig">
|
||||
<select class="form-control !h-[30px] !text-[13px]" ng-options="config.Name for config in configs | orderBy: 'Name'" ng-model="newConfig">
|
||||
<option selected disabled hidden value="">Select a config</option>
|
||||
</select>
|
||||
<a class="btn btn-default btn-sm" ng-click="addConfig(service, newConfig)"> <pr-icon icon="'plus'"></pr-icon> add config </a>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<rd-widget>
|
||||
<rd-widget-header icon="list" title-text="Container spec"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<table class="!mb-0 table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CMD</td>
|
||||
<td class="w-1/5">CMD</td>
|
||||
<td
|
||||
><code ng-if="service.Command">{{ service.Command | command }}</code></td
|
||||
>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<rd-widget-body classes="no-padding">
|
||||
<div class="form-inline" style="padding: 10px" authorization="DockerServiceUpdate">
|
||||
Driver:
|
||||
<select class="form-control" ng-model="service.LogDriverName" ng-change="updateLogDriverName(service)" ng-disabled="isUpdating">
|
||||
<select class="form-control !h-[30px] !text-[13px]" ng-model="service.LogDriverName" ng-change="updateLogDriverName(service)" ng-disabled="isUpdating">
|
||||
<option selected value="">Default logging driver</option>
|
||||
<option ng-repeat="driver in availableLoggingDrivers" ng-value="driver">{{ driver }}</option>
|
||||
<option value="none">none</option>
|
||||
|
@ -22,7 +22,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="option in service.LogDriverOpts">
|
||||
<td>
|
||||
<td class="w-1/2">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon fit-text-size">name</span>
|
||||
<input type="text" class="form-control" ng-model="option.key" ng-disabled="option.added || isUpdating" placeholder="e.g. FOO" />
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<p>There are no mounts for this service.</p>
|
||||
</rd-widget-body>
|
||||
<rd-widget-body ng-if="service.ServiceMounts.length > 0" classes="no-padding">
|
||||
<table class="table">
|
||||
<table class="mb-0 table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-if="isAdmin || allowBindMounts">Type</th>
|
||||
|
@ -20,11 +20,11 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="mount in service.ServiceMounts">
|
||||
<td ng-if="isAdmin || allowBindMounts">
|
||||
<tr ng-repeat="(index, mount) in service.ServiceMounts">
|
||||
<td class="!pt-6 !align-top" ng-if="isAdmin || allowBindMounts">
|
||||
<select
|
||||
name="mountType"
|
||||
class="form-control"
|
||||
class="form-control !h-[30px] !text-[13px]"
|
||||
ng-model="mount.Type"
|
||||
ng-change="onChangeMountType(service, mount)"
|
||||
ng-disabled="isUpdating"
|
||||
|
@ -34,47 +34,53 @@
|
|||
<option value="bind">Bind</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-model="mount.Source"
|
||||
ng-change="updateMount(service, mount)"
|
||||
ng-options="vol.Id as ((vol.Id|truncate:30) + ' - ' + (vol.Driver|truncate:30)) for vol in availableVolumes"
|
||||
ng-if="mount.Type === 'volume'"
|
||||
disable-authorization="DockerServiceUpdate"
|
||||
<td class="!pt-6 !pb-0 !align-top">
|
||||
<div class="mb-6">
|
||||
<select
|
||||
class="form-control !h-[30px] !text-[13px]"
|
||||
ng-model="mount.Source"
|
||||
ng-change="updateMount(service, mount)"
|
||||
ng-options="vol.Id as ((vol.Id|truncate:30) + ' - ' + (vol.Driver|truncate:30)) for vol in availableVolumes"
|
||||
ng-if="mount.Type === 'volume'"
|
||||
disable-authorization="DockerServiceUpdate"
|
||||
>
|
||||
<option selected disabled hidden value="">Select a volume</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control !h-[30px] !text-[13px]"
|
||||
name=""
|
||||
ng-model="mount.Source"
|
||||
placeholder="e.g. /tmp/portainer/data"
|
||||
ng-change="updateMount(service, mount)"
|
||||
ng-disabled="isUpdating || (!isAdmin && !allowBindMounts && mount.Type === 'bind')"
|
||||
ng-if="mount.Type === 'bind'"
|
||||
/>
|
||||
</div>
|
||||
<div class="small text-warning !-mt-6" ng-show="!mount.Source">
|
||||
<div class="vertical-center"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Source is required. </div></div
|
||||
>
|
||||
<option selected disabled hidden value="">Select a volume</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name=""
|
||||
ng-model="mount.Source"
|
||||
placeholder="e.g. /tmp/portainer/data"
|
||||
ng-change="updateMount(service, mount)"
|
||||
ng-disabled="isUpdating || (!isAdmin && !allowBindMounts && mount.Type === 'bind')"
|
||||
ng-if="mount.Type === 'bind'"
|
||||
/>
|
||||
<div class="col-sm-12 small text-warning" ng-show="!mount.Source"> <pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Source is required. </div>
|
||||
</td>
|
||||
<td>
|
||||
<td class="!pt-6 !pb-0 !align-top">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
class="form-control mb-6 !h-[30px] !text-[13px]"
|
||||
ng-model="mount.Target"
|
||||
placeholder="e.g. /tmp/portainer/data"
|
||||
ng-change="updateMount(service, mount)"
|
||||
ng-disabled="isUpdating"
|
||||
disable-authorization="DockerServiceUpdate"
|
||||
/>
|
||||
<div class="col-sm-12 small text-warning" ng-show="!mount.Target"> <pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Target is required. </div>
|
||||
<div class="small text-warning !-mt-6" ng-show="!mount.Target">
|
||||
<div class="vertical-center"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Target is required. </div></div
|
||||
>
|
||||
</td>
|
||||
<td authorization="DockerServiceUpdate">
|
||||
<input type="checkbox" class="form-control" ng-model="mount.ReadOnly" ng-change="updateMount(service, mount)" ng-disabled="isUpdating" />
|
||||
<por-switch-field checked="mount.ReadOnly" disabled="isUpdating" on-change="(toggleMountReadOnly)" index="index"></por-switch-field>
|
||||
</td>
|
||||
<td authorization="DockerServiceUpdate">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-dangerlight" type="button" ng-click="removeMount(service, $index)" ng-disabled="isUpdating">
|
||||
<button class="btn btn-dangerlight btn-sm" type="button" ng-click="removeMount(service, $index)" ng-disabled="isUpdating">
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<td>
|
||||
<select
|
||||
ng-if="network.Editable"
|
||||
class="form-control"
|
||||
class="form-control !h-[30px] !rounded !text-[13px]"
|
||||
ng-model="network.Id"
|
||||
ng-change="updateNetwork(service)"
|
||||
ng-options="net.Id as net.Name for net in filterNetworks(swarmNetworks, network)"
|
||||
|
@ -42,7 +42,7 @@
|
|||
</td>
|
||||
<td ng-if="network.Editable" authorization="DockerServiceUpdate">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-dangerlight" type="button" ng-click="removeNetwork(service, $index)" ng-disabled="isUpdating">
|
||||
<button class="btn btn-sm btn-dangerlight" type="button" ng-click="removeNetwork(service, $index)" ng-disabled="isUpdating">
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<tr ng-repeat="portBinding in service.Ports">
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon">host</span>
|
||||
<span class="input-group-addon !leading-none">host</span>
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
|
@ -39,7 +39,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon">container</span>
|
||||
<span class="input-group-addon !leading-none">container</span>
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
|
@ -54,7 +54,7 @@
|
|||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<select
|
||||
class="selectpicker form-control"
|
||||
class="selectpicker form-control !rounded"
|
||||
ng-model="portBinding.Protocol"
|
||||
ng-change="updatePublishedPort(service, mapping)"
|
||||
ng-disabled="isUpdating"
|
||||
|
@ -68,7 +68,7 @@
|
|||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<select
|
||||
class="selectpicker form-control"
|
||||
class="selectpicker form-control !rounded"
|
||||
ng-model="portBinding.PublishMode"
|
||||
ng-change="updatePublishedPort(service, mapping)"
|
||||
ng-disabled="isUpdating"
|
||||
|
@ -81,7 +81,7 @@
|
|||
</td>
|
||||
<td authorization="DockerServiceUpdate">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-dangerlight" type="button" ng-click="removePortPublishedBinding(service, $index)" ng-disabled="isUpdating">
|
||||
<button class="btn btn-sm btn-dangerlight" type="button" ng-click="removePortPublishedBinding(service, $index)" ng-disabled="isUpdating">
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<td>
|
||||
<div class="input-group input-group-sm">
|
||||
<select
|
||||
class="selectpicker form-control"
|
||||
class="selectpicker form-control !rounded"
|
||||
ng-model="service.RestartCondition"
|
||||
ng-change="updateServiceAttribute(service, 'RestartCondition')"
|
||||
disable-authorization="DockerServiceUpdate"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<rd-widget-body classes="no-padding">
|
||||
<div class="form-inline" style="padding: 10px" authorization="DockerServiceUpdate">
|
||||
Add a secret:
|
||||
<select class="form-control" ng-options="secret.Name for secret in secrets | orderBy: 'Name'" ng-model="state.addSecret.secret">
|
||||
<select class="form-control !h-[30px] !text-[13px]" ng-options="secret.Name for secret in secrets | orderBy: 'Name'" ng-model="state.addSecret.secret">
|
||||
<option selected disabled hidden value="">Select a secret</option>
|
||||
</select>
|
||||
<div class="form-group" ng-if="applicationState.endpoint.apiVersion >= 1.3 && state.addSecret.override">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<rd-widget>
|
||||
<rd-widget-header icon="list" title-text="Update configuration"> </rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<table class="mb-0 table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Update Parallelism</td>
|
||||
|
@ -38,8 +38,8 @@
|
|||
<tr>
|
||||
<td>Update Failure Action</td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
<label class="radio-inline">
|
||||
<div class="form-group !mb-0">
|
||||
<label class="radio-inline align-baseline">
|
||||
<input
|
||||
type="radio"
|
||||
name="failure_action"
|
||||
|
@ -50,7 +50,7 @@
|
|||
/>
|
||||
Continue
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<label class="radio-inline align-baseline">
|
||||
<input
|
||||
type="radio"
|
||||
name="failure_action"
|
||||
|
@ -70,7 +70,7 @@
|
|||
<tr ng-if="applicationState.endpoint.apiVersion >= 1.29">
|
||||
<td>Order</td>
|
||||
<td>
|
||||
<div class="form-group">
|
||||
<div class="form-group !mb-0">
|
||||
<label class="radio-inline">
|
||||
<input
|
||||
type="radio"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9 col-md-9 col-xs-9">
|
||||
<rd-widget>
|
||||
|
@ -17,7 +16,7 @@
|
|||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td class="w-1/5">Name</td>
|
||||
<td ng-if="applicationState.endpoint.apiVersion <= 1.24">
|
||||
<input type="text" class="form-control" ng-model="service.Name" ng-change="updateServiceAttribute(service, 'Name')" ng-disabled="isUpdating" />
|
||||
</td>
|
||||
|
@ -66,26 +65,29 @@
|
|||
<td>{{ service.Image }}</td>
|
||||
</tr>
|
||||
<tr ng-if="isAdmin && applicationState.endpoint.type !== 4">
|
||||
<td colspan="{{ webhookURL ? '1' : '2' }}">
|
||||
<por-switch-field
|
||||
tooltip="'Webhook (or callback URI) used to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'"
|
||||
checked="WebhookExists"
|
||||
disabled="!isAdmin"
|
||||
on-change="(onWebhookChange)"
|
||||
label="'Service webhook'"
|
||||
></por-switch-field>
|
||||
</td>
|
||||
<td ng-if="webhookURL">
|
||||
<span class="text-muted">{{ webhookURL | truncatelr }}</span>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-sm space-left" ng-if="webhookURL" ng-click="copyWebhook()">
|
||||
<span>
|
||||
<pr-icon icon="'copy'" class-name="'mr-1'"></pr-icon>
|
||||
Copy link</span
|
||||
<td>
|
||||
<div class="inline-flex items-center">
|
||||
<div> Service webhook </div>
|
||||
<portainer-tooltip
|
||||
message="'Webhook (or callback URI) used to automate the update of this service. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and re-deploy this service.'"
|
||||
>
|
||||
</button>
|
||||
<span>
|
||||
<pr-icon id="copyNotification" icon="'check'" mode="'success'" style="display: none"></pr-icon>
|
||||
</span>
|
||||
</portainer-tooltip>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex flex-wrap items-center">
|
||||
<por-switch-field label-class="'!mr-0'" checked="WebhookExists" disabled="disabledWebhookButton(WebhookExists)" on-change="(onWebhookChange)"></por-switch-field>
|
||||
<span ng-if="webhookURL">
|
||||
<span class="text-muted">{{ webhookURL | truncatelr }}</span>
|
||||
<button type="button" class="btn btn-sm btn-primary btn-sm space-left" ng-if="webhookURL" ng-click="copyWebhook()">
|
||||
<pr-icon icon="'copy'" class-name="'mr-1'"></pr-icon>
|
||||
Copy link
|
||||
</button>
|
||||
<span>
|
||||
<pr-icon id="copyNotification" icon="'check'" mode="'success'" style="display: none"></pr-icon>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr authorization="DockerServiceLogs, DockerServiceUpdate, DockerServiceDelete">
|
||||
|
|
|
@ -236,6 +236,13 @@ angular.module('portainer.docker').controller('ServiceController', [
|
|||
updateServiceArray(service, 'ServiceMounts', service.ServiceMounts);
|
||||
};
|
||||
|
||||
$scope.toggleMountReadOnly = function toggleMountReadOnly(isReadOnly, index) {
|
||||
$scope.$evalAsync(function () {
|
||||
updateServiceArray($scope.service, 'ServiceMounts', $scope.service.ServiceMounts);
|
||||
$scope.service.ServiceMounts[index].ReadOnly = isReadOnly;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNetwork = function addNetwork(service) {
|
||||
if (!service.Networks) {
|
||||
service.Networks = [];
|
||||
|
@ -334,9 +341,11 @@ angular.module('portainer.docker').controller('ServiceController', [
|
|||
};
|
||||
|
||||
$scope.onWebhookChange = function (enabled) {
|
||||
enabled = enabled | '';
|
||||
$scope.$evalAsync(() => {
|
||||
$scope.updateWebhook($scope.service);
|
||||
$scope.WebhookExists = enabled;
|
||||
updateServiceAttribute($scope.service, 'Webhooks', enabled);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -724,6 +733,7 @@ angular.module('portainer.docker').controller('ServiceController', [
|
|||
$scope.isAdmin = Authentication.isAdmin();
|
||||
$scope.availableNetworks = data.availableNetworks;
|
||||
$scope.swarmNetworks = _.filter($scope.availableNetworks, (network) => network.Scope === 'swarm');
|
||||
$scope.WebhookExists = false;
|
||||
|
||||
const serviceNetworks = _.uniqBy(_.concat($scope.service.Model.Spec.Networks || [], $scope.service.Model.Spec.TaskTemplate.Networks || []), 'Target');
|
||||
const networks = _.filter(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue