mirror of
https://github.com/portainer/portainer.git
synced 2025-08-03 04:45:21 +02:00
feat(edge/stacks): sync EE codechanges [EE-498] (#8580)
This commit is contained in:
parent
0ec7dfce69
commit
93bf630105
53 changed files with 1572 additions and 424 deletions
|
@ -169,15 +169,7 @@
|
|||
</div>
|
||||
<!-- !upload -->
|
||||
|
||||
<div class="col-sm-12 form-section-title"> Edge Groups </div>
|
||||
<div class="form-group" ng-if="$ctrl.edgeGroups">
|
||||
<div class="col-sm-12">
|
||||
<edge-groups-selector ng-if="!$ctrl.noGroups" value="$ctrl.model.EdgeGroups" on-change="($ctrl.onChangeGroups)" items="$ctrl.edgeGroups"></edge-groups-selector>
|
||||
</div>
|
||||
<div ng-if="$ctrl.noGroups" class="col-sm-12 small text-muted">
|
||||
No Edge groups are available. Head over to the <a ui-sref="edge.groups">Edge groups view</a> to create one.
|
||||
</div>
|
||||
</div>
|
||||
<edge-groups-selector ng-if="$ctrl.model.EdgeGroups" value="$ctrl.model.EdgeGroups" on-change="($ctrl.onChangeGroups)"></edge-groups-selector>
|
||||
|
||||
<div class="col-sm-12 form-section-title"> Target environments </div>
|
||||
<!-- node-selection -->
|
||||
|
|
|
@ -6,11 +6,9 @@ import { cronMethodOptions } from '@/react/edge/edge-jobs/CreateView/cron-method
|
|||
|
||||
export class EdgeJobFormController {
|
||||
/* @ngInject */
|
||||
constructor($async, $scope, EdgeGroupService, Notifications) {
|
||||
constructor($async, $scope) {
|
||||
this.$scope = $scope;
|
||||
this.$async = $async;
|
||||
this.EdgeGroupService = EdgeGroupService;
|
||||
this.Notifications = Notifications;
|
||||
|
||||
this.cronMethods = cronMethodOptions;
|
||||
this.buildMethods = [editor, upload];
|
||||
|
@ -127,18 +125,8 @@ export class EdgeJobFormController {
|
|||
this.model.Endpoints = _.filter(this.model.Endpoints, (id) => id !== endpoint.Id);
|
||||
}
|
||||
|
||||
async getEdgeGroups() {
|
||||
try {
|
||||
this.edgeGroups = await this.EdgeGroupService.groups();
|
||||
this.noGroups = this.edgeGroups.length === 0;
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to retrieve Edge groups');
|
||||
}
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.onChangeModel(this.model);
|
||||
this.getEdgeGroups();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
<form class="form-horizontal">
|
||||
<edge-groups-selector value="$ctrl.model.EdgeGroups" items="$ctrl.edgeGroups" on-change="($ctrl.onChangeGroups)"></edge-groups-selector>
|
||||
|
||||
<edge-stack-deployment-type-selector
|
||||
allow-kube-to-select-compose="$ctrl.allowKubeToSelectCompose"
|
||||
value="$ctrl.model.DeploymentType"
|
||||
has-docker-endpoint="$ctrl.hasDockerEndpoint()"
|
||||
has-kube-endpoint="$ctrl.hasKubeEndpoint()"
|
||||
on-change="($ctrl.onChangeDeploymentType)"
|
||||
read-only="$ctrl.state.readOnlyCompose"
|
||||
></edge-stack-deployment-type-selector>
|
||||
|
||||
<div class="text-muted small flex gap-1" ng-show="!$ctrl.model.DeploymentType && $ctrl.hasKubeEndpoint()">
|
||||
<pr-icon icon="'alert-circle'" mode="'warning'" class-name="'!mt-1'"></pr-icon>
|
||||
<div>
|
||||
<p>
|
||||
Portainer no longer supports <a href="https://docs.docker.com/compose/compose-file/" target="_blank">docker-compose</a> format manifests for Kubernetes deployments, and we
|
||||
have removed the <a href="https://kompose.io/" target="_blank">Kompose</a> conversion tool which enables this. The reason for this is because Kompose now poses a security
|
||||
risk, since it has a number of Common Vulnerabilities and Exposures (CVEs).
|
||||
</p>
|
||||
<p
|
||||
>Unfortunately, while the Kompose project has a maintainer and is part of the CNCF, it is not being actively maintained. Releases are very infrequent and new pull requests
|
||||
to the project (including ones we've submitted) are taking months to be merged, with new CVEs arising in the meantime.</p
|
||||
>
|
||||
<p>
|
||||
We advise installing your own instance of Kompose in a sandbox environment, performing conversions of your Docker Compose files to Kubernetes manifests and using those
|
||||
manifests to set up applications.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<web-editor-form
|
||||
ng-if="$ctrl.model.DeploymentType === $ctrl.EditorType.Compose"
|
||||
value="$ctrl.model.StackFileContent"
|
||||
yml="true"
|
||||
identifier="compose-editor"
|
||||
placeholder="Define or paste the content of your docker compose file here"
|
||||
on-change="($ctrl.onChangeComposeConfig)"
|
||||
read-only="$ctrl.hasKubeEndpoint()"
|
||||
>
|
||||
<editor-description>
|
||||
<div>
|
||||
You can get more information about Compose file format in the
|
||||
<a href="https://docs.docker.com/compose/compose-file/" target="_blank"> official documentation </a>
|
||||
.
|
||||
</div>
|
||||
</editor-description>
|
||||
</web-editor-form>
|
||||
|
||||
<div ng-if="$ctrl.model.DeploymentType === $ctrl.EditorType.Kubernetes">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<por-switch-field
|
||||
label="'Use namespace(s) specified from manifest'"
|
||||
tooltip="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'"
|
||||
checked="$ctrl.formValues.UseManifestNamespaces"
|
||||
on-change="($ctrl.onChangeUseManifestNamespaces)"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<web-editor-form
|
||||
value="$ctrl.model.StackFileContent"
|
||||
yml="true"
|
||||
identifier="kube-manifest-editor"
|
||||
placeholder="Define or paste the content of your manifest here"
|
||||
on-change="($ctrl.onChangeKubeManifest)"
|
||||
>
|
||||
<editor-description>
|
||||
<p>
|
||||
You can get more information about Kubernetes file format in the
|
||||
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
|
||||
</p>
|
||||
</editor-description>
|
||||
</web-editor-form>
|
||||
</div>
|
||||
|
||||
<!-- actions -->
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-disabled="$ctrl.actionInProgress || !$ctrl.isFormValid() || (!$ctrl.model.DeploymentType && $ctrl.hasKubeEndpoint())"
|
||||
ng-click="$ctrl.submitAction()"
|
||||
button-spinner="$ctrl.actionInProgress"
|
||||
>
|
||||
<span ng-hide="$ctrl.actionInProgress">Update the stack</span>
|
||||
<span ng-show="$ctrl.actionInProgress">Update in progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
|
@ -1,116 +0,0 @@
|
|||
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
||||
import { EditorType } from '@/react/edge/edge-stacks/types';
|
||||
import { getValidEditorTypes } from '@/react/edge/edge-stacks/utils';
|
||||
export class EditEdgeStackFormController {
|
||||
/* @ngInject */
|
||||
constructor($scope) {
|
||||
this.$scope = $scope;
|
||||
this.state = {
|
||||
endpointTypes: [],
|
||||
readOnlyCompose: false,
|
||||
};
|
||||
|
||||
this.fileContents = {
|
||||
0: '',
|
||||
1: '',
|
||||
};
|
||||
|
||||
this.EditorType = EditorType;
|
||||
|
||||
this.onChangeGroups = this.onChangeGroups.bind(this);
|
||||
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
||||
this.onChangeComposeConfig = this.onChangeComposeConfig.bind(this);
|
||||
this.onChangeKubeManifest = this.onChangeKubeManifest.bind(this);
|
||||
this.hasDockerEndpoint = this.hasDockerEndpoint.bind(this);
|
||||
this.hasKubeEndpoint = this.hasKubeEndpoint.bind(this);
|
||||
this.onChangeDeploymentType = this.onChangeDeploymentType.bind(this);
|
||||
this.removeLineBreaks = this.removeLineBreaks.bind(this);
|
||||
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
||||
this.onChangeUseManifestNamespaces = this.onChangeUseManifestNamespaces.bind(this);
|
||||
this.selectValidDeploymentType = this.selectValidDeploymentType.bind(this);
|
||||
}
|
||||
|
||||
onChangeUseManifestNamespaces(value) {
|
||||
this.$scope.$evalAsync(() => {
|
||||
this.model.UseManifestNamespaces = value;
|
||||
});
|
||||
}
|
||||
|
||||
hasKubeEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment);
|
||||
}
|
||||
|
||||
hasDockerEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnDockerEnvironment);
|
||||
}
|
||||
|
||||
onChangeGroups(groups) {
|
||||
return this.$scope.$evalAsync(() => {
|
||||
this.model.EdgeGroups = groups;
|
||||
this.setEnvironmentTypesInSelection(groups);
|
||||
this.selectValidDeploymentType();
|
||||
this.state.readOnlyCompose = this.hasKubeEndpoint();
|
||||
});
|
||||
}
|
||||
|
||||
isFormValid() {
|
||||
return this.model.EdgeGroups.length && this.model.StackFileContent && this.validateEndpointsForDeployment();
|
||||
}
|
||||
|
||||
setEnvironmentTypesInSelection(groups) {
|
||||
const edgeGroups = groups.map((id) => this.edgeGroups.find((e) => e.Id === id));
|
||||
this.state.endpointTypes = edgeGroups.flatMap((group) => group.EndpointTypes);
|
||||
}
|
||||
|
||||
selectValidDeploymentType() {
|
||||
const validTypes = getValidEditorTypes(this.state.endpointTypes, this.allowKubeToSelectCompose);
|
||||
|
||||
if (!validTypes.includes(this.model.DeploymentType)) {
|
||||
this.onChangeDeploymentType(validTypes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
removeLineBreaks(value) {
|
||||
return value.replace(/(\r\n|\n|\r)/gm, '');
|
||||
}
|
||||
|
||||
onChangeFileContent(type, value) {
|
||||
const oldValue = this.fileContents[type];
|
||||
if (this.removeLineBreaks(oldValue) !== this.removeLineBreaks(value)) {
|
||||
this.model.StackFileContent = value;
|
||||
this.fileContents[type] = value;
|
||||
this.isEditorDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
onChangeKubeManifest(value) {
|
||||
this.onChangeFileContent(1, value);
|
||||
}
|
||||
|
||||
onChangeComposeConfig(value) {
|
||||
this.onChangeFileContent(0, value);
|
||||
}
|
||||
|
||||
onChangeDeploymentType(deploymentType) {
|
||||
return this.$scope.$evalAsync(() => {
|
||||
this.model.DeploymentType = deploymentType;
|
||||
this.model.StackFileContent = this.fileContents[deploymentType];
|
||||
});
|
||||
}
|
||||
|
||||
validateEndpointsForDeployment() {
|
||||
return this.model.DeploymentType == 0 || !this.hasDockerEndpoint();
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.setEnvironmentTypesInSelection(this.model.EdgeGroups);
|
||||
this.fileContents[this.model.DeploymentType] = this.model.StackFileContent;
|
||||
|
||||
// allow kube to view compose if it's an existing kube compose stack
|
||||
const initiallyContainsKubeEnv = this.hasKubeEndpoint();
|
||||
const isComposeStack = this.model.DeploymentType === 0;
|
||||
this.allowKubeToSelectCompose = initiallyContainsKubeEnv && isComposeStack;
|
||||
this.state.readOnlyCompose = this.allowKubeToSelectCompose;
|
||||
this.selectValidDeploymentType();
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { EditEdgeStackFormController } from './editEdgeStackFormController';
|
||||
|
||||
angular.module('portainer.edge').component('editEdgeStackForm', {
|
||||
templateUrl: './editEdgeStackForm.html',
|
||||
controller: EditEdgeStackFormController,
|
||||
bindings: {
|
||||
model: '<',
|
||||
actionInProgress: '<',
|
||||
submitAction: '<',
|
||||
edgeGroups: '<',
|
||||
isEditorDirty: '=',
|
||||
},
|
||||
});
|
|
@ -7,6 +7,8 @@ import { EdgeCheckinIntervalField } from '@/react/edge/components/EdgeCheckInInt
|
|||
import { EdgeScriptForm } from '@/react/edge/components/EdgeScriptForm';
|
||||
import { EdgeAsyncIntervalsForm } from '@/react/edge/components/EdgeAsyncIntervalsForm';
|
||||
import { EdgeStackDeploymentTypeSelector } from '@/react/edge/edge-stacks/components/EdgeStackDeploymentTypeSelector';
|
||||
import { EditEdgeStackForm } from '@/react/edge/edge-stacks/ItemView/EditEdgeStackForm/EditEdgeStackForm';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
|
||||
export const componentsModule = angular
|
||||
|
@ -60,6 +62,18 @@ export const componentsModule = angular
|
|||
'onChange',
|
||||
'hasDockerEndpoint',
|
||||
'hasKubeEndpoint',
|
||||
'hasNomadEndpoint',
|
||||
'allowKubeToSelectCompose',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'editEdgeStackForm',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(EditEdgeStackForm))), [
|
||||
'edgeStack',
|
||||
'fileContent',
|
||||
'isSubmitting',
|
||||
'onEditorChange',
|
||||
'onSubmit',
|
||||
'allowKubeToSelectCompose',
|
||||
])
|
||||
).name;
|
||||
|
|
|
@ -65,9 +65,5 @@ angular.module('portainer.edge').factory('EdgeStackService', function EdgeStackS
|
|||
}
|
||||
};
|
||||
|
||||
service.update = function update(stack) {
|
||||
return EdgeStacks.update(stack).$promise;
|
||||
};
|
||||
|
||||
return service;
|
||||
});
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { EditorType } from '@/react/edge/edge-stacks/types';
|
||||
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
||||
import { getValidEditorTypes } from '@/react/edge/edge-stacks/utils';
|
||||
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';
|
||||
|
||||
export default class CreateEdgeStackViewController {
|
||||
/* @ngInject */
|
||||
|
@ -27,6 +28,7 @@ export default class CreateEdgeStackViewController {
|
|||
};
|
||||
|
||||
this.EditorType = EditorType;
|
||||
this.EnvironmentType = EnvironmentType;
|
||||
|
||||
this.state = {
|
||||
Method: 'editor',
|
||||
|
@ -36,6 +38,7 @@ export default class CreateEdgeStackViewController {
|
|||
isEditorDirty: false,
|
||||
hasKubeEndpoint: false,
|
||||
endpointTypes: [],
|
||||
baseWebhookUrl: baseEdgeStackWebhookUrl(),
|
||||
};
|
||||
|
||||
this.edgeGroups = null;
|
||||
|
@ -49,8 +52,7 @@ export default class CreateEdgeStackViewController {
|
|||
this.createStackFromFileUpload = this.createStackFromFileUpload.bind(this);
|
||||
this.createStackFromGitRepository = this.createStackFromGitRepository.bind(this);
|
||||
this.onChangeGroups = this.onChangeGroups.bind(this);
|
||||
this.hasDockerEndpoint = this.hasDockerEndpoint.bind(this);
|
||||
this.hasKubeEndpoint = this.hasKubeEndpoint.bind(this);
|
||||
this.hasType = this.hasType.bind(this);
|
||||
this.onChangeDeploymentType = this.onChangeDeploymentType.bind(this);
|
||||
}
|
||||
|
||||
|
@ -139,9 +141,11 @@ export default class CreateEdgeStackViewController {
|
|||
}
|
||||
|
||||
checkIfEndpointTypes(groups) {
|
||||
const edgeGroups = groups.map((id) => this.edgeGroups.find((e) => e.Id === id));
|
||||
this.state.endpointTypes = edgeGroups.flatMap((group) => group.EndpointTypes);
|
||||
this.selectValidDeploymentType();
|
||||
return this.$scope.$evalAsync(() => {
|
||||
const edgeGroups = groups.map((id) => this.edgeGroups.find((e) => e.Id === id));
|
||||
this.state.endpointTypes = edgeGroups.flatMap((group) => group.EndpointTypes);
|
||||
this.selectValidDeploymentType();
|
||||
});
|
||||
}
|
||||
|
||||
selectValidDeploymentType() {
|
||||
|
@ -152,12 +156,8 @@ export default class CreateEdgeStackViewController {
|
|||
}
|
||||
}
|
||||
|
||||
hasKubeEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment);
|
||||
}
|
||||
|
||||
hasDockerEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnDockerEnvironment);
|
||||
hasType(envType) {
|
||||
return this.state.endpointTypes.includes(envType);
|
||||
}
|
||||
|
||||
validateForm(method) {
|
||||
|
|
|
@ -39,12 +39,18 @@
|
|||
</div>
|
||||
<!-- !name-input -->
|
||||
|
||||
<edge-groups-selector value="$ctrl.formValues.Groups" on-change="($ctrl.onChangeGroups)" items="$ctrl.edgeGroups"></edge-groups-selector>
|
||||
<edge-groups-selector ng-if="$ctrl.formValues.Groups" value="$ctrl.formValues.Groups" on-change="($ctrl.onChangeGroups)"></edge-groups-selector>
|
||||
|
||||
<p class="col-sm-12 vertical-center help-block small text-warning" ng-if="$ctrl.formValues.DeploymentType === undefined">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> There are no available deployment types when there is more than one type of environment in your edge group
|
||||
selection (e.g. Kubernetes and Docker environments). Please select edge groups that have environments of the same type.
|
||||
</p>
|
||||
|
||||
<edge-stack-deployment-type-selector
|
||||
value="$ctrl.formValues.DeploymentType"
|
||||
has-docker-endpoint="$ctrl.hasDockerEndpoint()"
|
||||
has-kube-endpoint="$ctrl.hasKubeEndpoint()"
|
||||
has-docker-endpoint="$ctrl.hasType($ctrl.EnvironmentType.EdgeAgentOnDocker)"
|
||||
has-kube-endpoint="$ctrl.hasType($ctrl.EnvironmentType.EdgeAgentOnKubernetes)"
|
||||
has-nomad-endpoint="$ctrl.hasType($ctrl.EnvironmentType.EdgeAgentOnNomad)"
|
||||
on-change="($ctrl.onChangeDeploymentType)"
|
||||
></edge-stack-deployment-type-selector>
|
||||
|
||||
|
|
|
@ -21,7 +21,14 @@
|
|||
<file-upload-description> You can upload a Compose file from your computer. </file-upload-description>
|
||||
</file-upload-form>
|
||||
|
||||
<git-form ng-if="$ctrl.state.Method === 'repository'" value="$ctrl.formValues" on-change="($ctrl.onChangeFormValues)"></git-form>
|
||||
<git-form
|
||||
ng-if="$ctrl.state.Method === 'repository'"
|
||||
value="$ctrl.formValues"
|
||||
on-change="($ctrl.onChangeFormValues)"
|
||||
base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}"
|
||||
webhook-id="{{ $ctrl.state.webhookId }}"
|
||||
docs-links
|
||||
></git-form>
|
||||
|
||||
<!-- template -->
|
||||
<div ng-if="$ctrl.state.Method === 'template'">
|
||||
|
|
|
@ -32,4 +32,11 @@
|
|||
</file-upload-description>
|
||||
</file-upload-form>
|
||||
|
||||
<git-form ng-if="$ctrl.state.Method === 'repository'" deploy-method="kubernetes" value="$ctrl.formValues" on-change="($ctrl.onChangeFormValues)"></git-form>
|
||||
<git-form
|
||||
ng-if="$ctrl.state.Method === 'repository'"
|
||||
deploy-method="kubernetes"
|
||||
value="$ctrl.formValues"
|
||||
on-change="($ctrl.onChangeFormValues)"
|
||||
base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}"
|
||||
webhook-id="{{ $ctrl.state.webhookId }}"
|
||||
></git-form>
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
|
||||
<div style="padding: 20px">
|
||||
<edit-edge-stack-form
|
||||
ng-if="$ctrl.edgeGroups && $ctrl.stack && $ctrl.formValues.content"
|
||||
edge-groups="$ctrl.edgeGroups"
|
||||
model="$ctrl.formValues"
|
||||
action-in-progress="$ctrl.state.actionInProgress"
|
||||
submit-action="$ctrl.deployStack"
|
||||
is-editor-dirty="$ctrl.state.isEditorDirty"
|
||||
edge-stack="$ctrl.stack"
|
||||
is-submitting="$ctrl.state.actionInProgress"
|
||||
on-submit="($ctrl.deployStack)"
|
||||
on-editor-change="($ctrl.onEditorChange)"
|
||||
file-content="$ctrl.formValues.content"
|
||||
allow-kube-to-select-compose="$ctrl.allowKubeToSelectCompose"
|
||||
></edit-edge-stack-form>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import _ from 'lodash-es';
|
||||
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
||||
import { confirmWebEditorDiscard } from '@@/modals/confirm';
|
||||
import { EnvironmentType } from '@/react/portainer/environments/types';
|
||||
import { createWebhookId } from '@/portainer/helpers/webhookHelper';
|
||||
|
||||
export class EditEdgeStackViewController {
|
||||
/* @ngInject */
|
||||
|
@ -18,56 +20,74 @@ export class EditEdgeStackViewController {
|
|||
this.state = {
|
||||
actionInProgress: false,
|
||||
activeTab: 0,
|
||||
isEditorDirty: false,
|
||||
isStackDeployed: false,
|
||||
};
|
||||
|
||||
this.formValues = {
|
||||
content: '',
|
||||
};
|
||||
|
||||
this.deployStack = this.deployStack.bind(this);
|
||||
this.deployStackAsync = this.deployStackAsync.bind(this);
|
||||
this.getPaginatedEndpoints = this.getPaginatedEndpoints.bind(this);
|
||||
this.getPaginatedEndpointsAsync = this.getPaginatedEndpointsAsync.bind(this);
|
||||
this.onEditorChange = this.onEditorChange.bind(this);
|
||||
this.isEditorDirty = this.isEditorDirty.bind(this);
|
||||
}
|
||||
|
||||
async $onInit() {
|
||||
const { stackId, tab } = this.$state.params;
|
||||
this.state.activeTab = tab;
|
||||
try {
|
||||
const [edgeGroups, model, file] = await Promise.all([this.EdgeGroupService.groups(), this.EdgeStackService.stack(stackId), this.EdgeStackService.stackFile(stackId)]);
|
||||
this.edgeGroups = edgeGroups;
|
||||
this.stack = model;
|
||||
this.stackEndpointIds = this.filterStackEndpoints(model.EdgeGroups, edgeGroups);
|
||||
this.originalFileContent = file;
|
||||
this.formValues = {
|
||||
StackFileContent: file,
|
||||
EdgeGroups: this.stack.EdgeGroups,
|
||||
UseManifestNamespaces: this.stack.UseManifestNamespaces,
|
||||
DeploymentType: this.stack.DeploymentType,
|
||||
};
|
||||
this.oldFileContent = this.formValues.StackFileContent;
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to retrieve stack data');
|
||||
}
|
||||
return this.$async(async () => {
|
||||
const { stackId, tab } = this.$state.params;
|
||||
this.state.activeTab = tab;
|
||||
try {
|
||||
const [edgeGroups, model, file] = await Promise.all([this.EdgeGroupService.groups(), this.EdgeStackService.stack(stackId), this.EdgeStackService.stackFile(stackId)]);
|
||||
|
||||
this.$window.onbeforeunload = () => {
|
||||
if (this.formValues.StackFileContent !== this.oldFileContent && this.state.isEditorDirty) {
|
||||
return '';
|
||||
this.edgeGroups = edgeGroups;
|
||||
this.stack = model;
|
||||
this.stackEndpointIds = this.filterStackEndpoints(model.EdgeGroups, edgeGroups);
|
||||
this.originalFileContent = file;
|
||||
this.formValues = {
|
||||
content: file,
|
||||
};
|
||||
|
||||
const stackEdgeGroups = model.EdgeGroups.map((id) => this.edgeGroups.find((e) => e.Id === id));
|
||||
const endpointTypes = stackEdgeGroups.flatMap((group) => group.EndpointTypes);
|
||||
const initiallyContainsKubeEnv = endpointTypes.includes(EnvironmentType.EdgeAgentOnKubernetes);
|
||||
const isComposeStack = this.stack.DeploymentType === 0;
|
||||
|
||||
this.allowKubeToSelectCompose = initiallyContainsKubeEnv && isComposeStack;
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to retrieve stack data');
|
||||
}
|
||||
};
|
||||
|
||||
this.oldFileContent = this.formValues.StackFileContent;
|
||||
|
||||
this.$window.onbeforeunload = () => {
|
||||
if (this.isEditorDirty()) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
$onDestroy() {
|
||||
this.state.isEditorDirty = false;
|
||||
this.$window.onbeforeunload = undefined;
|
||||
}
|
||||
|
||||
async uiCanExit() {
|
||||
if (
|
||||
this.formValues.StackFileContent &&
|
||||
this.formValues.StackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== this.oldFileContent.replace(/(\r\n|\n|\r)/gm, '') &&
|
||||
this.state.isEditorDirty
|
||||
) {
|
||||
if (this.isEditorDirty()) {
|
||||
return confirmWebEditorDiscard();
|
||||
}
|
||||
}
|
||||
|
||||
onEditorChange(content) {
|
||||
this.formValues.content = content;
|
||||
}
|
||||
|
||||
isEditorDirty() {
|
||||
return !this.state.isStackDeployed && this.formValues.content.replace(/(\r\n|\n|\r)/gm, '') !== this.originalFileContent.replace(/(\r\n|\n|\r)/gm, '');
|
||||
}
|
||||
|
||||
filterStackEndpoints(groupIds, groups) {
|
||||
return _.flatten(
|
||||
_.map(groupIds, (Id) => {
|
||||
|
@ -77,19 +97,24 @@ export class EditEdgeStackViewController {
|
|||
);
|
||||
}
|
||||
|
||||
deployStack() {
|
||||
return this.$async(this.deployStackAsync);
|
||||
deployStack(values) {
|
||||
return this.deployStackAsync(values);
|
||||
}
|
||||
|
||||
async deployStackAsync() {
|
||||
async deployStackAsync(values) {
|
||||
this.state.actionInProgress = true;
|
||||
try {
|
||||
if (this.originalFileContent != this.formValues.StackFileContent || this.formValues.UseManifestNamespaces !== this.stack.UseManifestNamespaces) {
|
||||
this.formValues.Version = this.stack.Version + 1;
|
||||
}
|
||||
await this.EdgeStackService.updateStack(this.stack.Id, this.formValues);
|
||||
const updateVersion = !!(this.originalFileContent != values.content || values.useManifestNamespaces !== this.stack.UseManifestNamespaces);
|
||||
|
||||
await this.EdgeStackService.updateStack(this.stack.Id, {
|
||||
stackFileContent: values.content,
|
||||
edgeGroups: values.edgeGroups,
|
||||
deploymentType: values.deploymentType,
|
||||
updateVersion,
|
||||
webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '',
|
||||
});
|
||||
this.Notifications.success('Success', 'Stack successfully deployed');
|
||||
this.state.isEditorDirty = false;
|
||||
this.state.isStackDeployed = true;
|
||||
this.$state.go('edge.stacks');
|
||||
} catch (err) {
|
||||
this.Notifications.error('Deployment error', err, 'Unable to deploy stack');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue