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').
This field must consist of alphabetic characters, digits, '_', '-', or '.', and must
+ not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1'.
+
+
+
+ Portainer was unable to retrieve any metrics associated to that node. Please contact your administrator to ensure that the Kubernetes metrics feature is properly configured.
+
+
+
Date: Mon, 14 Jun 2021 18:59:07 +1200
Subject: [PATCH 06/10] feat(UX): introduce new env variables UI (#4175)
* feat(app): introduce new env vars ui
feat(app): introduce new env vars ui
feat(UX): WIP new env variables UI
feat(UX): update button and placeholder
feat(UX): mention .env file in message
feat(UX): allow add/remove value & load correctly
feat(UX): restrict filesize to 1MB
feat(UX): vertical align error message
feat(UX): fill UI from file & when switching modes
feat(UX): strip un-needed newline character
feat(UX): introduce component to other views
feat(UX): fix title alignment
feat(UX): only populate editor on mode switch when key exists
feat(UX): prevent trimming of whitespace on values
feat(UX): change editor to async
feat(UX): add message describing use
feat(UX): Refactor variable text to editorText
refactor(app): rename env vars controller
refactor(app): move env var explanation to parent
refactor(app): order env var panels
refactor(app): move simple env vars mode to component
refactor(app): parse env vars
refactor(app): move styles to css
refactor(app): rename functions
refactor(container): parse env vars
refactor(env-vars): move utils to helper module
refactor(env-vars): use util function for parse dot env file
fix(env-vars): ignore comments
refactor(services): use env vars utils
refactor(env-vars): rename files
refactor(env-panel): use utils
style(stack): revert EnvContent to Env
style(service): revert EnvContent to Env
style(container): revert EnvContent to Env
refactor(env-vars): support default value
refactor(service): use new env var component
refactor(env-var): use one way data flow
refactor(containers): remove unused function
* fix(env-vars): prevent using non .env files
* refactor(env-vars): move env vars items to a component
* feat(app): fixed env vars form validation in Stack
* feat(services): disable env form submit if invalid
* fix(app): show key pairs correctly
* fix(env-var): use the same validation as with kubernetes
* fix(env-vars): parse env var
Co-authored-by: Chaim Lev-Ari
Co-authored-by: Felix Han
---
app/docker/helpers/serviceHelper.js | 33 ----------
.../create/createContainerController.js | 34 ++++------
.../containers/create/createcontainer.html | 36 ++---------
.../create/createServiceController.js | 23 +++----
.../views/services/create/createservice.html | 38 +++--------
.../edit/includes/environmentvariables.html | 55 ++++------------
.../views/services/edit/serviceController.js | 40 +++++++-----
.../environment-variables-panel.controller.js | 34 ++++++++++
.../environment-variables-panel.css | 11 ++++
.../environment-variables-panel.html | 30 +++++++++
...t-variables-simple-mode-item.controller.js | 41 ++++++++++++
...nvironment-variables-simple-mode-item.html | 63 +++++++++++++++++++
.../index.js | 17 +++++
...onment-variables-simple-mode.controller.js | 43 +++++++++++++
.../environment-variables-simple-mode.css | 33 ++++++++++
.../environment-variables-simple-mode.html | 36 +++++++++++
.../index.js | 15 +++++
.../environment-variables-panel/index.js | 15 +++++
app/portainer/filters/filters.js | 8 +++
app/portainer/helpers/env-vars.js | 61 ++++++++++++++++++
.../stacks/create/createStackController.js | 13 ++--
.../views/stacks/create/createstack.html | 35 ++---------
app/portainer/views/stacks/edit/stack.html | 37 +++--------
.../views/stacks/edit/stackController.js | 20 +++---
24 files changed, 506 insertions(+), 265 deletions(-)
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-panel.controller.js
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-panel.css
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-panel.html
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.controller.js
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/environment-variables-simple-mode-item.html
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode-item/index.js
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.controller.js
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.css
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/environment-variables-simple-mode.html
create mode 100644 app/portainer/components/environment-variables-panel/environment-variables-simple-mode/index.js
create mode 100644 app/portainer/components/environment-variables-panel/index.js
create mode 100644 app/portainer/helpers/env-vars.js
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
-
-
-
-
-
-
-
-
-
Logging
@@ -443,6 +416,15 @@
+
+
+
+
+
-
-
-
- Git repository
-
-
-
- You can use the URL of a git repository.
-
-
-
-
-
-
-
-
-
-
- Specify a reference of the repository using the following syntax: branches with refs/heads/branch_name or tags with refs/tags/tag_name. If
- not specified, will use the default HEAD reference normally the master branch.
-
-
-
-
-
-
-
-
-
-
- Indicate the path to the Compose file from the root of your repository.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- If your git account has 2FA enabled, you may receive an authentication required error when deploying your stack. In this case, you will need to provide
- a personal-access token instead of your password.
-
-
+
Date: Wed, 16 Jun 2021 07:28:44 +0200
Subject: [PATCH 10/10] feat(k8s/advanced-deployment): allow standard users to
see and use advanced deployment feature EE-446 (#5050)
---
api/http/handler/stacks/stack_create.go | 8 ++------
api/internal/endpointutils/endpointutils.go | 14 ++++++++++++++
.../templates/advancedDeploymentPanel.html | 2 +-
.../views/applications/applications.html | 2 +-
.../views/configurations/configurations.html | 2 +-
app/kubernetes/views/volumes/volumes.html | 2 +-
6 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/api/http/handler/stacks/stack_create.go b/api/http/handler/stacks/stack_create.go
index eae955cf5..1919993dd 100644
--- a/api/http/handler/stacks/stack_create.go
+++ b/api/http/handler/stacks/stack_create.go
@@ -14,9 +14,9 @@ import (
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/bolt/errors"
gittypes "github.com/portainer/portainer/api/git/types"
- httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
+ "github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/stackutils"
)
@@ -78,7 +78,7 @@ func (handler *Handler) stackCreate(w http.ResponseWriter, r *http.Request) *htt
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
- if !endpoint.SecuritySettings.AllowStackManagementForRegularUsers {
+ if endpointutils.IsDockerEndpoint(endpoint) && !endpoint.SecuritySettings.AllowStackManagementForRegularUsers {
securityContext, err := security.RetrieveRestrictedRequestContext(r)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve user info from request context", err}
@@ -112,10 +112,6 @@ func (handler *Handler) stackCreate(w http.ResponseWriter, r *http.Request) *htt
case portainer.DockerComposeStack:
return handler.createComposeStack(w, r, method, endpoint, tokenData.ID)
case portainer.KubernetesStack:
- if tokenData.Role != portainer.AdministratorRole {
- return &httperror.HandlerError{http.StatusForbidden, "Access denied", httperrors.ErrUnauthorized}
- }
-
return handler.createKubernetesStack(w, r, endpoint)
}
diff --git a/api/internal/endpointutils/endpointutils.go b/api/internal/endpointutils/endpointutils.go
index 249ee11cb..48c2c5fd1 100644
--- a/api/internal/endpointutils/endpointutils.go
+++ b/api/internal/endpointutils/endpointutils.go
@@ -9,3 +9,17 @@ import (
func IsLocalEndpoint(endpoint *portainer.Endpoint) bool {
return strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://") || endpoint.Type == 5
}
+
+// IsKubernetesEndpoint returns true if this is a kubernetes endpoint
+func IsKubernetesEndpoint(endpoint *portainer.Endpoint) bool {
+ return endpoint.Type == portainer.KubernetesLocalEnvironment ||
+ endpoint.Type == portainer.AgentOnKubernetesEnvironment ||
+ endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment
+}
+
+// IsDockerEndpoint returns true if this is a docker endpoint
+func IsDockerEndpoint(endpoint *portainer.Endpoint) bool {
+ return endpoint.Type == portainer.DockerEnvironment ||
+ endpoint.Type == portainer.AgentOnDockerEnvironment ||
+ endpoint.Type == portainer.EdgeAgentOnDockerEnvironment
+}
diff --git a/app/kubernetes/templates/advancedDeploymentPanel.html b/app/kubernetes/templates/advancedDeploymentPanel.html
index 1072dc615..34044cbf8 100644
--- a/app/kubernetes/templates/advancedDeploymentPanel.html
+++ b/app/kubernetes/templates/advancedDeploymentPanel.html
@@ -2,7 +2,7 @@
- As an administrator user, you have access to the advanced deployment feature allowing you to deploy any Kubernetes manifest inside your cluster.
+ Advanced deployment allows you to deploy any Kubernetes manifest inside your cluster.