<page-header ng-if="ctrl.state.viewReady" title="'Namespace details'" breadcrumbs="[{ label:'Namespaces', link:'kubernetes.resourcePools' }, ctrl.pool.Namespace.Name]" reload="true" ></page-header> <kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading> <div ng-if="ctrl.state.viewReady"> <div class="row"> <div class="col-sm-12"> <rd-widget> <rd-widget-body classes="no-padding"> <uib-tabset active="ctrl.state.activeTab" justified="true" type="pills"> <uib-tab index="0" classes="btn-sm" select="ctrl.selectTab(0)"> <uib-tab-heading> <i class="fa fa-layer-group space-right" aria-hidden="true"></i> Namespace </uib-tab-heading> <form class="form-horizontal" autocomplete="off" name="resourcePoolEditForm" style="padding: 20px; margin-top: 10px"> <!-- name-input --> <div class="form-group"> <div class="col-sm-12"> <table class="table"> <tbody> <tr> <td>Name</td> <td> {{ ctrl.pool.Namespace.Name }} <span class="label label-info image-tag label-margins" ng-if="ctrl.isSystem">system</span> </td> </tr> </tbody> </table> </div> </div> <!-- !name-input --> <div ng-if="ctrl.isAdmin && ctrl.isEditable" class="col-sm-12 form-section-title">Quota</div> <!-- quotas-switch --> <div ng-if="ctrl.isAdmin && ctrl.isEditable" class="form-group"> <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></i> </label> </div> </div> <div class="form-group" ng-if="ctrl.formValues.HasQuota && ctrl.isAdmin && ctrl.isEditable && !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> <div ng-if="ctrl.formValues.HasQuota"> <kubernetes-resource-reservation ng-if="ctrl.pool.Quota" description="Resource reservation represents the total amount of resource assigned to all the applications deployed inside this namespace." cpu-reservation="ctrl.state.resourceReservation.CPU" memory-reservation="ctrl.state.resourceReservation.Memory" cpu-limit="ctrl.formValues.CpuLimit" memory-limit="ctrl.formValues.MemoryLimit" display-usage="ctrl.state.useServerMetrics" cpu-usage="ctrl.state.resourceUsage.CPU" memory-usage="ctrl.state.resourceUsage.Memory" > </kubernetes-resource-reservation> </div> <!-- !quotas-switch --> <div ng-if="ctrl.formValues.HasQuota && ctrl.isAdmin && ctrl.isEditable"> <div class="col-sm-12 form-section-title"> Resource limits </div> <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 limit </label> <div class="col-sm-3"> <slider model="ctrl.formValues.MemoryLimit" floor="ctrl.ResourceQuotaDefaults.MemoryLimit" ceil="ctrl.state.sliderMaxMemory" step="128" ng-if="ctrl.state.sliderMaxMemory" ></slider> </div> <div class="col-sm-2"> <input name="memory_limit" type="number" min="{{ ctrl.ResourceQuotaDefaults.MemoryLimit }}" max="{{ ctrl.state.sliderMaxMemory }}" class="form-control" ng-model="ctrl.formValues.MemoryLimit" id="memory-limit" required /> </div> <div class="col-sm-4"> <p class="small text-muted" style="margin-top: 7px"> Memory limit (<b>MB</b>) </p> </div> </div> <div class="form-group" ng-show="resourcePoolEditForm.memory_limit.$invalid"> <div class="col-sm-12 small text-warning"> <div ng-messages="resourcePoolEditForm.pool_name.$error"> <p ><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Value must be between {{ ctrl.ResourceQuotaDefaults.MemoryLimit }} and {{ ctrl.state.sliderMaxMemory }}</p > </div> </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 limit </label> <div class="col-sm-5"> <slider model="ctrl.formValues.CpuLimit" floor="ctrl.ResourceQuotaDefaults.CpuLimit" ceil="ctrl.state.sliderMaxCpu" step="0.1" precision="2" ng-if="ctrl.state.sliderMaxCpu" ></slider> </div> <div class="col-sm-4" style="margin-top: 20px"> <p class="small text-muted"> Maximum CPU usage </p> </div> </div> <!-- !cpu-limit-input --> </div> </div> <!-- #region LOADBALANCERS --> <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> 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> </div> <div class="form-group"> <div class="col-sm-12"> <por-switch-field data-cy="'k8sNamespaceCreate-loadBalancerQuotaToggle'" label="'Load Balancer quota'" name="'k8s-resourcepool-Lbquota'" feature-id="ctrl.LBQuotaFeatureId" checked="ctrl.formValues.UseLoadBalancersQuota" on-change="(ctrl.onToggleLoadBalancersQuota)" ></por-switch-field> </div> </div> <!-- #endregion --> <div ng-if="ctrl.isAdmin && ctrl.isEditable && ctrl.state.canUseIngress"> <div class="col-sm-12 form-section-title"> Ingresses </div> <!-- #region INGRESSES --> <div class="form-group" ng-if="ctrl.formValues.IngressClasses.length === 0"> <div class="col-sm-12 small text-muted"> The ingress feature must be enabled in the <a ui-sref="kubernetes.cluster.setup">environment configuration view</a> to be able to register ingresses inside this namespace. </div> </div> <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> 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> </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></i> </label> </div> </div> <div ng-if="ic.Selected"> <div class="form-group"> <div class="col-sm-12"> <label class="control-label text-left"> Hostnames <portainer-tooltip message="'Hostnames associated to the ingress inside this namespace. Users will be able to expose and access their applications over the ingress via one of these hostname.'" > </portainer-tooltip> </label> <span class="label label-default interactive" style="margin-left: 10px" ng-click="ctrl.addHostname(ic)"> <i class="fa fa-plus-circle" aria-hidden="true"></i> 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="form-inline"> <div class="col-sm-10 input-group input-group-sm" ng-class="{ striked: item.NeedsDeletion }"> <span class="input-group-addon">Hostname</span> <input type="text" class="form-control" name="hostname_{{ ic.IngressClass.Name }}_{{ $index }}" ng-model="item.Host" ng-change="ctrl.onChangeIngressHostname()" placeholder="foo" pattern="[\*a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*" required /> </div> <div class="col-sm-1 input-group input-group-sm" ng-if="$index > 0"> <button ng-if="!item.NeedsDeletion" 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> <button ng-if="item.NeedsDeletion" class="btn btn-sm btn-primary" type="button" ng-click="ctrl.restoreHostname(item)"> <i class="fa fa-trash-restore" aria-hidden="true"></i> </button> </div> </div> <div class="small text-warning" style="margin-top: 5px" ng-show="resourcePoolEditForm['hostname_' + ic.IngressClass.Name + '_' + $index].$invalid || item.Duplicate" > <ng-messages for="resourcePoolEditForm['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="pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> 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="item.Duplicate"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> 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> 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)"> <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)" > <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)" > <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> </div> <div class="col-sm-12 form-inline" style="margin-top: 10px"> <div ng-repeat="annotation in ic.Annotations track by $index" style="margin-top: 2px"> <div class="input-group col-sm-5 input-group-sm"> <span class="input-group-addon">Key</span> <input type="text" class="form-control" ng-model="annotation.Key" placeholder="{{ ic.IngressClass.Type === ctrl.IngressClassTypes.NGINX ? 'e.g. nginx.ingress.kubernetes.io/enable-rewrite-log' : 'e.g. traefik.ingress.kubernetes.io/router.priority' }}" required /> </div> <div class="input-group col-sm-5 input-group-sm"> <span class="input-group-addon">Value</span> <input type="text" class="form-control" ng-model="annotation.Value" placeholder="{{ ic.IngressClass.Type === ctrl.IngressClassTypes.NGINX ? 'e.g. true or false' : 'e.g. 42' }}" required /> </div> <div class="col-sm-1 input-group input-group-sm"> <button class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeAnnotation(ic, $index)"> <i class="fa fa-trash-alt" aria-hidden="true"></i> </button> </div> </div> </div> </div> <!-- #endregion --> </div> <!-- #region REGISTRIES --> <div> <div class="col-sm-12 form-section-title"> Registries </div> <div class="form-group" ng-if="!ctrl.isAdmin || ctrl.isSystem"> <label class="col-sm-3 col-lg-2 control-label text-left" style="padding-top: 0"> Selected registries </label> <div class="col-sm-9 col-lg-4"> {{ ctrl.selectedRegistries ? ctrl.selectedRegistries : 'None' }} </div> </div> <div ng-if="ctrl.isAdmin && !ctrl.isSystem"> <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> </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> <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. </span> <span class="small text-muted" ng-if="!ctrl.registries.length && !ctrl.state.isAdmin"> No registries available. Contact your administrator to create a container registry. </span> <span isteven-multi-select ng-if="ctrl.registries.length" input-model="ctrl.registries" output-model="ctrl.formValues.Registries" button-label="Name" item-label="Name" tick-property="Checked" helper-elements="filter" search-property="Name" translation="{nothingSelected: 'Select one or more registry', search: 'Search...'}" > </span> </div> </div> </div> </div> <!-- #endregion --> <!-- #region STORAGES --> <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> 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> standard </div> <storage-class-switch value="sc.Selected" name="sc.Name" on-change="(ctrl.onToggleStorageQuota)" authorization="K8sResourcePoolDetailsW"></storage-class-switch> <!-- #endregion --> <!-- summary --> <kubernetes-summary-view ng-if="resourcePoolEditForm.$valid && !ctrl.isUpdateButtonDisabled()" form-values="ctrl.formValues" old-form-values="ctrl.savedFormValues" ></kubernetes-summary-view> <!-- !summary --> <!-- actions --> <div ng-if="ctrl.isAdmin" class="col-sm-12 form-section-title"> Actions </div> <div ng-if="ctrl.isAdmin" class="form-group"> <div class="col-sm-12"> <button type="button" ng-if="!ctrl.isSystem" class="btn btn-primary btn-sm" ng-disabled="!resourcePoolEditForm.$valid || ctrl.isUpdateButtonDisabled()" ng-click="ctrl.updateResourcePool()" button-spinner="ctrl.state.actionInProgress" > <span ng-hide="ctrl.state.actionInProgress" data-cy="k8sNamespaceEdit-updateNamespaceButton">Update namespace</span> <span ng-show="ctrl.state.actionInProgress">Update in progress...</span> </button> <button ng-if="!ctrl.isDefaultNamespace" type="button" class="btn btn-primary btn-sm" ng-click="ctrl.markUnmarkAsSystem()" button-spinner="ctrl.state.actionInProgress" data-cy="k8sNamespaceEdit-markSystem" > <span ng-if="ctrl.isSystem">Unmark as system</span> <span ng-if="!ctrl.isSystem">Mark as system</span> </button> </div> </div> <!-- !actions --> </form> </uib-tab> <uib-tab index="1" classes="btn-sm" select="ctrl.selectTab(1)"> <uib-tab-heading> <i class="fa fa-history space-right" aria-hidden="true"></i> Events <div ng-if="ctrl.hasEventWarnings()"> <i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> {{ ctrl.state.eventWarningCount }} warning(s) </div> </uib-tab-heading> <kubernetes-events-datatable title-text="Events" title-icon="fa-history" dataset="ctrl.events" table-key="kubernetes.resourcepool.events" order-by="Date" reverse-order="true" loading="ctrl.state.eventsLoading" refresh-callback="ctrl.getEvents" ></kubernetes-events-datatable> </uib-tab> <uib-tab index="2" ng-if="ctrl.pool.Yaml" select="ctrl.showEditor()" classes="btn-sm"> <uib-tab-heading> <i class="fa fa-code space-right" aria-hidden="true"></i> YAML </uib-tab-heading> <div style="padding-right: 25px" ng-if="ctrl.state.showEditorTab"> <kubernetes-yaml-inspector key="resource-pool-yaml" data="ctrl.pool.Yaml"></kubernetes-yaml-inspector> </div> </uib-tab> </uib-tabset> </rd-widget-body> </rd-widget> </div> </div> <div class="row" ng-if="ctrl.applications && ctrl.applications.length > 0"> <div class="col-sm-12"> <kubernetes-resource-pool-applications-datatable dataset="ctrl.applications" table-key="kubernetes.resourcepool.applications" order-by="Name" refresh-callback="ctrl.getApplications" loading="ctrl.state.applicationsLoading" title-text="Applications running in this namespace" title-icon="fa-laptop-code" > </kubernetes-resource-pool-applications-datatable> </div> </div> <div class="row" ng-if="ctrl.ingresses && ctrl.ingresses.length > 0"> <div class="col-sm-12"> <kubernetes-resource-pool-ingresses-datatable dataset="ctrl.ingresses" table-key="kubernetes.resourcepool.ingresses" order-by="Name" refresh-callback="ctrl.getIngresses" loading="ctrl.state.ingressesLoading" title-text="Ingress routes and applications" title-icon="fa-route" > </kubernetes-resource-pool-ingresses-datatable> </div> </div> </div>