Compare commits
7 Commits
fix/gh/439
...
fix/GH/449
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c48709fc3e | ||
|
|
3d9c10adf1 | ||
|
|
0d20988bef | ||
|
|
1545a42f08 | ||
|
|
a12f2ee893 | ||
|
|
174e28b850 | ||
|
|
3da9751c82 |
4
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -16,7 +16,7 @@ already open. You can ensure this by searching the issue list for this
|
||||
repository. If there is a duplicate, please close your issue and add a comment
|
||||
to the existing issue instead.
|
||||
|
||||
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
|
||||
Also, be sure to check our FAQ and documentation first: https://documentation.portainer.io/
|
||||
-->
|
||||
|
||||
**Bug description**
|
||||
@@ -27,7 +27,7 @@ A clear and concise description of what you expected to happen.
|
||||
|
||||
**Portainer Logs**
|
||||
Provide the logs of your Portainer container or Service.
|
||||
You can see how [here](https://portainer.readthedocs.io/en/stable/faq.html#how-do-i-get-the-logs-from-portainer)
|
||||
You can see how [here](https://documentation.portainer.io/archive/1.23.2/faq/#how-do-i-get-the-logs-from-portainer)
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ For community support: You can find more information about Portainer's community
|
||||
|
||||
## Privacy
|
||||
|
||||
**To make sure we focus our development effort in the right places we need to know which features get used most often. To give us this information we use [Matamo Analytics](https://matomo.org/), which is hosted in Germany and is fully GDPR compliant.**
|
||||
**To make sure we focus our development effort in the right places we need to know which features get used most often. To give us this information we use [Matomo Analytics](https://matomo.org/), which is hosted in Germany and is fully GDPR compliant.**
|
||||
|
||||
When Portainer first starts, you are given the option to DISABLE analytics. If you **don't** choose to disable it, we collect anonymous usage as per [our privacy policy](https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/). **Please note**, there is no personally identifiable information sent or stored at any time and we only use the data to help us improve Portainer.
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ angular.module('portainer.docker').factory('Container', [
|
||||
params: { all: 0, action: 'json', filters: '@filters' },
|
||||
isArray: true,
|
||||
interceptor: ContainersInterceptor,
|
||||
timeout: 15000,
|
||||
},
|
||||
get: {
|
||||
method: 'GET',
|
||||
@@ -48,20 +47,17 @@ angular.module('portainer.docker').factory('Container', [
|
||||
logs: {
|
||||
method: 'GET',
|
||||
params: { id: '@id', action: 'logs' },
|
||||
timeout: 4500,
|
||||
ignoreLoadingBar: true,
|
||||
transformResponse: logsHandler,
|
||||
},
|
||||
stats: {
|
||||
method: 'GET',
|
||||
params: { id: '@id', stream: false, action: 'stats' },
|
||||
timeout: 4500,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
top: {
|
||||
method: 'GET',
|
||||
params: { id: '@id', action: 'top' },
|
||||
timeout: 4500,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
start: {
|
||||
|
||||
@@ -16,7 +16,7 @@ angular.module('portainer.docker').factory('Image', [
|
||||
endpointId: EndpointProvider.endpointID,
|
||||
},
|
||||
{
|
||||
query: { method: 'GET', params: { all: 0, action: 'json' }, isArray: true, interceptor: ImagesInterceptor, timeout: 15000 },
|
||||
query: { method: 'GET', params: { all: 0, action: 'json' }, isArray: true, interceptor: ImagesInterceptor },
|
||||
get: { method: 'GET', params: { action: 'json' } },
|
||||
search: { method: 'GET', params: { action: 'search' } },
|
||||
history: { method: 'GET', params: { action: 'history' }, isArray: true },
|
||||
|
||||
@@ -18,7 +18,6 @@ angular.module('portainer.docker').factory('Network', [
|
||||
method: 'GET',
|
||||
isArray: true,
|
||||
interceptor: NetworksInterceptor,
|
||||
timeout: 15000,
|
||||
},
|
||||
get: {
|
||||
method: 'GET',
|
||||
|
||||
@@ -35,7 +35,6 @@ angular.module('portainer.docker').factory('Service', [
|
||||
logs: {
|
||||
method: 'GET',
|
||||
params: { id: '@id', action: 'logs' },
|
||||
timeout: 4500,
|
||||
ignoreLoadingBar: true,
|
||||
transformResponse: logsHandler,
|
||||
},
|
||||
|
||||
@@ -18,10 +18,9 @@ angular.module('portainer.docker').factory('System', [
|
||||
info: {
|
||||
method: 'GET',
|
||||
params: { action: 'info' },
|
||||
timeout: 15000,
|
||||
interceptor: InfoInterceptor,
|
||||
},
|
||||
version: { method: 'GET', params: { action: 'version' }, timeout: 4500, interceptor: VersionInterceptor },
|
||||
version: { method: 'GET', params: { action: 'version' }, interceptor: VersionInterceptor },
|
||||
events: {
|
||||
method: 'GET',
|
||||
params: { action: 'events', since: '@since', until: '@until' },
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.docker').factory('Task', [
|
||||
logs: {
|
||||
method: 'GET',
|
||||
params: { id: '@id', action: 'logs' },
|
||||
timeout: 4500,
|
||||
ignoreLoadingBar: true,
|
||||
transformResponse: logsHandler,
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ angular.module('portainer.docker').factory('Volume', [
|
||||
endpointId: EndpointProvider.endpointID,
|
||||
},
|
||||
{
|
||||
query: { method: 'GET', interceptor: VolumesInterceptor, timeout: 15000 },
|
||||
query: { method: 'GET', interceptor: VolumesInterceptor },
|
||||
get: { method: 'GET', params: { id: '@id' } },
|
||||
create: {
|
||||
method: 'POST',
|
||||
|
||||
@@ -12,9 +12,9 @@ angular.module('portainer.integrations.storidge').factory('Storidge', [
|
||||
{
|
||||
rebootCluster: { method: 'POST', params: { resource: 'clusters', action: 'reboot' } },
|
||||
shutdownCluster: { method: 'POST', params: { resource: 'clusters', action: 'shutdown' } },
|
||||
queryEvents: { method: 'GET', params: { resource: 'clusters', action: 'events' }, timeout: 4500, ignoreLoadingBar: true, isArray: true },
|
||||
queryEvents: { method: 'GET', params: { resource: 'clusters', action: 'events' }, ignoreLoadingBar: true, isArray: true },
|
||||
getVersion: { method: 'GET', params: { resource: 'clusters', action: 'version' } },
|
||||
getInfo: { method: 'GET', params: { resource: 'clusters', action: 'info' }, timeout: 4500, ignoreLoadingBar: true },
|
||||
getInfo: { method: 'GET', params: { resource: 'clusters', action: 'info' }, ignoreLoadingBar: true },
|
||||
|
||||
queryNodes: { method: 'GET', params: { resource: 'nodes' } },
|
||||
getNode: { method: 'GET', params: { resource: 'nodes', id: '@id' } },
|
||||
|
||||
@@ -14,7 +14,6 @@ angular.module('portainer.kubernetes').factory('KubernetesComponentStatus', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -15,7 +15,6 @@ angular.module('portainer.kubernetes').factory('KubernetesEndpoints', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesHorizontalPodAutoScale
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -15,7 +15,6 @@ function factory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -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: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ angular.module('portainer.kubernetes').factory('KubernetesNodes', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesConfigMaps', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesControllerRevisions',
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesDaemonSets', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesDeployments', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -11,7 +11,6 @@ angular.module('portainer.kubernetes').factory('KubernetesEndpoints', function K
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesEvents', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -7,7 +7,7 @@ angular.module('portainer.kubernetes').factory('KubernetesHealth', [
|
||||
API_ENDPOINT_ENDPOINTS + '/:id/kubernetes/healthz',
|
||||
{},
|
||||
{
|
||||
ping: { method: 'GET', timeout: 15000, params: { id: 'id' } },
|
||||
ping: { method: 'GET', params: { id: 'id' } },
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -16,7 +16,6 @@ angular.module('portainer.kubernetes').factory('KubernetesNamespaces', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesPersistentVolumeClaims
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -18,7 +18,6 @@ angular.module('portainer.kubernetes').factory('KubernetesPods', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesReplicaSets', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesResourceQuotas', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesSecrets', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesServices', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -17,7 +17,6 @@ angular.module('portainer.kubernetes').factory('KubernetesStatefulSets', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -16,7 +16,6 @@ angular.module('portainer.kubernetes').factory('KubernetesStorage', [
|
||||
{
|
||||
get: {
|
||||
method: 'GET',
|
||||
timeout: 15000,
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
getYaml: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -87,7 +87,7 @@ class KubernetesApplicationsController {
|
||||
_.forEach(this.ports, (item) => {
|
||||
item.Expanded = false;
|
||||
item.Highlighted = false;
|
||||
if (item.Name === application.Name) {
|
||||
if (item.Name === application.Name && item.Ports.length > 1) {
|
||||
item.Expanded = true;
|
||||
item.Highlighted = true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -339,8 +339,8 @@ class KubernetesNodeController {
|
||||
|
||||
this.availableEffects = _.values(KubernetesNodeTaintEffects);
|
||||
this.formValues = KubernetesNodeConverter.nodeToFormValues(this.node);
|
||||
this.formValues.Labels = KubernetesNodeHelper.computeUsedLabels(this.applications, this.formValues.Labels);
|
||||
this.formValues.Labels = KubernetesNodeHelper.reorderLabels(this.formValues.Labels);
|
||||
this.formValues.Labels = KubernetesNodeHelper.computeUsedLabels(this.applications, this.formValues.Labels);
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user