mirror of https://github.com/portainer/portainer
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
626 lines
16 KiB
626 lines
16 KiB
import { EnvironmentStatus } from '@/react/portainer/environments/types';
|
|
import { getSelfSubjectAccessReview } from '@/react/kubernetes/namespaces/getSelfSubjectAccessReview';
|
|
|
|
import { updateAxiosAdapter } from '@/portainer/services/axios';
|
|
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
|
|
import { CACHE_REFRESH_EVENT, CACHE_DURATION } from '../portainer/services/http-request.helper';
|
|
import { cache } from '../portainer/services/axios';
|
|
|
|
import registriesModule from './registries';
|
|
import customTemplateModule from './custom-templates';
|
|
import { reactModule } from './react';
|
|
import './views/kubernetes.css';
|
|
|
|
// The angular-cache npm package didn't have exclude options, so implement a custom cache
|
|
// with an added check to only cache kubernetes requests
|
|
class ExpirationCache {
|
|
constructor() {
|
|
this.store = new Map();
|
|
this.timeout = CACHE_DURATION;
|
|
}
|
|
|
|
get(key) {
|
|
return this.store.get(key);
|
|
}
|
|
|
|
put(key, val) {
|
|
// only cache requests with 'kubernetes' in the url
|
|
if (key.includes('kubernetes')) {
|
|
this.store.set(key, val);
|
|
// remove it once it's expired
|
|
setTimeout(() => {
|
|
this.remove(key);
|
|
}, this.timeout);
|
|
}
|
|
}
|
|
|
|
remove(key) {
|
|
this.store.delete(key);
|
|
}
|
|
|
|
removeAll() {
|
|
this.store = new Map();
|
|
}
|
|
|
|
delete() {
|
|
// skip because this is standalone, not a part of $cacheFactory
|
|
}
|
|
}
|
|
|
|
angular.module('portainer.kubernetes', ['portainer.app', registriesModule, customTemplateModule, reactModule]).config([
|
|
'$stateRegistryProvider',
|
|
function ($stateRegistryProvider) {
|
|
'use strict';
|
|
|
|
const kubernetes = {
|
|
name: 'kubernetes',
|
|
url: '/kubernetes',
|
|
parent: 'endpoint',
|
|
abstract: true,
|
|
|
|
onEnter: /* @ngInject */ function onEnter(
|
|
$async,
|
|
$state,
|
|
endpoint,
|
|
KubernetesHealthService,
|
|
Notifications,
|
|
StateManager,
|
|
$http,
|
|
Authentication,
|
|
UserService,
|
|
EndpointService,
|
|
EndpointProvider
|
|
) {
|
|
return $async(async () => {
|
|
// if the user wants to use front end cache for performance, set the angular caching settings
|
|
const userDetails = Authentication.getUserDetails();
|
|
const user = await UserService.user(userDetails.ID);
|
|
updateAxiosAdapter(user.UseCache);
|
|
if (user.UseCache) {
|
|
$http.defaults.cache = new ExpirationCache();
|
|
window.addEventListener(CACHE_REFRESH_EVENT, () => {
|
|
$http.defaults.cache.removeAll();
|
|
cache.store.clear();
|
|
});
|
|
}
|
|
|
|
const kubeTypes = [
|
|
PortainerEndpointTypes.KubernetesLocalEnvironment,
|
|
PortainerEndpointTypes.AgentOnKubernetesEnvironment,
|
|
PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment,
|
|
];
|
|
|
|
if (!kubeTypes.includes(endpoint.Type)) {
|
|
$state.go('portainer.home');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const status = await checkEndpointStatus(
|
|
endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
|
|
? KubernetesHealthService.ping(endpoint.Id)
|
|
: // use selfsubject access review to check if we can connect to the kubernetes environment
|
|
// because it gets a fast response, and is accessible to all users
|
|
getSelfSubjectAccessReview(endpoint.Id, 'default')
|
|
);
|
|
|
|
if (endpoint.Type !== PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
|
|
await updateEndpointStatus(endpoint, status);
|
|
}
|
|
endpoint.Status = status;
|
|
|
|
if (endpoint.Status === EnvironmentStatus.Down) {
|
|
throw new Error(
|
|
endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
|
|
? 'Unable to contact Edge agent, please ensure that the agent is properly running on the remote environment.'
|
|
: `The environment named ${endpoint.Name} is unreachable.`
|
|
);
|
|
}
|
|
|
|
await StateManager.updateEndpointState(endpoint);
|
|
} catch (e) {
|
|
let params = {};
|
|
|
|
if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
|
|
params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'kubernetes.dashboard' };
|
|
} else {
|
|
EndpointProvider.clean();
|
|
Notifications.error('Failed loading environment', e);
|
|
}
|
|
$state.go('portainer.home', params, { reload: true, inherit: false });
|
|
return false;
|
|
}
|
|
|
|
async function checkEndpointStatus(promise) {
|
|
try {
|
|
await promise;
|
|
return EnvironmentStatus.Up;
|
|
} catch (e) {
|
|
return EnvironmentStatus.Down;
|
|
}
|
|
}
|
|
|
|
async function updateEndpointStatus(endpoint, status) {
|
|
if (endpoint.Status === status) {
|
|
return;
|
|
}
|
|
await EndpointService.updateEndpoint(endpoint.Id, { Status: status });
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
const helmApplication = {
|
|
name: 'kubernetes.helm',
|
|
url: '/helm/:namespace/:name',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesHelmApplicationView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/inspect-helm',
|
|
},
|
|
};
|
|
|
|
const services = {
|
|
name: 'kubernetes.services',
|
|
url: '/services',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesServicesView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/networking/services',
|
|
},
|
|
};
|
|
|
|
const ingresses = {
|
|
name: 'kubernetes.ingresses',
|
|
url: '/ingresses',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesIngressesView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/networking/ingresses',
|
|
},
|
|
};
|
|
|
|
const ingressesCreate = {
|
|
name: 'kubernetes.ingresses.create',
|
|
url: '/add',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesIngressesCreateView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/networking/ingresses/add',
|
|
},
|
|
};
|
|
|
|
const ingressesEdit = {
|
|
name: 'kubernetes.ingresses.edit',
|
|
url: '/:namespace/:name/edit',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesIngressesCreateView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const applications = {
|
|
name: 'kubernetes.applications',
|
|
url: '/applications',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesApplicationsView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/applications',
|
|
},
|
|
};
|
|
|
|
const applicationCreation = {
|
|
name: 'kubernetes.applications.new',
|
|
url: '/new',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesCreateApplicationView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/applications/add',
|
|
},
|
|
};
|
|
|
|
const application = {
|
|
name: 'kubernetes.applications.application',
|
|
url: '/:namespace/:name?resource-type&tab',
|
|
views: {
|
|
'content@': {
|
|
component: 'applicationDetailsView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/applications/inspect',
|
|
},
|
|
};
|
|
|
|
const applicationEdit = {
|
|
name: 'kubernetes.applications.application.edit',
|
|
url: '/edit',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesCreateApplicationView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/applications/edit',
|
|
},
|
|
};
|
|
|
|
const applicationConsole = {
|
|
name: 'kubernetes.applications.application.console',
|
|
url: '/:pod/:container/console',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesConsoleView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const applicationLogs = {
|
|
name: 'kubernetes.applications.application.logs',
|
|
url: '/:pod/:container/logs',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesApplicationLogsView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const applicationStats = {
|
|
name: 'kubernetes.applications.application.stats',
|
|
url: '/:pod/:container/stats',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesApplicationStatsView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const stacks = {
|
|
name: 'kubernetes.stacks',
|
|
url: '/stacks',
|
|
abstract: true,
|
|
};
|
|
|
|
const stack = {
|
|
name: 'kubernetes.stacks.stack',
|
|
url: '/:namespace/:name',
|
|
abstract: true,
|
|
};
|
|
|
|
const stackLogs = {
|
|
name: 'kubernetes.stacks.stack.logs',
|
|
url: '/logs',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesStackLogsView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const configurations = {
|
|
name: 'kubernetes.configurations',
|
|
url: '/configurations?tab',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesConfigMapsAndSecretsView',
|
|
},
|
|
},
|
|
params: {
|
|
tab: null,
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/configurations',
|
|
},
|
|
};
|
|
const configmaps = {
|
|
name: 'kubernetes.configmaps',
|
|
url: '/configmaps',
|
|
abstract: true,
|
|
data: {
|
|
docs: '/user/kubernetes/configurations',
|
|
},
|
|
};
|
|
|
|
const configMapCreation = {
|
|
name: 'kubernetes.configmaps.new',
|
|
url: '/new',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesCreateConfigMapView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/configurations/add-configmap',
|
|
},
|
|
};
|
|
|
|
const configMap = {
|
|
name: 'kubernetes.configmaps.configmap',
|
|
url: '/:namespace/:name',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesConfigMapView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const secrets = {
|
|
name: 'kubernetes.secrets',
|
|
url: '/secrets',
|
|
abstract: true,
|
|
data: {
|
|
docs: '/user/kubernetes/configurations',
|
|
},
|
|
};
|
|
|
|
const secretCreation = {
|
|
name: 'kubernetes.secrets.new',
|
|
url: '/new',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesCreateSecretView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/configurations/add-secret',
|
|
},
|
|
};
|
|
|
|
const secret = {
|
|
name: 'kubernetes.secrets.secret',
|
|
url: '/:namespace/:name',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesSecretView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const cluster = {
|
|
name: 'kubernetes.cluster',
|
|
url: '/cluster',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesClusterView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/cluster/details',
|
|
},
|
|
};
|
|
|
|
const node = {
|
|
name: 'kubernetes.cluster.node',
|
|
url: '/:nodeName',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesNodeView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/cluster/node',
|
|
},
|
|
};
|
|
|
|
const nodeStats = {
|
|
name: 'kubernetes.cluster.node.stats',
|
|
url: '/stats',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesNodeStatsView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const dashboard = {
|
|
name: 'kubernetes.dashboard',
|
|
url: '/dashboard',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesDashboardView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/dashboard',
|
|
},
|
|
};
|
|
|
|
const deploy = {
|
|
name: 'kubernetes.deploy',
|
|
url: '/deploy?templateId&referrer&tab&buildMethod&chartName',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesDeployView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/applications/manifest',
|
|
},
|
|
};
|
|
|
|
const resourcePools = {
|
|
name: 'kubernetes.resourcePools',
|
|
url: '/pools',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesResourcePoolsView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/namespaces',
|
|
},
|
|
};
|
|
|
|
const namespaceCreation = {
|
|
name: 'kubernetes.resourcePools.new',
|
|
url: '/new',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesCreateNamespaceView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/namespaces/add',
|
|
},
|
|
};
|
|
|
|
const resourcePool = {
|
|
name: 'kubernetes.resourcePools.resourcePool',
|
|
url: '/:id',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesResourcePoolView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/namespaces/manage',
|
|
},
|
|
};
|
|
|
|
const resourcePoolAccess = {
|
|
name: 'kubernetes.resourcePools.resourcePool.access',
|
|
url: '/access',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesResourcePoolAccessView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/namespaces/access',
|
|
},
|
|
};
|
|
|
|
const volumes = {
|
|
name: 'kubernetes.volumes',
|
|
url: '/volumes',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesVolumesView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/volumes',
|
|
},
|
|
};
|
|
|
|
const volume = {
|
|
name: 'kubernetes.volumes.volume',
|
|
url: '/:namespace/:name',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesVolumeView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const registries = {
|
|
name: 'kubernetes.registries',
|
|
url: '/registries',
|
|
views: {
|
|
'content@': {
|
|
component: 'environmentRegistriesView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/cluster/registries',
|
|
},
|
|
};
|
|
|
|
const registriesAccess = {
|
|
name: 'kubernetes.registries.access',
|
|
url: '/:id/access',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesRegistryAccessView',
|
|
},
|
|
},
|
|
};
|
|
|
|
const endpointKubernetesConfiguration = {
|
|
name: 'kubernetes.cluster.setup',
|
|
url: '/configure',
|
|
views: {
|
|
'content@': {
|
|
component: 'kubernetesConfigureView',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/cluster/setup',
|
|
},
|
|
};
|
|
|
|
const endpointKubernetesSecurityConstraint = {
|
|
name: 'kubernetes.cluster.securityConstraint',
|
|
url: '/securityConstraint',
|
|
views: {
|
|
'content@': {
|
|
templateUrl: '../kubernetes/views/security-constraint/constraint.html',
|
|
controller: 'KubernetesSecurityConstraintController',
|
|
},
|
|
},
|
|
data: {
|
|
docs: '/user/kubernetes/cluster/security',
|
|
},
|
|
};
|
|
|
|
$stateRegistryProvider.register(kubernetes);
|
|
$stateRegistryProvider.register(helmApplication);
|
|
$stateRegistryProvider.register(applications);
|
|
$stateRegistryProvider.register(applicationCreation);
|
|
$stateRegistryProvider.register(application);
|
|
$stateRegistryProvider.register(applicationEdit);
|
|
$stateRegistryProvider.register(applicationConsole);
|
|
$stateRegistryProvider.register(applicationLogs);
|
|
$stateRegistryProvider.register(applicationStats);
|
|
$stateRegistryProvider.register(stacks);
|
|
$stateRegistryProvider.register(stack);
|
|
$stateRegistryProvider.register(stackLogs);
|
|
$stateRegistryProvider.register(configurations);
|
|
$stateRegistryProvider.register(configmaps);
|
|
$stateRegistryProvider.register(configMapCreation);
|
|
$stateRegistryProvider.register(secrets);
|
|
$stateRegistryProvider.register(secretCreation);
|
|
$stateRegistryProvider.register(configMap);
|
|
$stateRegistryProvider.register(secret);
|
|
$stateRegistryProvider.register(cluster);
|
|
$stateRegistryProvider.register(dashboard);
|
|
$stateRegistryProvider.register(deploy);
|
|
$stateRegistryProvider.register(node);
|
|
$stateRegistryProvider.register(nodeStats);
|
|
$stateRegistryProvider.register(resourcePools);
|
|
$stateRegistryProvider.register(namespaceCreation);
|
|
$stateRegistryProvider.register(resourcePool);
|
|
$stateRegistryProvider.register(resourcePoolAccess);
|
|
$stateRegistryProvider.register(volumes);
|
|
$stateRegistryProvider.register(volume);
|
|
$stateRegistryProvider.register(registries);
|
|
$stateRegistryProvider.register(registriesAccess);
|
|
$stateRegistryProvider.register(endpointKubernetesConfiguration);
|
|
$stateRegistryProvider.register(endpointKubernetesSecurityConstraint);
|
|
|
|
$stateRegistryProvider.register(services);
|
|
$stateRegistryProvider.register(ingresses);
|
|
$stateRegistryProvider.register(ingressesCreate);
|
|
$stateRegistryProvider.register(ingressesEdit);
|
|
},
|
|
]);
|