diff --git a/app/kubernetes/components/datatables/nodes-datatable/nodesDatatable.html b/app/kubernetes/components/datatables/nodes-datatable/nodesDatatable.html
index c6526a30e..8b976a235 100644
--- a/app/kubernetes/components/datatables/nodes-datatable/nodesDatatable.html
+++ b/app/kubernetes/components/datatables/nodes-datatable/nodesDatatable.html
@@ -114,7 +114,10 @@
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
>
- {{ item.Name }}
+
+ {{ item.Name }}
+
+ api
|
{{ item.Name }} |
{{ item.Role }} |
diff --git a/app/kubernetes/endpoint/converter.js b/app/kubernetes/endpoint/converter.js
index 780a9118c..6dd728785 100644
--- a/app/kubernetes/endpoint/converter.js
+++ b/app/kubernetes/endpoint/converter.js
@@ -1,4 +1,4 @@
-import { KubernetesEndpoint, KubernetesEndpointAnnotationLeader } from 'Kubernetes/endpoint/models';
+import { KubernetesEndpoint, KubernetesEndpointAnnotationLeader, KubernetesEndpointSubset } from 'Kubernetes/endpoint/models';
import _ from 'lodash-es';
class KubernetesEndpointConverter {
@@ -13,6 +13,16 @@ class KubernetesEndpointConverter {
const split = _.split(parsedJson.holderIdentity, '_');
res.HolderIdentity = split[0];
}
+
+ if (data.subsets) {
+ res.Subsets = _.map(data.subsets, (item) => {
+ const subset = new KubernetesEndpointSubset();
+ subset.Ips = _.map(item.addresses, 'ip');
+ const port = _.find(item.ports, { name: 'https' });
+ subset.Port = port ? port.port : undefined;
+ return subset;
+ });
+ }
return res;
}
}
diff --git a/app/kubernetes/endpoint/models.js b/app/kubernetes/endpoint/models.js
index bd493adfd..506573af3 100644
--- a/app/kubernetes/endpoint/models.js
+++ b/app/kubernetes/endpoint/models.js
@@ -8,6 +8,7 @@ const _KubernetesEndpoint = Object.freeze({
Name: '',
Namespace: '',
HolderIdentity: '',
+ Subsets: [],
});
export class KubernetesEndpoint {
@@ -15,3 +16,14 @@ export class KubernetesEndpoint {
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesEndpoint)));
}
}
+
+const _KubernetesEndpointSubset = Object.freeze({
+ Ips: [],
+ Port: 0,
+});
+
+export class KubernetesEndpointSubset {
+ constructor() {
+ Object.assign(this, JSON.parse(JSON.stringify(_KubernetesEndpointSubset)));
+ }
+}
diff --git a/app/kubernetes/node/models.js b/app/kubernetes/node/models.js
index 8b77bfece..f1c547969 100644
--- a/app/kubernetes/node/models.js
+++ b/app/kubernetes/node/models.js
@@ -11,7 +11,9 @@ const _KubernetesNode = Object.freeze({
Memory: '',
Version: '',
IPAddress: '',
+ Api: false,
Taints: [],
+ Port: 0,
});
export class KubernetesNode {
diff --git a/app/kubernetes/rest/endpoint.js b/app/kubernetes/rest/endpoint.js
new file mode 100644
index 000000000..7fce22e41
--- /dev/null
+++ b/app/kubernetes/rest/endpoint.js
@@ -0,0 +1,20 @@
+angular.module('portainer.kubernetes').factory('KubernetesEndpoints', function KubernetesEndpointsFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
+ 'use strict';
+ return function (namespace) {
+ const url = API_ENDPOINT_ENDPOINTS + '/:endpointId/kubernetes/api/v1' + (namespace ? '/namespaces/:namespace' : '') + '/endpoints/:id';
+ return $resource(
+ url,
+ {
+ endpointId: EndpointProvider.endpointID,
+ namespace: namespace,
+ },
+ {
+ get: {
+ method: 'GET',
+ timeout: 15000,
+ ignoreLoadingBar: true,
+ },
+ }
+ );
+ };
+});
diff --git a/app/kubernetes/views/cluster/cluster.html b/app/kubernetes/views/cluster/cluster.html
index ad95331bf..47a198c6e 100644
--- a/app/kubernetes/views/cluster/cluster.html
+++ b/app/kubernetes/views/cluster/cluster.html
@@ -51,7 +51,7 @@
-
+
Leader status
@@ -62,7 +62,7 @@
Component |
Leader node |
-
+
{{ ep.Name }}
|
diff --git a/app/kubernetes/views/cluster/clusterController.js b/app/kubernetes/views/cluster/clusterController.js
index a8ec935c0..7726834d2 100644
--- a/app/kubernetes/views/cluster/clusterController.js
+++ b/app/kubernetes/views/cluster/clusterController.js
@@ -51,8 +51,17 @@ class KubernetesClusterController {
async getEndpointsAsync() {
try {
- const endpoints = await this.KubernetesEndpointService.get('kube-system');
- this.endpoints = _.filter(endpoints, (ep) => ep.HolderIdentity);
+ const endpoints = await this.KubernetesEndpointService.get();
+ const systemEndpoints = _.filter(endpoints, { Namespace: 'kube-system' });
+ this.systemEndpoints = _.filter(systemEndpoints, (ep) => ep.HolderIdentity);
+
+ const kubernetesEndpoint = _.find(endpoints, { Name: 'kubernetes' });
+ if (kubernetesEndpoint && kubernetesEndpoint.Subsets) {
+ const ips = _.flatten(_.map(kubernetesEndpoint.Subsets, 'Ips'));
+ _.forEach(this.nodes, (node) => {
+ node.Api = _.includes(ips, node.IPAddress);
+ });
+ }
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve endpoints');
}
diff --git a/app/kubernetes/views/cluster/node/node.html b/app/kubernetes/views/cluster/node/node.html
index 6fabaaa77..cc503348f 100644
--- a/app/kubernetes/views/cluster/node/node.html
+++ b/app/kubernetes/views/cluster/node/node.html
@@ -18,7 +18,16 @@
Hostname |
- {{ ctrl.node.Name }} |
+
+ {{ ctrl.node.Name }}
+ api
+ |
+
+
+
+ Kubernetes API
+ |
+ {{ ctrl.node.IPAddress }}:{{ ctrl.node.Port }} |
Role |
diff --git a/app/kubernetes/views/cluster/node/nodeController.js b/app/kubernetes/views/cluster/node/nodeController.js
index eb4bb7327..1f803965a 100644
--- a/app/kubernetes/views/cluster/node/nodeController.js
+++ b/app/kubernetes/views/cluster/node/nodeController.js
@@ -6,7 +6,17 @@ import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
class KubernetesNodeController {
/* @ngInject */
- constructor($async, $state, Notifications, LocalStorage, KubernetesNodeService, KubernetesEventService, KubernetesPodService, KubernetesApplicationService) {
+ constructor(
+ $async,
+ $state,
+ Notifications,
+ LocalStorage,
+ KubernetesNodeService,
+ KubernetesEventService,
+ KubernetesPodService,
+ KubernetesApplicationService,
+ KubernetesEndpointService
+ ) {
this.$async = $async;
this.$state = $state;
this.Notifications = Notifications;
@@ -15,18 +25,44 @@ class KubernetesNodeController {
this.KubernetesEventService = KubernetesEventService;
this.KubernetesPodService = KubernetesPodService;
this.KubernetesApplicationService = KubernetesApplicationService;
+ this.KubernetesEndpointService = KubernetesEndpointService;
this.onInit = this.onInit.bind(this);
this.getNodeAsync = this.getNodeAsync.bind(this);
this.getEvents = this.getEvents.bind(this);
this.getEventsAsync = this.getEventsAsync.bind(this);
this.getApplicationsAsync = this.getApplicationsAsync.bind(this);
+ this.getEndpointsAsync = this.getEndpointsAsync.bind(this);
}
selectTab(index) {
this.LocalStorage.storeActiveTab('node', index);
}
+ async getEndpointsAsync() {
+ try {
+ const endpoints = await this.KubernetesEndpointService.get();
+ this.endpoint = _.find(endpoints, { Name: 'kubernetes' });
+ if (this.endpoint && this.endpoint.Subsets) {
+ _.forEach(this.endpoint.Subsets, (subset) => {
+ return _.forEach(subset.Ips, (ip) => {
+ if (ip === this.node.IPAddress) {
+ this.node.Api = true;
+ this.node.Port = subset.Port;
+ return false;
+ }
+ });
+ });
+ }
+ } catch (err) {
+ this.Notifications.error('Failure', err, 'Unable to retrieve endpoints');
+ }
+ }
+
+ getEndpoints() {
+ return this.$async(this.getEndpointsAsync);
+ }
+
async getNodeAsync() {
try {
this.state.dataLoading = true;
@@ -118,6 +154,7 @@ class KubernetesNodeController {
await this.getNode();
await this.getEvents();
await this.getApplications();
+ await this.getEndpoints();
this.state.viewReady = true;
}