mirror of https://github.com/portainer/portainer
feat(edge): add EnvVar to stack details [EE-5463] (#9036)
parent
1a9a564553
commit
f5e09618f0
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue