Refactor new catalog to handle requiredNamespace correctly

rancher/rancher#19697
This commit is contained in:
Westly Wright 2019-04-18 16:01:03 -07:00
parent a296c3aad9
commit 9bd0980b81
No known key found for this signature in database
GPG Key ID: 4FAB3D8673DC54A3
5 changed files with 197 additions and 125 deletions

View File

@ -5,12 +5,14 @@ import Route from '@ember/routing/route';
import { get, set, setProperties } from '@ember/object';
import { randomStr } from 'shared/utils/util';
import C from 'ui/utils/constants';
import Util from 'ui/utils/util';
export default Route.extend({
modalService: service('modal'),
catalog: service(),
scope: service(),
clusterStore: service(),
settings: service(),
parentRoute: 'catalog-tab',
@ -39,71 +41,92 @@ export default Route.extend({
}
return hash(dependencies, 'Load dependencies').then((results) => {
let neuNSN = results.tpl.get('displayName');
let dupe = results.namespaces.findBy('id', neuNSN);
var def = get(results, 'tpl.defaultVersion');
var links = get(results, 'tpl.versionLinks');
var app = get(results, 'app');
var catalogTemplateUrl = null;
if ( !results.namespace ) {
let { newNamespaceName, newNS } = this.newNamespace(dupe, neuNSN);
if ( dupe ) {
neuNSN = newNamespaceName;
}
results.namespace = newNS;
if (app && params.appId && !params.upgrade) {
def = get(app, 'externalIdInfo.version');
}
let kind = 'helm';
let neuApp = null;
var links;
catalogTemplateUrl = links[def];
links = results.tpl.versionLinks;
var version = get(this, 'settings.rancherVersion');
var verArr = Object.keys(links).filter((key) => !!links[key])
.map((key) => ({
version: key,
sortVersion: key,
link: links[key]
}));
if ( version ) {
catalogTemplateUrl = Util.addQueryParam(catalogTemplateUrl, 'rancherVersion', version);
}
if (results.app) {
if (get(params, 'clone')) {
let { newNamespaceName, newNS } = this.newNamespace(dupe, neuNSN);
return this.catalog.fetchByUrl(catalogTemplateUrl).then((catalogTemplate) => {
let { requiredNamespace } = catalogTemplate;
let neuNamespaceName = requiredNamespace ? requiredNamespace : results.tpl.get('displayName');
let existingNamespace = results.namespaces.findBy('id', neuNamespaceName);
let { namespace } = results;
let kind = 'helm';
let neuApp = null;
if ( dupe ) {
neuNSN = newNamespaceName;
let newNamespaceName;
if ( !namespace ) {
if (requiredNamespace) {
if (existingNamespace) {
// we dont want a unique namespace in this case.
namespace = existingNamespace;
} else {
( { newNamespaceName: neuNamespaceName, newNS: namespace } = this.newNamespace(existingNamespace, neuNamespaceName) );
}
} else {
( { newNamespaceName, newNS: namespace } = this.newNamespace(existingNamespace, neuNamespaceName) );
if ( existingNamespace ) {
neuNamespaceName = newNamespaceName;
}
}
results.namespace = newNS;
neuApp = results.app.cloneForNew();
set(neuApp, 'name', results.namespace.name);
} else {
neuApp = results.app;
}
} else {
neuApp = store.createRecord({
type: 'app',
name: results.namespace.name,
var verArr = Object.keys(links).filter((key) => !!links[key])
.map((key) => ({
version: key,
sortVersion: key,
link: links[key]
}));
if (results.app) {
if (get(params, 'clone')) {
neuApp = results.app.cloneForNew();
set(neuApp, 'name', this.dedupeName(get(namespace, 'displayName')));
} else {
neuApp = results.app;
}
} else {
neuApp = store.createRecord({
type: 'app',
name: neuNamespaceName,
});
}
if ( neuApp.id ) {
verArr.filter((ver) => ver.version === get(neuApp, 'externalIdInfo.version'))
.forEach((ver) => {
set(ver, 'version', `${ ver.version } (current)`);
})
}
return EmberObject.create({
allTemplates: this.modelFor(get(this, 'parentRoute')).get('catalog'),
catalogApp: neuApp,
catalogTemplateUrl: links[def],
catalogTemplate,
namespace,
namespaces: results.namespaces,
tpl: results.tpl,
tplKind: kind,
upgradeTemplate: results.upgrade,
versionLinks: links,
versionsArray: verArr,
});
}
if ( neuApp.id ) {
verArr.filter((ver) => ver.version === get(neuApp, 'externalIdInfo.version'))
.forEach((ver) => {
set(ver, 'version', `${ ver.version } (current)`);
})
}
return EmberObject.create({
allTemplates: this.modelFor(get(this, 'parentRoute')).get('catalog'),
catalogApp: neuApp,
namespace: results.namespace,
namespaces: results.namespaces,
tpl: results.tpl,
tplKind: kind,
upgradeTemplate: results.upgrade,
versionLinks: links,
versionsArray: verArr,
});
});
},
@ -132,11 +155,15 @@ export default Route.extend({
},
},
newNamespace(duplicateName, newNamespaceName) {
dedupeName(name) {
const suffix = randomStr(5, 5, 'novowels');
if ( duplicateName ) {
newNamespaceName = `${ get(duplicateName, 'displayName') }-${ suffix }`;
return `${ name }-${ suffix }`;
},
newNamespace(duplicateNamespace, newNamespaceName) {
if ( duplicateNamespace ) {
newNamespaceName = this.dedupeName(get(duplicateNamespace, 'displayName'));
}
const newNS = get(this, 'clusterStore').createRecord({

View File

@ -1,13 +1,15 @@
{{new-catalog
allTemplates=model.allTemplates
namespaces=model.namespaces
cancel=(action "cancel")
catalogApp=model.catalogApp
catalogTemplate=model.catalogTemplate
namespaceResource=model.namespace
namespaces=model.namespaces
parentRoute=parentRoute
upgrade=model.upgradeTemplate
templateResource=model.tpl
selectedTemplateUrl=model.catalogTemplateUrl
templateKind=model.tplKind
templateResource=model.tpl
upgrade=model.upgradeTemplate
versionLinks=model.versionLinks
versionsArray=model.versionsArray
}}

View File

@ -57,6 +57,7 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
questionsArray: null,
selectedTemplateUrl: null,
selectedTemplateModel: null,
catalogTemplate: null,
readmeContent: null,
appReadmeContent: null,
pastedAnswers: null,
@ -67,7 +68,7 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
primaryResource: alias('namespaceResource'),
editing: notEmpty('catalogApp.id'),
requiredNamespace: alias('selectedTemplateModel.requiredNamespace'),
requiredNamespace: alias('selectedTemplateModel.requiredNamespace'),
init() {
this._super(...arguments);
@ -75,11 +76,15 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
scheduleOnce('afterRender', () => {
if ( get(this, 'selectedTemplateUrl') ) {
this.templateChanged();
if (this.catalogTemplate) {
this.initTemplateModel(this.catalogTemplate);
} else {
this.templateChanged();
}
} else {
var def = get(this, 'templateResource.defaultVersion');
var def = get(this, 'templateResource.defaultVersion');
var links = get(this, 'versionLinks');
var app = get(this, 'catalogApp');
var app = get(this, 'catalogApp');
if (get(app, 'id') && !get(this, 'upgrade')) {
def = get(app, 'externalIdInfo.version');
@ -96,11 +101,12 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
didRender() {
if (!this.get('srcSet')) {
this.set('srcSet', true);
set(this, 'srcSet', true);
const $icon = this.$('img');
$icon.attr('src', $icon.data('src'));
this.$('img').on('error', () => {
$icon.attr('src', `${ this.get('app.baseAssets') }assets/images/generic-catalog.svg`);
});
@ -213,71 +219,97 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
var selectedTemplateModel = yield get(this, 'catalog').fetchByUrl(url)
.then((response) => {
if (response.questions) {
const questions = [];
const customAnswers = {};
response.questions.forEach((q) => {
questions.push(q);
const subquestions = get(q, 'subquestions');
if ( subquestions ) {
questions.pushObjects(subquestions);
}
});
questions.forEach((item) => {
// This will be the component that is rendered to edit this answer
item.inputComponent = `schema/input-${ item.type }`;
// Only types marked supported will show the component, Ember will explode if the component doesn't exist
item.supported = C.SUPPORTED_SCHEMA_INPUTS.indexOf(item.type) >= 0;
if (typeof current[item.variable] !== 'undefined') {
// If there's an existing value, use it (for upgrade)
item.answer = current[item.variable];
} else if (item.type === 'service' || item.type === 'certificate') {
// Loaded async and then the component picks the default
} else if ( item.type === 'boolean' ) {
// Coerce booleans
item.answer = (item.default === 'true' || item.default === true);
} else {
// Everything else
item.answer = item.default || null;
}
});
Object.keys(current).forEach((key) => {
const q = questions.findBy('variable', key);
if ( !q ) {
customAnswers[key] = current[key];
}
});
response.customAnswers = customAnswers;
this.parseQuestionsAndAnswers(response, current);
}
return response;
});
if (selectedTemplateModel && selectedTemplateModel.requiredNamespace) {
set(this, 'primaryResource.name', selectedTemplateModel.requiredNamespace);
}
set(this, 'selectedTemplateModel', selectedTemplateModel);
const files = Object.keys(selectedTemplateModel.get('files')) || [];
if ( files.length > 0 ) {
const valuesYaml = files.find((file) => file.endsWith('/values.yaml'));
set(this, 'previewTab', valuesYaml ? valuesYaml : files[0]);
}
this.initPreviewTab(selectedTemplateModel);
} else {
set(this, 'selectedTemplateModel', null);
set(this, 'readmeContent', null);
set(this, 'appReadmeContent', null);
set(this, 'noAppReadme', false);
setProperties(this, {
selectedTemplateModel: null,
readmeContent: null,
appReadmeContent: null,
noAppReadme: false,
});
}
this.updateReadme();
}),
initTemplateModel(templateModel) {
let currentAnswers = get(this, 'catalogApp.answers') || {};
this.parseQuestionsAndAnswers(templateModel, currentAnswers);
this.initPreviewTab(templateModel);
set(this, 'selectedTemplateModel', templateModel);
this.updateReadme();
},
initPreviewTab(selectedTemplateModel) {
const files = Object.keys(selectedTemplateModel.get('files')) || [];
if ( files.length > 0 ) {
const valuesYaml = files.find((file) => file.endsWith('/values.yaml'));
set(this, 'previewTab', valuesYaml ? valuesYaml : files[0]);
}
},
parseQuestionsAndAnswers(template, currentAnswers) {
const questions = [];
const customAnswers = {};
template.questions.forEach((q) => {
questions.push(q);
const subquestions = get(q, 'subquestions');
if ( subquestions ) {
questions.pushObjects(subquestions);
}
});
questions.forEach((item) => {
// This will be the component that is rendered to edit this answer
item.inputComponent = `schema/input-${ item.type }`;
// Only types marked supported will show the component, Ember will explode if the component doesn't exist
item.supported = C.SUPPORTED_SCHEMA_INPUTS.indexOf(item.type) >= 0;
if (typeof currentAnswers[item.variable] !== 'undefined') {
// If there's an existing value, use it (for upgrade)
item.answer = currentAnswers[item.variable];
} else if (item.type === 'service' || item.type === 'certificate') {
// Loaded async and then the component picks the default
} else if ( item.type === 'boolean' ) {
// Coerce booleans
item.answer = (item.default === 'true' || item.default === true);
} else {
// Everything else
item.answer = item.default || null;
}
});
Object.keys(currentAnswers).forEach((key) => {
const q = questions.findBy('variable', key);
if ( !q ) {
customAnswers[key] = currentAnswers[key];
}
});
template.customAnswers = customAnswers;
},
validate() {
this._super();
@ -300,8 +332,6 @@ export default Component.extend(NewOrEdit, CatalogApp, ChildHook, {
if ( requiredNamespace && (get(this, 'namespaces') || []).findBy('id', requiredNamespace) ) {
return resolve(get(this, 'primaryResource'));
} else if ( requiredNamespace ) {
set(this, 'primaryResource.name', requiredNamespace);
}
return this._super(...arguments);

View File

@ -120,11 +120,23 @@
<div class="row">
<div class="col span-12">
{{form-namespace
namespace=primaryResource
errors=namespaceErrors
registerHook=(action "registerHook")
}}
{{#if selectedTemplateModel.requiredNamespace}}
<label class="acc-label pb-5">
{{t "newCatalog.requiredNamespace"}}
</label>
<p class="mt-0">
{{primaryResource.displayName}}
</p>
<p class="help-block">
This app requires the specified namespace to be unchanged.
</p>
{{else}}
{{form-namespace
namespace=primaryResource
errors=namespaceErrors
registerHook=(action "registerHook")
}}
{{/if}}
</div>
</div>

View File

@ -5715,6 +5715,7 @@ newCatalog:
forceUpgrade: Delete and recreate resources if needed during the upgrade
forceRollback: Delete and recreate resources if needed during the rollback
templateFiles: Template Files
requiredNamespace: Required Namespace
seeMore: More information...
saveConfigure: Configure
saveNew: Launch