mirror of https://github.com/portainer/portainer
Merge pull request #4415 from portainer/feat/GH/4011-pods-as-applications
feat(k8s/applications): exposed naked pods as applicationspull/4495/head
commit
3d9c10adf1
|
@ -57,7 +57,7 @@
|
|||
<table class="table table-hover nowrap-cells">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<th ng-if="!$ctrl.isPod">
|
||||
<a ng-click="$ctrl.changeOrderBy('PodName')">
|
||||
Pod
|
||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'PodName' && !$ctrl.state.reverseOrder"></i>
|
||||
|
@ -107,7 +107,7 @@
|
|||
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
<td>{{ item.PodName }}</td>
|
||||
<td ng-if="!$ctrl.isPod">{{ item.PodName }}</td>
|
||||
<td>{{ item.Name }}</td>
|
||||
<td>{{ item.Image }}</td>
|
||||
<td
|
||||
|
|
|
@ -8,5 +8,6 @@ angular.module('portainer.kubernetes').component('kubernetesContainersDatatable'
|
|||
tableKey: '@',
|
||||
orderBy: '@',
|
||||
refreshCallback: '<',
|
||||
isPod: '<',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -151,11 +151,14 @@
|
|||
>{{ item.Image }} <span ng-if="item.Containers.length > 1">+ {{ item.Containers.length - 1 }}</span></td
|
||||
>
|
||||
<td>{{ item.ApplicationType | kubernetesApplicationTypeText }}</td>
|
||||
<td>
|
||||
<td ng-if="item.ApplicationType !== $ctrl.KubernetesApplicationTypes.POD">
|
||||
<span ng-if="item.DeploymentType === $ctrl.KubernetesApplicationDeploymentTypes.REPLICATED">Replicated</span>
|
||||
<span ng-if="item.DeploymentType === $ctrl.KubernetesApplicationDeploymentTypes.GLOBAL">Global</span>
|
||||
<code>{{ item.RunningPodsCount }}</code> / <code>{{ item.TotalPodsCount }}</code></td
|
||||
>
|
||||
<code>{{ item.RunningPodsCount }}</code> / <code>{{ item.TotalPodsCount }}</code>
|
||||
</td>
|
||||
<td ng-if="item.ApplicationType === $ctrl.KubernetesApplicationTypes.POD">
|
||||
{{ item.Pods[0].Status }}
|
||||
</td>
|
||||
<td>
|
||||
<span ng-if="item.PublishedPorts.length">
|
||||
<span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { KubernetesApplicationDeploymentTypes } from 'Kubernetes/models/application/models';
|
||||
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||
|
||||
angular.module('portainer.docker').controller('KubernetesApplicationsDatatableController', [
|
||||
|
@ -42,6 +42,7 @@ angular.module('portainer.docker').controller('KubernetesApplicationsDatatableCo
|
|||
this.$onInit = function () {
|
||||
this.isAdmin = Authentication.isAdmin();
|
||||
this.KubernetesApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes;
|
||||
this.KubernetesApplicationTypes = KubernetesApplicationTypes;
|
||||
this.setDefaults();
|
||||
this.prepareTableFromDataset();
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ function _apiPortsToPublishedPorts(pList, pRefs) {
|
|||
|
||||
class KubernetesApplicationConverter {
|
||||
static applicationCommon(res, data, pods, service, ingresses) {
|
||||
const containers = _.without(_.concat(data.spec.template.spec.containers, data.spec.template.spec.initContainers), undefined);
|
||||
const containers = data.spec.template ? _.without(_.concat(data.spec.template.spec.containers, data.spec.template.spec.initContainers), undefined) : data.spec.containers;
|
||||
res.Id = data.metadata.uid;
|
||||
res.Name = data.metadata.name;
|
||||
res.StackName = data.metadata.labels ? data.metadata.labels[KubernetesPortainerApplicationStackNameLabel] || '-' : '-';
|
||||
|
@ -61,7 +61,7 @@ class KubernetesApplicationConverter {
|
|||
res.Image = containers[0].image;
|
||||
res.CreationDate = data.metadata.creationTimestamp;
|
||||
res.Env = _.without(_.flatMap(_.map(containers, 'env')), undefined);
|
||||
res.Pods = KubernetesApplicationHelper.associatePodsAndApplication(pods, data);
|
||||
res.Pods = data.spec.selector ? KubernetesApplicationHelper.associatePodsAndApplication(pods, data.spec.selector) : [data];
|
||||
|
||||
const limits = {
|
||||
Cpu: 0,
|
||||
|
@ -118,7 +118,11 @@ class KubernetesApplicationConverter {
|
|||
res.PublishedPorts = ports;
|
||||
}
|
||||
|
||||
res.Volumes = data.spec.template.spec.volumes ? data.spec.template.spec.volumes : [];
|
||||
if (data.spec.templates) {
|
||||
res.Volumes = data.spec.template.spec.volumes ? data.spec.template.spec.volumes : [];
|
||||
} else {
|
||||
res.Volumes = data.spec.volumes;
|
||||
}
|
||||
|
||||
// TODO: review
|
||||
// this if() fixs direct use of PVC reference inside spec.template.spec.containers[0].volumeMounts
|
||||
|
@ -169,7 +173,7 @@ class KubernetesApplicationConverter {
|
|||
res.PersistedFolders = _.without(res.PersistedFolders, undefined);
|
||||
|
||||
res.ConfigurationVolumes = _.reduce(
|
||||
data.spec.template.spec.volumes,
|
||||
res.Volumes,
|
||||
(acc, volume) => {
|
||||
if (volume.configMap || volume.secret) {
|
||||
const matchingVolumeMount = _.find(_.flatMap(_.map(containers, 'volumeMounts')), { name: volume.name });
|
||||
|
@ -213,6 +217,13 @@ class KubernetesApplicationConverter {
|
|||
);
|
||||
}
|
||||
|
||||
static apiPodToApplication(data, pods, service, ingresses) {
|
||||
const res = new KubernetesApplication();
|
||||
KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses);
|
||||
res.ApplicationType = KubernetesApplicationTypes.POD;
|
||||
return res;
|
||||
}
|
||||
|
||||
static apiDeploymentToApplication(data, pods, service, ingresses) {
|
||||
const res = new KubernetesApplication();
|
||||
KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses);
|
||||
|
@ -310,7 +321,7 @@ class KubernetesApplicationConverter {
|
|||
} else if (daemonSet) {
|
||||
app = KubernetesDaemonSetConverter.applicationFormValuesToDaemonSet(formValues, claims);
|
||||
} else {
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use to convert form');
|
||||
}
|
||||
|
||||
let headlessService;
|
||||
|
|
|
@ -57,6 +57,8 @@ angular
|
|||
return KubernetesApplicationTypeStrings.DAEMONSET;
|
||||
case KubernetesApplicationTypes.STATEFULSET:
|
||||
return KubernetesApplicationTypeStrings.STATEFULSET;
|
||||
case KubernetesApplicationTypes.POD:
|
||||
return KubernetesApplicationTypeStrings.POD;
|
||||
default:
|
||||
return '-';
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ class KubernetesApplicationHelper {
|
|||
return !application.ApplicationOwner;
|
||||
}
|
||||
|
||||
static associatePodsAndApplication(pods, app) {
|
||||
return _.filter(pods, { Labels: app.spec.selector.matchLabels });
|
||||
static associatePodsAndApplication(pods, selector) {
|
||||
return _.filter(pods, ['metadata.labels', selector.matchLabels]);
|
||||
}
|
||||
|
||||
static associateContainerPersistedFoldersAndConfigurations(app, containers) {
|
||||
|
|
|
@ -20,7 +20,7 @@ class KubernetesApplicationRollbackHelper {
|
|||
result = KubernetesApplicationRollbackHelper._getStatefulSetPayload(application, targetRevision);
|
||||
break;
|
||||
default:
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use to convert patch');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class KubernetesHistoryHelper {
|
|||
[currentRevision, revisionsList] = KubernetesHistoryHelper._getStatefulSetRevisions(rawRevisions, application.Raw);
|
||||
break;
|
||||
default:
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use to get revisions');
|
||||
}
|
||||
revisionsList = _.sortBy(revisionsList, 'revision');
|
||||
return [currentRevision, revisionsList];
|
||||
|
|
|
@ -7,6 +7,9 @@ class KubernetesServiceHelper {
|
|||
}
|
||||
|
||||
static findApplicationBoundService(services, rawApp) {
|
||||
if (!rawApp.spec.template) {
|
||||
return undefined;
|
||||
}
|
||||
return _.find(services, (item) => item.spec.selector && _.isMatch(rawApp.spec.template.metadata.labels, item.spec.selector));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ export class KubernetesHorizontalPodAutoScalerHelper {
|
|||
return KubernetesApplicationTypeStrings.DAEMONSET;
|
||||
} else if ((app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET) || app instanceof KubernetesStatefulSet) {
|
||||
return KubernetesApplicationTypeStrings.STATEFULSET;
|
||||
// } else if () { ---> TODO: refactor - handle bare pod type !
|
||||
} else if (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.POD) {
|
||||
return KubernetesApplicationTypeStrings.POD;
|
||||
} else {
|
||||
throw new PortainerError('Unable to determine application type');
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@ export const KubernetesApplicationTypes = Object.freeze({
|
|||
DEPLOYMENT: 1,
|
||||
DAEMONSET: 2,
|
||||
STATEFULSET: 3,
|
||||
POD: 4,
|
||||
});
|
||||
|
||||
export const KubernetesApplicationTypeStrings = Object.freeze({
|
||||
DEPLOYMENT: 'Deployment',
|
||||
DAEMONSET: 'DaemonSet',
|
||||
STATEFULSET: 'StatefulSet',
|
||||
POD: 'Pod',
|
||||
});
|
||||
|
||||
export const KubernetesApplicationPublishingTypes = Object.freeze({
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
/**
|
||||
* Generic params
|
||||
*/
|
||||
const _KubernetesCommonParams = Object.freeze({
|
||||
id: '',
|
||||
});
|
||||
export class KubernetesCommonParams {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesCommonParams)));
|
||||
}
|
||||
export function KubernetesCommonParams() {
|
||||
return {
|
||||
id: '',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import _ from 'lodash-es';
|
||||
import angular from 'angular';
|
||||
import PortainerError from 'Portainer/error';
|
||||
|
||||
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
|
||||
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
||||
|
||||
class KubernetesPodService {
|
||||
/* @ngInject */
|
||||
|
@ -11,23 +9,43 @@ class KubernetesPodService {
|
|||
this.$async = $async;
|
||||
this.KubernetesPods = KubernetesPods;
|
||||
|
||||
this.getAsync = this.getAsync.bind(this);
|
||||
this.getAllAsync = this.getAllAsync.bind(this);
|
||||
this.logsAsync = this.logsAsync.bind(this);
|
||||
this.deleteAsync = this.deleteAsync.bind(this);
|
||||
}
|
||||
|
||||
async getAsync(namespace, name) {
|
||||
try {
|
||||
const params = new KubernetesCommonParams();
|
||||
params.id = name;
|
||||
const [raw, yaml] = await Promise.all([this.KubernetesPods(namespace).get(params).$promise, this.KubernetesPods(namespace).getYaml(params).$promise]);
|
||||
const res = {
|
||||
Raw: raw,
|
||||
Yaml: yaml.data,
|
||||
};
|
||||
return res;
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to retrieve pod', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET ALL
|
||||
*/
|
||||
async getAllAsync(namespace) {
|
||||
try {
|
||||
const data = await this.KubernetesPods(namespace).get().$promise;
|
||||
return _.map(data.items, (item) => KubernetesPodConverter.apiToModel(item));
|
||||
return data.items;
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to retrieve pods', err);
|
||||
}
|
||||
}
|
||||
|
||||
get(namespace) {
|
||||
get(namespace, name) {
|
||||
if (name) {
|
||||
return this.$async(this.getAsync, namespace, name);
|
||||
}
|
||||
return this.$async(this.getAllAsync, namespace);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import KubernetesServiceHelper from 'Kubernetes/helpers/serviceHelper';
|
|||
import { KubernetesHorizontalPodAutoScalerHelper } from 'Kubernetes/horizontal-pod-auto-scaler/helper';
|
||||
import { KubernetesHorizontalPodAutoScalerConverter } from 'Kubernetes/horizontal-pod-auto-scaler/converter';
|
||||
import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter';
|
||||
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
||||
|
||||
class KubernetesApplicationService {
|
||||
/* #region CONSTRUCTOR */
|
||||
|
@ -71,7 +72,7 @@ class KubernetesApplicationService {
|
|||
} else if (app instanceof KubernetesStatefulSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET)) {
|
||||
apiService = this.KubernetesStatefulSetService;
|
||||
} else {
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use to retrieve API Service');
|
||||
}
|
||||
return apiService;
|
||||
}
|
||||
|
@ -87,15 +88,18 @@ class KubernetesApplicationService {
|
|||
/* #region GET */
|
||||
async getAsync(namespace, name) {
|
||||
try {
|
||||
const [deployment, daemonSet, statefulSet, pods, autoScalers, ingresses] = await Promise.allSettled([
|
||||
const [deployment, daemonSet, statefulSet, pod, pods, autoScalers, ingresses] = await Promise.allSettled([
|
||||
this.KubernetesDeploymentService.get(namespace, name),
|
||||
this.KubernetesDaemonSetService.get(namespace, name),
|
||||
this.KubernetesStatefulSetService.get(namespace, name),
|
||||
this.KubernetesPodService.get(namespace, name),
|
||||
this.KubernetesPodService.get(namespace),
|
||||
this.KubernetesHorizontalPodAutoScalerService.get(namespace),
|
||||
this.KubernetesIngressService.get(namespace),
|
||||
]);
|
||||
|
||||
// const pod = _.find(pods.value, ['metadata.namespace', namespace, 'metadata.name', name]);
|
||||
|
||||
let rootItem;
|
||||
let converterFunc;
|
||||
if (deployment.status === 'fulfilled') {
|
||||
|
@ -107,8 +111,11 @@ class KubernetesApplicationService {
|
|||
} else if (statefulSet.status === 'fulfilled') {
|
||||
rootItem = statefulSet;
|
||||
converterFunc = KubernetesApplicationConverter.apiStatefulSetToapplication;
|
||||
} else if (pod.status === 'fulfilled') {
|
||||
rootItem = pod;
|
||||
converterFunc = KubernetesApplicationConverter.apiPodToApplication;
|
||||
} else {
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use to convert application');
|
||||
}
|
||||
|
||||
const services = await this.KubernetesServiceService.get(namespace);
|
||||
|
@ -118,6 +125,7 @@ class KubernetesApplicationService {
|
|||
const application = converterFunc(rootItem.value.Raw, pods.value, service.Raw, ingresses.value);
|
||||
application.Yaml = rootItem.value.Yaml;
|
||||
application.Raw = rootItem.value.Raw;
|
||||
application.Pods = _.map(application.Pods, (item) => KubernetesPodConverter.apiToModel(item));
|
||||
application.Containers = KubernetesApplicationHelper.associateContainersAndApplication(application);
|
||||
|
||||
const boundScaler = KubernetesHorizontalPodAutoScalerHelper.findApplicationBoundScaler(autoScalers.value, application);
|
||||
|
@ -173,7 +181,14 @@ class KubernetesApplicationService {
|
|||
convertToApplication(item, KubernetesApplicationConverter.apiStatefulSetToapplication, services, pods, ingresses)
|
||||
);
|
||||
|
||||
const applications = _.concat(deploymentApplications, daemonSetApplications, statefulSetApplications);
|
||||
const boundPods = _.concat(_.flatMap(deploymentApplications, 'Pods'), _.flatMap(daemonSetApplications, 'Pods'), _.flatMap(statefulSetApplications, 'Pods'));
|
||||
const unboundPods = _.without(pods, ...boundPods);
|
||||
const nakedPodsApplications = _.map(unboundPods, (item) => convertToApplication(item, KubernetesApplicationConverter.apiPodToApplication, services, pods, ingresses));
|
||||
|
||||
const applications = _.concat(deploymentApplications, daemonSetApplications, statefulSetApplications, nakedPodsApplications);
|
||||
_.forEach(applications, (app) => {
|
||||
app.Pods = _.map(app.Pods, (item) => KubernetesPodConverter.apiToModel(item));
|
||||
});
|
||||
await Promise.all(
|
||||
_.forEach(applications, async (application) => {
|
||||
const boundScaler = KubernetesHorizontalPodAutoScalerHelper.findApplicationBoundScaler(autoScalers, application);
|
||||
|
|
|
@ -32,13 +32,17 @@ class KubernetesHistoryService {
|
|||
case KubernetesApplicationTypes.STATEFULSET:
|
||||
rawRevisions = await this.KubernetesControllerRevisionService.get(namespace);
|
||||
break;
|
||||
case KubernetesApplicationTypes.POD:
|
||||
rawRevisions = [];
|
||||
break;
|
||||
default:
|
||||
throw new PortainerError('Unable to determine which association to use');
|
||||
throw new PortainerError('Unable to determine which association to use for history');
|
||||
}
|
||||
if (rawRevisions.length) {
|
||||
const [currentRevision, revisionsList] = KubernetesHistoryHelper.getRevisions(rawRevisions, application);
|
||||
application.CurrentRevision = currentRevision;
|
||||
application.Revisions = revisionsList;
|
||||
}
|
||||
|
||||
const [currentRevision, revisionsList] = KubernetesHistoryHelper.getRevisions(rawRevisions, application);
|
||||
application.CurrentRevision = currentRevision;
|
||||
application.Revisions = revisionsList;
|
||||
return application;
|
||||
} catch (err) {
|
||||
throw new PortainerError('', err);
|
||||
|
|
|
@ -43,16 +43,21 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>
|
||||
<td ng-if="ctrl.application.ApplicationType !== ctrl.KubernetesApplicationTypes.POD">
|
||||
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.REPLICATED">Replicated</span>
|
||||
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.GLOBAL">Global</span>
|
||||
<code>{{ ctrl.application.RunningPodsCount }}</code> / <code>{{ ctrl.application.TotalPodsCount }}</code>
|
||||
</td>
|
||||
<td ng-if="ctrl.application.ApplicationType === ctrl.KubernetesApplicationTypes.POD">
|
||||
{{ ctrl.application.Pods[0].Status }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="ctrl.application.Requests.Cpu || ctrl.application.Requests.Memory">
|
||||
<td>
|
||||
<div>Resource reservations</div>
|
||||
<div class="text-muted small">per instance</div>
|
||||
<div ng-if="ctrl.application.ApplicationType !== ctrl.KubernetesApplicationTypes.POD" class="text-muted small">
|
||||
per instance
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-if="ctrl.application.Requests.Cpu">CPU {{ ctrl.application.Requests.Cpu | kubernetesApplicationCPUValue }}</div>
|
||||
|
@ -557,7 +562,8 @@
|
|||
title-icon="fa-server"
|
||||
dataset="ctrl.allContainers"
|
||||
table-key="kubernetes.application.containers"
|
||||
order-by="PodName"
|
||||
is-pod="ctrl.application.ApplicationType === ctrl.KubernetesApplicationTypes.POD"
|
||||
order-by="{{ ctrl.application.ApplicationType === ctrl.KubernetesApplicationTypes.POD ? 'Name' : 'PodName' }}"
|
||||
>
|
||||
</kubernetes-containers-datatable>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import angular from 'angular';
|
||||
import * as _ from 'lodash-es';
|
||||
import * as JsonPatch from 'fast-json-patch';
|
||||
import { KubernetesApplicationDataAccessPolicies, KubernetesApplicationDeploymentTypes } from 'Kubernetes/models/application/models';
|
||||
import { KubernetesApplicationDataAccessPolicies, KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
||||
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||
import { KubernetesServiceTypes } from 'Kubernetes/models/service/models';
|
||||
|
@ -123,6 +123,8 @@ class KubernetesApplicationController {
|
|||
|
||||
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
|
||||
|
||||
this.KubernetesApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes;
|
||||
this.KubernetesApplicationTypes = KubernetesApplicationTypes;
|
||||
this.ApplicationDataAccessPolicies = KubernetesApplicationDataAccessPolicies;
|
||||
this.KubernetesServiceTypes = KubernetesServiceTypes;
|
||||
this.KubernetesPodContainerTypes = KubernetesPodContainerTypes;
|
||||
|
@ -340,7 +342,6 @@ class KubernetesApplicationController {
|
|||
SelectedRevision: undefined,
|
||||
};
|
||||
|
||||
this.KubernetesApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes;
|
||||
await this.getApplication();
|
||||
await this.getEvents();
|
||||
this.state.viewReady = true;
|
||||
|
|
|
@ -61,7 +61,7 @@ class InitEndpointController {
|
|||
case PortainerEndpointConnectionTypes.AGENT:
|
||||
return this.createAgentEndpoint();
|
||||
default:
|
||||
this.Notifications.error('Failure', 'Unable to determine which action to do');
|
||||
this.Notifications.error('Failure', 'Unable to determine which action to do to create endpoint');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue