diff --git a/app/docker/helpers/serviceHelper.js b/app/docker/helpers/serviceHelper.js index 8788c2c74..cd2e658ab 100644 --- a/app/docker/helpers/serviceHelper.js +++ b/app/docker/helpers/serviceHelper.js @@ -67,39 +67,6 @@ angular.module('portainer.docker').factory('ServiceHelper', [ return []; }; - helper.translateEnvironmentVariables = function (env) { - if (env) { - var variables = []; - env.forEach(function (variable) { - var idx = variable.indexOf('='); - var keyValue = [variable.slice(0, idx), variable.slice(idx + 1)]; - var originalValue = keyValue.length > 1 ? keyValue[1] : ''; - variables.push({ - key: keyValue[0], - value: originalValue, - originalKey: keyValue[0], - originalValue: originalValue, - added: true, - }); - }); - return variables; - } - return []; - }; - - helper.translateEnvironmentVariablesToEnv = function (env) { - if (env) { - var variables = []; - env.forEach(function (variable) { - if (variable.key && variable.key !== '') { - variables.push(variable.key + '=' + variable.value); - } - }); - return variables; - } - return []; - }; - helper.translatePreferencesToKeyValue = function (preferences) { if (preferences) { var keyValuePreferences = []; diff --git a/app/docker/views/containers/create/createContainerController.js b/app/docker/views/containers/create/createContainerController.js index 19965b7ae..f75b000ae 100644 --- a/app/docker/views/containers/create/createContainerController.js +++ b/app/docker/views/containers/create/createContainerController.js @@ -1,5 +1,8 @@ import _ from 'lodash-es'; + +import * as envVarsUtils from '@/portainer/helpers/env-vars'; import { PorImageRegistryModel } from 'Docker/models/porImageRegistry'; + import { ContainerCapabilities, ContainerCapability } from '../../../models/containerCapabilities'; import { AccessControlFormData } from '../../../../portainer/components/accessControlForm/porAccessControlFormModel'; import { ContainerDetailsViewModel } from '../../../models/container'; @@ -78,6 +81,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ MemoryReservation: 0, CmdMode: 'default', EntrypointMode: 'default', + Env: [], NodeName: null, capabilities: [], Sysctls: [], @@ -95,6 +99,11 @@ angular.module('portainer.docker').controller('CreateContainerController', [ pullImageValidity: true, }; + $scope.handleEnvVarChange = handleEnvVarChange; + function handleEnvVarChange(value) { + $scope.formValues.Env = value; + } + $scope.refreshSlider = function () { $timeout(function () { $scope.$broadcast('rzSliderForceRender'); @@ -153,14 +162,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [ $scope.formValues.Volumes.splice(index, 1); }; - $scope.addEnvironmentVariable = function () { - $scope.config.Env.push({ name: '', value: '' }); - }; - - $scope.removeEnvironmentVariable = function (index) { - $scope.config.Env.splice(index, 1); - }; - $scope.addPortBinding = function () { $scope.config.HostConfig.PortBindings.push({ hostPort: '', containerPort: '', protocol: 'tcp' }); }; @@ -254,13 +255,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ } function prepareEnvironmentVariables(config) { - var env = []; - config.Env.forEach(function (v) { - if (v.name && v.value) { - env.push(v.name + '=' + v.value); - } - }); - config.Env = env; + config.Env = envVarsUtils.convertToArrayOfStrings($scope.formValues.Env); } function prepareVolumes(config) { @@ -537,14 +532,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ } function loadFromContainerEnvironmentVariables() { - var envArr = []; - for (var e in $scope.config.Env) { - if ({}.hasOwnProperty.call($scope.config.Env, e)) { - var arr = $scope.config.Env[e].split(/\=(.*)/); - envArr.push({ name: arr[0], value: arr[1] }); - } - } - $scope.config.Env = envArr; + $scope.formValues.Env = envVarsUtils.parseArrayOfStrings($scope.config.Env); } function loadFromContainerLabels() { diff --git a/app/docker/views/containers/create/createcontainer.html b/app/docker/views/containers/create/createcontainer.html index de43d1fa2..6f2aaa9cd 100644 --- a/app/docker/views/containers/create/createcontainer.html +++ b/app/docker/views/containers/create/createcontainer.html @@ -583,37 +583,13 @@
-
- -
-
- - - add environment variable - -
- -
-
-
- name - -
-
- value - -
- -
-
- -
- -
+
- +
diff --git a/app/docker/views/services/create/createServiceController.js b/app/docker/views/services/create/createServiceController.js index 6aa70264c..0458ed513 100644 --- a/app/docker/views/services/create/createServiceController.js +++ b/app/docker/views/services/create/createServiceController.js @@ -1,4 +1,6 @@ import _ from 'lodash-es'; + +import * as envVarsUtils from '@/portainer/helpers/env-vars'; import { PorImageRegistryModel } from 'Docker/models/porImageRegistry'; import { AccessControlFormData } from '../../../../portainer/components/accessControlForm/porAccessControlFormModel'; @@ -109,6 +111,11 @@ angular.module('portainer.docker').controller('CreateServiceController', [ $scope.allowBindMounts = false; + $scope.handleEnvVarChange = handleEnvVarChange; + function handleEnvVarChange(value) { + $scope.formValues.Env = value; + } + $scope.refreshSlider = function () { $timeout(function () { $scope.$broadcast('rzSliderForceRender'); @@ -168,14 +175,6 @@ angular.module('portainer.docker').controller('CreateServiceController', [ $scope.formValues.Secrets.splice(index, 1); }; - $scope.addEnvironmentVariable = function () { - $scope.formValues.Env.push({ name: '', value: '' }); - }; - - $scope.removeEnvironmentVariable = function (index) { - $scope.formValues.Env.splice(index, 1); - }; - $scope.addPlacementConstraint = function () { $scope.formValues.PlacementConstraints.push({ key: '', operator: '==', value: '' }); }; @@ -277,13 +276,7 @@ angular.module('portainer.docker').controller('CreateServiceController', [ } function prepareEnvConfig(config, input) { - var env = []; - input.Env.forEach(function (v) { - if (v.name) { - env.push(v.name + '=' + v.value); - } - }); - config.TaskTemplate.ContainerSpec.Env = env; + config.TaskTemplate.ContainerSpec.Env = envVarsUtils.convertToArrayOfStrings(input.Env); } function prepareLabelsConfig(config, input) { diff --git a/app/docker/views/services/create/createservice.html b/app/docker/views/services/create/createservice.html index 3e2be2cba..45a0a9f16 100644 --- a/app/docker/views/services/create/createservice.html +++ b/app/docker/views/services/create/createservice.html @@ -160,6 +160,7 @@
  • Command & Logging
  • Volumes
  • Network
  • +
  • Env
  • Labels
  • Update config & Restart
  • Secrets
  • @@ -202,34 +203,6 @@
    - -
    -
    - - - add environment variable - -
    - -
    -
    -
    - name - -
    -
    - value - -
    - -
    -
    - -
    - -
    Logging
    @@ -443,6 +416,15 @@ + +
    + +
    +
    diff --git a/app/docker/views/services/edit/includes/environmentvariables.html b/app/docker/views/services/edit/includes/environmentvariables.html index 795c8d77a..20b0acdd6 100644 --- a/app/docker/views/services/edit/includes/environmentvariables.html +++ b/app/docker/views/services/edit/includes/environmentvariables.html @@ -1,8 +1,8 @@ -
    + @@ -10,49 +10,20 @@

    There are no environment variables for this service.

    - - - - - - - - - - - - - - -
    NameValue
    -
    - name - -
    -
    -
    - value - - - - -
    -
    + + +
    diff --git a/app/docker/views/services/edit/serviceController.js b/app/docker/views/services/edit/serviceController.js index cb41d94d8..f1aef75a4 100644 --- a/app/docker/views/services/edit/serviceController.js +++ b/app/docker/views/services/edit/serviceController.js @@ -18,6 +18,9 @@ require('./includes/tasks.html'); require('./includes/updateconfig.html'); import _ from 'lodash-es'; + +import * as envVarsUtils from '@/portainer/helpers/env-vars'; + import { PorImageRegistryModel } from 'Docker/models/porImageRegistry'; angular.module('portainer.docker').controller('ServiceController', [ @@ -114,21 +117,25 @@ angular.module('portainer.docker').controller('ServiceController', [ }; $scope.addEnvironmentVariable = function addEnvironmentVariable(service) { - service.EnvironmentVariables.push({ key: '', value: '', originalValue: '' }); + service.EnvironmentVariables.push({ name: '', value: '' }); updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables); }; - $scope.removeEnvironmentVariable = function removeEnvironmentVariable(service, item) { - const index = service.EnvironmentVariables.indexOf(item); - const removedElement = service.EnvironmentVariables.splice(index, 1); - if (removedElement !== null) { - updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables); - } - }; - $scope.updateEnvironmentVariable = function updateEnvironmentVariable(service, variable) { - if (variable.value !== variable.originalValue || variable.key !== variable.originalKey) { - updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables); - } - }; + + $scope.onChangeEnvVars = onChangeEnvVars; + + function onChangeEnvVars(env) { + const service = $scope.service; + + const orgEnv = service.EnvironmentVariables; + service.EnvironmentVariables = env.map((v) => { + const orgVar = orgEnv.find(({ name }) => v.name === name); + const added = orgVar && orgVar.added; + return { ...v, added }; + }); + + updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables); + } + $scope.addConfig = function addConfig(service, config) { if ( config && @@ -395,7 +402,7 @@ angular.module('portainer.docker').controller('ServiceController', [ var config = ServiceHelper.serviceToConfig(service.Model); config.Name = service.Name; config.Labels = LabelHelper.fromKeyValueToLabelHash(service.ServiceLabels); - config.TaskTemplate.ContainerSpec.Env = ServiceHelper.translateEnvironmentVariablesToEnv(service.EnvironmentVariables); + config.TaskTemplate.ContainerSpec.Env = envVarsUtils.convertToArrayOfStrings(service.EnvironmentVariables); config.TaskTemplate.ContainerSpec.Labels = LabelHelper.fromKeyValueToLabelHash(service.ServiceContainerLabels); if ($scope.hasChanges(service, ['Image'])) { @@ -625,7 +632,10 @@ angular.module('portainer.docker').controller('ServiceController', [ function translateServiceArrays(service) { service.ServiceSecrets = service.Secrets ? service.Secrets.map(SecretHelper.flattenSecret) : []; service.ServiceConfigs = service.Configs ? service.Configs.map(ConfigHelper.flattenConfig) : []; - service.EnvironmentVariables = ServiceHelper.translateEnvironmentVariables(service.Env); + service.EnvironmentVariables = envVarsUtils + .parseArrayOfStrings(service.Env) + .map((v) => ({ ...v, added: true })) + .sort((v1, v2) => (v1.name > v2.name ? 1 : -1)); service.LogDriverOpts = ServiceHelper.translateLogDriverOptsToKeyValue(service.LogDriverOpts); service.ServiceLabels = LabelHelper.fromLabelHashToKeyValue(service.Labels); service.ServiceContainerLabels = LabelHelper.fromLabelHashToKeyValue(service.ContainerLabels); diff --git a/app/portainer/components/environment-variables-panel/environment-variables-panel.controller.js b/app/portainer/components/environment-variables-panel/environment-variables-panel.controller.js new file mode 100644 index 000000000..9f842e40e --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-panel.controller.js @@ -0,0 +1,34 @@ +import { parseDotEnvFile, convertToArrayOfStrings } from '@/portainer/helpers/env-vars'; + +export default class EnvironmentVariablesPanelController { + /* @ngInject */ + constructor() { + this.mode = 'simple'; + this.editorText = ''; + + this.switchEnvMode = this.switchEnvMode.bind(this); + this.editorUpdate = this.editorUpdate.bind(this); + this.handleSimpleChange = this.handleSimpleChange.bind(this); + } + + switchEnvMode() { + if (this.mode === 'simple') { + const editorText = convertToArrayOfStrings(this.ngModel).join('\n'); + + this.editorText = editorText; + + this.mode = 'advanced'; + } else { + this.mode = 'simple'; + } + } + + handleSimpleChange(value) { + this.onChange(value); + } + + editorUpdate(cm) { + this.editorText = cm.getValue(); + this.onChange(parseDotEnvFile(this.editorText)); + } +} diff --git a/app/portainer/components/environment-variables-panel/environment-variables-panel.css b/app/portainer/components/environment-variables-panel/environment-variables-panel.css new file mode 100644 index 000000000..84b30444e --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-panel.css @@ -0,0 +1,11 @@ +.environment-variables-panel { + margin-top: 15px; +} + +.environment-variables-panel--explanation { + margin-bottom: 5px; +} + +.environment-variables-panel--advanced > * + * { + margin-top: 5px; +} diff --git a/app/portainer/components/environment-variables-panel/environment-variables-panel.html b/app/portainer/components/environment-variables-panel/environment-variables-panel.html new file mode 100644 index 000000000..c84f37ff2 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-panel.html @@ -0,0 +1,30 @@ + +
    +
    + Environment variables +
    +
    + {{::$ctrl.explanation}} +
    + + + +
    + +
    + + Switch to simple mode to define variables line by line, or load from .env file +
    +
    + +
    +
    +
    +
    diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.controller.js b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.controller.js new file mode 100644 index 000000000..2138e4f57 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.controller.js @@ -0,0 +1,41 @@ +import { KEY_REGEX, VALUE_REGEX } from '@/portainer/helpers/env-vars'; + +class EnvironmentVariablesSimpleModeItemController { + /* @ngInject */ + constructor() { + this.KEY_REGEX = KEY_REGEX; + this.VALUE_REGEX = VALUE_REGEX; + } + + onChangeName(name) { + const fieldIsInvalid = typeof name === 'undefined'; + if (fieldIsInvalid) { + return; + } + + this.onChange(this.index, { ...this.variable, name }); + } + + onChangeValue(value) { + const fieldIsInvalid = typeof value === 'undefined'; + if (fieldIsInvalid) { + return; + } + + this.onChange(this.index, { ...this.variable, value }); + } + + hasValue() { + return typeof this.variable.value !== 'undefined'; + } + + removeValue() { + this.onChange(this.index, { name: this.variable.name }); + } + + $onInit() { + this.formName = `variableForm${this.index}`; + } +} + +export default EnvironmentVariablesSimpleModeItemController; diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.html b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.html new file mode 100644 index 000000000..c53af0699 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.html @@ -0,0 +1,63 @@ + +
    +
    + name + +
    +
    +
    +
    +

    Name is required.

    +

    + + This field must consist alphanumeric characters, '-' or '_', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-var', or 'MY_VAR123'). +

    +
    +
    +
    +
    +
    +
    +
    + value + +
    + +
    +
    +
    +
    +

    Value is required.

    +

    Value is invalid.

    +
    +
    +
    +
    +
    + + +
    +
    diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/index.js b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/index.js new file mode 100644 index 000000000..b95b3b7db --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/index.js @@ -0,0 +1,17 @@ +import angular from 'angular'; +import controller from './environment-variables-simple-mode-item.controller.js'; + +export const environmentVariablesSimpleModeItem = { + templateUrl: './environment-variables-simple-mode-item.html', + controller, + + bindings: { + variable: '<', + index: '<', + + onChange: '<', + onRemove: '<', + }, +}; + +angular.module('portainer.app').component('environmentVariablesSimpleModeItem', environmentVariablesSimpleModeItem); diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.controller.js b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.controller.js new file mode 100644 index 000000000..813911832 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.controller.js @@ -0,0 +1,43 @@ +import { parseDotEnvFile } from '@/portainer/helpers/env-vars'; + +export default class EnvironmentVariablesSimpleModeController { + /* @ngInject */ + constructor($async) { + this.$async = $async; + + this.onChangeVariable = this.onChangeVariable.bind(this); + this.remove = this.remove.bind(this); + } + + add() { + this.onChange([...this.ngModel, { name: '', value: '' }]); + } + + remove(index) { + this.onChange(this.ngModel.filter((_, i) => i !== index)); + } + + addFromFile(file) { + return this.$async(async () => { + if (!file) { + return; + } + const text = await this.getTextFromFile(file); + const parsed = parseDotEnvFile(text); + this.onChange(this.ngModel.concat(parsed)); + }); + } + + getTextFromFile(file) { + return new Promise((resolve, reject) => { + const temporaryFileReader = new FileReader(); + temporaryFileReader.readAsText(file); + temporaryFileReader.onload = (event) => resolve(event.target.result); + temporaryFileReader.onerror = (error) => reject(error); + }); + } + + onChangeVariable(index, variable) { + this.onChange(this.ngModel.map((v, i) => (i !== index ? v : variable))); + } +} diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.css b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.css new file mode 100644 index 000000000..155c9684a --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.css @@ -0,0 +1,33 @@ +.advanced-actions > * + * { + margin-top: 5px; +} + +.environment-variables-simple-mode--actions { + display: flex; + align-items: center; + margin-left: 10px; + margin-top: 10px; +} + +.env-items-list { + margin-top: 10px; +} + +.env-items-list > * + * { + margin-top: 2px; +} + +.env-items-list .env-item { + display: flex; +} + +.env-item .env-item-key { +} + +.env-item .env-item-value { + display: flex; +} + +.env-item .env-item-value .input-group { + flex: 1; +} diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.html b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.html new file mode 100644 index 000000000..66a6f0fe4 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.html @@ -0,0 +1,36 @@ +
    + +
    + + Switch to advanced mode to copy & paste multiple variables +
    +
    + + + + + File too large! Try uploading a file smaller than 1MB + +
    +
    + +
    +
    diff --git a/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/index.js b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/index.js new file mode 100644 index 000000000..d51f2c11d --- /dev/null +++ b/app/portainer/components/environment-variables-panel/environment-variables-simple-mode/index.js @@ -0,0 +1,15 @@ +import angular from 'angular'; + +import './environment-variables-simple-mode.css'; + +import controller from './environment-variables-simple-mode.controller'; + +angular.module('portainer.app').component('environmentVariablesSimpleMode', { + templateUrl: './environment-variables-simple-mode.html', + controller, + bindings: { + ngModel: '<', + onSwitchModeClick: '<', + onChange: '<', + }, +}); diff --git a/app/portainer/components/environment-variables-panel/index.js b/app/portainer/components/environment-variables-panel/index.js new file mode 100644 index 000000000..9da15a489 --- /dev/null +++ b/app/portainer/components/environment-variables-panel/index.js @@ -0,0 +1,15 @@ +import angular from 'angular'; + +import './environment-variables-panel.css'; + +import controller from './environment-variables-panel.controller.js'; + +angular.module('portainer.app').component('environmentVariablesPanel', { + templateUrl: './environment-variables-panel.html', + controller, + bindings: { + ngModel: '<', + explanation: '@', + onChange: '<', + }, +}); diff --git a/app/portainer/filters/filters.js b/app/portainer/filters/filters.js index f31705979..4ed03afc7 100644 --- a/app/portainer/filters/filters.js +++ b/app/portainer/filters/filters.js @@ -79,12 +79,20 @@ angular .filter('key', function () { 'use strict'; return function (pair, separator) { + if (!pair.includes(separator)) { + return pair; + } + return pair.slice(0, pair.indexOf(separator)); }; }) .filter('value', function () { 'use strict'; return function (pair, separator) { + if (!pair.includes(separator)) { + return ''; + } + return pair.slice(pair.indexOf(separator) + 1); }; }) diff --git a/app/portainer/helpers/env-vars.js b/app/portainer/helpers/env-vars.js new file mode 100644 index 000000000..972fc5ebd --- /dev/null +++ b/app/portainer/helpers/env-vars.js @@ -0,0 +1,61 @@ +import _ from 'lodash-es'; + +export const KEY_REGEX = /[a-zA-Z]([-_a-zA-Z0-9]*[a-zA-Z0-9])?/.source; + +export const VALUE_REGEX = /(.*)?/.source; + +const KEY_VALUE_REGEX = new RegExp(`^(${KEY_REGEX})\\s*=(${VALUE_REGEX})$`); +const NEWLINES_REGEX = /\n|\r|\r\n/; + +/** + * @param {string} src the source of the .env file + * + * @returns {[{name: string, value: string}]} array of {name, value} + */ +export function parseDotEnvFile(src) { + return parseArrayOfStrings( + _.compact(src.split(NEWLINES_REGEX)) + .map((v) => v.trim()) + .filter((v) => !v.startsWith('#')) + ); +} + +/** + * parses an array of name=value to array of {name, value} + * + * @param {[string]} array array of strings in format name=value + * + * @returns {[{name: string, value: string}]} array of {name, value} + */ +export function parseArrayOfStrings(array) { + if (!array) { + return []; + } + + return _.compact( + array.map((variableString) => { + if (!variableString.includes('=')) { + return { name: variableString }; + } + + const parsedKeyValArr = variableString.trim().match(KEY_VALUE_REGEX); + if (parsedKeyValArr != null && parsedKeyValArr.length > 4) { + return { name: parsedKeyValArr[1], value: parsedKeyValArr[3] || '' }; + } + }) + ); +} +/** + * converts an array of {name, value} to array of `name=value`, name is always defined + * + * @param {[{name, value}]} array array of {name, value} + * + * @returns {[string]} array of `name=value` + */ +export function convertToArrayOfStrings(array) { + if (!array) { + return []; + } + + return array.filter((variable) => variable.name).map(({ name, value }) => (value || value === '' ? `${name}=${value}` : name)); +} diff --git a/app/portainer/views/stacks/create/createStackController.js b/app/portainer/views/stacks/create/createStackController.js index 76a932e67..f632101ea 100644 --- a/app/portainer/views/stacks/create/createStackController.js +++ b/app/portainer/views/stacks/create/createStackController.js @@ -53,14 +53,6 @@ angular } }; - $scope.addEnvironmentVariable = function () { - $scope.formValues.Env.push({ name: '', value: '' }); - }; - - $scope.removeEnvironmentVariable = function (index) { - $scope.formValues.Env.splice(index, 1); - }; - function validateForm(accessControlData, isAdmin) { $scope.state.formValidationError = ''; var error = ''; @@ -123,6 +115,11 @@ angular } } + $scope.handleEnvVarChange = handleEnvVarChange; + function handleEnvVarChange(value) { + $scope.formValues.Env = value; + } + $scope.deployStack = function () { var name = $scope.formValues.Name; var method = $scope.state.Method; diff --git a/app/portainer/views/stacks/create/createstack.html b/app/portainer/views/stacks/create/createstack.html index 6719edfec..4edb6cf50 100644 --- a/app/portainer/views/stacks/create/createstack.html +++ b/app/portainer/views/stacks/create/createstack.html @@ -7,7 +7,7 @@
    - +
    @@ -261,36 +261,8 @@
    -
    -
    - Environment -
    -
    -
    - - - add environment variable - -
    - -
    -
    -
    - name - -
    -
    - value - -
    - -
    -
    - -
    -
    + + @@ -303,6 +275,7 @@ type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress + || !createStackForm.$valid || (state.Method === 'editor' && (!formValues.StackFileContent || state.editorYamlValidationError)) || (state.Method === 'upload' && (!formValues.StackFile || state.uploadYamlValidationError)) || (state.Method === 'template' && (!formValues.StackFileContent || !selectedTemplate || state.editorYamlValidationError)) diff --git a/app/portainer/views/stacks/edit/stack.html b/app/portainer/views/stacks/edit/stack.html index 707bf5c2e..1981b613d 100644 --- a/app/portainer/views/stacks/edit/stack.html +++ b/app/portainer/views/stacks/edit/stack.html @@ -126,7 +126,7 @@ Editor - +
    This stack will be deployed using the equivalent of docker-compose. Only Compose file format version 2 is supported at the moment. @@ -152,34 +152,11 @@
    -
    - Environment -
    -
    -
    - - - add environment variable - -
    - -
    -
    -
    - name - -
    -
    - value - -
    - -
    -
    - -
    +
    @@ -207,7 +184,7 @@