mirror of https://github.com/rancher/dashboard.git
195 lines
6.2 KiB
JavaScript
195 lines
6.2 KiB
JavaScript
import { DEFAULT_PROJECT, SYSTEM_PROJECT } from '@shell/config/labels-annotations';
|
|
import { MANAGEMENT, NAMESPACE, NORMAN } from '@shell/config/types';
|
|
import HybridModel from '@shell/plugins/steve/hybrid-class';
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
|
|
function clearUnusedResourceQuotas(spec, types) {
|
|
types.forEach((type) => {
|
|
if (spec[type]?.limit && !isEmpty(spec[type].limit)) {
|
|
Object.keys(spec[type].limit).forEach((key) => {
|
|
if (!spec[type].limit[key]) {
|
|
delete spec[type].limit[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
if (spec[type]?.usedLimit && !isEmpty(spec[type].usedLimit)) {
|
|
Object.keys(spec[type].usedLimit).forEach((key) => {
|
|
if (!spec[type].usedLimit[key]) {
|
|
delete spec[type].usedLimit[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
if ( spec[type]?.limit && isEmpty(spec[type].limit) ) {
|
|
spec[type].limit = null;
|
|
}
|
|
|
|
if ( spec[type]?.usedLimit && isEmpty(spec[type].usedLimit) ) {
|
|
spec[type].usedLimit = null;
|
|
}
|
|
|
|
if ( spec[type] && !isEmpty(spec[type]) && Object.keys(spec[type]).every( (k) => spec[type][k] === null ) ) {
|
|
spec[type] = null;
|
|
}
|
|
});
|
|
|
|
return spec;
|
|
}
|
|
|
|
export default class Project extends HybridModel {
|
|
get isSystem() {
|
|
return this.metadata?.labels?.[SYSTEM_PROJECT] === 'true';
|
|
}
|
|
|
|
get isDefault() {
|
|
return this.metadata?.labels?.[DEFAULT_PROJECT] === 'true';
|
|
}
|
|
|
|
get namespaces() {
|
|
// I don't know how you'd end up with a project outside of rancher, but just in case...
|
|
if ( !this.$rootGetters['isRancher'] ) {
|
|
return [];
|
|
}
|
|
|
|
const inStore = this.$rootGetters['currentProduct'].inStore;
|
|
|
|
const all = this.$rootGetters[`${ inStore }/all`](NAMESPACE);
|
|
|
|
return all.filter((ns) => {
|
|
return ns.projectId === this.metadata.name;
|
|
});
|
|
}
|
|
|
|
get description() {
|
|
return this.spec?.description;
|
|
}
|
|
|
|
get doneOverride() {
|
|
return this.listLocation;
|
|
}
|
|
|
|
get listLocation() {
|
|
// Harvester uses these resource directly... but has different routes. listLocation covers routes leading back to route
|
|
if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
|
|
return { name: `${ HARVESTER }-c-cluster-projectsnamespaces` };
|
|
}
|
|
|
|
return { name: 'c-cluster-product-projectsnamespaces' };
|
|
}
|
|
|
|
get _detailLocation() {
|
|
// Harvester uses these resource directly... but has different routes. detailLocation covers routes leading to resource (like edit)
|
|
const _detailLocation = super._detailLocation;
|
|
|
|
if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
|
|
_detailLocation.name = `${ HARVESTER }-${ _detailLocation.name }`.replace('-product', '');
|
|
}
|
|
|
|
return _detailLocation;
|
|
}
|
|
|
|
get parentLocationOverride() {
|
|
return this.listLocation;
|
|
}
|
|
|
|
async save(forceReplaceOnReq) {
|
|
const norman = await this.norman;
|
|
|
|
// PUT requests to Norman have trouble with nested objects due to the
|
|
// merging strategy performed on the backend. Whenever a field is
|
|
// removed, the resource should be replaced instead of merged,
|
|
// and the PUT request should have a query param _replace=true.
|
|
const newValue = await norman.save({ replace: forceReplaceOnReq });
|
|
|
|
return newValue;
|
|
}
|
|
|
|
async remove() {
|
|
const norman = await this.norman;
|
|
|
|
await norman.remove(...arguments);
|
|
await this.$dispatch('management/findAll', { type: MANAGEMENT.PROJECT, opt: { force: true } }, { root: true });
|
|
}
|
|
|
|
get norman() {
|
|
return this.id ? this.normanEditProject : this.normanNewProject;
|
|
}
|
|
|
|
get normanNewProject() {
|
|
return (async() => {
|
|
const clearedResourceQuotas = clearUnusedResourceQuotas(this.spec, ['resourceQuota', 'namespaceDefaultResourceQuota']);
|
|
|
|
const normanProject = await this.$dispatch('rancher/create', {
|
|
type: NORMAN.PROJECT,
|
|
name: this.spec.displayName,
|
|
description: this.spec.description,
|
|
annotations: this.metadata.annotations,
|
|
labels: this.metadata.labels,
|
|
clusterId: this.$rootGetters['currentCluster'].id,
|
|
creatorId: this.$rootGetters['auth/principalId'],
|
|
containerDefaultResourceLimit: this.spec.containerDefaultResourceLimit,
|
|
}, { root: true });
|
|
|
|
// The backend seemingly required both labels/annotation and metadata.labels/annotations or it doesn't save the labels and annotations
|
|
normanProject.setAnnotations(this.metadata.annotations);
|
|
normanProject.setLabels(this.metadata.labels);
|
|
normanProject.setResourceQuotas(clearedResourceQuotas);
|
|
|
|
return normanProject;
|
|
})();
|
|
}
|
|
|
|
get normanEditProject() {
|
|
return (async() => {
|
|
const normanProject = await this.$dispatch('rancher/find', {
|
|
type: NORMAN.PROJECT,
|
|
id: this.id.replace('/', ':'),
|
|
}, { root: true });
|
|
|
|
const clearedResourceQuotas = clearUnusedResourceQuotas(this.spec, ['resourceQuota', 'namespaceDefaultResourceQuota']);
|
|
|
|
normanProject.setAnnotations(this.metadata.annotations);
|
|
normanProject.setLabels(this.metadata.labels);
|
|
normanProject.setResourceQuotas(clearedResourceQuotas);
|
|
normanProject.description = this.spec.description;
|
|
normanProject.name = this.spec.displayName;
|
|
normanProject.containerDefaultResourceLimit = this.spec.containerDefaultResourceLimit;
|
|
|
|
return normanProject;
|
|
})();
|
|
}
|
|
|
|
// users with permissions for projectroletemplatebindings should be able to manage members on projects
|
|
get canUpdate() {
|
|
return super.canUpdate || this.canUpdateProjectBindings;
|
|
}
|
|
|
|
get canUpdateProjectBindings() {
|
|
const schema = this.$rootGetters[`rancher/schemaFor`](NORMAN.PROJECT_ROLE_TEMPLATE_BINDING);
|
|
|
|
return schema?.collectionMethods.includes('POST');
|
|
}
|
|
|
|
get canEditYaml() {
|
|
return this.schema?.resourceMethods?.find((x) => x === 'blocked-PUT') ? false : super.canUpdate;
|
|
}
|
|
|
|
get confirmRemove() {
|
|
return true;
|
|
}
|
|
|
|
get glance() {
|
|
const glance = [...this._glance];
|
|
|
|
const namespaceIndex = glance.findIndex((item) => item.name === 'namespace');
|
|
|
|
if (namespaceIndex > -1) {
|
|
glance.splice(namespaceIndex, 1);
|
|
}
|
|
|
|
return glance;
|
|
}
|
|
}
|