mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
refactor(custom-templates): render template variables [EE-2602] (#6937)
This commit is contained in:
parent
71c0e8e661
commit
1ccdb64938
32 changed files with 829 additions and 47 deletions
|
@ -1,5 +1,6 @@
|
|||
import { buildOption } from '@/portainer/components/BoxSelector';
|
||||
import { AccessControlFormData } from '@/portainer/components/accessControlForm/porAccessControlFormModel';
|
||||
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
|
||||
|
||||
class KubeCreateCustomTemplateViewController {
|
||||
/* @ngInject */
|
||||
|
@ -18,6 +19,7 @@ class KubeCreateCustomTemplateViewController {
|
|||
actionInProgress: false,
|
||||
formValidationError: '',
|
||||
isEditorDirty: false,
|
||||
isTemplateValid: true,
|
||||
};
|
||||
|
||||
this.formValues = {
|
||||
|
@ -28,26 +30,54 @@ class KubeCreateCustomTemplateViewController {
|
|||
Note: '',
|
||||
Logo: '',
|
||||
AccessControlData: new AccessControlFormData(),
|
||||
Variables: [],
|
||||
};
|
||||
|
||||
this.onChangeFile = this.onChangeFile.bind(this);
|
||||
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
||||
this.onChangeMethod = this.onChangeMethod.bind(this);
|
||||
this.onBeforeOnload = this.onBeforeOnload.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.onVariablesChange = this.onVariablesChange.bind(this);
|
||||
}
|
||||
|
||||
onChangeMethod(method) {
|
||||
this.state.method = method;
|
||||
this.formValues.Variables = [];
|
||||
}
|
||||
|
||||
onChangeFileContent(content) {
|
||||
this.formValues.FileContent = content;
|
||||
this.handleChange({ FileContent: content });
|
||||
this.parseTemplate(content);
|
||||
this.state.isEditorDirty = true;
|
||||
}
|
||||
|
||||
parseTemplate(templateStr) {
|
||||
const variables = getTemplateVariables(templateStr);
|
||||
|
||||
const isValid = !!variables;
|
||||
|
||||
this.state.isTemplateValid = isValid;
|
||||
|
||||
if (isValid) {
|
||||
this.onVariablesChange(intersectVariables(this.formValues.Variables, variables));
|
||||
}
|
||||
}
|
||||
|
||||
onVariablesChange(value) {
|
||||
this.handleChange({ Variables: value });
|
||||
}
|
||||
|
||||
onChangeFile(file) {
|
||||
this.handleChange({ File: file });
|
||||
}
|
||||
|
||||
handleChange(values) {
|
||||
return this.$async(async () => {
|
||||
this.formValues.File = file;
|
||||
this.formValues = {
|
||||
...this.formValues,
|
||||
...values,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -113,6 +143,11 @@ class KubeCreateCustomTemplateViewController {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!this.state.isTemplateValid) {
|
||||
this.state.formValidationError = 'Template is not valid';
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAdmin = this.Authentication.isAdmin();
|
||||
const accessControlData = this.formValues.AccessControlData;
|
||||
const error = this.FormValidator.validateAccessControl(accessControlData, isAdmin);
|
||||
|
@ -130,6 +165,7 @@ class KubeCreateCustomTemplateViewController {
|
|||
const { fileContent, type } = this.$state.params;
|
||||
|
||||
this.formValues.FileContent = fileContent;
|
||||
this.parseTemplate(fileContent);
|
||||
if (type) {
|
||||
this.formValues.Type = +type;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
<file-upload-description> You can upload a Manifest file from your computer. </file-upload-description>
|
||||
</file-upload-form>
|
||||
|
||||
<custom-templates-variables-definition-field
|
||||
value="$ctrl.formValues.Variables"
|
||||
on-change="($ctrl.onVariablesChange)"
|
||||
is-variables-names-from-parent="$ctrl.state.method === 'editor'"
|
||||
></custom-templates-variables-definition-field>
|
||||
|
||||
<por-access-control-form form-data="$ctrl.formValues.AccessControlData"></por-access-control-form>
|
||||
|
||||
<!-- actions -->
|
||||
|
@ -45,7 +51,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.state.actionInProgress || $ctrl.form.$invalid || ($ctrl.state.method === 'editor' && !$ctrl.formValues.FileContent)"
|
||||
ng-disabled="!$ctrl.state.isTemplateValid ||$ctrl.state.actionInProgress || $ctrl.form.$invalid || ($ctrl.state.method === 'editor' && !$ctrl.formValues.FileContent)"
|
||||
ng-click="$ctrl.createCustomTemplate()"
|
||||
button-spinner="$ctrl.state.actionInProgress"
|
||||
>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ResourceControlViewModel } from '@/portainer/access-control/models/ResourceControlViewModel';
|
||||
import { AccessControlFormData } from '@/portainer/components/accessControlForm/porAccessControlFormModel';
|
||||
import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils';
|
||||
|
||||
class KubeEditCustomTemplateViewController {
|
||||
/* @ngInject */
|
||||
|
@ -10,6 +11,7 @@ class KubeEditCustomTemplateViewController {
|
|||
this.state = {
|
||||
formValidationError: '',
|
||||
isEditorDirty: false,
|
||||
isTemplateValid: true,
|
||||
};
|
||||
this.templates = [];
|
||||
|
||||
|
@ -17,6 +19,8 @@ class KubeEditCustomTemplateViewController {
|
|||
this.submitAction = this.submitAction.bind(this);
|
||||
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
||||
this.onBeforeUnload = this.onBeforeUnload.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.onVariablesChange = this.onVariablesChange.bind(this);
|
||||
}
|
||||
|
||||
getTemplate() {
|
||||
|
@ -26,7 +30,12 @@ class KubeEditCustomTemplateViewController {
|
|||
|
||||
const [template, file] = await Promise.all([this.CustomTemplateService.customTemplate(id), this.CustomTemplateService.customTemplateFile(id)]);
|
||||
template.FileContent = file;
|
||||
template.Variables = template.Variables || [];
|
||||
|
||||
this.formValues = template;
|
||||
|
||||
this.parseTemplate(file);
|
||||
|
||||
this.oldFileContent = this.formValues.FileContent;
|
||||
|
||||
this.formValues.ResourceControl = new ResourceControlViewModel(template.ResourceControl);
|
||||
|
@ -37,6 +46,31 @@ class KubeEditCustomTemplateViewController {
|
|||
});
|
||||
}
|
||||
|
||||
onVariablesChange(values) {
|
||||
this.handleChange({ Variables: values });
|
||||
}
|
||||
|
||||
handleChange(values) {
|
||||
return this.$async(async () => {
|
||||
this.formValues = {
|
||||
...this.formValues,
|
||||
...values,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
parseTemplate(templateStr) {
|
||||
const variables = getTemplateVariables(templateStr);
|
||||
|
||||
const isValid = !!variables;
|
||||
|
||||
this.state.isTemplateValid = isValid;
|
||||
|
||||
if (isValid) {
|
||||
this.onVariablesChange(intersectVariables(this.formValues.Variables, variables));
|
||||
}
|
||||
}
|
||||
|
||||
validateForm() {
|
||||
this.state.formValidationError = '';
|
||||
|
||||
|
@ -94,6 +128,7 @@ class KubeEditCustomTemplateViewController {
|
|||
onChangeFileContent(value) {
|
||||
if (stripSpaces(this.formValues.FileContent) !== stripSpaces(value)) {
|
||||
this.formValues.FileContent = value;
|
||||
this.parseTemplate(value);
|
||||
this.state.isEditorDirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@
|
|||
</editor-description>
|
||||
</web-editor-form>
|
||||
|
||||
<custom-templates-variables-definition-field
|
||||
value="$ctrl.formValues.Variables"
|
||||
on-change="($ctrl.onVariablesChange)"
|
||||
is-variables-names-from-parent="true"
|
||||
></custom-templates-variables-definition-field>
|
||||
|
||||
<por-access-control-form form-data="$ctrl.formValues.AccessControlData" resource-control="$ctrl.formValues.ResourceControl"></por-access-control-form>
|
||||
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
|
|
|
@ -84,13 +84,21 @@
|
|||
></git-form>
|
||||
<!-- !repository -->
|
||||
|
||||
<custom-template-selector
|
||||
ng-show="ctrl.state.BuildMethod === ctrl.BuildMethods.CUSTOM_TEMPLATE"
|
||||
new-template-path="kubernetes.templates.custom.new"
|
||||
stack-type="3"
|
||||
on-change="(ctrl.onChangeTemplateId)"
|
||||
value="ctrl.state.templateId"
|
||||
></custom-template-selector>
|
||||
<div ng-show="ctrl.state.BuildMethod === ctrl.BuildMethods.CUSTOM_TEMPLATE">
|
||||
<custom-template-selector
|
||||
new-template-path="kubernetes.templates.custom.new"
|
||||
stack-type="3"
|
||||
on-change="(ctrl.onChangeTemplateId)"
|
||||
value="ctrl.state.templateId"
|
||||
></custom-template-selector>
|
||||
|
||||
<custom-templates-variables-field
|
||||
ng-if="ctrl.state.template"
|
||||
definitions="ctrl.state.template.Variables"
|
||||
value="ctrl.formValues.Variables"
|
||||
on-change="(ctrl.onChangeTemplateVariables)"
|
||||
></custom-templates-variables-field>
|
||||
</div>
|
||||
|
||||
<!-- editor -->
|
||||
<web-editor-form
|
||||
|
|
|
@ -2,10 +2,11 @@ import angular from 'angular';
|
|||
import _ from 'lodash-es';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import PortainerError from 'Portainer/error';
|
||||
|
||||
import PortainerError from '@/portainer/error';
|
||||
import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, KubernetesDeployRequestMethods, RepositoryMechanismTypes } from 'Kubernetes/models/deploy';
|
||||
import { buildOption } from '@/portainer/components/BoxSelector';
|
||||
import { renderTemplate } from '@/react/portainer/custom-templates/components/utils';
|
||||
|
||||
class KubernetesDeployController {
|
||||
/* @ngInject */
|
||||
|
@ -42,6 +43,7 @@ class KubernetesDeployController {
|
|||
viewReady: false,
|
||||
isEditorDirty: false,
|
||||
templateId: null,
|
||||
template: null,
|
||||
};
|
||||
|
||||
this.formValues = {
|
||||
|
@ -56,7 +58,8 @@ class KubernetesDeployController {
|
|||
RepositoryAutomaticUpdates: false,
|
||||
RepositoryMechanism: RepositoryMechanismTypes.INTERVAL,
|
||||
RepositoryFetchInterval: '5m',
|
||||
RepositoryWebhookURL: this.WebhookHelper.returnStackWebhookUrl(uuidv4()),
|
||||
RepositoryWebhookURL: WebhookHelper.returnStackWebhookUrl(uuidv4()),
|
||||
Variables: {},
|
||||
};
|
||||
|
||||
this.ManifestDeployTypes = KubernetesDeployManifestTypes;
|
||||
|
@ -70,6 +73,18 @@ class KubernetesDeployController {
|
|||
this.buildAnalyticsProperties = this.buildAnalyticsProperties.bind(this);
|
||||
this.onChangeMethod = this.onChangeMethod.bind(this);
|
||||
this.onChangeDeployType = this.onChangeDeployType.bind(this);
|
||||
this.onChangeTemplateVariables = this.onChangeTemplateVariables.bind(this);
|
||||
}
|
||||
|
||||
onChangeTemplateVariables(value) {
|
||||
this.onChangeFormValues({ Variables: value });
|
||||
|
||||
this.renderTemplate();
|
||||
}
|
||||
|
||||
renderTemplate() {
|
||||
const rendered = renderTemplate(this.state.templateContent, this.formValues.Variables, this.state.template.Variables);
|
||||
this.onChangeFormValues({ EditorContent: rendered });
|
||||
}
|
||||
|
||||
buildAnalyticsProperties() {
|
||||
|
@ -157,17 +172,24 @@ class KubernetesDeployController {
|
|||
};
|
||||
}
|
||||
|
||||
onChangeTemplateId(templateId) {
|
||||
onChangeTemplateId(templateId, template) {
|
||||
return this.$async(async () => {
|
||||
if (this.state.templateId === templateId) {
|
||||
if (!template || (this.state.templateId === templateId && this.state.template === template)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.templateId = templateId;
|
||||
this.state.template = template;
|
||||
|
||||
try {
|
||||
const fileContent = await this.CustomTemplateService.customTemplateFile(templateId);
|
||||
this.state.templateContent = fileContent;
|
||||
this.onChangeFileContent(fileContent);
|
||||
|
||||
if (template.Variables && template.Variables.length > 0) {
|
||||
const variables = Object.fromEntries(template.Variables.map((variable) => [variable.name, '']));
|
||||
this.onChangeTemplateVariables(variables);
|
||||
}
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to load template file');
|
||||
}
|
||||
|
@ -307,7 +329,7 @@ class KubernetesDeployController {
|
|||
const templateId = parseInt(this.$state.params.templateId, 10);
|
||||
if (templateId && !Number.isNaN(templateId)) {
|
||||
this.state.BuildMethod = KubernetesDeployBuildMethods.CUSTOM_TEMPLATE;
|
||||
this.onChangeTemplateId(templateId);
|
||||
this.state.templateId = templateId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue