feat(edge): add EnvVar to stack details [EE-5463] (#9036)

pull/9150/head
Chaim Lev-Ari 2023-07-04 11:14:35 +07:00 committed by GitHub
parent 1a9a564553
commit f5e09618f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 59 additions and 6 deletions

View File

@ -1,6 +1,9 @@
package edge package edge
import "github.com/portainer/portainer/api/filesystem" import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/filesystem"
)
type ( type (
@ -40,6 +43,9 @@ type (
SupportRelativePath bool SupportRelativePath bool
// Mount point for relative path // Mount point for relative path
FilesystemPath string 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. // RegistryCredentials holds the credentials for a Docker registry.

View File

@ -4,6 +4,7 @@ import { STACK_NAME_VALIDATION_REGEX } from '@/react/constants';
import { confirmWebEditorDiscard } from '@@/modals/confirm'; import { confirmWebEditorDiscard } from '@@/modals/confirm';
import { baseEdgeStackWebhookUrl } from '@/portainer/helpers/webhookHelper'; import { baseEdgeStackWebhookUrl } from '@/portainer/helpers/webhookHelper';
import { EnvironmentType } from '@/react/portainer/environments/types'; import { EnvironmentType } from '@/react/portainer/environments/types';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
export default class CreateEdgeStackViewController { export default class CreateEdgeStackViewController {
/* @ngInject */ /* @ngInject */
@ -19,16 +20,17 @@ export default class CreateEdgeStackViewController {
RepositoryAuthentication: false, RepositoryAuthentication: false,
RepositoryUsername: '', RepositoryUsername: '',
RepositoryPassword: '', RepositoryPassword: '',
Env: [],
ComposeFilePathInRepository: '', ComposeFilePathInRepository: '',
Groups: [], Groups: [],
DeploymentType: 0, DeploymentType: 0,
UseManifestNamespaces: false, UseManifestNamespaces: false,
TLSSkipVerify: false, TLSSkipVerify: false,
envVars: [],
}; };
this.EditorType = EditorType; this.EditorType = EditorType;
this.EnvironmentType = EnvironmentType; this.EnvironmentType = EnvironmentType;
this.isBE = isBE;
this.state = { this.state = {
Method: 'editor', Method: 'editor',
@ -54,6 +56,13 @@ export default class CreateEdgeStackViewController {
this.onChangeGroups = this.onChangeGroups.bind(this); this.onChangeGroups = this.onChangeGroups.bind(this);
this.hasType = this.hasType.bind(this); this.hasType = this.hasType.bind(this);
this.onChangeDeploymentType = this.onChangeDeploymentType.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() { buildAnalyticsProperties() {
@ -183,7 +192,7 @@ export default class CreateEdgeStackViewController {
} }
createStackFromFileContent(name) { createStackFromFileContent(name) {
const { StackFileContent, Groups, DeploymentType, UseManifestNamespaces } = this.formValues; const { StackFileContent, Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues;
return this.EdgeStackService.createStackFromFileContent({ return this.EdgeStackService.createStackFromFileContent({
name, name,
@ -191,24 +200,26 @@ export default class CreateEdgeStackViewController {
EdgeGroups: Groups, EdgeGroups: Groups,
DeploymentType, DeploymentType,
UseManifestNamespaces, UseManifestNamespaces,
envVars,
}); });
} }
createStackFromFileUpload(name) { createStackFromFileUpload(name) {
const { StackFile, Groups, DeploymentType, UseManifestNamespaces } = this.formValues; const { StackFile, Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues;
return this.EdgeStackService.createStackFromFileUpload( return this.EdgeStackService.createStackFromFileUpload(
{ {
Name: name, Name: name,
EdgeGroups: Groups, EdgeGroups: Groups,
DeploymentType, DeploymentType,
UseManifestNamespaces, UseManifestNamespaces,
envVars,
}, },
StackFile StackFile
); );
} }
createStackFromGitRepository(name) { createStackFromGitRepository(name) {
const { Groups, DeploymentType, UseManifestNamespaces } = this.formValues; const { Groups, DeploymentType, UseManifestNamespaces, envVars } = this.formValues;
const repositoryOptions = { const repositoryOptions = {
RepositoryURL: this.formValues.RepositoryURL, RepositoryURL: this.formValues.RepositoryURL,
RepositoryReferenceName: this.formValues.RepositoryReferenceName, RepositoryReferenceName: this.formValues.RepositoryReferenceName,
@ -224,6 +235,7 @@ export default class CreateEdgeStackViewController {
EdgeGroups: Groups, EdgeGroups: Groups,
DeploymentType, DeploymentType,
UseManifestNamespaces, UseManifestNamespaces,
envVars,
}, },
repositoryOptions repositoryOptions
); );

View File

@ -66,6 +66,10 @@
state="$ctrl.state" state="$ctrl.state"
></edge-stacks-kube-manifest-form> ></edge-stacks-kube-manifest-form>
<div ng-if="$ctrl.isBE">
<environment-variables-panel values="$ctrl.formValues.envVars" on-change="($ctrl.onEnvVarChange)"></environment-variables-panel>
</div>
<!-- actions --> <!-- actions -->
<div class="col-sm-12 form-section-title"> Actions </div> <div class="col-sm-12 form-section-title"> Actions </div>
<div class="form-group"> <div class="form-group">

View File

@ -102,6 +102,7 @@ export class EditEdgeStackViewController {
deploymentType: values.deploymentType, deploymentType: values.deploymentType,
updateVersion, updateVersion,
webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '', webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '',
envVars: values.envVars,
}); });
this.Notifications.success('Success', 'Stack successfully deployed'); this.Notifications.success('Success', 'Stack successfully deployed');
this.state.isStackDeployed = true; this.state.isStackDeployed = true;

View File

@ -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({ return Upload.upload({
url: `api/edge_stacks/create/file`, url: `api/edge_stacks/create/file`,
data: { data: {
file, file,
EdgeGroups: Upload.json(EdgeGroups), EdgeGroups: Upload.json(EdgeGroups),
EnvVars: Upload.json(envVars),
...payload, ...payload,
}, },
ignoreLoadingBar: true, ignoreLoadingBar: true,

View File

@ -15,6 +15,10 @@ import { TextTip } from '@@/Tip/TextTip';
import { SwitchField } from '@@/form-components/SwitchField'; import { SwitchField } from '@@/form-components/SwitchField';
import { LoadingButton } from '@@/buttons'; import { LoadingButton } from '@@/buttons';
import { FormError } from '@@/form-components/FormError'; import { FormError } from '@@/form-components/FormError';
import {
EnvironmentVariablesPanel,
envVarValidation,
} from '@@/form-components/EnvironmentVariablesFieldset';
import { PrivateRegistryFieldsetWrapper } from './PrivateRegistryFieldsetWrapper'; import { PrivateRegistryFieldsetWrapper } from './PrivateRegistryFieldsetWrapper';
import { FormValues } from './types'; import { FormValues } from './types';
@ -61,6 +65,7 @@ export function EditEdgeStackForm({
prePullImage: edgeStack.PrePullImage, prePullImage: edgeStack.PrePullImage,
retryDeploy: edgeStack.RetryDeploy, retryDeploy: edgeStack.RetryDeploy,
webhookEnabled: !!edgeStack.Webhook, webhookEnabled: !!edgeStack.Webhook,
envVars: edgeStack.EnvVars || [],
}; };
return ( return (
@ -181,6 +186,13 @@ function InnerForm({
onFieldError={(error) => setFieldError('privateRegistryId', error)} onFieldError={(error) => setFieldError('privateRegistryId', error)}
error={errors.privateRegistryId} error={errors.privateRegistryId}
/> />
<EnvironmentVariablesPanel
onChange={(value) => setFieldValue('envVars', value)}
values={values.envVars}
errors={errors.envVars}
/>
{values.deploymentType === DeploymentType.Compose && ( {values.deploymentType === DeploymentType.Compose && (
<> <>
<div className="form-group"> <div className="form-group">
@ -271,5 +283,6 @@ function formValidation(): SchemaOf<FormValues> {
.required() .required()
.min(1, 'At least one edge group is required'), .min(1, 'At least one edge group is required'),
webhookEnabled: boolean().default(false), webhookEnabled: boolean().default(false),
envVars: envVarValidation(),
}); });
} }

View File

@ -31,6 +31,8 @@ import { LoadingButton } from '@@/buttons';
import { FormSection } from '@@/form-components/FormSection'; import { FormSection } from '@@/form-components/FormSection';
import { TextTip } from '@@/Tip/TextTip'; import { TextTip } from '@@/Tip/TextTip';
import { FormError } from '@@/form-components/FormError'; import { FormError } from '@@/form-components/FormError';
import { EnvironmentVariablesPanel } from '@@/form-components/EnvironmentVariablesFieldset';
import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types';
import { useValidateEnvironmentTypes } from '../useEdgeGroupHasType'; import { useValidateEnvironmentTypes } from '../useEdgeGroupHasType';
import { atLeastTwo } from '../atLeastTwo'; import { atLeastTwo } from '../atLeastTwo';
@ -43,6 +45,7 @@ interface FormValues {
autoUpdate: AutoUpdateModel; autoUpdate: AutoUpdateModel;
refName: string; refName: string;
authentication: GitAuthModel; authentication: GitAuthModel;
envVars: EnvVar[];
} }
export function GitForm({ stack }: { stack: EdgeStack }) { export function GitForm({ stack }: { stack: EdgeStack }) {
@ -63,6 +66,7 @@ export function GitForm({ stack }: { stack: EdgeStack }) {
autoUpdate: parseAutoUpdateResponse(stack.AutoUpdate), autoUpdate: parseAutoUpdateResponse(stack.AutoUpdate),
refName: stack.GitConfig.ReferenceName, refName: stack.GitConfig.ReferenceName,
authentication: parseAuthResponse(stack.GitConfig.Authentication), authentication: parseAuthResponse(stack.GitConfig.Authentication),
envVars: stack.EnvVars || [],
}; };
const webhookId = stack.AutoUpdate?.Webhook || createWebhookId(); const webhookId = stack.AutoUpdate?.Webhook || createWebhookId();
@ -253,6 +257,12 @@ function InnerForm({
} }
errors={errors.authentication} errors={errors.authentication}
/> />
<EnvironmentVariablesPanel
onChange={(value) => setFieldValue('envVars', value)}
values={values.envVars}
errors={errors.envVars}
/>
</FormSection> </FormSection>
<FormSection title="Actions"> <FormSection title="Actions">

View File

@ -1,6 +1,8 @@
import { EdgeGroup } from '@/react/edge/edge-groups/types'; import { EdgeGroup } from '@/react/edge/edge-groups/types';
import { DeploymentType } from '@/react/edge/edge-stacks/types'; import { DeploymentType } from '@/react/edge/edge-stacks/types';
import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types';
export interface FormValues { export interface FormValues {
edgeGroups: EdgeGroup['Id'][]; edgeGroups: EdgeGroup['Id'][];
deploymentType: DeploymentType; deploymentType: DeploymentType;
@ -10,4 +12,5 @@ export interface FormValues {
prePullImage: boolean; prePullImage: boolean;
retryDeploy: boolean; retryDeploy: boolean;
webhookEnabled: boolean; webhookEnabled: boolean;
envVars: EnvVar[];
} }

View File

@ -5,6 +5,8 @@ import {
} from '@/react/portainer/gitops/types'; } from '@/react/portainer/gitops/types';
import { RegistryId } from '@/react/portainer/registries/types'; import { RegistryId } from '@/react/portainer/registries/types';
import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types';
import { EdgeGroup } from '../edge-groups/types'; import { EdgeGroup } from '../edge-groups/types';
interface EdgeStackStatusDetails { interface EdgeStackStatusDetails {
@ -57,6 +59,7 @@ export type EdgeStack = {
Prune: boolean; Prune: boolean;
RetryDeploy: boolean; RetryDeploy: boolean;
Webhook?: string; Webhook?: string;
EnvVars?: EnvVar[];
}; };
export enum EditorType { export enum EditorType {