@ -15,8 +15,8 @@
< form class = "form-horizontal" autocomplete = "off" name = "resourcePoolCreationForm" >
<!-- #region NAME INPUT -->
< div class = "form-group" >
< label for = "pool_name" class = "col-sm- 1 control-label text-left "> Name< / label >
< div class = "col-sm- 11 ">
< label for = "pool_name" class = "col-sm- 3 col-lg-2 control-label text-left required "> Name< / label >
< div class = "col-sm- 8 ">
< input
type="text"
class="form-control"
@ -29,20 +29,25 @@
required
auto-focus
/>
< span class = "help-block" >
< div class = "form-group" ng-show = "resourcePoolCreationForm.pool_name.$invalid || $ctrl.state.isAlreadyExist || $ctrl.state.hasPrefixKube" >
< div class = "col-sm-12 small text-muted" >
< div ng-messages = "resourcePoolCreationForm.pool_name.$error" >
< p class = "vertical-center" ng-message = "required" > < pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > This field is required.< / p >
< p class = "vertical-center" ng-message = "pattern"
>< pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > This field must consist of lower case alphanumeric characters or '-', and
contain at most 63 characters, and must start and end with an alphanumeric character.< /p
>
< / div >
< p class = "vertical-center" ng-if = "$ctrl.state.hasPrefixKube"
>< pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > Prefix "kube-" is reserved for Kubernetes system namespaces.< /p
>
< / div >
< / div >
< / span >
< / div >
< / div >
< div class = "form-group" ng-show = "resourcePoolCreationForm.pool_name.$invalid || $ctrl.state.isAlreadyExist || $ctrl.state.hasPrefixKube" >
< div class = "col-sm-12 small text-warning" >
< div ng-messages = "resourcePoolCreationForm.pool_name.$error" >
< p ng-message = "required" > < i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > This field is required.< / p >
< p ng-message = "pattern"
>< i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > This field must consist of lower case alphanumeric characters or '-', and contain at most 63
characters, and must start and end with an alphanumeric character.< /p
>
< / div >
< p ng-if = "$ctrl.state.hasPrefixKube" > < i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > Prefix "kube-" is reserved for Kubernetes system namespaces.< / p >
< / div >
< / div >
<!-- #endregion -->
< div class = "col-sm-12 form-section-title" > Quota < / div >
@ -50,31 +55,39 @@
<!-- quotas - switch -->
< div class = "form-group" >
< div class = "col-sm-12 small text-muted" >
< p >
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< p class = "vertical-center" >
< pr-icon class = "vertical-center" icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
A namespace segments the underlying physical Kubernetes cluster into smaller virtual clusters. You should assign a capped limit of resources to this namespace or
disable for the safe operation of your platform.
< / p >
< / div >
< div class = "col-sm-12" >
< label class = "control-label text-left" > Resource assignment < / label >
< label class = "switch" style = "margin-left: 20px" >
< input type = "checkbox" ng-model = "$ctrl.formValues.HasQuota" / > < i data-cy = "k8sNamespaceCreate-resourceAssignmentToggle" > < / i >
< / label >
< por-switch-field
data-cy="'k8sNamespaceCreate-resourceAssignmentToggle'"
label="'Resource assignment'"
label-class="'col-sm-3 col-lg-2'"
name="'k8s-resourcepool-resourcequota'"
checked="$ctrl.formValues.HasQuota"
on-change="($ctrl.onToggleResourceQuota)"
>< / por-switch-field >
< / div >
< / div >
< div class = "form-group" ng-if = "$ctrl.formValues.HasQuota && !$ctrl.isQuotaValid()" >
< span class = "col-sm-12 text-warning small" >
< p > < i class = "fa fa-exclamation-triangle" aria-hidden = "true" style = "margin-right: 2px" > < / i > At least a single limit must be set for the quota to be valid. < / p >
< / span >
< / div >
<!-- !quotas - switch -->
< div ng-if = "$ctrl.formValues.HasQuota" >
< div class = "col-sm-12 form-section-title" > Resource limits < / div >
< div >
< div class = "form-group" ng-if = "$ctrl.formValues.HasQuota && !$ctrl.isQuotaValid()" >
< span class = "col-sm-12 small text-muted" >
< p class = "vertical-center"
>< pr-icon class = "vertical-center" icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > At least a single limit must be set for the quota to be
valid.
< / p >
< / span >
< / div >
<!-- memory - limit - input -->
< div class = "form-group" >
< label for = "memory-limit" class = "col-sm-3 col-lg-2 control-label text-left" style = "margin-top: 20px" > Memory < / label >
< label for = "memory-limit" class = "col-sm-3 col-lg-2 control-label text-left" > Memory < / label >
< div class = "col-sm-3" >
< slider
model="$ctrl.formValues.MemoryLimit"
@ -98,24 +111,28 @@
data-cy="k8sNamespaceCreate-memoryLimitInput"
required
/>
< span class = "help-block" >
< div class = "form-group" ng-show = "resourcePoolCreationForm.memory_limit.$invalid" >
< div class = "col-sm-12 small text-muted" >
< div ng-messages = "resourcePoolCreationForm.pool_name.$error" >
< p class = "vertical-center"
>< pr-icon class = "vertical-center" icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > Value must be between
{{ $ctrl.defaults.MemoryLimit }} and
{{ $ctrl.state.sliderMaxMemory }}
< / p >
< / div >
< / div >
< / div >
< / span >
< / div >
< div class = "col-sm-4" >
< p class = "small text-muted" style = "margin-top: 7px" > Maximum memory usage (< b > MB< / b > ) < / p >
< / div >
< / div >
< div class = "form-group" ng-show = "resourcePoolCreationForm.memory_limit.$invalid" >
< div class = "col-sm-12 small text-warning" >
< div ng-messages = "resourcePoolCreationForm.pool_name.$error" >
< p
>< i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > Value must be between {{ $ctrl.defaults.MemoryLimit }} and {{ $ctrl.state.sliderMaxMemory }}
< / p >
< / div >
< p class = "small text-muted" > Maximum memory usage (MB) < / p >
< / div >
< / div >
<!-- !memory - limit - input -->
<!-- cpu - limit - input -->
< div class = "form-group" >
< label for = "cpu-limit" class = "col-sm-3 col-lg-2 control-label text-left" style = "margin-top: 20px" > CPU < / label >
< label for = "cpu-limit" class = "col-sm-3 col-lg-2 control-label text-left" > CPU < / label >
< div class = "col-sm-5" >
< slider
model="$ctrl.formValues.CpuLimit"
@ -128,7 +145,7 @@
>
< / slider >
< / div >
< div class = "col-sm-4" style = "margin-top: 20px" >
< div class = "col-sm-4" >
< p class = "small text-muted" > Maximum CPU usage < / p >
< / div >
< / div >
@ -141,8 +158,8 @@
< div class = "col-sm-12 form-section-title" > Load balancers < / div >
< div class = "form-group" >
< span class = "col-sm-12 text-muted small ">
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< span class = "col-sm-12 text-muted small vertical-center ">
< pr-icon icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
You can set a quota on the amount of external load balancers that can be created inside this namespace. Set this quota to 0 to effectively disable the use of load
balancers in this namespace.
< / span >
@ -152,6 +169,7 @@
< por-switch-field
data-cy="'k8sNamespaceCreate-loadBalancerQuotaToggle'"
label="'Load Balancer quota'"
label-class="'col-sm-3 col-lg-2'"
name="'k8s-resourcepool-lbquota'"
feature-id="$ctrl.LBQuotaFeatureId"
checked="$ctrl.formValues.UseLoadBalancersQuota"
@ -165,14 +183,14 @@
< div class = "col-sm-12 form-section-title" > Storage < / div >
< div class = "form-group" >
< span class = "col-sm-12 text-muted small ">
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< span class = "col-sm-12 text-muted small vertical-center ">
< pr-icon icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
Quotas can be set on each storage option to prevent users from exceeding a specific threshold when deploying applications. You can set a quota to 0 to effectively
prevent the usage of a specific storage option inside this namespace.
< / span >
< / div >
< div class = "col-sm-12 form-section-title ">
< i class = "fa fa-route" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< div class = "col-sm-12 form-section-title vertical-center ">
< pr-icon icon = "'svg-route' "> < / pr- icon >
standard
< / div >
@ -192,25 +210,30 @@
< div class = "form-group" ng-if = "$ctrl.formValues.IngressClasses.length > 0" >
< div class = "col-sm-12 small text-muted" >
< p >
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< p class = "vertical-center" >
< pr-icon icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
Enable and configure ingresses available to users when deploying applications.
< / p >
< / div >
< / div >
< div class = "form-group" ng-repeat-start = "ic in $ctrl.formValues.IngressClasses track by ic.IngressClass.Name" >
< div class = "text-muted col-sm-12" style = "width: 100%" >
< div style = "border-bottom: 1px solid #cdcdcd; padding-bottom: 5px" >
< i class = "fa fa-route" aria-hidden = "true" style = "margin-right: 2px" > < / i > {{ ic.IngressClass.Name }}
< div class = "row" >
< div class = "col-sm-12" >
< span class = "text-muted vertical-center" > < pr-icon icon = "'svg-route'" > < / pr-icon > {{ ic.IngressClass.Name }}< / span >
< hr class = "mt-2 mb-0" / >
< / div >
< / div >
< div class = "col-sm-12" style = "margin-top: 10px" >
< label class = "control-label text-left" > Allow users to use this ingress < / label >
< label class = "switch" style = "margin-left: 20px" >
< input type = "checkbox" ng-model = "ic.Selected" / > < i data-cy = "namespaceCreate-ingressToggle{{ ic.IngressClass.Name }}" > < / i >
< / label >
< div class = "row" >
< div class = "col-sm-3 col-lg-2" >
< label class = "control-label text-left" > Allow users to use this ingress < / label >
< / div >
< div class = "col-sm-8 pt-2" >
< label class = "switch" >
< input type = "checkbox" ng-model = "ic.Selected" / > < span class = "slider round" data-cy = "namespaceCreate-ingressToggle{{ ic.IngressClass.Name }}" > < / span >
< / label >
< / div >
< / div >
< / div >
@ -224,20 +247,15 @@
>
< / portainer-tooltip >
< / label >
< span
class="label label-default interactive"
style="margin-left: 10px"
ng-click="$ctrl.addHostname(ic)"
data-cy="namespaceCreate-addHostButton{{ ic.IngressClass.Name }}"
>
< i class = "fa fa-plus-circle" aria-hidden = "true" > < / i > add hostname
< span class = "label label-default interactive" ng-click = "$ctrl.addHostname(ic)" data-cy = "namespaceCreate-addHostButton{{ ic.IngressClass.Name }}" >
< pr-icon icon = "'plus'" feather = "true" > < / pr-icon > add hostname
< / span >
< / div >
< div class = "col-sm-12 " style = "margin-top: 10px ">
< div ng-repeat = "item in ic.Hosts track by $index" style = "margin-top: 2px" >
< div class = "col-sm-12 pt-4" >
< div ng-repeat = "item in ic.Hosts track by $index" >
< div class = "form-inline" >
< div class = "col-sm- 10 input-group input-group-sm ">
< span class = "input-group-addon "> Hostname< / span >
< div class = "col-sm- 8 input-group input-group-sm pt-2 ">
< span class = "input-group-addon required "> Hostname< / span >
< input
type="text"
class="form-control"
@ -251,87 +269,85 @@
/>
< / div >
< div class = "col-sm-1 input-group input-group-sm" ng-if = "$index > 0" >
< button class = "btn btn- sm btn-danger " type = "button" ng-click = "$ctrl.removeHostname(ic, $index)" >
< i class = "fa fa-trash-alt" aria-hidden = "true" > < / i>
< button class = "btn btn- md btn-light btn-only-icon !h-[30px] " type = "button" ng-click = "$ctrl.removeHostname(ic, $index)" >
< pr-icon icon = "'trash-2'" size = "'md'" feather = "true" > < / pr- icon >
< / button >
< / div >
< / div >
< div
class="small text-warning"
style="margin-top: 5px"
class="small text-muted pt-1"
ng-show="
resourcePoolCreationForm['hostname_' + ic.IngressClass.Name + '_' + $index].$invalid ||
$ctrl.state.duplicates.ingressHosts.refs[ic.IngressClass.Name][$index] !== undefined
"
>
< ng-messages for = "resourcePoolCreationForm['hostname_' + ic.IngressClass.Name + '_' + $index].$error" >
< p ng-message = "required" > < i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > Hostname is required.< / p >
< p ng-message = "required" > < pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > Hostname is required.< / p >
< p ng-message = "pattern" >
< i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i >
< pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon >
This field must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com').
< / p >
< / ng-messages >
< p ng-if = "$ctrl.state.duplicates.ingressHosts.refs[ic.IngressClass.Name][$index] !== undefined" >
< i class = "fa fa-exclamation-triangle" aria-hidden = "true" > < / i > This hostname is already used.
< pr-icon icon = "'alert-triangle'" feather = "true" mode = "'warning'" > < / pr-icon > This hostname is already used.
< / p >
< / div >
< / div >
< / div >
< / div >
< / div >
< div ng-repeat-end class = "form-group" ng-if = "ic.Selected" style = "margin-bottom: 20px" >
< div class = "col-sm-12 small text-muted" style = "margin-top: 5px" >
< p >
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
< div ng-repeat-end class = "form-group" ng-if = "ic.Selected" >
< div class = "col-sm-12 small text-muted" >
< p class = "vertical-center" >
< pr-icon icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
You can specify a list of annotations that will be associated to the ingress.
< / p >
< / div >
< div class = "col-sm-12" >
< label class = "control-label text-left" > Annotations< / label >
< span
class="label label-default interactive"
style="margin-left: 10px"
ng-click="$ctrl.addAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
< i class = "fa fa-plus-circle" aria-hidden = "true" > < / i > add annotation
< / span >
< portainer-tooltip
message="'Use annotations to configure options for an ingress. Review Nginx or Traefik documentation to find the annotations supported by your choice of ingress type.'"
>
< / portainer-tooltip >
< span
class="label label-default interactive"
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
style="margin-left: 10px"
ng-click="$ctrl.addRewriteAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
< i class = "fa fa-plus-circle" aria-hidden = "true" > < / i > add rewrite annotation
< / span >
< portainer-tooltip
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
message="'When the exposed URLs for your applications differ from the specified paths in the ingress, use the rewrite target annotation to denote the path to redirect to.'"
>
< / portainer-tooltip >
< span
class="label label-default interactive"
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
style="margin-left: 10px"
ng-click="$ctrl.addUseregexAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
< i class = "fa fa-plus-circle" aria-hidden = "true" > < / i > add regular expression annotation
< / span >
< portainer-tooltip
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
message="'Enable use of regular expressions in ingress paths (set in the ingress details of an application). Use this along with rewrite-target to specify the regex capturing group to be replaced, e.g. path regex of ^/foo/(,*) and rewrite-target of /bar/$1 rewrites example.com/foo/account to example.com/bar/account.'"
>
< / portainer-tooltip >
< label class = "control-label text-left" > Annotations < / label >
< label class = "control-label text-left" >
< span class = "label label-default interactive" ng-click = "$ctrl.addAnnotation(ic)" data-cy = "namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}" >
< pr-icon icon = "'plus'" feather = "true" > < / pr-icon > add annotation
< / span >
< portainer-tooltip
message="'Use annotations to configure options for an ingress. Review Nginx or Traefik documentation to find the annotations supported by your choice of ingress type.'"
>
< / portainer-tooltip >
< / label >
< label class = "control-label text-left" >
< span
class="label label-default interactive"
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
ng-click="$ctrl.addRewriteAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
< pr-icon icon = "'plus'" feather = "true" > < / pr-icon > add rewrite annotation
< / span >
< portainer-tooltip
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
message="'When the exposed URLs for your applications differ from the specified paths in the ingress, use the rewrite target annotation to denote the path to redirect to.'"
>
< / portainer-tooltip >
< / label >
< label class = "control-label text-left" >
< span
class="label label-default interactive"
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
ng-click="$ctrl.addUseregexAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
< pr-icon icon = "'plus'" feather = "true" > < / pr-icon > add regular expression annotation
< / span >
< portainer-tooltip
ng-if="ic.IngressClass.Type === $ctrl.IngressClassTypes.NGINX"
message="'Enable use of regular expressions in ingress paths (set in the ingress details of an application). Use this along with rewrite-target to specify the regex capturing group to be replaced, e.g. path regex of ^/foo/(,*) and rewrite-target of /bar/$1 rewrites example.com/foo/account to example.com/bar/account.'"
>
< / portainer-tooltip >
< / label >
< / div >
< div class = "col-sm-12 form-inline " style = "margin-top: 10px ">
< div ng-repeat= "annotation in ic.Annotations track by $inde x" style = "margin-top: 2p x">
< div class = "col-sm-12 form-inline pt-4 ">
< div class= "pt-2" ng-repeat= "annotation in ic.Annotations track by $inde x">
< div class = "input-group col-sm-5 input-group-sm" >
< span class = "input-group-addon" > Key< / span >
< input
@ -347,7 +363,7 @@
data-cy="namespaceCreate-annotationKey{{ ic.IngressClass.Name }}"
/>
< / div >
< div class = "input-group col-sm-5 input-group-sm">
< div class = "input-group input-group-sm col-sm-5 ">
< span class = "input-group-addon" > Value< / span >
< input
type="text"
@ -358,14 +374,14 @@
data-cy="namespaceCreate-annotationValue{{ ic.IngressClass.Name }}"
/>
< / div >
< div class = " col-sm-1 input-group input-group-sm">
< div class = " input-group input-group-sm col-sm-1 ">
< button
class="btn btn-sm btn-danger "
class="btn btn-sm btn-light btn-only-icon !h-[30px] "
type="button"
ng-click="$ctrl.removeAnnotation(ic, $index)"
data-cy="namespaceCreate-deleteAnnotationButton{{ ic.IngressClass.Name }}"
>
< i class = "fa fa-trash-alt" aria-hidden = "true" > < / i>
< pr-icon icon = "'trash-2'" size = "'md'" feather = "true" > < / pr- icon >
< / button >
< / div >
< / div >
@ -378,15 +394,15 @@
< div class = "col-sm-12 form-section-title" > Registries < / div >
< div class = "form-group" >
< div class = "col-sm-12 small text-muted" >
< p >
< i class = "fa fa-info-circle blue-icon" aria-hidden = "true" style = "margin-right: 2px "> < / i>
Define which registry can be used by users who have access to this namespace.
< p class = "vertical-center" >
< pr-icon icon = "'info'" feather = "true" mode = "'primary' "> < / pr- icon >
Define which registries can be used by users who have access to this namespace.
< / p >
< / div >
< / div >
< div class = "form-group" >
< label class = "col-sm-3 col-lg-2 control-label text-left" style = "padding-top: 0" > Select registries < / label >
< label class = "col-sm-3 col-lg-2 control-label text-left" > Select registries < / label >
< div class = "col-sm-9 col-lg-4" >
< span class = "small text-muted" ng-if = "!$ctrl.registries.length && $ctrl.state.isAdmin" >
No registries available. Head over < a ui-sref = "portainer.registries" > registry view< / a > to define container registry.
@ -404,7 +420,7 @@
tick-property="Checked"
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more registry ', search: 'Search...'}"
translation="{nothingSelected: 'Select one or more registries ', search: 'Search...'}"
data-cy="namespaceCreate-registrySelect"
>
< / span >
@ -422,7 +438,7 @@
< div class = "col-sm-12" >
< button
type="button"
class="btn btn-primary btn-sm"
class="btn btn-primary btn-sm !ml-0 "
ng-disabled="!resourcePoolCreationForm.$valid || $ctrl.isCreateButtonDisabled()"
ng-click="$ctrl.createResourcePool()"
button-spinner="$ctrl.state.actionInProgress"