From f5e09618f028b71239092553319356e3d35aac1b Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 4 Jul 2023 11:14:35 +0700 Subject: [PATCH] feat(edge): add EnvVar to stack details [EE-5463] (#9036) --- api/edge/edge.go | 8 +++++++- .../create-edge-stack-view.controller.js | 20 +++++++++++++++---- .../create-edge-stack-view.html | 4 ++++ .../editEdgeStackViewController.js | 1 + app/portainer/services/fileUpload.js | 3 ++- .../EditEdgeStackForm/EditEdgeStackForm.tsx | 13 ++++++++++++ .../EditEdgeStackForm/GitForm/GitForm.tsx | 10 ++++++++++ .../ItemView/EditEdgeStackForm/types.ts | 3 +++ app/react/edge/edge-stacks/types.ts | 3 +++ 9 files changed, 59 insertions(+), 6 deletions(-) diff --git a/api/edge/edge.go b/api/edge/edge.go index ebeee170f..864fbf39a 100644 --- a/api/edge/edge.go +++ b/api/edge/edge.go @@ -1,6 +1,9 @@ package edge -import "github.com/portainer/portainer/api/filesystem" +import ( + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" +) type ( @@ -40,6 +43,9 @@ type ( SupportRelativePath bool // Mount point for relative path FilesystemPath string + // Used only for EE + // EnvVars is a list of environment variables to inject into the stack + EnvVars []portainer.Pair } // RegistryCredentials holds the credentials for a Docker registry. diff --git a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js index 44047571c..f17755926 100644 --- a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js +++ b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.controller.js @@ -4,6 +4,7 @@ import { STACK_NAME_VALIDATION_REGEX } from '@/react/constants'; import { confirmWebEditorDiscard } from '@@/modals/confirm'; import { baseEdgeStackWebhookUrl } from '@/portainer/helpers/webhookHelper'; import { EnvironmentType } from '@/react/portainer/environments/types'; +import { isBE } from '@/react/portainer/feature-flags/feature-flags.service'; export default class CreateEdgeStackViewController { /* @ngInject */ @@ -19,16 +20,17 @@ export default class CreateEdgeStackViewController { RepositoryAuthentication: false, RepositoryUsername: '', RepositoryPassword: '', - Env: [], ComposeFilePathInRepository: '', Groups: [], DeploymentType: 0, UseManifestNamespaces: false, TLSSkipVerify: false, + envVars: [], }; this.EditorType = EditorType; this.EnvironmentType = EnvironmentType; + this.isBE = isBE; this.state = { Method: 'editor', @@ -54,6 +56,13 @@ export default class CreateEdgeStackViewController { this.onChangeGroups = this.onChangeGroups.bind(this); this.hasType = this.hasType.bind(this); this.onChangeDeploymentType = this.onChangeDeploymentType.bind(this); + this.onEnvVarChange = this.onEnvVarChange.bind(this); + } + + onEnvVarChange(envVars) { + return this.$scope.$evalAsync(() => { + this.formValues.envVars = envVars; + }); } buildAnalyticsProperties() { @@ -183,7 +192,7 @@ export default class CreateEdgeStackViewController { } createStackFromFileContent(name) { - const { StackFileContent, Groups, DeploymentType, UseManifestNamespaces } = this.formValues; + const { StackFileContent, Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues; return this.EdgeStackService.createStackFromFileContent({ name, @@ -191,24 +200,26 @@ export default class CreateEdgeStackViewController { EdgeGroups: Groups, DeploymentType, UseManifestNamespaces, + envVars, }); } createStackFromFileUpload(name) { - const { StackFile, Groups, DeploymentType, UseManifestNamespaces } = this.formValues; + const { StackFile, Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues; return this.EdgeStackService.createStackFromFileUpload( { Name: name, EdgeGroups: Groups, DeploymentType, UseManifestNamespaces, + envVars, }, StackFile ); } createStackFromGitRepository(name) { - const { Groups, DeploymentType, UseManifestNamespaces } = this.formValues; + const { Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues; const repositoryOptions = { RepositoryURL: this.formValues.RepositoryURL, RepositoryReferenceName: this.formValues.RepositoryReferenceName, @@ -224,6 +235,7 @@ export default class CreateEdgeStackViewController { EdgeGroups: Groups, DeploymentType, UseManifestNamespaces, + envVars, }, repositoryOptions ); diff --git a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.html b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.html index 63b8bdab5..234057816 100644 --- a/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.html +++ b/app/edge/views/edge-stacks/createEdgeStackView/create-edge-stack-view.html @@ -66,6 +66,10 @@ state="$ctrl.state" > +
+ +
+
Actions
diff --git a/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js b/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js index bf9c27043..503cfa0ec 100644 --- a/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js +++ b/app/edge/views/edge-stacks/editEdgeStackView/editEdgeStackViewController.js @@ -102,6 +102,7 @@ export class EditEdgeStackViewController { deploymentType: values.deploymentType, updateVersion, webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '', + envVars: values.envVars, }); this.Notifications.success('Success', 'Stack successfully deployed'); this.state.isStackDeployed = true; diff --git a/app/portainer/services/fileUpload.js b/app/portainer/services/fileUpload.js index 37653f41c..bc9664c3f 100644 --- a/app/portainer/services/fileUpload.js +++ b/app/portainer/services/fileUpload.js @@ -111,12 +111,13 @@ angular.module('portainer.app').factory('FileUploadService', [ }); }; - service.createEdgeStack = function createEdgeStack({ EdgeGroups, ...payload }, file) { + service.createEdgeStack = function createEdgeStack({ EdgeGroups, envVars, ...payload }, file) { return Upload.upload({ url: `api/edge_stacks/create/file`, data: { file, EdgeGroups: Upload.json(EdgeGroups), + EnvVars: Upload.json(envVars), ...payload, }, ignoreLoadingBar: true, diff --git a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/EditEdgeStackForm.tsx b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/EditEdgeStackForm.tsx index 0bae932ac..5681d0c77 100644 --- a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/EditEdgeStackForm.tsx +++ b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/EditEdgeStackForm.tsx @@ -15,6 +15,10 @@ import { TextTip } from '@@/Tip/TextTip'; import { SwitchField } from '@@/form-components/SwitchField'; import { LoadingButton } from '@@/buttons'; import { FormError } from '@@/form-components/FormError'; +import { + EnvironmentVariablesPanel, + envVarValidation, +} from '@@/form-components/EnvironmentVariablesFieldset'; import { PrivateRegistryFieldsetWrapper } from './PrivateRegistryFieldsetWrapper'; import { FormValues } from './types'; @@ -61,6 +65,7 @@ export function EditEdgeStackForm({ prePullImage: edgeStack.PrePullImage, retryDeploy: edgeStack.RetryDeploy, webhookEnabled: !!edgeStack.Webhook, + envVars: edgeStack.EnvVars || [], }; return ( @@ -181,6 +186,13 @@ function InnerForm({ onFieldError={(error) => setFieldError('privateRegistryId', error)} error={errors.privateRegistryId} /> + + setFieldValue('envVars', value)} + values={values.envVars} + errors={errors.envVars} + /> + {values.deploymentType === DeploymentType.Compose && ( <>
@@ -271,5 +283,6 @@ function formValidation(): SchemaOf { .required() .min(1, 'At least one edge group is required'), webhookEnabled: boolean().default(false), + envVars: envVarValidation(), }); } diff --git a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/GitForm/GitForm.tsx b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/GitForm/GitForm.tsx index 4726ce1a0..5c44a820b 100644 --- a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/GitForm/GitForm.tsx +++ b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/GitForm/GitForm.tsx @@ -31,6 +31,8 @@ import { LoadingButton } from '@@/buttons'; import { FormSection } from '@@/form-components/FormSection'; import { TextTip } from '@@/Tip/TextTip'; import { FormError } from '@@/form-components/FormError'; +import { EnvironmentVariablesPanel } from '@@/form-components/EnvironmentVariablesFieldset'; +import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types'; import { useValidateEnvironmentTypes } from '../useEdgeGroupHasType'; import { atLeastTwo } from '../atLeastTwo'; @@ -43,6 +45,7 @@ interface FormValues { autoUpdate: AutoUpdateModel; refName: string; authentication: GitAuthModel; + envVars: EnvVar[]; } export function GitForm({ stack }: { stack: EdgeStack }) { @@ -63,6 +66,7 @@ export function GitForm({ stack }: { stack: EdgeStack }) { autoUpdate: parseAutoUpdateResponse(stack.AutoUpdate), refName: stack.GitConfig.ReferenceName, authentication: parseAuthResponse(stack.GitConfig.Authentication), + envVars: stack.EnvVars || [], }; const webhookId = stack.AutoUpdate?.Webhook || createWebhookId(); @@ -253,6 +257,12 @@ function InnerForm({ } errors={errors.authentication} /> + + setFieldValue('envVars', value)} + values={values.envVars} + errors={errors.envVars} + /> diff --git a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/types.ts b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/types.ts index 85b6328cd..25401b674 100644 --- a/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/types.ts +++ b/app/react/edge/edge-stacks/ItemView/EditEdgeStackForm/types.ts @@ -1,6 +1,8 @@ import { EdgeGroup } from '@/react/edge/edge-groups/types'; import { DeploymentType } from '@/react/edge/edge-stacks/types'; +import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types'; + export interface FormValues { edgeGroups: EdgeGroup['Id'][]; deploymentType: DeploymentType; @@ -10,4 +12,5 @@ export interface FormValues { prePullImage: boolean; retryDeploy: boolean; webhookEnabled: boolean; + envVars: EnvVar[]; } diff --git a/app/react/edge/edge-stacks/types.ts b/app/react/edge/edge-stacks/types.ts index 5d1ac61ae..88ab61ff7 100644 --- a/app/react/edge/edge-stacks/types.ts +++ b/app/react/edge/edge-stacks/types.ts @@ -5,6 +5,8 @@ import { } from '@/react/portainer/gitops/types'; import { RegistryId } from '@/react/portainer/registries/types'; +import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types'; + import { EdgeGroup } from '../edge-groups/types'; interface EdgeStackStatusDetails { @@ -57,6 +59,7 @@ export type EdgeStack = { Prune: boolean; RetryDeploy: boolean; Webhook?: string; + EnvVars?: EnvVar[]; }; export enum EditorType {