mirror of https://github.com/portainer/portainer
188 lines
6.8 KiB
JavaScript
188 lines
6.8 KiB
JavaScript
import _ from 'lodash-es';
|
|
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
|
|
|
export default class HelmTemplatesController {
|
|
/* @ngInject */
|
|
constructor($analytics, $async, $state, $window, $anchorScroll, Authentication, HelmService, KubernetesResourcePoolService, Notifications, ModalService) {
|
|
this.$analytics = $analytics;
|
|
this.$async = $async;
|
|
this.$window = $window;
|
|
this.$state = $state;
|
|
this.$anchorScroll = $anchorScroll;
|
|
this.Authentication = Authentication;
|
|
this.HelmService = HelmService;
|
|
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
|
|
this.Notifications = Notifications;
|
|
this.ModalService = ModalService;
|
|
|
|
this.editorUpdate = this.editorUpdate.bind(this);
|
|
this.uiCanExit = this.uiCanExit.bind(this);
|
|
this.installHelmchart = this.installHelmchart.bind(this);
|
|
this.getHelmValues = this.getHelmValues.bind(this);
|
|
this.selectHelmChart = this.selectHelmChart.bind(this);
|
|
this.getHelmRepoURLs = this.getHelmRepoURLs.bind(this);
|
|
this.getLatestCharts = this.getLatestCharts.bind(this);
|
|
this.getResourcePools = this.getResourcePools.bind(this);
|
|
|
|
$window.onbeforeunload = () => {
|
|
if (this.state.isEditorDirty) {
|
|
return '';
|
|
}
|
|
};
|
|
}
|
|
|
|
editorUpdate(content) {
|
|
const contentvalues = content.getValue();
|
|
if (this.state.originalvalues === contentvalues) {
|
|
this.state.isEditorDirty = false;
|
|
} else {
|
|
this.state.values = contentvalues;
|
|
this.state.isEditorDirty = true;
|
|
}
|
|
}
|
|
|
|
async uiCanExit() {
|
|
if (this.state.isEditorDirty) {
|
|
return this.ModalService.confirmWebEditorDiscard();
|
|
}
|
|
}
|
|
|
|
async installHelmchart() {
|
|
this.state.actionInProgress = true;
|
|
try {
|
|
const payload = {
|
|
Name: this.state.appName,
|
|
Repo: this.state.chart.repo,
|
|
Chart: this.state.chart.name,
|
|
Values: this.state.values,
|
|
Namespace: this.state.resourcePool.Namespace.Name,
|
|
};
|
|
await this.HelmService.install(this.endpoint.Id, payload);
|
|
this.Notifications.success('Helm Chart successfully installed');
|
|
this.$analytics.eventTrack('kubernetes-helm-install', { category: 'kubernetes', metadata: { 'chart-name': this.state.chart.name } });
|
|
this.state.isEditorDirty = false;
|
|
this.$state.go('kubernetes.applications');
|
|
} catch (err) {
|
|
this.Notifications.error('Installation error', err);
|
|
} finally {
|
|
this.state.actionInProgress = false;
|
|
}
|
|
}
|
|
|
|
async getHelmValues() {
|
|
this.state.loadingValues = true;
|
|
try {
|
|
const { values } = await this.HelmService.values(this.state.chart.repo, this.state.chart.name);
|
|
this.state.values = values;
|
|
this.state.originalvalues = values;
|
|
} catch (err) {
|
|
this.Notifications.error('Failure', err, 'Unable to retrieve helm chart values.');
|
|
} finally {
|
|
this.state.loadingValues = false;
|
|
}
|
|
}
|
|
|
|
async selectHelmChart(chart) {
|
|
window.scrollTo(0, 0);
|
|
this.state.showCustomValues = false;
|
|
this.state.chart = chart;
|
|
await this.getHelmValues();
|
|
}
|
|
|
|
/**
|
|
* @description This function is used to get the helm repo urls for the endpoint and user
|
|
* @returns {Promise<string[]>} list of helm repo urls
|
|
*/
|
|
async getHelmRepoURLs() {
|
|
this.state.reposLoading = true;
|
|
try {
|
|
// fetch globally set helm repo and user helm repos (parallel)
|
|
const { GlobalRepository, UserRepositories } = await this.HelmService.getHelmRepositories(this.endpoint.Id);
|
|
this.state.globalRepository = GlobalRepository;
|
|
const userHelmReposUrls = UserRepositories.map((repo) => repo.URL);
|
|
const uniqueHelmRepos = [...new Set([GlobalRepository, ...userHelmReposUrls])].map((url) => url.toLowerCase()).filter((url) => url); // remove duplicates and blank, to lowercase
|
|
this.state.repos = uniqueHelmRepos;
|
|
return uniqueHelmRepos;
|
|
} catch (err) {
|
|
this.Notifications.error('Failure', err, 'Unable to retrieve helm repo urls.');
|
|
} finally {
|
|
this.state.reposLoading = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description This function is used to fetch the respective index.yaml files for the provided helm repo urls
|
|
* @param {string[]} helmRepos list of helm repositories
|
|
* @param {bool} append append charts returned from repo to existing list of helm charts
|
|
*/
|
|
async getLatestCharts(helmRepos) {
|
|
this.state.chartsLoading = true;
|
|
try {
|
|
const promiseList = helmRepos.map((repo) => this.HelmService.search(repo));
|
|
// fetch helm charts from all the provided helm repositories (parallel)
|
|
// Promise.allSettled is used to account for promise failure(s) - in cases the user has provided invalid helm repo
|
|
const chartPromises = await Promise.allSettled(promiseList);
|
|
const latestCharts = chartPromises
|
|
.filter((tp) => tp.status === 'fulfilled') // remove failed promises
|
|
.map((tp) => ({ entries: tp.value.entries, repo: helmRepos[chartPromises.indexOf(tp)] })) // extract chart entries with respective repo data
|
|
.flatMap(
|
|
({ entries, repo }) => Object.values(entries).map((charts) => ({ ...charts[0], repo })) // flatten chart entries to single array with respective repo
|
|
);
|
|
|
|
this.state.charts = latestCharts;
|
|
} catch (err) {
|
|
this.Notifications.error('Failure', err, 'Unable to retrieve helm repo charts.');
|
|
} finally {
|
|
this.state.chartsLoading = false;
|
|
}
|
|
}
|
|
|
|
async getResourcePools() {
|
|
this.state.resourcePoolsLoading = true;
|
|
try {
|
|
const resourcePools = await this.KubernetesResourcePoolService.get();
|
|
|
|
const nonSystemNamespaces = resourcePools.filter((resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
|
|
this.state.resourcePools = _.sortBy(nonSystemNamespaces, ({ Namespace }) => (Namespace.Name === 'default' ? 0 : 1));
|
|
this.state.resourcePool = this.state.resourcePools[0];
|
|
} catch (err) {
|
|
this.Notifications.error('Failure', err, 'Unable to retrieve initial helm data.');
|
|
} finally {
|
|
this.state.resourcePoolsLoading = false;
|
|
}
|
|
}
|
|
|
|
$onInit() {
|
|
return this.$async(async () => {
|
|
this.state = {
|
|
appName: '',
|
|
chart: null,
|
|
showCustomValues: false,
|
|
actionInProgress: false,
|
|
resourcePools: [],
|
|
resourcePool: '',
|
|
values: null,
|
|
originalvalues: null,
|
|
repos: [],
|
|
charts: [],
|
|
loadingValues: false,
|
|
isEditorDirty: false,
|
|
chartsLoading: false,
|
|
resourcePoolsLoading: false,
|
|
viewReady: false,
|
|
isAdmin: this.Authentication.isAdmin(),
|
|
globalRepository: undefined,
|
|
};
|
|
|
|
const helmRepos = await this.getHelmRepoURLs();
|
|
await Promise.all([this.getLatestCharts(helmRepos), this.getResourcePools()]);
|
|
|
|
this.state.viewReady = true;
|
|
});
|
|
}
|
|
|
|
$onDestroy() {
|
|
this.state.isEditorDirty = false;
|
|
}
|
|
}
|