mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
feat(k8s): advanced deployment from Git repo EE-447 (#5166)
* feat(stack): UI updates in git repo deployment method for k8s EE-640. (#5097) * feat(stack): UI updates in git repo deployment method for k8s EE-640. * feat(stack): supports the combination of GIT + COMPOSE. * feat(stack): rename variable * feat(stack): add git repo deployment method for k8s EE-638 * cleanup * update payload validation rules * make repo ref optional in frond end Co-authored-by: fhanportainer <79428273+fhanportainer@users.noreply.github.com>
This commit is contained in:
parent
6b759438b8
commit
caa6c15032
11 changed files with 409 additions and 63 deletions
|
@ -2,3 +2,13 @@ export const KubernetesDeployManifestTypes = Object.freeze({
|
|||
KUBERNETES: 1,
|
||||
COMPOSE: 2,
|
||||
});
|
||||
|
||||
export const KubernetesDeployBuildMethods = Object.freeze({
|
||||
GIT: 1,
|
||||
WEB_EDITOR: 2,
|
||||
});
|
||||
|
||||
export const KubernetesDeployRequestMethods = Object.freeze({
|
||||
REPOSITORY: 'repository',
|
||||
STRING: 'string',
|
||||
});
|
||||
|
|
|
@ -207,8 +207,8 @@
|
|||
<ng-messages for="kubernetesApplicationCreationForm['environment_variable_name_' + $index].$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Environment variable name is required.</p>
|
||||
<p ng-message="pattern"
|
||||
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field must consist of alphabetic characters, digits, '_', '-', or '.', and must
|
||||
not start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1'.</p
|
||||
><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field must consist of alphabetic characters, digits, '_', '-', or '.', and must not
|
||||
start with a digit (e.g. 'my.env-name', or 'MY_ENV.NAME', or 'MyEnvName1'.</p
|
||||
>
|
||||
</ng-messages>
|
||||
<p ng-if="ctrl.state.duplicates.environmentVariables.refs[$index] !== undefined"
|
||||
|
|
|
@ -175,7 +175,7 @@ class KubernetesConfigureController {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
enableMetricsServer() {
|
||||
if (this.formValues.UseServerMetrics) {
|
||||
this.state.metrics.userClick = true;
|
||||
|
|
|
@ -52,41 +52,162 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- !deploy-type -->
|
||||
<!-- editor -->
|
||||
|
||||
<!-- build method -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Web editor
|
||||
Build method
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE">
|
||||
<p>
|
||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></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.
|
||||
</p>
|
||||
<p>
|
||||
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>.
|
||||
</p>
|
||||
</span>
|
||||
<span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES">
|
||||
<p>
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...).
|
||||
</p>
|
||||
<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>
|
||||
</span>
|
||||
<div class="form-group"></div>
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="build_method_git" ng-model="ctrl.state.BuildMethod" ng-value="ctrl.BuildMethods.GIT" />
|
||||
<label for="build_method_git">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-github" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Git Repository
|
||||
</div>
|
||||
<p>Use a git repository</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="build_method_web_editor" ng-model="ctrl.state.BuildMethod" ng-value="ctrl.BuildMethods.WEB_EDITOR" />
|
||||
<label for="build_method_web_editor">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-edit" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Web editor
|
||||
</div>
|
||||
<p>Use our Web editor</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<code-editor
|
||||
identifier="kubernetes-deploy-editor"
|
||||
placeholder="# Define or paste the content of your manifest file here"
|
||||
yml="false"
|
||||
on-change="(ctrl.editorUpdate)"
|
||||
></code-editor>
|
||||
|
||||
<!-- !deploy-type -->
|
||||
|
||||
<!-- repository -->
|
||||
<div ng-show="ctrl.state.BuildMethod === ctrl.BuildMethods.GIT">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Git repository
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
You can use the URL of a git repository.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stack_repository_url" class="col-sm-2 control-label text-left">Repository URL</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="ctrl.formValues.RepositoryURL"
|
||||
id="stack_repository_url"
|
||||
placeholder="https://github.com/portainer/deployment-repository"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Specify a reference of the repository using the following syntax: branches with
|
||||
<code>refs/heads/branch_name</code> or tags with <code>refs/tags/tag_name</code>. If not specified, will use the default <code>HEAD</code> reference normally
|
||||
the <code>master</code> branch.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stack_repository_url" class="col-sm-2 control-label text-left">Repository reference</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="ctrl.formValues.RepositoryReferenceName"
|
||||
id="stack_repository_reference_name"
|
||||
placeholder="refs/heads/master"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Indicate the path to the yaml file from the root of your repository.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stack_repository_path" class="col-sm-2 control-label text-left">Manifest path</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="ctrl.formValues.FilePathInRepository" id="stack_manifest_path" placeholder="deployment.yml" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label class="control-label text-left">
|
||||
Authentication
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.RepositoryAuthentication" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="ctrl.formValues.RepositoryAuthentication">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
If your git account has 2FA enabled, you may receive an
|
||||
<code>authentication required</code> error when deploying your stack. In this case, you will need to provide a personal-access token instead of your password.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group" ng-if="ctrl.formValues.RepositoryAuthentication">
|
||||
<label for="repository_username" class="col-sm-1 control-label text-left">Username</label>
|
||||
<div class="col-sm-11 col-md-5">
|
||||
<input type="text" class="form-control" ng-model="ctrl.formValues.RepositoryUsername" name="repository_username" placeholder="myGitUser" />
|
||||
</div>
|
||||
<label for="repository_password" class="col-sm-1 control-label text-left">
|
||||
Password
|
||||
</label>
|
||||
<div class="col-sm-11 col-md-5">
|
||||
<input type="password" class="form-control" ng-model="ctrl.formValues.RepositoryPassword" name="repository_password" placeholder="myPassword" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !repository -->
|
||||
|
||||
<!-- editor -->
|
||||
<div ng-if="ctrl.state.BuildMethod === ctrl.BuildMethods.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" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE">
|
||||
<p>
|
||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></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.
|
||||
</p>
|
||||
<p>
|
||||
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>.
|
||||
</p>
|
||||
</span>
|
||||
<span
|
||||
class="col-sm-12 text-muted small"
|
||||
ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES && ctrl.state.BuildMethod === ctrl.BuildMethods.WEB_EDITOR"
|
||||
>
|
||||
<p>
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...).
|
||||
</p>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<code-editor
|
||||
identifier="kubernetes-deploy-editor"
|
||||
placeholder="# Define or paste the content of your manifest file here"
|
||||
yml="false"
|
||||
value="ctrl.formValues.EditorContent"
|
||||
on-change="(ctrl.editorUpdate)"
|
||||
></code-editor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !editor -->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import angular from 'angular';
|
||||
import _ from 'lodash-es';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { KubernetesDeployManifestTypes } from 'Kubernetes/models/deploy';
|
||||
import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, KubernetesDeployRequestMethods } from 'Kubernetes/models/deploy';
|
||||
|
||||
class KubernetesDeployController {
|
||||
/* @ngInject */
|
||||
|
@ -23,7 +23,14 @@ class KubernetesDeployController {
|
|||
}
|
||||
|
||||
disableDeploy() {
|
||||
return _.isEmpty(this.formValues.EditorContent) || _.isEmpty(this.formValues.Namespace) || this.state.actionInProgress;
|
||||
const isGitFormInvalid =
|
||||
this.state.BuildMethod === KubernetesDeployBuildMethods.GIT &&
|
||||
(!this.formValues.RepositoryURL ||
|
||||
!this.formValues.FilePathInRepository ||
|
||||
(this.formValues.RepositoryAuthentication && (!this.formValues.RepositoryUsername || !this.formValues.RepositoryPassword)));
|
||||
const isWebEditorInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.WEB_EDITOR && _.isEmpty(this.formValues.EditorContent);
|
||||
|
||||
return isGitFormInvalid || isWebEditorInvalid || _.isEmpty(this.formValues.Namespace) || this.state.actionInProgress;
|
||||
}
|
||||
|
||||
async editorUpdateAsync(cm) {
|
||||
|
@ -46,8 +53,28 @@ class KubernetesDeployController {
|
|||
this.state.actionInProgress = true;
|
||||
|
||||
try {
|
||||
const compose = this.state.DeployType === this.ManifestDeployTypes.COMPOSE;
|
||||
await this.StackService.kubernetesDeploy(this.endpointId, this.formValues.Namespace, this.formValues.EditorContent, compose);
|
||||
const method = this.state.BuildMethod === this.BuildMethods.GIT ? KubernetesDeployRequestMethods.REPOSITORY : KubernetesDeployRequestMethods.STRING;
|
||||
|
||||
const payload = {
|
||||
ComposeFormat: this.state.DeployType === this.ManifestDeployTypes.COMPOSE,
|
||||
Namespace: this.formValues.Namespace,
|
||||
};
|
||||
|
||||
if (method === KubernetesDeployRequestMethods.REPOSITORY) {
|
||||
payload.RepositoryURL = this.formValues.RepositoryURL;
|
||||
payload.RepositoryReferenceName = this.formValues.RepositoryReferenceName;
|
||||
payload.RepositoryAuthentication = this.formValues.RepositoryAuthentication ? true : false;
|
||||
if (payload.RepositoryAuthentication) {
|
||||
payload.RepositoryUsername = this.formValues.RepositoryUsername;
|
||||
payload.RepositoryPassword = this.formValues.RepositoryPassword;
|
||||
}
|
||||
payload.FilePathInRepository = this.formValues.FilePathInRepository;
|
||||
} else {
|
||||
payload.StackFileContent = this.formValues.EditorContent;
|
||||
}
|
||||
|
||||
await this.StackService.kubernetesDeploy(this.endpointId, method, payload);
|
||||
|
||||
this.Notifications.success('Manifest successfully deployed');
|
||||
this.state.isEditorDirty = false;
|
||||
this.$state.go('kubernetes.applications');
|
||||
|
@ -92,10 +119,10 @@ class KubernetesDeployController {
|
|||
return this.ModalService.confirmWebEditorDiscard();
|
||||
}
|
||||
}
|
||||
|
||||
async onInit() {
|
||||
this.state = {
|
||||
DeployType: KubernetesDeployManifestTypes.KUBERNETES,
|
||||
BuildMethod: KubernetesDeployBuildMethods.GIT,
|
||||
tabLogsDisabled: true,
|
||||
activeTab: 0,
|
||||
viewReady: false,
|
||||
|
@ -104,6 +131,7 @@ class KubernetesDeployController {
|
|||
|
||||
this.formValues = {};
|
||||
this.ManifestDeployTypes = KubernetesDeployManifestTypes;
|
||||
this.BuildMethods = KubernetesDeployBuildMethods;
|
||||
this.endpointId = this.EndpointProvider.endpointID();
|
||||
|
||||
await this.getNamespaces();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue