mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
feat(edgestacks): support kubernetes edge stacks (#5276) [EE-393]
This commit is contained in:
parent
79ca51c92e
commit
5c8450c4c0
56 changed files with 1466 additions and 521 deletions
|
@ -1,4 +1,12 @@
|
|||
<ui-select multiple ng-model="$ctrl.model" close-on-select="false" data-cy="edgeGroupCreate-edgeGroupsSelector">
|
||||
<!-- on-select/on-remove are called with model because ui-select uses 2-way-binding -->
|
||||
<ui-select
|
||||
multiple
|
||||
ng-model="$ctrl.model"
|
||||
close-on-select="false"
|
||||
on-select="$ctrl.onChange($ctrl.model)"
|
||||
on-remove="$ctrl.onChange($ctrl.model)"
|
||||
data-cy="edgeGroupCreate-edgeGroupsSelector"
|
||||
>
|
||||
<ui-select-match placeholder="Select one or multiple group(s)">
|
||||
<span>
|
||||
{{ $item.Name }}
|
||||
|
|
|
@ -3,7 +3,8 @@ import angular from 'angular';
|
|||
angular.module('portainer.edge').component('edgeGroupsSelector', {
|
||||
templateUrl: './edgeGroupsSelector.html',
|
||||
bindings: {
|
||||
model: '=',
|
||||
model: '<',
|
||||
items: '<',
|
||||
onChange: '<',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
export default class EdgeStackDeploymentTypeSelectorController {
|
||||
/* @ngInject */
|
||||
constructor() {
|
||||
this.deploymentOptions = [
|
||||
{ id: 'deployment_compose', icon: 'fab fa-docker', label: 'Compose', description: 'docker-compose format', value: 0 },
|
||||
{
|
||||
id: 'deployment_kube',
|
||||
icon: 'fa fa-cubes',
|
||||
label: 'Kubernetes',
|
||||
description: 'Kubernetes manifest format',
|
||||
value: 1,
|
||||
disabled: () => {
|
||||
return this.hasDockerEndpoint();
|
||||
},
|
||||
tooltip: () => {
|
||||
return this.hasDockerEndpoint() ? 'Cannot use this option with Edge Docker endpoints' : '';
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Deployment type
|
||||
</div>
|
||||
<box-selector radio-name="deploymentType" ng-model="$ctrl.value" options="$ctrl.deploymentOptions" on-change="($ctrl.onChange)"></box-selector>
|
|
@ -0,0 +1,15 @@
|
|||
import angular from 'angular';
|
||||
import controller from './edge-stack-deployment-type-selector.controller.js';
|
||||
|
||||
export const edgeStackDeploymentTypeSelector = {
|
||||
templateUrl: './edge-stack-deployment-type-selector.html',
|
||||
controller,
|
||||
|
||||
bindings: {
|
||||
value: '<',
|
||||
onChange: '<',
|
||||
hasDockerEndpoint: '<',
|
||||
},
|
||||
};
|
||||
|
||||
angular.module('portainer.edge').component('edgeStackDeploymentTypeSelector', edgeStackDeploymentTypeSelector);
|
|
@ -4,52 +4,70 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<edge-groups-selector model="$ctrl.model.EdgeGroups" items="$ctrl.edgeGroups"></edge-groups-selector>
|
||||
<edge-groups-selector model="$ctrl.model.EdgeGroups" items="$ctrl.edgeGroups" on-change="($ctrl.onChangeGroups)"></edge-groups-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- web-editor -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Web editor
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
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>
|
||||
.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" ng-if="!$ctrl.validateEndpointsForDeployment()">
|
||||
<div class="col-sm-12">
|
||||
<code-editor
|
||||
value="$ctrl.model.StackFileContent"
|
||||
identifier="stack-creation-editor"
|
||||
placeholder="# Define or paste the content of your docker-compose file here"
|
||||
yml="true"
|
||||
on-change="($ctrl.editorUpdate)"
|
||||
></code-editor>
|
||||
<div class="small text-muted space-right text-warning">
|
||||
<i class="fa fa-exclamation-triangle orange-icon" aria-hidden="true"></i>
|
||||
One or more of the selected Edge group contains Edge Docker endpoints that cannot be used with a Kubernetes Edge stack.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !web-editor -->
|
||||
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Options
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<edge-stack-deployment-type-selector
|
||||
value="$ctrl.model.DeploymentType"
|
||||
has-docker-endpoint="$ctrl.hasDockerEndpoint"
|
||||
on-change="($ctrl.onChangeDeploymentType)"
|
||||
></edge-stack-deployment-type-selector>
|
||||
|
||||
<div class="form-group" ng-if="$ctrl.model.DeploymentType === 0 && $ctrl.hasKubeEndpoint()">
|
||||
<div class="col-sm-12">
|
||||
<label for="EnablePrune" class="control-label text-left">
|
||||
Prune services
|
||||
<portainer-tooltip position="bottom" message="Prune services that are not longer referenced in the stack file."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" name="EnablePrune" ng-model="$ctrl.model.Prune" />
|
||||
<i></i>
|
||||
</label>
|
||||
<div class="small text-muted space-right">
|
||||
<i class="fa fa-exclamation-triangle orange-icon" aria-hidden="true"></i>
|
||||
Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that not all the
|
||||
Compose format options are supported by Kompose at the moment.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<web-editor-form
|
||||
ng-if="$ctrl.model.DeploymentType === 0"
|
||||
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)"
|
||||
>
|
||||
<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>
|
||||
|
||||
<web-editor-form
|
||||
ng-if="$ctrl.model.DeploymentType === 1"
|
||||
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>
|
||||
|
||||
<!-- actions -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Actions
|
||||
|
@ -59,9 +77,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.actionInProgress
|
||||
|| !$ctrl.model.EdgeGroups.length
|
||||
|| !$ctrl.model.StackFileContent"
|
||||
ng-disabled="$ctrl.actionInProgress || !$ctrl.isFormValid()"
|
||||
ng-click="$ctrl.submitAction()"
|
||||
button-spinner="$ctrl.actionInProgress"
|
||||
>
|
||||
|
|
|
@ -1,13 +1,83 @@
|
|||
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
||||
|
||||
export class EditEdgeStackFormController {
|
||||
/* @ngInject */
|
||||
constructor() {
|
||||
this.editorUpdate = this.editorUpdate.bind(this);
|
||||
this.state = {
|
||||
endpointTypes: [],
|
||||
};
|
||||
|
||||
this.fileContents = {
|
||||
0: '',
|
||||
1: '',
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
editorUpdate(cm) {
|
||||
if (this.model.StackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== cm.getValue().replace(/(\r\n|\n|\r)/gm, '')) {
|
||||
this.model.StackFileContent = cm.getValue();
|
||||
hasKubeEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment);
|
||||
}
|
||||
|
||||
hasDockerEndpoint() {
|
||||
return this.state.endpointTypes.includes(PortainerEndpointTypes.EdgeAgentOnDockerEnvironment);
|
||||
}
|
||||
|
||||
onChangeGroups(groups) {
|
||||
this.model.EdgeGroups = groups;
|
||||
|
||||
this.checkEndpointTypes(groups);
|
||||
}
|
||||
|
||||
isFormValid() {
|
||||
return this.model.EdgeGroups.length && this.model.StackFileContent && this.validateEndpointsForDeployment();
|
||||
}
|
||||
|
||||
checkEndpointTypes(groups) {
|
||||
const edgeGroups = groups.map((id) => this.edgeGroups.find((e) => e.Id === id));
|
||||
this.state.endpointTypes = edgeGroups.flatMap((group) => group.EndpointTypes);
|
||||
}
|
||||
|
||||
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) {
|
||||
this.model.DeploymentType = deploymentType;
|
||||
|
||||
this.model.StackFileContent = this.fileContents[deploymentType];
|
||||
}
|
||||
|
||||
validateEndpointsForDeployment() {
|
||||
return this.model.DeploymentType == 0 || !this.hasDockerEndpoint();
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.checkEndpointTypes(this.model.EdgeGroups);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ export class EdgeGroupFormController {
|
|||
async getDynamicEndpointsAsync() {
|
||||
const { pageNumber, limit, search } = this.endpoints.state;
|
||||
const start = (pageNumber - 1) * limit + 1;
|
||||
const query = { search, types: [4], tagIds: this.model.TagIds, tagsPartialMatch: this.model.PartialMatch };
|
||||
const query = { search, types: [4, 7], tagIds: this.model.TagIds, tagsPartialMatch: this.model.PartialMatch };
|
||||
|
||||
const response = await this.EndpointService.endpoints(start, limit, query);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue