mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
refactor(app): app service form to react [EE-5415] (#8994)
This commit is contained in:
parent
2d05103fed
commit
69776b4863
26 changed files with 1224 additions and 538 deletions
|
@ -1,95 +0,0 @@
|
|||
import _ from 'lodash-es';
|
||||
import { KubernetesServicePort } from 'Kubernetes/models/service/models';
|
||||
import { KubernetesFormValidationReferences } from 'Kubernetes/models/application/formValues';
|
||||
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
|
||||
import { KubernetesApplicationPublishingTypes } from 'Kubernetes/models/application/models/constants';
|
||||
|
||||
export default class KubeServicesItemViewController {
|
||||
/* @ngInject */
|
||||
constructor(EndpointProvider, Authentication) {
|
||||
this.EndpointProvider = EndpointProvider;
|
||||
this.Authentication = Authentication;
|
||||
this.KubernetesApplicationPublishingTypes = KubernetesApplicationPublishingTypes;
|
||||
}
|
||||
|
||||
addPort() {
|
||||
const port = new KubernetesServicePort();
|
||||
port.nodePort = '';
|
||||
port.port = '';
|
||||
port.targetPort = '';
|
||||
port.protocol = 'TCP';
|
||||
this.service.Ports.push(port);
|
||||
}
|
||||
|
||||
removePort(index) {
|
||||
this.service.Ports.splice(index, 1);
|
||||
}
|
||||
|
||||
servicePort(index) {
|
||||
const targetPort = this.service.Ports[index].targetPort;
|
||||
this.service.Ports[index].port = targetPort;
|
||||
this.onChangeServicePort();
|
||||
}
|
||||
|
||||
isAdmin() {
|
||||
return this.Authentication.isAdmin();
|
||||
}
|
||||
|
||||
onChangeContainerPort() {
|
||||
const state = this.state.duplicates.targetPort;
|
||||
const source = _.map(this.service.Ports, (sp) => sp.targetPort);
|
||||
const duplicates = KubernetesFormValidationHelper.getDuplicates(source);
|
||||
state.refs = duplicates;
|
||||
state.hasRefs = Object.keys(duplicates).length > 0;
|
||||
}
|
||||
|
||||
onChangeServicePort() {
|
||||
const state = this.state.duplicates.servicePort;
|
||||
const source = _.map(this.service.Ports, (sp) => sp.port);
|
||||
const duplicates = KubernetesFormValidationHelper.getDuplicates(source);
|
||||
state.refs = duplicates;
|
||||
state.hasRefs = Object.keys(duplicates).length > 0;
|
||||
|
||||
this.service.servicePortError = state.hasRefs;
|
||||
}
|
||||
|
||||
onChangeNodePort() {
|
||||
const state = this.state.duplicates.nodePort;
|
||||
|
||||
// create a list of all the node ports (number[]) in the cluster and current form
|
||||
const clusterNodePortsWithoutCurrentService = this.nodePortServices
|
||||
.filter((npService) => npService.Name !== this.service.Name)
|
||||
.map((npService) => npService.Ports)
|
||||
.flat()
|
||||
.map((npServicePorts) => npServicePorts.NodePort);
|
||||
const formNodePortsWithoutCurrentService = this.formServices
|
||||
.filter((formService) => formService.Type === KubernetesApplicationPublishingTypes.NODE_PORT && formService.Name !== this.service.Name)
|
||||
.map((formService) => formService.Ports)
|
||||
.flat()
|
||||
.map((formServicePorts) => formServicePorts.nodePort);
|
||||
const serviceNodePorts = this.service.Ports.map((sp) => sp.nodePort);
|
||||
// getDuplicates cares about the index, so put the serviceNodePorts at the start
|
||||
const allNodePortsWithoutCurrentService = [...clusterNodePortsWithoutCurrentService, ...formNodePortsWithoutCurrentService];
|
||||
|
||||
const duplicates = KubernetesFormValidationHelper.getDuplicateNodePorts(serviceNodePorts, allNodePortsWithoutCurrentService);
|
||||
state.refs = duplicates;
|
||||
state.hasRefs = Object.keys(duplicates).length > 0;
|
||||
|
||||
this.service.nodePortError = state.hasRefs;
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
if (this.service.Ports.length === 0) {
|
||||
this.addPort();
|
||||
}
|
||||
|
||||
this.state = {
|
||||
duplicates: {
|
||||
targetPort: new KubernetesFormValidationReferences(),
|
||||
servicePort: new KubernetesFormValidationReferences(),
|
||||
nodePort: new KubernetesFormValidationReferences(),
|
||||
},
|
||||
endpointId: this.EndpointProvider.endpointID(),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
<ng-form name="serviceForm">
|
||||
<div ng-if="$ctrl.isAdmin()" class="small" ng-show="$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled">
|
||||
<p class="text-warning vertical-center pt-2">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> No Load balancer is available in this cluster, click
|
||||
<a class="hyperlink" ui-sref="kubernetes.cluster.setup">here</a> to configure load balancer.
|
||||
</p>
|
||||
</div>
|
||||
<div ng-if="!$ctrl.isAdmin()" class="small" ng-show="$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled">
|
||||
<p class="text-warning vertical-center pt-2">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> No Load balancer is available in this cluster, contact your administrator.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-if="
|
||||
($ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && $ctrl.loadbalancerEnabled) ||
|
||||
$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.CLUSTER_IP ||
|
||||
$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT
|
||||
"
|
||||
>
|
||||
<div ng-show="!$ctrl.multiItemDisable" class="vertical-center mt-5 mb-5">
|
||||
<label class="control-label !pt-0 text-left">Published ports</label>
|
||||
</div>
|
||||
<div ng-repeat="servicePort in $ctrl.service.Ports" class="service-form row mt-5">
|
||||
<div class="form-group col-sm-3 !mx-0 !pl-0">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon required">Container port</span>
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="container_port_{{ $index }}"
|
||||
ng-model="servicePort.targetPort"
|
||||
placeholder="80"
|
||||
ng-min="1"
|
||||
ng-max="65535"
|
||||
min="1"
|
||||
max="65535"
|
||||
ng-change="$ctrl.servicePort($index)"
|
||||
required
|
||||
ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)"
|
||||
ng-change="$ctrl.onChangeContainerPort()"
|
||||
data-cy="k8sAppCreate-containerPort_{{ $index }}"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
<div class="small text-warning mt-1" ng-if="$ctrl.state.duplicates.targetPort.refs[$index] !== undefined">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This container port is already used.
|
||||
</div>
|
||||
<div class="small text-warning mt-1" ng-messages="serviceForm['container_port_'+$index].$error">
|
||||
<p class="vertical-center" ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Container port number is required.</p>
|
||||
<p class="vertical-center" ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Container port number must be inside the range 1-65535.</p>
|
||||
<p class="vertical-center" ng-message="max"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Container port number must be inside the range 1-65535.</p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-3 !mx-0 !pl-0">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon required">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"
|
||||
min="1"
|
||||
max="65535"
|
||||
required
|
||||
ng-disabled="$ctrl.originalIngresses.length === 0 || ($ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled)"
|
||||
ng-change="$ctrl.onChangeServicePort()"
|
||||
data-cy="k8sAppCreate-servicePort_{{ $index }}"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
<div class="small text-warning mt-1" ng-if="$ctrl.state.duplicates.servicePort.refs[$index] !== undefined">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This service port is already used.
|
||||
</div>
|
||||
<div class="small text-warning mt-1">
|
||||
<div ng-messages="serviceForm['service_port_'+$index].$error">
|
||||
<p class="vertical-center" ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Service port number is required.</p>
|
||||
<p class="vertical-center" ng-message="min"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Service port number must be inside the range 1-65535.</p>
|
||||
<p class="vertical-center" ng-message="max"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Service port number must be inside the range 1-65535.</p>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-3 !mx-0 !pl-0" ng-if="$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.NODE_PORT">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon required">Nodeport</span>
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="node_port_{{ $index }}"
|
||||
ng-model="servicePort.nodePort"
|
||||
placeholder="30080"
|
||||
ng-min="30000"
|
||||
ng-max="32767"
|
||||
min="30000"
|
||||
max="32767"
|
||||
required
|
||||
ng-change="$ctrl.onChangeNodePort()"
|
||||
data-cy="k8sAppCreate-nodeportPort_{{ $index }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<span>
|
||||
<div class="small text-warning mt-1">
|
||||
<div ng-messages="serviceForm['node_port_'+$index].$error">
|
||||
<p class="vertical-center" ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Nodeport is required.</p>
|
||||
<p class="vertical-center" ng-message="min"
|
||||
><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Nodeport number must be inside the range 30000-32767 or blank for system allocated.</p
|
||||
>
|
||||
<p class="vertical-center" ng-message="max"
|
||||
><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Nodeport number must be inside the range 30000-32767 or blank for system allocated.</p
|
||||
>
|
||||
<div class="text-warning mt-1" ng-if="$ctrl.state.duplicates.nodePort.refs[$index] !== undefined">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This node port is already used.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-sm-3 !mx-0 !pl-0" ng-if="$ctrl.service.Type === $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"
|
||||
min="1"
|
||||
max="65535"
|
||||
required
|
||||
ng-disabled="$ctrl.service.Type === $ctrl.KubernetesApplicationPublishingTypes.LOAD_BALANCER && !$ctrl.loadbalancerEnabled"
|
||||
data-cy="k8sAppCreate-loadbalancerPort_{{ $index }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-3 !mx-0 !pl-0">
|
||||
<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.service.Ports.length === 1"
|
||||
ng-show="!$ctrl.multiItemDisable"
|
||||
class="btn btn-sm btn-dangerlight btn-only-icon"
|
||||
type="button"
|
||||
ng-click="$ctrl.removePort($index)"
|
||||
data-cy="k8sAppCreate-rmPortButton_{{ $index }}"
|
||||
>
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<span class="btn btn-primary btn-sm btn btn-sm btn-light mb-2 !ml-0" ng-click="$ctrl.addPort()" data-cy="k8sAppCreate-addNewPortButton">
|
||||
<pr-icon icon="'plus'" size="'sm'"></pr-icon> Publish a new port
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-form>
|
|
@ -1,14 +0,0 @@
|
|||
import angular from 'angular';
|
||||
import controller from './kube-services-item.controller';
|
||||
|
||||
angular.module('portainer.kubernetes').component('kubeServicesItemView', {
|
||||
templateUrl: './kube-services-item.html',
|
||||
controller,
|
||||
bindings: {
|
||||
nodePortServices: '<',
|
||||
formServices: '<',
|
||||
service: '=',
|
||||
isEdit: '<',
|
||||
loadbalancerEnabled: '<',
|
||||
},
|
||||
});
|
|
@ -1,105 +0,0 @@
|
|||
import { KubernetesService, KubernetesServicePort, KubernetesServiceTypes } from '@/kubernetes/models/service/models';
|
||||
import { KubernetesApplicationPublishingTypes } from '@/kubernetes/models/application/models/constants';
|
||||
import { notifyError } from '@/portainer/services/notifications';
|
||||
import { getServices } from '@/react/kubernetes/networks/services/service';
|
||||
|
||||
export default class KubeServicesViewController {
|
||||
/* @ngInject */
|
||||
constructor($async, EndpointProvider, Authentication) {
|
||||
this.$async = $async;
|
||||
this.EndpointProvider = EndpointProvider;
|
||||
this.Authentication = Authentication;
|
||||
this.asyncOnInit = this.asyncOnInit.bind(this);
|
||||
}
|
||||
|
||||
addEntry(service) {
|
||||
const p = new KubernetesService();
|
||||
p.Type = service;
|
||||
|
||||
p.Selector = this.formValues.Selector;
|
||||
|
||||
p.Name = this.getUniqName();
|
||||
this.formValues.Services.push(p);
|
||||
}
|
||||
|
||||
getUniqName() {
|
||||
//services name will follow thia patten: service, service-2, service-3...
|
||||
let nameIndex = 2;
|
||||
let UniqName = this.formValues.Name;
|
||||
const services = this.formValues.Services;
|
||||
|
||||
const sortServices = services.sort((a, b) => {
|
||||
return a.Name.localeCompare(b.Name);
|
||||
});
|
||||
|
||||
if (sortServices.length !== 0) {
|
||||
sortServices.forEach((service) => {
|
||||
if (service.Name === UniqName) {
|
||||
UniqName = this.formValues.Name + '-' + nameIndex;
|
||||
nameIndex += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
return UniqName;
|
||||
}
|
||||
|
||||
deleteService(index) {
|
||||
this.formValues.Services.splice(index, 1);
|
||||
}
|
||||
|
||||
addPort(index) {
|
||||
const p = new KubernetesServicePort();
|
||||
this.formValues.Services[index].Ports.push(p);
|
||||
}
|
||||
|
||||
serviceType(type) {
|
||||
switch (type) {
|
||||
case KubernetesApplicationPublishingTypes.CLUSTER_IP:
|
||||
return KubernetesServiceTypes.CLUSTER_IP;
|
||||
case KubernetesApplicationPublishingTypes.NODE_PORT:
|
||||
return KubernetesServiceTypes.NODE_PORT;
|
||||
case KubernetesApplicationPublishingTypes.LOAD_BALANCER:
|
||||
return KubernetesServiceTypes.LOAD_BALANCER;
|
||||
}
|
||||
}
|
||||
|
||||
isAdmin() {
|
||||
return this.Authentication.isAdmin();
|
||||
}
|
||||
|
||||
async asyncOnInit() {
|
||||
try {
|
||||
// get all nodeport services in the cluster, to validate unique nodeports in the form
|
||||
const allSettledServices = await Promise.allSettled(this.namespaces.map((namespace) => getServices(this.state.endpointId, namespace)));
|
||||
const allServices = allSettledServices
|
||||
.filter((settledService) => settledService.status === 'fulfilled' && settledService.value)
|
||||
.map((fulfilledService) => fulfilledService.value)
|
||||
.flat();
|
||||
this.nodePortServices = allServices.filter((service) => service.Type === 'NodePort');
|
||||
} catch (error) {
|
||||
notifyError('Failure', error, 'Failed getting services');
|
||||
}
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.state = {
|
||||
serviceType: [
|
||||
{
|
||||
typeName: KubernetesServiceTypes.CLUSTER_IP,
|
||||
typeValue: KubernetesApplicationPublishingTypes.CLUSTER_IP,
|
||||
},
|
||||
{
|
||||
typeName: KubernetesServiceTypes.NODE_PORT,
|
||||
typeValue: KubernetesApplicationPublishingTypes.NODE_PORT,
|
||||
},
|
||||
{
|
||||
typeName: KubernetesServiceTypes.LOAD_BALANCER,
|
||||
typeValue: KubernetesApplicationPublishingTypes.LOAD_BALANCER,
|
||||
},
|
||||
],
|
||||
selected: KubernetesApplicationPublishingTypes.CLUSTER_IP,
|
||||
endpointId: this.EndpointProvider.endpointID(),
|
||||
};
|
||||
return this.$async(this.asyncOnInit);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
<div class="col-sm-12 form-section-title"> Publishing the application </div>
|
||||
<div class="col-sm-12 !p-0">
|
||||
<div class="small">
|
||||
<p class="text-muted vertical-center">
|
||||
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
|
||||
<span>Publish your application by creating a ClusterIP service for it, which you may then expose via <a target="_blank" ui-sref="kubernetes.ingresses">an ingress</a>.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 form-inline">
|
||||
<div class="col-sm-6" style="padding-left: 0px">
|
||||
<select
|
||||
class="form-control"
|
||||
ng-model="$ctrl.state.selected"
|
||||
ng-options="item.typeValue as item.typeName for item in $ctrl.state.serviceType"
|
||||
data-cy="k8sAppCreate-publishingModeDropdown"
|
||||
></select>
|
||||
<button type="button" class="btn btn-md btn-default vertical-center !ml-0" ng-click="$ctrl.addEntry( $ctrl.state.selected )" data-cy="k8sAppCreate-createServiceButton">
|
||||
<span
|
||||
class="interactive vertical-center"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="top"
|
||||
tooltip-class="portainer-tooltip"
|
||||
uib-tooltip="Different service types expose the deployment in alternate ways.
|
||||
ClusterIP exposes it within the cluster (for internal access only).
|
||||
NodePort exposes it (on a high port) across all nodes.
|
||||
LoadBalancer exposes it via an external load balancer."
|
||||
>
|
||||
<pr-icon icon="'plus'" size="'sm'"></pr-icon> Create service
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 20px" ng-repeat="service in $ctrl.formValues.Services">
|
||||
<div>
|
||||
<div class="text-muted vertical-center">
|
||||
<pr-icon ng-if="$ctrl.serviceType(service.Type) === 'ClusterIP'" icon="'list'"></pr-icon>
|
||||
<pr-icon ng-if="$ctrl.serviceType(service.Type) === 'LoadBalancer'" icon="'svg-dataflow'"></pr-icon>
|
||||
<pr-icon ng-if="$ctrl.serviceType(service.Type) === 'NodePort'" icon="'list'"></pr-icon>
|
||||
{{ $ctrl.serviceType(service.Type) }}
|
||||
</div>
|
||||
<kube-services-item-view
|
||||
node-port-services="$ctrl.nodePortServices"
|
||||
form-services="$ctrl.formValues.Services"
|
||||
service="$ctrl.formValues.Services[$index]"
|
||||
is-edit="$ctrl.isEdit"
|
||||
loadbalancer-enabled="$ctrl.loadbalancerEnabled"
|
||||
></kube-services-item-view>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-dangerlight space-right vertical-center"
|
||||
style="margin-left: 0; margin-top: 10px"
|
||||
ng-click="$ctrl.deleteService( $index )"
|
||||
data-cy="k8sConfigCreate-removeButton"
|
||||
>
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.formValues.Services[$index].Ingress && $ctrl.formValues.OriginalIngresses.length === 0">
|
||||
<div class="text-muted">
|
||||
<pr-icon icon="'svg-route'" class-name="'mr-0.5'"></pr-icon>
|
||||
Ingress
|
||||
</div>
|
||||
<div ng-if="$ctrl.isAdmin()" class="small">
|
||||
<p class="text-warning vertical-center pt-2">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Ingress is not configured in this namespace, select another namespace or click
|
||||
<a ui-sref="kubernetes.cluster.setup">here</a> to configure ingress.
|
||||
</p>
|
||||
</div>
|
||||
<div ng-if="!$ctrl.isAdmin()" class="small">
|
||||
<p class="text-warning vertical-center pt-2">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Ingress is not configured in this namespace, select another namespace or contact your administrator.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-dangerlight space-right vertical-center"
|
||||
style="margin-left: 0; margin-top: 10px"
|
||||
ng-click="$ctrl.deleteService( $index )"
|
||||
data-cy="k8sConfigCreate-removeButton"
|
||||
>
|
||||
<pr-icon icon="'trash-2'" size="'md'"></pr-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,13 +0,0 @@
|
|||
import angular from 'angular';
|
||||
import controller from './kube-services.controller';
|
||||
|
||||
angular.module('portainer.kubernetes').component('kubeServicesView', {
|
||||
templateUrl: './kube-services.html',
|
||||
controller,
|
||||
bindings: {
|
||||
formValues: '=',
|
||||
isEdit: '<',
|
||||
namespaces: '<',
|
||||
loadbalancerEnabled: '<',
|
||||
},
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue