feat(kubernetes): UI improvements kube app create EE-3462 (#7149)

pull/4366/merge
Prabhat Khera 2022-07-11 14:05:23 +12:00 committed by GitHub
parent de59ea030a
commit 82fb5f7ac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 469 additions and 385 deletions

View File

@ -122,3 +122,11 @@ pr-icon {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.btn-only-icon {
padding: 6px;
}
.btn-only-icon pr-icon {
margin-top: 0;
}

View File

@ -1,7 +1,7 @@
<div class="form-group" ng-if="$ctrl.pullRateLimits"> <div class="form-group" ng-if="$ctrl.pullRateLimits">
<div class="col-sm-12 small"> <div class="col-sm-12 small">
<div ng-if="$ctrl.pullRateLimits.remaining > 0" class="text-muted"> <div ng-if="$ctrl.pullRateLimits.remaining > 0" class="text-muted vertical-center">
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
<span ng-if="$ctrl.isAuthenticated"> <span ng-if="$ctrl.isAuthenticated">
You are currently using a free account to pull images from DockerHub and will be limited to 200 pulls every 6 hours. Remaining pulls: You are currently using a free account to pull images from DockerHub and will be limited to 200 pulls every 6 hours. Remaining pulls:
<span style="font-weight: bold">{{ $ctrl.pullRateLimits.remaining }}/{{ $ctrl.pullRateLimits.limit }}</span> <span style="font-weight: bold">{{ $ctrl.pullRateLimits.remaining }}/{{ $ctrl.pullRateLimits.limit }}</span>
@ -19,8 +19,8 @@
</span> </span>
</span> </span>
</div> </div>
<div ng-if="$ctrl.pullRateLimits.remaining <= 0" class="text-warning"> <div ng-if="$ctrl.pullRateLimits.remaining <= 0" class="text-warning vertical-center">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
<span ng-if="$ctrl.isAuthenticated"> <span ng-if="$ctrl.isAuthenticated">
Your authorized pull count quota as a free user is now exceeded. Your authorized pull count quota as a free user is now exceeded.
<span ng-transclude="rateLimitExceeded">You will not be able to pull any image from the DockerHub registry.</span> <span ng-transclude="rateLimitExceeded">You will not be able to pull any image from the DockerHub registry.</span>

View File

@ -1,8 +1,8 @@
<!-- use registry --> <!-- use registry -->
<div> <div class="row">
<div class="form-group" ng-if="$ctrl.model.UseRegistry"> <div class="form-group" ng-if="$ctrl.model.UseRegistry">
<label for="image_registry" class="control-label text-left" ng-class="$ctrl.labelClass"> Registry </label> <label for="image_registry" class="control-label text-left col-sm-3 col-lg-2" ng-class="$ctrl.labelClass"> Registry </label>
<div ng-class="$ctrl.inputClass"> <div ng-class="$ctrl.inputClass" class="col-sm-8">
<select <select
ng-options="registry as registry.Name for registry in $ctrl.registries track by registry.Id" ng-options="registry as registry.Name for registry in $ctrl.registries track by registry.Id"
ng-model="$ctrl.model.Registry" ng-model="$ctrl.model.Registry"
@ -11,8 +11,10 @@
data-cy="component-registrySelect" data-cy="component-registrySelect"
></select> ></select>
</div> </div>
<label for="image_name" ng-class="$ctrl.labelClass" class="margin-sm-top control-label text-left">Image</label> </div>
<div ng-class="$ctrl.inputClass" class="margin-sm-top"> <div class="form-group" ng-if="$ctrl.model.UseRegistry">
<label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left col-sm-3 col-lg-2">Image</label>
<div ng-class="$ctrl.inputClass" class="col-sm-8">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon" id="registry-name">{{ $ctrl.displayedRegistryURL() }}</span> <span class="input-group-addon" id="registry-name">{{ $ctrl.displayedRegistryURL() }}</span>
<input <input
@ -30,11 +32,11 @@
<span ng-if="$ctrl.isDockerHubRegistry()" class="input-group-btn"> <span ng-if="$ctrl.isDockerHubRegistry()" class="input-group-btn">
<a <a
href="https://hub.docker.com/search?type=image&q={{ $ctrl.model.Image | trimshasum | trimversiontag }}" href="https://hub.docker.com/search?type=image&q={{ $ctrl.model.Image | trimshasum | trimversiontag }}"
class="btn btn-default" class="btn btn-default vertical-center"
title="Search image on Docker Hub" title="Search image on Docker Hub"
target="_blank" target="_blank"
> >
<i class="fab fa-docker"></i> Search <i class="fab fa-docker text-blue-6"></i> Search
</a> </a>
</span> </span>
</div> </div>
@ -45,13 +47,13 @@
<div ng-if="!$ctrl.model.UseRegistry"> <div ng-if="!$ctrl.model.UseRegistry">
<div class="form-group"> <div class="form-group">
<span class="small"> <span class="small">
<p class="text-muted" style="margin-left: 15px"> <p class="text-muted mb-5" style="margin-left: 15px">
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
When using advanced mode, image and repository <b>must be</b> publicly available. When using advanced mode, image and repository <b>must be</b> publicly available.
</p> </p>
</span> </span>
<label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left">Image </label> <label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left col-sm-3 col-lg-2">Image </label>
<div ng-class="$ctrl.inputClass"> <div ng-class="$ctrl.inputClass" class="col-sm-8">
<input type="text" class="form-control" ng-model="$ctrl.model.Image" name="image_name" placeholder="e.g. registry:port/my-image:my-tag" required /> <input type="text" class="form-control" ng-model="$ctrl.model.Image" name="image_name" placeholder="e.g. registry:port/my-image:my-tag" required />
</div> </div>
</div> </div>
@ -59,10 +61,10 @@
<!-- ! don't use registry --> <!-- ! don't use registry -->
<!-- info message --> <!-- info message -->
<div class="form-group" ng-show="$ctrl.form.image_name.$invalid"> <div class="form-group" ng-show="$ctrl.form.image_name.$invalid">
<div class="col-sm-12 small text-warning"> <div class="col-sm-12 small">
<div ng-messages="$ctrl.form.image_name.$error"> <div ng-messages="$ctrl.form.image_name.$error">
<p ng-message="required"> <p ng-message="required">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Image name is required. <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Image name is required.
<span ng-if="$ctrl.canPull">Tag must be specified otherwise Portainer will pull all tags associated to the image.</span> <span ng-if="$ctrl.canPull">Tag must be specified otherwise Portainer will pull all tags associated to the image.</span>
</p> </p>
</div> </div>

View File

@ -1,6 +1,7 @@
import registriesModule from './registries'; import registriesModule from './registries';
import customTemplateModule from './custom-templates'; import customTemplateModule from './custom-templates';
import { reactModule } from './react'; import { reactModule } from './react';
import './views/kubernetes.css';
angular.module('portainer.kubernetes', ['portainer.app', registriesModule, customTemplateModule, reactModule]).config([ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, customTemplateModule, reactModule]).config([
'$stateRegistryProvider', '$stateRegistryProvider',

View File

@ -1,12 +1,14 @@
<ng-form name="serviceForm"> <ng-form name="serviceForm">
<div ng-if="$ctrl.isAdmin()" class="small text-warning" ng-show="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled"> <div ng-if="$ctrl.isAdmin()" class="small" ng-show="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled">
<p style="margin-top: 10px"> <p style="margin-top: 10px">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> No Load balancer is available in this cluster, click <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon> No Load balancer is available in this cluster, click
<a ui-sref="portainer.k8sendpoint.kubernetesConfig({id: $ctrl.state.endpointId})">here</a> to configure load balancer. <a ui-sref="portainer.k8sendpoint.kubernetesConfig({id: $ctrl.state.endpointId})">here</a> to configure load balancer.
</p> </p>
</div> </div>
<div ng-if="!$ctrl.isAdmin()" class="small text-warning" ng-show="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled"> <div ng-if="!$ctrl.isAdmin()" class="small" ng-show="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled">
<p style="margin-top: 10px"> <i class="fa fa-exclamation-circle" aria-hidden="true"></i> No Load balancer is available in this cluster, contract your administrator. </p> <p style="margin-top: 10px">
<pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon> No Load balancer is available in this cluster, contract your administrator.
</p>
</div> </div>
<div <div
@ -16,223 +18,231 @@
$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT $ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT
" "
> >
<div ng-show="!$ctrl.multiItemDisable" style="margin-top: 5px; margin-bottom: 5px"> <div ng-show="!$ctrl.multiItemDisable" class="mt-5 mb-5">
<label class="control-label text-left">Published ports</label> <label class="control-label text-left">Published ports</label>
<span class="label label-default interactive" style="margin-left: 10px" ng-click="$ctrl.addPort()" data-cy="k8sAppCreate-addNewPortButton"> <span class="label label-default interactive ml-10 vertical-center" ng-click="$ctrl.addPort()" data-cy="k8sAppCreate-addNewPortButton">
<i class="fa fa-plus-circle" aria-hidden="true"></i> publish a new port <pr-icon icon="'plus'" mode="'alt'" size="'sm'" feather="true"></pr-icon> publish a new port
</span> </span>
</div> </div>
<div ng-repeat="servicePort in $ctrl.servicePorts" style="margin-top: 10px"> <div ng-repeat="servicePort in $ctrl.servicePorts" class="mt-5 service-form row">
<div class="input-group input-group-sm"> <div class="form-group !mx-0 !pl-0 col-sm-3">
<span class="input-group-addon">container port</span> <div class="input-group input-group-sm">
<input <span class="input-group-addon">container port</span>
type="number" <input
class="form-control" type="number"
name="container_port_{{ $index }}" class="form-control"
ng-model="servicePort.targetPort" name="container_port_{{ $index }}"
placeholder="80" ng-model="servicePort.targetPort"
ng-min="1" placeholder="80"
ng-max="65535" ng-min="1"
ng-change="$ctrl.servicePort($index)" ng-max="65535"
required ng-change="$ctrl.servicePort($index)"
ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)" required
ng-change="$ctrl.onChangeContainerPort()" ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)"
data-cy="k8sAppCreate-containerPort_{{ $index }}" ng-change="$ctrl.onChangeContainerPort()"
/> data-cy="k8sAppCreate-containerPort_{{ $index }}"
</div> />
<div class="input-group input-group-sm">
<span class="input-group-addon">service port</span>
<input
type="number"
class="form-control"
name="service_port_{{ $index }}"
ng-model="servicePort.port"
placeholder="80"
ng-min="1"
ng-max="65535"
required
ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)"
ng-change="$ctrl.onChangeServicePort()"
data-cy="k8sAppCreate-servicePort_{{ $index }}"
/>
</div>
<div ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT" class="input-group input-group-sm">
<span class="input-group-addon">nodeport</span>
<input
type="number"
class="form-control"
name="node_port_{{ $index }}"
ng-model="servicePort.nodePort"
placeholder="30080"
ng-min="30000"
ng-max="32767"
ng-change="$ctrl.onChangeNodePort()"
data-cy="k8sAppCreate-nodeportPort_{{ $index }}"
/>
</div>
<div ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER" class="input-group input-group-sm">
<span class="input-group-addon">loadbalancer port</span>
<input
type="number"
class="form-control"
name="loadbalancer_port_{{ $index }}"
ng-model="servicePort.port"
placeholder="80"
ng-min="1"
ng-max="65535"
required
ng-disabled="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled"
data-cy="k8sAppCreate-loadbalancerPort_{{ $index }}"
/>
</div>
<div ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType" class="input-group input-group-sm">
<span class="input-group-addon">ingress</span>
<select
class="form-control"
name="ingress_port_{{ $index }}"
ng-model="servicePort.ingress.IngressName"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-options="ingress.Name as ingress.Name for ingress in $ctrl.originalIngresses"
data-cy="k8sAppCreate-ingressPort_{{ $index }}"
>
<option selected disabled hidden value="">Select an ingress</option>
</select>
</div>
<div ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType" class="input-group input-group-sm">
<span class="input-group-addon">hostname</span>
<select
class="form-control"
name="hostname_port_{{ $index }}"
ng-model="servicePort.ingress.Host"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-options="host as host for host in ($ctrl.originalIngresses | filter:{ Name: servicePort.ingress.IngressName })[0].Hosts"
data-cy="k8sAppCreate-hostnamePort_{{ $index }}"
>
<option selected disabled hidden value="">Select a hostname</option>
</select>
</div>
<div ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType" class="input-group input-group-sm">
<span class="input-group-addon">route</span>
<input
class="form-control"
name="ingress_route_{{ $index }}"
ng-model="servicePort.ingress.Path"
placeholder="route"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-pattern="/^(\/?[a-zA-Z0-9]+([a-zA-Z0-9-/_]*[a-zA-Z0-9])?|[a-zA-Z0-9]+)|(\/){1}$/"
data-cy="k8sAppCreate-route_{{ $index }}"
/>
</div>
<div class="input-group col-sm-2 input-group-sm">
<div class="btn-group btn-group-sm">
<label
class="btn btn-primary"
ng-model="servicePort.protocol"
uib-btn-radio="'TCP'"
ng-change="ctrl.onChangePortProtocol($index)"
ng-disabled="ctrl.isProtocolOptionDisabled($index, 'TCP')"
data-cy="k8sAppCreate-TCPButton_{{ $index }}"
>TCP</label
>
<label
class="btn btn-primary"
ng-model="servicePort.protocol"
uib-btn-radio="'UDP'"
ng-change="ctrl.onChangePortProtocol($index)"
ng-disabled="ctrl.isProtocolOptionDisabled($index, 'UDP')"
data-cy="k8sAppCreate-UDPButton_{{ $index }}"
>UDP</label
>
</div> </div>
<button <span>
ng-disabled="$ctrl.servicePorts.length === 1" <div class="small mt-1" ng-if="$ctrl.state.duplicates.targetPort.refs[$index] !== undefined">
ng-show="!$ctrl.multiItemDisable" <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This container port is already used.
class="btn btn-sm btn-danger" </div>
type="button" <div class="small mt-1" ng-messages="serviceForm['container_port_'+$index].$error">
ng-click="$ctrl.removePort($index)" <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Container port number is required.</p>
data-cy="k8sAppCreate-rmPortButton_{{ $index }}" <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Container port number must be inside the range 1-65535.</p>
> <p ng-message="max"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Container port number must be inside the range 1-65535.</p>
<i class="fa fa-trash-alt" aria-hidden="true"></i> </div>
</button> </span>
</div> </div>
<div class="col-sm-12 input-group input-group-sm"> <div class="form-group !mx-0 !pl-0 col-sm-3">
<div class="col-sm-2"> <div class="input-group input-group-sm">
<div class="small text-warning" style="margin-top: 5px"> <span class="input-group-addon">service port</span>
<p ng-if="$ctrl.state.duplicates.targetPort.refs[$index] !== undefined"> <input
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This container port is already used. type="number"
</p> class="form-control"
</div> name="service_port_{{ $index }}"
<div class="small text-warning" ng-messages="serviceForm['container_port_'+$index].$error"> ng-model="servicePort.port"
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Container port number is required.</p> placeholder="80"
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Container port number must be inside the range 1-65535.</p> ng-min="1"
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Container port number must be inside the range 1-65535.</p> ng-max="65535"
</div> required
ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)"
ng-change="$ctrl.onChangeServicePort()"
data-cy="k8sAppCreate-servicePort_{{ $index }}"
/>
</div> </div>
<span>
<div class="col-sm-2"> <div class="small mt-1" ng-if="$ctrl.state.duplicates.servicePort.refs[$index] !== undefined">
<div class="small text-warning" style="margin-top: 5px"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This service port is already used.
<p ng-if="$ctrl.state.duplicates.servicePort.refs[$index] !== undefined">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This service port is already used.
</p>
</div> </div>
<div class="small text-warning" style="margin-top: 5px"> <div class="small mt-1">
<div ng-messages="serviceForm['service_port_'+$index].$error"> <div ng-messages="serviceForm['service_port_'+$index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Service port number is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Service port number is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Container port number must be inside the range 1-65535.</p> <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Container port number must be inside the range 1-65535.</p>
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Container port number must be inside the range 1-65535.</p> <p ng-message="max"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Container port number must be inside the range 1-65535.</p>
</div> </div>
</div> </div>
</div> </span>
</div>
<div class="col-sm-6"> <div class="form-group !mx-0 !pl-0 col-sm-3" ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT">
<div class="small text-warning" style="margin-top: 5px"> <div class="input-group input-group-sm">
<div ng-messages="serviceForm['node_port_'+$index].$error"> <span class="input-group-addon">nodeport</span>
<p ng-message="min" <input
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Node port number must be inside the range 30000-32767 or blank for system allocated.</p type="number"
> class="form-control"
<p ng-message="max" name="node_port_{{ $index }}"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Node port number must be inside the range 30000-32767 or blank for system allocated.</p ng-model="servicePort.nodePort"
> placeholder="30080"
ng-min="30000"
ng-max="32767"
ng-change="$ctrl.onChangeNodePort()"
data-cy="k8sAppCreate-nodeportPort_{{ $index }}"
/>
</div>
<div class="w-full">
<span>
<div class="small mt-1">
<div ng-messages="serviceForm['node_port_'+$index].$error">
<p ng-message="min"
><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Node port number must be inside the range 30000-32767 or blank for system
allocated.</p
>
<p ng-message="max"
><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Node port number must be inside the range 30000-32767 or blank for system
allocated.</p
>
</div>
</div> </div>
</div> </span>
</div> </div>
</div>
<div class="form-group !mx-0 !pl-0 col-sm-3" ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER">
<div class="input-group input-group-sm">
<span class="input-group-addon">loadbalancer port</span>
<input
type="number"
class="form-control"
name="loadbalancer_port_{{ $index }}"
ng-model="servicePort.port"
placeholder="80"
ng-min="1"
ng-max="65535"
required
ng-disabled="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled"
data-cy="k8sAppCreate-loadbalancerPort_{{ $index }}"
/>
</div>
</div>
<div class="col-sm-2"> <div class="form-group !mx-0 !pl-0 col-sm-3" ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType">
<div class="small text-warning" style="margin-top: 5px"> <div class="input-group input-group-sm">
<span class="input-group-addon">ingress</span>
<select
class="form-control"
name="ingress_port_{{ $index }}"
ng-model="servicePort.ingress.IngressName"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-options="ingress.Name as ingress.Name for ingress in $ctrl.originalIngresses"
data-cy="k8sAppCreate-ingressPort_{{ $index }}"
>
<option selected disabled hidden value="">Select an ingress</option>
</select>
</div>
<span>
<div class="small mt-5">
<div ng-messages="serviceForm['ingress_port_'+$index].$error"> <div ng-messages="serviceForm['ingress_port_'+$index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Ingress selection is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Ingress selection is required.</p>
</div> </div>
</div> </div>
</div> </span>
</div>
<div class="col-sm-2"> <div class="form-group !mx-0 !pl-0 col-sm-3" ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType">
<div class="small text-warning" style="margin-top: 5px"> <div class="input-group input-group-sm">
<span class="input-group-addon">hostname</span>
<select
class="form-control"
name="hostname_port_{{ $index }}"
ng-model="servicePort.ingress.Host"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-options="host as host for host in ($ctrl.originalIngresses | filter:{ Name: servicePort.ingress.IngressName })[0].Hosts"
data-cy="k8sAppCreate-hostnamePort_{{ $index }}"
>
<option selected disabled hidden value="">Select a hostname</option>
</select>
</div>
<span>
<div class="small mt-1">
<div ng-messages="serviceForm['hostname_port_'+$index].$error"> <div ng-messages="serviceForm['hostname_port_'+$index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Host is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Host is required.</p>
</div> </div>
</div> </div>
</span>
</div>
<div class="form-group !mx-0 !pl-0 col-sm-3 clear-both" ng-if="$ctrl.serviceType === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP && $ctrl.ingressType">
<div class="input-group input-group-sm">
<span class="input-group-addon">route</span>
<input
class="form-control"
name="ingress_route_{{ $index }}"
ng-model="servicePort.ingress.Path"
placeholder="route"
required
ng-disabled="$ctrl.originalIngresses.length === 0"
ng-pattern="/^(\/?[a-zA-Z0-9]+([a-zA-Z0-9-/_]*[a-zA-Z0-9])?|[a-zA-Z0-9]+)|(\/){1}$/"
data-cy="k8sAppCreate-route_{{ $index }}"
/>
</div> </div>
<div class="col-sm-8" ng-show=""> <span>
<div class="small text-warning" style="margin-top: 5px"> <div class="small mt-1">
<div ng-messages="serviceForm['ingress_route_'+$index].$error"> <div ng-messages="serviceForm['ingress_route_'+$index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Route is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Route is required.</p>
<p ng-message="pattern" <p ng-message="pattern"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field must consist of alphanumeric characters or the special characters: '-', '_' or '/'. It ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field must consist of alphanumeric characters or the special characters: '-', '_'
must start and end with an alphanumeric character (e.g. 'my-route', or 'route-123').</p or '/'. It must start and end with an alphanumeric character (e.g. 'my-route', or 'route-123').</p
> >
</div> </div>
</div> </div>
</span>
</div>
<div class="form-group !mx-0 !pl-0 col-sm-2">
<div class="input-group input-group-sm">
<div class="btn-group btn-group-sm">
<label
class="btn btn-light"
ng-model="servicePort.protocol"
uib-btn-radio="'TCP'"
ng-change="ctrl.onChangePortProtocol($index)"
ng-disabled="ctrl.isProtocolOptionDisabled($index, 'TCP')"
data-cy="k8sAppCreate-TCPButton_{{ $index }}"
>TCP</label
>
<label
class="btn btn-light"
ng-model="servicePort.protocol"
uib-btn-radio="'UDP'"
ng-change="ctrl.onChangePortProtocol($index)"
ng-disabled="ctrl.isProtocolOptionDisabled($index, 'UDP')"
data-cy="k8sAppCreate-UDPButton_{{ $index }}"
>UDP</label
>
</div>
<button
ng-disabled="$ctrl.servicePorts.length === 1"
ng-show="!$ctrl.multiItemDisable"
class="btn btn-sm btn-light btn-only-icon"
type="button"
ng-click="$ctrl.removePort($index)"
data-cy="k8sAppCreate-rmPortButton_{{ $index }}"
>
<pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon>
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -9,8 +9,14 @@
ng-options="item.typeValue as item.typeName for item in $ctrl.state.serviceType" ng-options="item.typeValue as item.typeName for item in $ctrl.state.serviceType"
data-cy="k8sAppCreate-publishingModeDropdown" data-cy="k8sAppCreate-publishingModeDropdown"
></select> ></select>
<button type="button" class="btn btn-sm btn-default" style="margin-left: 0" ng-click="$ctrl.addEntry( $ctrl.state.selected )" data-cy="k8sAppCreate-createServiceButton"> <button
<i class="fa fa-plus-circle" aria-hidden="true"></i> Create service type="button"
class="btn btn-sm btn-default vertical-center"
style="margin-left: 0"
ng-click="$ctrl.addEntry( $ctrl.state.selected )"
data-cy="k8sAppCreate-createServiceButton"
>
<pr-icon icon="'plus'" size="'sm'" feather="true"></pr-icon> Create service
</button> </button>
</div> </div>
</div> </div>
@ -33,12 +39,12 @@
></kube-services-item-view> ></kube-services-item-view>
<button <button
type="button" type="button"
class="btn btn-sm btn-danger space-right" class="btn btn-sm btn-dangerlight space-right vertical-center"
style="margin-left: 0; margin-top: 10px" style="margin-left: 0; margin-top: 10px"
ng-click="$ctrl.deleteService( $index )" ng-click="$ctrl.deleteService( $index )"
data-cy="k8sConfigCreate-removeButton" data-cy="k8sConfigCreate-removeButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> Remove <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
</button> </button>
</div> </div>
@ -47,25 +53,26 @@
<i class="fa fa-route" aria-hidden="true" style="margin-right: 2px"></i> <i class="fa fa-route" aria-hidden="true" style="margin-right: 2px"></i>
Ingress Ingress
</div> </div>
<div ng-if="$ctrl.isAdmin()" class="small text-warning"> <div ng-if="$ctrl.isAdmin()" class="small">
<p style="margin-top: 10px"> <p style="margin-top: 10px">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> Ingress is not configured in this namespace, select another namespace or click <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Ingress is not configured in this namespace, select another namespace or click
<a ui-sref="portainer.k8sendpoint.kubernetesConfig({id: $ctrl.state.endpointId})">here</a> to configure ingress. <a ui-sref="portainer.k8sendpoint.kubernetesConfig({id: $ctrl.state.endpointId})">here</a> to configure ingress.
</p> </p>
</div> </div>
<div ng-if="!$ctrl.isAdmin()" class="small text-warning"> <div ng-if="!$ctrl.isAdmin()" class="small">
<p style="margin-top: 10px"> <p style="margin-top: 10px">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> Ingress is not configured in this namespace, select another namespace or contact your administrator. <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Ingress is not configured in this namespace, select another namespace or contact your
administrator.
</p> </p>
</div> </div>
<button <button
type="button" type="button"
class="btn btn-sm btn-danger space-right" class="btn btn-sm btn-dangerlight space-right vertical-center"
style="margin-left: 0; margin-top: 10px" style="margin-left: 0; margin-top: 10px"
ng-click="$ctrl.deleteService( $index )" ng-click="$ctrl.deleteService( $index )"
data-cy="k8sConfigCreate-removeButton" data-cy="k8sConfigCreate-removeButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> Remove <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
</button> </button>
</div> </div>
@ -85,12 +92,12 @@
></kube-services-item-view> ></kube-services-item-view>
<button <button
type="button" type="button"
class="btn btn-sm btn-danger space-right" class="btn btn-sm btn-dangerlight space-right vertical-center"
style="margin-left: 0; margin-top: 10px" style="margin-left: 0; margin-top: 10px"
ng-click="$ctrl.deleteService( $index )" ng-click="$ctrl.deleteService( $index )"
data-cy="k8sConfigCreate-removeButton" data-cy="k8sConfigCreate-removeButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> Remove <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
</button> </button>
</div> </div>
</div> </div>

View File

@ -33,7 +33,7 @@
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading> <kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady"> <div ng-if="ctrl.state.viewReady">
<div class="row"> <div class="row kubernetes-create">
<div class="col-xs-12"> <div class="col-xs-12">
<rd-widget> <rd-widget>
<rd-widget-body> <rd-widget-body>
@ -50,8 +50,8 @@
<div class="col-sm-12 form-section-title" ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"> Namespace </div> <div class="col-sm-12 form-section-title" ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"> Namespace </div>
<!-- #region NAMESPACE --> <!-- #region NAMESPACE -->
<div class="form-group" ng-if="ctrl.formValues.ResourcePool"> <div class="form-group" ng-if="ctrl.formValues.ResourcePool">
<label for="resource-pool-selector" class="col-sm-1 control-label text-left">Namespace</label> <label for="resource-pool-selector" class="col-sm-3 col-lg-2 control-label text-left">Namespace</label>
<div class="col-sm-11"> <div class="col-sm-8">
<select <select
class="form-control" class="form-control"
id="resource-pool-selector" id="resource-pool-selector"
@ -65,14 +65,14 @@
</div> </div>
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool"> <div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-triangle'" feather="true"></pr-icon>
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the
namespace. namespace.
</div> </div>
</div> </div>
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool"> <div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-triangle'" feather="true"></pr-icon>
You do not have access to any namespace. Contact your administrator to get access to a namespace. You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div> </div>
</div> </div>
@ -98,7 +98,7 @@
<editor-description> <editor-description>
<span class="text-muted small" ng-show="ctrl.stack.IsComposeFormat"> <span class="text-muted small" ng-show="ctrl.stack.IsComposeFormat">
<p> <p>
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that not Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that not
all the Compose format options are supported by Kompose at the moment. all the Compose format options are supported by Kompose at the moment.
</p> </p>
@ -109,7 +109,7 @@
</span> </span>
<span class="text-muted small" ng-show="!ctrl.stack.IsComposeFormat"> <span class="text-muted small" ng-show="!ctrl.stack.IsComposeFormat">
<p> <p>
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...). This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...).
</p> </p>
<p> <p>
@ -124,8 +124,8 @@
<div class="col-sm-12 form-section-title"> Application </div> <div class="col-sm-12 form-section-title"> Application </div>
<!-- #region NAME FIELD --> <!-- #region NAME FIELD -->
<div class="form-group"> <div class="form-group">
<label for="application_name" class="col-sm-1 control-label text-left">Name</label> <label for="application_name" class="col-sm-3 col-lg-2 control-label text-left">Name</label>
<div class="col-sm-11"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -142,17 +142,17 @@
</div> </div>
</div> </div>
<div class="form-group" ng-show="kubernetesApplicationCreationForm.application_name.$invalid || ctrl.state.alreadyExists"> <div class="form-group" ng-show="kubernetesApplicationCreationForm.application_name.$invalid || ctrl.state.alreadyExists">
<div class="col-sm-12 small text-warning"> <div class="col-sm-12 small">
<div ng-messages="kubernetesApplicationCreationForm.application_name.$error"> <div ng-messages="kubernetesApplicationCreationForm.application_name.$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field is required.</p>
<p ng-message="pattern"> <p ng-message="pattern">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
This field must consist of lower case alphanumeric characters or '-', contain at most 63 characters, start with an alphabetic character, and end with an This field must consist of lower case alphanumeric characters or '-', contain at most 63 characters, start with an alphabetic character, and end with an
alphanumeric character (e.g. 'my-name', or 'abc-123'). alphanumeric character (e.g. 'my-name', or 'abc-123').
</p> </p>
</div> </div>
<p ng-if="ctrl.state.alreadyExists"> <p ng-if="ctrl.state.alreadyExists">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
An application with the same name already exists inside the selected namespace. An application with the same name already exists inside the selected namespace.
</p> </p>
</div> </div>
@ -160,8 +160,7 @@
<!-- #endregion --> <!-- #endregion -->
<!-- #region IMAGE FIELD --> <!-- #region IMAGE FIELD -->
<div class="form-group mb-0">
<div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<por-image-registry <por-image-registry
model="ctrl.formValues.ImageModel" model="ctrl.formValues.ImageModel"
@ -177,20 +176,22 @@
></por-image-registry> ></por-image-registry>
</div> </div>
</div> </div>
<!-- #end region IMAGE FIELD -->
<div ng-if="ctrl.formValues.ResourcePool"> <div ng-if="ctrl.formValues.ResourcePool">
<div class="col-sm-12 form-section-title"> Stack </div> <div class="col-sm-12 form-section-title"> Stack </div>
<!-- #region STACK --> <!-- #region STACK -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
Portainer can automatically bundle multiple applications inside a stack. Enter a name of a new stack or select an existing stack in the list. Leave empty to Portainer can automatically bundle multiple applications inside a stack. Enter a name of a new stack or select an existing stack in the list. Leave empty to
use the application name. use the application name.
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stack_name" class="col-sm-1 control-label text-left">Stack</label> <label for="stack_name" class="col-sm-3 col-lg-2 control-label text-left">Stack</label>
<div class="col-sm-11"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -213,12 +214,12 @@
<label class="control-label text-left">Environment variables</label> <label class="control-label text-left">Environment variables</label>
<span <span
ng-if="ctrl.formValues.Containers.length <= 1" ng-if="ctrl.formValues.Containers.length <= 1"
class="label label-default interactive" class="label label-default interactive vertical-center"
style="margin-left: 10px" style="margin-left: 10px"
ng-click="ctrl.addEnvironmentVariable()" ng-click="ctrl.addEnvironmentVariable()"
data-cy="k8sAppCreate-addEnvVarButton" data-cy="k8sAppCreate-addEnvVarButton"
> >
<i class="fa fa-plus-circle" aria-hidden="true"></i> add environment variable <pr-icon icon="'plus'" mode="'alt'" size="'sm'" feather="true"></pr-icon> add environment variable
</span> </span>
</div> </div>
@ -257,17 +258,17 @@
</div> </div>
<div class="col-sm-2 input-group input-group-sm" ng-if="ctrl.formValues.Containers.length <= 1"> <div class="col-sm-2 input-group input-group-sm" ng-if="ctrl.formValues.Containers.length <= 1">
<button ng-if="!envVar.NeedsDeletion" class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeEnvironmentVariable(envVar)"> <button ng-if="!envVar.NeedsDeletion" class="btn btn-md btn-light btn-only-icon" type="button" ng-click="ctrl.removeEnvironmentVariable(envVar)">
<i class="fa fa-trash-alt" aria-hidden="true"></i> <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon>
</button> </button>
<button <button
ng-if="envVar.NeedsDeletion" ng-if="envVar.NeedsDeletion"
class="btn btn-sm btn-primary" class="btn btn-sm btn-light btn-only-icon"
type="button" type="button"
ng-click="ctrl.restoreEnvironmentVariable(envVar)" ng-click="ctrl.restoreEnvironmentVariable(envVar)"
data-cy="k8sAppCreate-removeEnvVarButton_{{ $index }}" data-cy="k8sAppCreate-removeEnvVarButton_{{ $index }}"
> >
<i class="fa fa-trash-restore" aria-hidden="true"></i> <pr-icon icon="'rotate-cw'" size="'md'" feather="true"></pr-icon>
</button> </button>
</div> </div>
</div> </div>
@ -279,7 +280,7 @@
> >
<div class="col-sm-4 input-group input-group-sm"> <div class="col-sm-4 input-group input-group-sm">
<div <div
class="small text-warning" class="small"
style="margin-top: 5px" style="margin-top: 5px"
ng-show=" ng-show="
kubernetesApplicationCreationForm['environment_variable_name_' + $index].$invalid || kubernetesApplicationCreationForm['environment_variable_name_' + $index].$invalid ||
@ -287,14 +288,14 @@
" "
> >
<ng-messages for="kubernetesApplicationCreationForm['environment_variable_name_' + $index].$error"> <ng-messages for="kubernetesApplicationCreationForm['environment_variable_name_' + $index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Environment variable name is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Environment variable name is required.</p>
<p ng-message="pattern" <p ng-message="pattern"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field must consist of alphabetic characters, digits, '_', '-', or '.', and ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field must consist of alphabetic characters, digits, '_', '-',
must not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1'.</p or '.', and must not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1'.</p
> >
</ng-messages> </ng-messages>
<p ng-if="ctrl.state.duplicates.environmentVariables.refs[$index] !== undefined" <p ng-if="ctrl.state.duplicates.environmentVariables.refs[$index] !== undefined"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This environment variable is already defined.</p ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This environment variable is already defined.</p
> >
</div> </div>
</div> </div>
@ -312,17 +313,17 @@
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left">Configurations</label> <label class="control-label text-left">Configurations</label>
<span <span
class="label label-default interactive" class="label label-default interactive vertical-center"
style="margin-left: 10px" style="margin-left: 10px"
ng-click="ctrl.addConfiguration()" ng-click="ctrl.addConfiguration()"
ng-if="ctrl.formValues.Containers.length <= 1" ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-addConfigButton" data-cy="k8sAppCreate-addConfigButton"
> >
<i class="fa fa-plus-circle" aria-hidden="true"></i> add configuration <pr-icon icon="'plus'" mode="'alt'" size="'sm'" feather="true"></pr-icon> add configuration
</span> </span>
</div> </div>
<div class="col-sm-12 small text-muted" style="margin-top: 15px" ng-if="ctrl.formValues.Configurations.length"> <div class="col-sm-12 small text-muted" style="margin-top: 15px" ng-if="ctrl.formValues.Configurations.length">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
Portainer will automatically expose all the keys of a configuration as environment variables. This behavior can be overridden to filesystem mounts for each Portainer will automatically expose all the keys of a configuration as environment variables. This behavior can be overridden to filesystem mounts for each
key via the override button. key via the override button.
</div> </div>
@ -330,8 +331,8 @@
<!-- config-element --> <!-- config-element -->
<div class="form-group" ng-repeat="(index, config) in ctrl.formValues.Configurations"> <div class="form-group" ng-repeat="(index, config) in ctrl.formValues.Configurations">
<label for="stack_name" class="col-md-1 col-sm-2 control-label text-left">Configuration</label> <label for="stack_name" class="col-sm-3 col-lg-2 control-label text-left">Configuration</label>
<div class="col-sm-5"> <div class="col-sm-6">
<select <select
class="form-control" class="form-control"
ng-model="config.SelectedConfiguration" ng-model="config.SelectedConfiguration"
@ -341,41 +342,41 @@
data-cy="k8sAppCreate-addConfigSelect_{{ $index }}" data-cy="k8sAppCreate-addConfigSelect_{{ $index }}"
></select> ></select>
</div> </div>
<div class="col-sm-6" style="margin-top: 2px"> <div class="col-sm-3" style="margin-top: 2px">
<button <button
class="btn btn-sm btn-primary" class="btn btn-sm btn-light vertical-center"
type="button" type="button"
ng-if="!config.Overriden" ng-if="!config.Overriden"
ng-click="ctrl.overrideConfiguration(index)" ng-click="ctrl.overrideConfiguration(index)"
ng-disabled="!config.SelectedConfiguration || ctrl.formValues.Containers.length > 1" ng-disabled="!config.SelectedConfiguration || ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-configOverrideButton_{{ $index }}" data-cy="k8sAppCreate-configOverrideButton_{{ $index }}"
> >
<i class="fa fa-list" aria-hidden="true"></i> Override <pr-icon icon="'list'" size="'md'" feather="true"></pr-icon> Override
</button> </button>
<button <button
class="btn btn-sm btn-primary" class="btn btn-sm btn-light vertical-center"
type="button" type="button"
ng-if="config.Overriden" ng-if="config.Overriden"
ng-click="ctrl.resetConfiguration(index)" ng-click="ctrl.resetConfiguration(index)"
ng-disabled="ctrl.formValues.Containers.length > 1" ng-disabled="ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-configAutoButton_{{ $index }}" data-cy="k8sAppCreate-configAutoButton_{{ $index }}"
> >
<i class="fa fa-undo" aria-hidden="true"></i> Auto <pr-icon icon="'rotate-cw'" size="'md'" feather="true"></pr-icon> Auto
</button> </button>
<button <button
class="btn btn-sm btn-danger" class="btn btn-sm btn-dangerlight vertical-center"
type="button" type="button"
ng-click="ctrl.removeConfiguration(index)" ng-click="ctrl.removeConfiguration(index)"
ng-if="ctrl.formValues.Containers.length <= 1" ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-configRemoveButton" data-cy="k8sAppCreate-configRemoveButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> Remove <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
</button> </button>
</div> </div>
<!-- no-override --> <!-- no-override -->
<div class="col-sm-12" style="margin-top: 10px" ng-if="config.SelectedConfiguration && !config.Overriden"> <div class="col-sm-12" style="margin-top: 10px" ng-if="config.SelectedConfiguration && !config.Overriden">
<div class="col-md-1 col-sm-2"></div> <div class="col-sm-3 col-lg-2"></div>
<div class="col-md-11 col-sm-10 small text-muted" style="padding-left: 5px"> <div class="col-sm-6 small text-muted" style="padding-left: 5px">
The following keys will be loaded from the <code>{{ config.SelectedConfiguration.Name }}</code> configuration as environment variables: The following keys will be loaded from the <code>{{ config.SelectedConfiguration.Name }}</code> configuration as environment variables:
<span ng-repeat="(key, _) in config.SelectedConfiguration.Data"> <span ng-repeat="(key, _) in config.SelectedConfiguration.Data">
<code>{{ key }}</code <code>{{ key }}</code
@ -414,10 +415,10 @@
<div class="input-group col-sm-4 btn-group btn-group-sm"> <div class="input-group col-sm-4 btn-group btn-group-sm">
<label class="btn btn-primary" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.ENVIRONMENT"> <label class="btn btn-primary" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.ENVIRONMENT">
<i class="fa fa-list" aria-hidden="true"></i> Environment <pr-icon icon="'list'" feather="true"></pr-icon> Environment
</label> </label>
<label class="btn btn-primary" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM"> <label class="btn btn-primary" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<i class="fa fa-file" aria-hidden="true"></i> Filesystem <pr-icon icon="'file-text'" feather="true"></pr-icon> Filesystem
</label> </label>
</div> </div>
</div> </div>
@ -440,10 +441,10 @@
" "
> >
<ng-messages for="kubernetesApplicationCreationForm['overriden_key_path_' + index + '_' + keyIndex].$error"> <ng-messages for="kubernetesApplicationCreationForm['overriden_key_path_' + index + '_' + keyIndex].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Path is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Path is required.</p>
</ng-messages> </ng-messages>
<p ng-if="ctrl.state.duplicates.configurationPaths.refs[index + '_' + keyIndex] !== undefined" <p ng-if="ctrl.state.duplicates.configurationPaths.refs[index + '_' + keyIndex] !== undefined"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This path is already used.</p ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This path is already used.</p
> >
</div> </div>
</div> </div>
@ -460,13 +461,13 @@
<!-- #region PERSISTED FOLDERS --> <!-- #region PERSISTED FOLDERS -->
<div class="form-group" ng-if="!ctrl.storageClassAvailable()"> <div class="form-group" ng-if="!ctrl.storageClassAvailable()">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
No storage option is available to persist data, contact your administrator to enable a storage option. No storage option is available to persist data, contact your administrator to enable a storage option.
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.storageClassAvailable()"> <div class="form-group" ng-if="ctrl.storageClassAvailable()">
<div class="col-sm-12" style="margin-top: 5px"> <div class="col-sm-12" style="margin-top: 5px" ng-if="!ctrl.allQuotasExhaustedAndNoVolumesAvailable()">
<label class="control-label text-left">Persisted folders</label> <label class="control-label text-left">Persisted folders</label>
<span <span
class="label label-default interactive" class="label label-default interactive"
@ -475,7 +476,14 @@
ng-if="ctrl.isAddPersistentFolderButtonShowed()" ng-if="ctrl.isAddPersistentFolderButtonShowed()"
data-cy="k8sAppCreate-addPersistentFolderButton" data-cy="k8sAppCreate-addPersistentFolderButton"
> >
<i class="fa fa-plus-circle" aria-hidden="true"></i> add persisted folder <pr-icon icon="'plus'" mode="'alt'" size="'sm'" feather="true"></pr-icon> add persisted folder
</span>
</div>
<div class="col-sm-12" style="margin-top: 5px" ng-if="ctrl.allQuotasExhaustedAndNoVolumesAvailable()">
<span class="small text-muted">
<pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
This namespace has exhausted its storage capacity. Contact your administrator to expand the capacity of the namespace.
</span> </span>
</div> </div>
@ -536,6 +544,7 @@
ng-min="0" ng-min="0"
required required
ng-disabled="ctrl.isEditAndExistingPersistedFolder($index) || ctrl.formValues.Containers.length > 1" ng-disabled="ctrl.isEditAndExistingPersistedFolder($index) || ctrl.formValues.Containers.length > 1"
ng-change="ctrl.onChangeVolumeRequestedSize()"
/> />
<span class="input-group-addon" style="padding: 0"> <span class="input-group-addon" style="padding: 0">
<select <select
@ -543,6 +552,7 @@
ng-style="{ width: '100%', height: '100%', cursor: ctrl.isEditAndExistingPersistedFolder($index) ? 'not-allowed' : 'auto' }" ng-style="{ width: '100%', height: '100%', cursor: ctrl.isEditAndExistingPersistedFolder($index) ? 'not-allowed' : 'auto' }"
ng-options="unit for unit in ctrl.state.availableSizeUnits" ng-options="unit for unit in ctrl.state.availableSizeUnits"
ng-disabled="ctrl.isEditAndExistingPersistedFolder($index) || ctrl.formValues.Containers.length > 1" ng-disabled="ctrl.isEditAndExistingPersistedFolder($index) || ctrl.formValues.Containers.length > 1"
ng-change="ctrl.onChangeVolumeRequestedSize()"
></select> ></select>
</span> </span>
</div> </div>
@ -591,7 +601,7 @@
ng-click="ctrl.removePersistedFolder($index)" ng-click="ctrl.removePersistedFolder($index)"
data-cy="k8sAppCreate-rmPersistentFolderButton" data-cy="k8sAppCreate-rmPersistentFolderButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> <pr-icon icon="'trash-2'" feather="true"></pr-icon>
</button> </button>
<button <button
ng-if="persistedFolder.NeedsDeletion" ng-if="persistedFolder.NeedsDeletion"
@ -600,7 +610,7 @@
ng-click="ctrl.restorePersistedFolder($index)" ng-click="ctrl.restorePersistedFolder($index)"
data-cy="k8sAppCreate-restorePersistentButton" data-cy="k8sAppCreate-restorePersistentButton"
> >
<i class="fa fa-trash-restore" aria-hidden="true"></i> <pr-icon icon="'rotate-cw'" feather="true"></pr-icon>
</button> </button>
</div> </div>
</div> </div>
@ -611,6 +621,7 @@
kubernetesApplicationCreationForm['persisted_folder_path_' + $index].$invalid || kubernetesApplicationCreationForm['persisted_folder_path_' + $index].$invalid ||
ctrl.state.duplicates.persistedFolders.refs[$index] !== undefined || ctrl.state.duplicates.persistedFolders.refs[$index] !== undefined ||
kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$invalid || kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$invalid ||
ctrl.state.exceeded.persistedFolders.refs[$index] !== undefined ||
kubernetesApplicationCreationForm['existing_volumes_' + $index].$invalid || kubernetesApplicationCreationForm['existing_volumes_' + $index].$invalid ||
ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined
" "
@ -624,10 +635,10 @@
" "
> >
<ng-messages for="kubernetesApplicationCreationForm['persisted_folder_path_' + $index].$error"> <ng-messages for="kubernetesApplicationCreationForm['persisted_folder_path_' + $index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Path is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Path is required.</p>
</ng-messages> </ng-messages>
<p ng-if="ctrl.state.duplicates.persistedFolders.refs[$index] !== undefined" <p ng-if="ctrl.state.duplicates.persistedFolders.refs[$index] !== undefined"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This path is already defined.</p ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This path is already defined.</p
> >
</div> </div>
</div> </div>
@ -635,21 +646,33 @@
<div class="input-group col-sm-2 input-group-sm"></div> <div class="input-group col-sm-2 input-group-sm"></div>
<div class="input-group col-sm-5 input-group-sm"> <div class="input-group col-sm-5 input-group-sm">
<div class="small text-warning" style="margin-top: 5px" ng-show="kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$invalid"> <div
class="small text-warning"
style="margin-top: 5px"
ng-show="
kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$invalid || ctrl.state.exceeded.persistedFolders.refs[$index] !== undefined
"
>
<ng-messages for="kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$error"> <ng-messages for="kubernetesApplicationCreationForm['persisted_folder_size_' + $index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Size is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Size is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This value must be greater than zero.</p> <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This value must be greater than zero.</p>
</ng-messages> </ng-messages>
<p ng-if="ctrl.state.exceeded.persistedFolders.refs[$index] !== undefined">
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
You can only request up to
{{ ctrl.state.storages.availabilities[persistedFolder.StorageClass.Name] | kubernetesAppStorageRequestSizeHumanReadable }} for
{{ persistedFolder.StorageClass.Name }}
</p>
</div> </div>
<div <div
class="small text-warning" class="small text-warning"
ng-show="kubernetesApplicationCreationForm['existing_volumes_' + $index].$invalid || ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined" ng-show="kubernetesApplicationCreationForm['existing_volumes_' + $index].$invalid || ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined"
> >
<ng-messages for="kubernetesApplicationCreationForm['existing_volumes_' + $index].$error"> <ng-messages for="kubernetesApplicationCreationForm['existing_volumes_' + $index].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Volume is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Volume is required.</p>
</ng-messages> </ng-messages>
<p ng-if="ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined" <p ng-if="ctrl.state.duplicates.existingVolumes.refs[$index] !== undefined"
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This volume is already used.</p ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This volume is already used.</p
> >
</div> </div>
</div> </div>
@ -761,14 +784,14 @@
<!-- #region RESOURCE RESERVATIONS --> <!-- #region RESOURCE RESERVATIONS -->
<div class="form-group" ng-if="!ctrl.state.resourcePoolHasQuota"> <div class="form-group" ng-if="!ctrl.state.resourcePoolHasQuota">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
Resource reservations are applied per instance of the application. Resource reservations are applied per instance of the application.
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && !ctrl.resourceQuotaCapacityExceeded()"> <div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && !ctrl.resourceQuotaCapacityExceeded()">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
A resource quota is set on this namespace, you must specify resource reservations. Resource reservations are applied per instance of the application. Maximums A resource quota is set on this namespace, you must specify resource reservations. Resource reservations are applied per instance of the application. Maximums
are inherited from the namespace quota. are inherited from the namespace quota.
</div> </div>
@ -776,7 +799,7 @@
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded()"> <div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded()">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'danger'" feather="true"></pr-icon>
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of
the namespace. the namespace.
</div> </div>
@ -820,7 +843,7 @@
<div class="col-sm-12 small text-warning"> <div class="col-sm-12 small text-warning">
<div ng-messages="kubernetesApplicationCreationForm.memory_limit.$error"> <div ng-messages="kubernetesApplicationCreationForm.memory_limit.$error">
<p <p
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Value must be between {{ ctrl.state.sliders.memory.min }} and ><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Value must be between {{ ctrl.state.sliders.memory.min }} and
{{ ctrl.state.sliders.memory.max }} {{ ctrl.state.sliders.memory.max }}
</p> </p>
</div> </div>
@ -851,7 +874,7 @@
<div class="form-group" ng-if="ctrl.nodeLimitsOverflow()"> <div class="form-group" ng-if="ctrl.nodeLimitsOverflow()">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'danger'" feather="true"></pr-icon>
These reservations would exceed the resources currently available in the cluster. These reservations would exceed the resources currently available in the cluster.
</div> </div>
</div> </div>
@ -942,10 +965,10 @@
</div> </div>
</div> </div>
<div class="form-group" ng-if="kubernetesApplicationCreationForm['replica_count'].$invalid"> <div class="form-group" ng-if="kubernetesApplicationCreationForm['replica_count'].$invalid">
<div class="col-sm-12 small text-warning"> <div class="col-sm-12 small">
<ng-messages for="kubernetesApplicationCreationForm['replica_count'].$error"> <ng-messages for="kubernetesApplicationCreationForm['replica_count'].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Instance count is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Instance count is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Instance count must be greater than 0.</p> <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Instance count must be greater than 0.</p>
</ng-messages> </ng-messages>
</div> </div>
</div> </div>
@ -956,7 +979,7 @@
ng-if="!ctrl.resourceReservationsOverflow() && ctrl.formValues.ReplicaCount > 1 && (ctrl.formValues.CpuLimit !== 0 || ctrl.formValues.MemoryLimit !== 0)" ng-if="!ctrl.resourceReservationsOverflow() && ctrl.formValues.ReplicaCount > 1 && (ctrl.formValues.CpuLimit !== 0 || ctrl.formValues.MemoryLimit !== 0)"
> >
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
This application will reserve the following resources: This application will reserve the following resources:
<b>{{ ctrl.formValues.CpuLimit * ctrl.formValues.ReplicaCount | kubernetesApplicationCPUValue }} CPU</b> and <b>{{ ctrl.formValues.CpuLimit * ctrl.formValues.ReplicaCount | kubernetesApplicationCPUValue }} CPU</b> and
<b>{{ ctrl.formValues.MemoryLimit * ctrl.formValues.ReplicaCount }} MB</b> of memory. <b>{{ ctrl.formValues.MemoryLimit * ctrl.formValues.ReplicaCount }} MB</b> of memory.
@ -964,15 +987,22 @@
</div> </div>
<div class="form-group" ng-if="ctrl.resourceReservationsOverflow()"> <div class="form-group" ng-if="ctrl.resourceReservationsOverflow()">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'danger'" feather="true"></pr-icon>
This application would exceed available resources. Please review resource reservations or the instance count. This application would exceed available resources. Please review resource reservations or the instance count.
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.state.storages.quotaExceeded">
<div class="col-sm-12 small text-muted">
<pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
This application would exceed available storage. Please review the persisted folders or the instance count.
</div>
</div>
<div class="form-group" ng-if="!ctrl.supportScalableReplicaDeployment()"> <div class="form-group" ng-if="!ctrl.supportScalableReplicaDeployment()">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
The following storage option(s) do not support concurrent access from multiples instances: <code>{{ ctrl.getNonScalableStorage() }}</code The following storage option(s) do not support concurrent access from multiples instances: <code>{{ ctrl.getNonScalableStorage() }}</code
>. You will not be able to scale that application. >. You will not be able to scale that application.
</div> </div>
@ -1037,9 +1067,11 @@
<div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_min'].$invalid"> <div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_min'].$invalid">
<div class="small text-warning" style="margin-top: 5px"> <div class="small text-warning" style="margin-top: 5px">
<ng-messages for="kubernetesApplicationCreationForm['auto_scaler_min'].$error"> <ng-messages for="kubernetesApplicationCreationForm['auto_scaler_min'].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum instances is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Minimum instances is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum instances must be greater than 0.</p> <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Minimum instances must be greater than 0.</p>
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum instances must be smaller than maximum instances.</p> <p ng-message="max"
><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Minimum instances must be smaller than maximum instances.</p
>
</ng-messages> </ng-messages>
</div> </div>
</div> </div>
@ -1057,8 +1089,10 @@
<div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_max'].$invalid || ctrl.autoScalerOverflow()"> <div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_max'].$invalid || ctrl.autoScalerOverflow()">
<div class="small text-warning" style="margin-top: 5px"> <div class="small text-warning" style="margin-top: 5px">
<ng-messages for="kubernetesApplicationCreationForm['auto_scaler_max'].$error"> <ng-messages for="kubernetesApplicationCreationForm['auto_scaler_max'].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum instances is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Maximum instances is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Maximum instances must be greater than minimum instances.</p> <p ng-message="min"
><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Maximum instances must be greater than minimum instances.</p
>
</ng-messages> </ng-messages>
</div> </div>
</div> </div>
@ -1079,9 +1113,9 @@
<div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_cpu'].$invalid"> <div class="input-group input-group-sm" ng-show="kubernetesApplicationCreationForm['auto_scaler_cpu'].$invalid">
<div class="small text-warning" style="margin-top: 5px"> <div class="small text-warning" style="margin-top: 5px">
<ng-messages for="kubernetesApplicationCreationForm['auto_scaler_cpu'].$error"> <ng-messages for="kubernetesApplicationCreationForm['auto_scaler_cpu'].$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Target CPU usage is required.</p> <p ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Target CPU usage is required.</p>
<p ng-message="min"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Target CPU usage must be greater than 0.</p> <p ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Target CPU usage must be greater than 0.</p>
<p ng-message="max"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Target CPU usage must be smaller than 100.</p> <p ng-message="max"><pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Target CPU usage must be smaller than 100.</p>
</ng-messages> </ng-messages>
</div> </div>
</div> </div>
@ -1092,7 +1126,7 @@
<div class="form-group" ng-if="ctrl.autoScalerOverflow()" style="margin-bottom: 10px"> <div class="form-group" ng-if="ctrl.autoScalerOverflow()" style="margin-bottom: 10px">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'danger'" feather="true"></pr-icon>
This application would exceed available resources. Please review resource reservations or the maximum instance count of the auto-scaling policy. This application would exceed available resources. Please review resource reservations or the maximum instance count of the auto-scaling policy.
</div> </div>
</div> </div>
@ -1106,13 +1140,13 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<label class="control-label text-left">Placement rules</label> <label class="control-label text-left">Placement rules</label>
<span class="label label-default interactive" style="margin-left: 10px" ng-click="ctrl.addPlacement()"> <span class="label label-default interactive vertical-center" style="margin-left: 10px" ng-click="ctrl.addPlacement()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add rule <pr-icon icon="'plus'" mode="'alt'" size="'sm'" feather="true"></pr-icon> add rule
</span> </span>
</div> </div>
<div class="col-sm-12 small text-muted" ng-if="ctrl.formValues.Placements.length > 0" style="margin-top: 10px"> <div class="col-sm-12 small text-muted" ng-if="ctrl.formValues.Placements.length > 0" style="margin-top: 10px">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
Deploy this application on nodes that respect <b>ALL</b> of the following placement rules. Placement rules are based on node labels. Deploy this application on nodes that respect <b>ALL</b> of the following placement rules. Placement rules are based on node labels.
</div> </div>
@ -1143,21 +1177,21 @@
<div class="col-sm-1 input-group"> <div class="col-sm-1 input-group">
<button <button
ng-if="!placement.NeedsDeletion" ng-if="!placement.NeedsDeletion"
class="btn btn-sm btn-danger" class="btn btn-sm btn-light btn-only-icon"
type="button" type="button"
ng-click="ctrl.removePlacement($index)" ng-click="ctrl.removePlacement($index)"
data-cy="k8sAppCreate-deletePlacementButton" data-cy="k8sAppCreate-deletePlacementButton"
> >
<i class="fa fa-trash-alt" aria-hidden="true"></i> <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon>
</button> </button>
<button <button
ng-if="placement.NeedsDeletion" ng-if="placement.NeedsDeletion"
class="btn btn-sm btn-primary" class="btn btn-sm btn-light btn-only-icon"
type="button" type="button"
ng-click="ctrl.restorePlacement($index)" ng-click="ctrl.restorePlacement($index)"
data-cy="k8sAppCreate-restorePlacementButton" data-cy="k8sAppCreate-restorePlacementButton"
> >
<i class="fa fa-trash-restore" aria-hidden="true"></i> <pr-icon icon="'rotate-cw'" size="'md'" feather="true"></pr-icon>
</button> </button>
</div> </div>
</div> </div>
@ -1165,7 +1199,7 @@
<div class="col-sm-5 input-group"> <div class="col-sm-5 input-group">
<div class="small text-warning" style="margin-top: 5px" ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined"> <div class="small text-warning" style="margin-top: 5px" ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined">
<p ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined"> <p ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This label is already defined. <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This label is already defined.
</p> </p>
</div> </div>
</div> </div>
@ -1257,14 +1291,14 @@
</div> </div>
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool"> <div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-danger"> <div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'danger'" feather="true"></pr-icon>
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the
namespace. namespace.
</div> </div>
</div> </div>
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool"> <div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
You do not have access to any namespace. Contact your administrator to get access to a namespace. You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div> </div>
</div> </div>

View File

@ -5,18 +5,18 @@
<div ng-if="ctrl.state.viewReady"> <div ng-if="ctrl.state.viewReady">
<kubernetes-feedback-panel></kubernetes-feedback-panel> <kubernetes-feedback-panel></kubernetes-feedback-panel>
<div class="row"> <div class="row kubernetes-deploy">
<div class="col-sm-12"> <div class="col-sm-12">
<rd-widget> <rd-widget>
<rd-widget-body> <rd-widget-body>
<uib-tabset active="ctrl.state.activeTab" justified="true" type="pills"> <uib-tabset active="ctrl.state.activeTab" justified="true" type="pills">
<uib-tab index="0"> <uib-tab index="0">
<uib-tab-heading> <i class="fa fa-code space-right" aria-hidden="true"></i> Deploy </uib-tab-heading> <uib-tab-heading> <pr-icon icon="'code'" feather="true"></pr-icon> Deploy </uib-tab-heading>
<div class="col-sm-12 form-section-title"> Namespace </div> <div class="col-sm-12 form-section-title"> Namespace </div>
<form class="form-horizontal" style="margin-top: 20px" name="deploymentForm"> <form class="form-horizontal" style="margin-top: 20px" name="deploymentForm">
<div class="form-group" ng-if="ctrl.formValues.Namespace"> <div class="form-group" ng-if="ctrl.formValues.Namespace">
<label for="target_node" class="col-lg-1 col-sm-2 control-label text-left">Namespace</label> <label for="target_node" class="col-lg-2 col-sm-3 control-label text-left">Namespace</label>
<div class="col-lg-11 col-sm-10"> <div class="col-sm-8">
<select <select
ng-disabled="ctrl.formValues.namespace_toggle" ng-disabled="ctrl.formValues.namespace_toggle"
class="form-control" class="form-control"
@ -26,11 +26,16 @@
</div> </div>
</div> </div>
<div class="form-group" ng-if="ctrl.formValues.Namespace"> <div class="form-group" ng-if="ctrl.formValues.Namespace">
<div class="col-sm-12"> <label for="toggle_logo" class="col-lg-2 col-sm-3 control-label text-left">
<label for="toggle_logo" class="control-label text-left"> Use namespace(s) specified from manifest </label> Use namespace(s) specified from manifest
<portainer-tooltip message="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'"> <portainer-tooltip message="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'">
</portainer-tooltip> </portainer-tooltip>
<label class="switch" style="margin-left: 20px"> <input type="checkbox" name="toggle_logo" ng-model="ctrl.formValues.namespace_toggle" /><i></i> </label> </label>
<div class="col-sm-8">
<label class="switch">
<input type="checkbox" name="toggle_logo" ng-model="ctrl.formValues.namespace_toggle" />
<i></i>
</label>
</div> </div>
</div> </div>
<div class="form-group" ng-if="!ctrl.formValues.Namespace"> <div class="form-group" ng-if="!ctrl.formValues.Namespace">
@ -41,8 +46,8 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stack_name" class="col-sm-1 control-label text-left">Name</label> <label for="stack_name" class="col-lg-2 col-sm-3 control-label text-left">Name</label>
<div class="col-lg-11 col-sm-10"> <div class="col-sm-8">
<input type="text" class="form-control" ng-model="ctrl.formValues.StackName" id="stack_name" placeholder="my-app" auto-focus /> <input type="text" class="form-control" ng-model="ctrl.formValues.StackName" id="stack_name" placeholder="my-app" auto-focus />
</div> </div>
</div> </div>
@ -110,7 +115,7 @@
<editor-description> <editor-description>
<span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE"> <span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE">
<p> <p>
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that
not all the Compose format options are supported by Kompose at the moment. not all the Compose format options are supported by Kompose at the moment.
</p> </p>
@ -119,12 +124,9 @@
<a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>. <a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>.
</p> </p>
</span> </span>
<span <span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES">
class="col-sm-12 text-muted small"
ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES && ctrl.state.BuildMethod === ctrl.BuildMethods.WEB_EDITOR"
>
<p> <p>
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...). This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...).
</p> </p>
<p> <p>
@ -144,8 +146,8 @@
<span class="col-sm-12 text-muted small"> Indicate the URL to the manifest. </span> <span class="col-sm-12 text-muted small"> Indicate the URL to the manifest. </span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="manifest_url" class="col-sm-1 control-label text-left">URL</label> <label for="manifest_url" class="col-sm-3 col-lg-2 control-label text-left">URL</label>
<div class="col-sm-11"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -166,7 +168,7 @@
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
ng-disabled="!deploymentForm.$valid || ctrl.disableDeploy()" ng-disabled="!deploymentForm.$valid ||ctrl.disableDeploy()"
ng-click="ctrl.deploy()" ng-click="ctrl.deploy()"
button-spinner="ctrl.state.actionInProgress" button-spinner="ctrl.state.actionInProgress"
data-cy="k8sAppDeploy-deployButton" data-cy="k8sAppDeploy-deployButton"
@ -185,7 +187,7 @@
</uib-tab> </uib-tab>
<uib-tab index="1" disable="ctrl.state.tabLogsDisabled"> <uib-tab index="1" disable="ctrl.state.tabLogsDisabled">
<uib-tab-heading> <i class="fa fa-file space-right" aria-hidden="true"></i> Logs </uib-tab-heading> <uib-tab-heading> <pr-icon icon="'file-text'" feather="true"></pr-icon> Logs </uib-tab-heading>
<form class="form-horizontal" style="margin-top: 20px"> <form class="form-horizontal" style="margin-top: 20px">
<div class="form-group" ng-if="ctrl.state.activeTab === 1"> <div class="form-group" ng-if="ctrl.state.activeTab === 1">
<div class="col-sm-12"> <div class="col-sm-12">

View File

@ -0,0 +1,11 @@
.service-form .form-group {
vertical-align: top;
}
.service-form .form-group .input-group {
width: 100%;
}
.service-form .clear-both {
clear: both;
}

View File

@ -6,19 +6,19 @@
> >
Summary Summary
<span class="small space-left"> <span class="small space-left">
<a ng-if="!$ctrl.state.expandedTemplate"><i class="fa fa-angle-down" aria-hidden="true"></i> expand</a> <a ng-if="!$ctrl.state.expandedTemplate"><pr-icon icon="'chevron-down'" feather="true"></pr-icon> expand</a>
<a ng-if="$ctrl.state.expandedTemplate"><i class="fa fa-angle-up" aria-hidden="true"></i> collapse</a> <a ng-if="$ctrl.state.expandedTemplate"><pr-icon icon="'chevron-up'" feather="true"></pr-icon> collapse</a>
</span> </span>
</div> </div>
<div class="form-group" ng-if="$ctrl.state.expandedTemplate"> <div class="form-group" ng-if="$ctrl.state.expandedTemplate">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
Portainer will execute the following Kubernetes actions. Portainer will execute the following Kubernetes actions.
</div> </div>
<div class="col-sm-12 small text-muted" style="padding-top: 1em" ng-if="$ctrl.state.resources.length > 0"> <div class="col-sm-12 small text-muted" style="padding-top: 1em" ng-if="$ctrl.state.resources.length > 0">
<ul> <ul class="ml-5">
<li ng-repeat="summary in $ctrl.state.resources" ng-if="summary.action && summary.kind && summary.name"> <li ng-repeat="summary in $ctrl.state.resources" ng-if="summary.action && summary.kind && summary.name">
{{ summary.action }} {{ summary.action }}
{{ $ctrl.getArticle(summary.kind, summary.action) }} {{ $ctrl.getArticle(summary.kind, summary.action) }}

View File

@ -13,15 +13,15 @@
required required
/> />
</div> </div>
<button class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.removeValue()"> <button class="btn btn-sm btn-light btn-only-icon" type="button" ng-click="$ctrl.removeValue()" title="Remove">
<i class="fa fa-trash" aria-hidden="true"></i> <pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon>
</button> </button>
</div> </div>
<div ng-show="$ctrl[$ctrl.formName].name.$invalid"> <div ng-show="$ctrl[$ctrl.formName].name.$invalid">
<div class="col-sm-12 small text-warning"> <div class="small">
<div ng-messages="$ctrl[$ctrl.formName].name.$error"> <div ng-messages="$ctrl[$ctrl.formName].name.$error" class="mt-1">
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Path is required. </p> <p ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Path is required. </p>
<p ng-message="pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> File path must include yaml, yml, json, or hcl extension </p> <p ng-message="pattern"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> File path must include yaml, yml, json, or hcl extension </p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,9 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-12" style="margin-top: 5px"> <div class="col-sm-12" style="margin-top: 5px">
<label class="control-label text-left">Additional paths</label> <label class="control-label text-left">Additional paths</label>
<span class="label label-default interactive" style="margin-left: 10px" ng-click="$ctrl.add()"> <i class="fa fa-plus-circle" aria-hidden="true"></i> add file </span> <span class="label label-default interactive" style="margin-left: 10px" ng-click="$ctrl.add()">
<pr-icon icon="'plus'" size="'sm'" mode="'alt'" feather="true"></pr-icon> add file
</span>
</div> </div>
<div class="col-sm-12 form-inline" style="margin-top: 10px"> <div class="col-sm-12 form-inline" style="margin-top: 10px">
<git-form-additional-file-item <git-form-additional-file-item

View File

@ -9,14 +9,14 @@
></por-switch-field> ></por-switch-field>
</div> </div>
</div> </div>
<div class="small text-warning" style="margin: 5px 0 15px 0" ng-if="$ctrl.model.RepositoryAuthentication && $ctrl.showAuthExplanation"> <div class="small" style="margin: 5px 0 15px 0" ng-if="$ctrl.model.RepositoryAuthentication && $ctrl.showAuthExplanation">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
<span class="text-muted">Enabling authentication will store the credentials and it is advisable to use a git service account</span> <span class="text-muted">Enabling authentication will store the credentials and it is advisable to use a git service account</span>
</div> </div>
<div ng-if="$ctrl.model.RepositoryAuthentication"> <div ng-if="$ctrl.model.RepositoryAuthentication" class="row">
<div class="form-group"> <div class="form-group">
<label for="repository_username" class="control-label text-left inline-label"> Username </label> <label for="repository_username" class="col-lg-2 col-sm-3 control-label text-left"> Username </label>
<div class="inline-input"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -29,11 +29,11 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="repository_password" class="control-label text-left inline-label"> <label for="repository_password" class="col-lg-2 col-sm-3 control-label text-left">
Personal Access Token Personal Access Token
<portainer-tooltip message="'Provide a personal access token or password'"></portainer-tooltip> <portainer-tooltip message="'Provide a personal access token or password'"></portainer-tooltip>
</label> </label>
<div class="inline-input"> <div class="col-sm-8">
<input <input
type="password" type="password"
class="form-control" class="form-control"

View File

@ -5,18 +5,18 @@
</div> </div>
</div> </div>
<div class="small text-warning" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="small text-warning" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
<span class="text-muted">Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption.</span> <span class="text-muted">Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption.</span>
</div> </div>
<div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<label for="repository_mechanism" class="col-sm-1 control-label text-left"> Mechanism </label> <label for="repository_mechanism" class="col-lg-2 col-sm-3 control-label text-left"> Mechanism </label>
<div class="col-sm-11"> <div class="col-sm-8">
<div class="input-group col-sm-10 input-group-sm"> <div class="input-group col-sm-10 input-group-sm">
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<label class="btn btn-primary" ng-click="$ctrl.onChangeMechanism($ctrl.model.RepositoryMechanism)" ng-model="$ctrl.model.RepositoryMechanism" uib-btn-radio="'Interval'" <label class="btn btn-light" ng-click="$ctrl.onChangeMechanism($ctrl.model.RepositoryMechanism)" ng-model="$ctrl.model.RepositoryMechanism" uib-btn-radio="'Interval'"
>Polling</label >Polling</label
> >
<label class="btn btn-primary" ng-click="$ctrl.onChangeMechanism($ctrl.model.RepositoryMechanism)" ng-model="$ctrl.model.RepositoryMechanism" uib-btn-radio="'Webhook'" <label class="btn btn-light" ng-click="$ctrl.onChangeMechanism($ctrl.model.RepositoryMechanism)" ng-model="$ctrl.model.RepositoryMechanism" uib-btn-radio="'Webhook'"
>Webhook</label >Webhook</label
> >
</div> </div>
@ -25,20 +25,20 @@
</div> </div>
<div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates && $ctrl.model.RepositoryMechanism === 'Webhook'"> <div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates && $ctrl.model.RepositoryMechanism === 'Webhook'">
<label for="repository_mechanism" class="col-sm-1 control-label text-left"> Webhook </label> <label for="repository_mechanism" class="col-sm-3 col-lg-2 control-label text-left"> Webhook </label>
<div class="col-sm-11"> <div class="col-sm-8">
<span class="text-muted"> {{ $ctrl.model.RepositoryWebhookURL | truncatelr }} </span> <span class="text-muted"> {{ $ctrl.model.RepositoryWebhookURL | truncatelr }} </span>
<button type="button" class="btn btn-sm btn-primary btn-sm space-left" ng-if="$ctrl.model.RepositoryWebhookURL" ng-click="$ctrl.copyWebhook()"> <button type="button" class="btn btn-sm btn-light btn-sm space-left" ng-if="$ctrl.model.RepositoryWebhookURL" ng-click="$ctrl.copyWebhook()">
<span><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy link</span> <span><pr-icon icon="'copy'" size="'sm'" feather="true"></pr-icon> Copy link</span>
</button> </button>
<span> <span>
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none"></i> <pr-icon icon="'check'" mode="'success'" feather="true" style="display: none"></pr-icon>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates && $ctrl.model.RepositoryMechanism === 'Interval'"> <div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates && $ctrl.model.RepositoryMechanism === 'Interval'">
<label for="repository_fetch_interval" class="col-sm-1 control-label text-left"> Fetch interval </label> <label for="repository_fetch_interval" class="col-sm-3 col-lg-2 control-label text-left"> Fetch interval </label>
<div class="col-sm-11"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -51,11 +51,27 @@
/> />
</div> </div>
</div> </div>
<div class="form-group" ng-if="$ctrl.showForcePullImage && $ctrl.model.RepositoryAutomaticUpdates"> <div class="form-group col-md-12" ng-show="autoUpdateForm.repository_fetch_interval.$touched && autoUpdateForm.repository_fetch_interval.$invalid">
<div class="col-sm-12"> <div class="small">
<por-switch-field name="forcePullImage" feature-id="$ctrl.stackPullImageFeature" checked="$ctrl.model.ForcePullImage" label="'Pull latest image'"> </por-switch-field> <div ng-messages="autoUpdateForm.repository_fetch_interval.$error">
<p ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field is required.</p>
<p ng-message="invalidIntervalFormat"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Please enter a valid time interval.</p>
<p ng-message="minimumInterval"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Minimum interval is 1m</p>
</div>
</div> </div>
</div> </div>
<div class="form-group" ng-if="$ctrl.showForcePullImage && $ctrl.model.RepositoryAutomaticUpdates">
<div class="col-sm-12">
<por-switch-field
name="'forcePullImage'"
feature="$ctrl.limitedFeaturePullImage"
checked="$ctrl.model.ForcePullImage"
label="'Pull latest image'"
on-change="($ctrl.onChangeForcePullImage)"
></por-switch-field>
</div>
</div>
<div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<div class="col-sm-12"> <div class="col-sm-12">
<por-switch-field <por-switch-field
@ -67,21 +83,12 @@
></por-switch-field> ></por-switch-field>
</div> </div>
</div> </div>
<div class="small text-warning" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="small" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true"></i> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
<span class="text-muted">When enabled, enforces automatic deployment at each interval or webhook invocation.</span> <span class="text-muted">When enabled, enforces automatic deployment at each interval or webhook invocation.</span>
</div> </div>
<div class="form-group col-md-12" ng-show="autoUpdateForm.repository_fetch_interval.$touched && autoUpdateForm.repository_fetch_interval.$invalid"> <div class="small" style="margin: 5px 0 10px 0" ng-if="!$ctrl.model.RepositoryAutomaticUpdates">
<div class="small text-warning"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
<div ng-messages="autoUpdateForm.repository_fetch_interval.$error">
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
<p ng-message="invalidIntervalFormat"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Please enter a valid time interval.</p>
<p ng-message="minimumInterval"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Minimum interval is 1m</p>
</div>
</div>
</div>
<div class="small text-warning" style="margin: 5px 0 10px 0" ng-if="!$ctrl.model.RepositoryAutomaticUpdates">
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true"></i>
<span class="text-muted">When enabled, updates from the git repository will occur automatically at an interval or webhook.</span> <span class="text-muted">When enabled, updates from the git repository will occur automatically at an interval or webhook.</span>
</div> </div>
</ng-form> </ng-form>

View File

@ -1,13 +1,13 @@
<ng-form name="pathForm"> <ng-form name="pathForm">
<div class="form-group"> <div class="form-group">
<span class="col-sm-12 text-muted small" <span class="col-sm-12 text-muted small"
><i class="fa fa-info-circle blue-icon space-right" aria-hidden="true"></i>Indicate the path to the {{ $ctrl.deployMethod == 'compose' ? 'Compose' : 'Manifest' }} file from ><pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon> Indicate the path to the {{ $ctrl.deployMethod == 'compose' ? 'Compose' : 'Manifest' }} file from
the root of your repository (requires a yaml, yml, json, or hcl file extension) the root of your repository (requires a yaml, yml, json, or hcl file extension)
</span> </span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stack_repository_path" class="col-sm-2 control-label text-left">{{ $ctrl.deployMethod == 'compose' ? 'Compose' : 'Manifest' }} path</label> <label for="stack_repository_path" class="col-lg-2 col-sm-3 control-label text-left">{{ $ctrl.deployMethod == 'compose' ? 'Compose' : 'Manifest' }} path</label>
<div class="col-sm-10"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"
@ -19,7 +19,7 @@
ng-pattern="/.+\.(yml|yaml|json|hcl)$/i" ng-pattern="/.+\.(yml|yaml|json|hcl)$/i"
required required
/> />
<p class="mt-10 text-warning" ng-show="pathForm.repoPathField.$error.pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Invalid file path </p> <p class="mt-2" ng-show="pathForm.repoPathField.$error.pattern"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Invalid file path </p>
</div> </div>
</div> </div>
</ng-form> </ng-form>

View File

@ -5,8 +5,8 @@
</span> </span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stack_repository_reference_name" class="col-sm-2 control-label text-left">Repository reference</label> <label for="stack_repository_reference_name" class="col-lg-2 col-sm-3 control-label text-left">Repository reference</label>
<div class="col-sm-10"> <div class="col-sm-8">
<input <input
type="text" type="text"
class="form-control" class="form-control"

View File

@ -2,8 +2,8 @@
<span class="col-sm-12 text-muted small"> You can use the URL of a git repository. </span> <span class="col-sm-12 text-muted small"> You can use the URL of a git repository. </span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="stack_repository_url" class="col-sm-2 control-label text-left">Repository URL</label> <label for="stack_repository_url" class="col-lg-2 col-sm-3 control-label text-left">Repository URL</label>
<div class="col-sm-10"> <div class="col-sm-8">
<input <input
type="text" type="text"
name="repoUrlField" name="repoUrlField"