mirror of https://github.com/portainer/portainer
Revert multiple commits to set it back to tag 2.19 (#10946)
* Revert "docs(dashboard): update link for swarm node [EE-6318] (#10770)" This reverts commitpull/11470/head30356d2c15
. * Revert "docs(api): default to pascal case for property name [EE-6471] (#10861)" This reverts commit392819576c
. * Revert "fix(edge/jobs): clear logs [EE-5923] (#10819)" This reverts commite01386f63d
. * Revert "disable html5 validation (#10843)" This reverts commit4b0f08e92a
. * Revert "fix(edgestack): allow to set retry deployment toggle EE-6167 (#10676) (#10805)" This reverts commiteee632b22d
. * Revert "fix(stack): edit git stack validation (#10812)" This reverts commitd3b150b29c
. * Revert "Revert "Revert "fix(rollback): reversed rollback code from 2.19.4 [EE-6435] (#10811)" (#10832)" This reverts commit32e05bb705
. * Revert "fix(setting/ssl): cert files are optional to upload [EE-6139] (#10779)" This reverts commit7408668dbb
. * Revert "fix(endpoint): delete the endpoint proxy when updating an endpoint address [EE-5577] (#10824)" This reverts commit4b5ea01456
. * Revert "fix(swagger): custom template create docs EE-6428 (#10806)" This reverts commit0d55cb3e08
. * Revert "fix(images): sort by tags [EE-6410] (#10755)" This reverts commit7f51c727a0
. * Revert "fix(stacks): sort by date [EE-5766] (#10758)" This reverts commit57b80cd9ac
. * Revert "fix(UI): remember backup settings tab [EE-6347] (#10764)" This reverts commitc20452492d
. * Revert "fix(backup ui): minor typo on backup page EE-6348 (#10717)" This reverts commitd58046eb68
. * Revert "fix(app): shift external to the top [EE-6392] (#10753) (#10802)" This reverts commit4795e85d18
. * Revert "fix(app): update sliders when limits are known [EE-5933] (#10769) (#10801)" This reverts commitd090b0043a
. * Revert "fix(gitops): correct commit hash link [EE-6346] (#10800)" This reverts commit0e59cf76ec
. * Revert "fix toast error (#10804)" This reverts commit9978b88ed4
. * Revert "fix(kube): configmaps and secrets from envFrom in the app detail screen [EE-6282] (#10741) (#10798)" This reverts commitc1a01558d0
. * Revert "fix(stacks): allow editing custom templates before stack deployment EE-6380 (#10713)" This reverts commitf0aa0554f8
. * Reapply "fix(rollback): reversed rollback code from 2.19.4 [EE-6435] (#10811) This reverts commitc58fa274e7
.
parent
d2eb53eb18
commit
6f5d9c357f
2
Makefile
2
Makefile
|
@ -114,7 +114,7 @@ dev-extension: build-server build-client ## Run the extension in development mod
|
|||
##@ Docs
|
||||
.PHONY: docs-build docs-validate docs-clean docs-validate-clean
|
||||
docs-build: init-dist ## Build docs
|
||||
cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 -p pascalcase --markdownFiles ./
|
||||
cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 --markdownFiles ./
|
||||
|
||||
docs-validate: docs-build ## Validate docs
|
||||
yarn swagger2openapi --warnOnly dist/docs/swagger.yaml -o dist/docs/openapi.yaml
|
||||
|
|
|
@ -149,7 +149,7 @@ func isValidNote(note string) bool {
|
|||
// @success 200 {object} portainer.CustomTemplate
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /custom_templates/create/string [post]
|
||||
// @router /custom_templates/string [post]
|
||||
func (handler *Handler) createCustomTemplateFromFileContent(r *http.Request) (*portainer.CustomTemplate, error) {
|
||||
var payload customTemplateFromFileContentPayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
|
@ -263,7 +263,7 @@ func (payload *customTemplateFromGitRepositoryPayload) Validate(r *http.Request)
|
|||
// @success 200 {object} portainer.CustomTemplate
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /custom_templates/create/repository [post]
|
||||
// @router /custom_templates/repository [post]
|
||||
func (handler *Handler) createCustomTemplateFromGitRepository(r *http.Request) (*portainer.CustomTemplate, error) {
|
||||
var payload customTemplateFromGitRepositoryPayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
|
@ -443,7 +443,7 @@ func (payload *customTemplateFromFileUploadPayload) Validate(r *http.Request) er
|
|||
// @success 200 {object} portainer.CustomTemplate
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /custom_templates/create/file [post]
|
||||
// @router /custom_templates/file [post]
|
||||
func (handler *Handler) createCustomTemplateFromFileUpload(r *http.Request) (*portainer.CustomTemplate, error) {
|
||||
payload := &customTemplateFromFileUploadPayload{}
|
||||
err := payload.Validate(r)
|
||||
|
|
|
@ -110,6 +110,11 @@ func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJo
|
|||
return httperror.InternalServerError("Unable to persist Edge job changes in the database", err)
|
||||
}
|
||||
|
||||
err = handler.FileService.ClearEdgeJobTaskLogs(strconv.Itoa(int(edgeJobID)), strconv.Itoa(int(endpointID)))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to clear log file from disk", err)
|
||||
}
|
||||
|
||||
endpoint, err := tx.Endpoint().Endpoint(endpointID)
|
||||
if err != nil {
|
||||
return httperror.NotFound("Unable to retrieve environment from the database", err)
|
||||
|
|
|
@ -89,8 +89,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
return httperror.InternalServerError("Unable to find an environment with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
updateEndpointProxy := shouldReloadTLSConfiguration(endpoint, &payload)
|
||||
|
||||
if payload.Name != nil {
|
||||
name := *payload.Name
|
||||
isUnique, err := handler.isNameUnique(name, endpoint.ID)
|
||||
|
@ -106,9 +104,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
|
||||
}
|
||||
|
||||
if payload.URL != nil && *payload.URL != endpoint.URL {
|
||||
if payload.URL != nil {
|
||||
endpoint.URL = *payload.URL
|
||||
updateEndpointProxy = true
|
||||
}
|
||||
|
||||
if payload.PublicURL != nil {
|
||||
|
@ -182,8 +179,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
|
||||
if endpoint.Type == portainer.AzureEnvironment {
|
||||
updateEndpointProxy = true
|
||||
|
||||
credentials := endpoint.AzureCredentials
|
||||
if payload.AzureApplicationID != nil {
|
||||
credentials.ApplicationID = *payload.AzureApplicationID
|
||||
|
@ -252,7 +247,10 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
}
|
||||
|
||||
if updateEndpointProxy {
|
||||
if (payload.URL != nil && *payload.URL != endpoint.URL) ||
|
||||
(payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS) ||
|
||||
endpoint.Type == portainer.AzureEnvironment ||
|
||||
shouldReloadTLSConfiguration(endpoint, &payload) {
|
||||
handler.ProxyManager.DeleteEndpointProxy(endpoint.ID)
|
||||
_, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
|
||||
if err != nil {
|
||||
|
@ -293,12 +291,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
|
||||
func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpointUpdatePayload) bool {
|
||||
|
||||
// If we change anything in the tls config then we need to reload the proxy
|
||||
if payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS {
|
||||
return true
|
||||
}
|
||||
|
||||
// When updating Docker API environment, as long as TLS is true and TLSSkipVerify is false,
|
||||
// we assume that new TLS files have been uploaded and we need to reload the TLS configuration.
|
||||
if endpoint.Type != portainer.DockerEnvironment ||
|
||||
|
|
|
@ -34,7 +34,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([
|
|||
endpoint.Status = status;
|
||||
|
||||
if (status === EnvironmentStatus.Down) {
|
||||
throw new Error(`The environment named ${endpoint.Name} is unreachable.`);
|
||||
throw new Error('Environment is unreachable.');
|
||||
}
|
||||
|
||||
await StateManager.updateEndpointState(endpoint);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
|
||||
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
|
||||
Portainer is connected to a node that is part of a Swarm cluster. Some resources located on other nodes in the cluster might not be available for management, have a look at
|
||||
<a href="https://docs.portainer.io/admin/environments/add/swarm/agent" target="_blank">our agent setup</a> for more details.
|
||||
<a href="https://docs.portainer.io/start/install/agent/swarm/linux" target="_blank">our agent setup</a> for more details.
|
||||
</p>
|
||||
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'WORKER'">
|
||||
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
|
||||
|
|
|
@ -101,7 +101,6 @@ export class EditEdgeStackViewController {
|
|||
edgeGroups: values.edgeGroups,
|
||||
deploymentType: values.deploymentType,
|
||||
updateVersion,
|
||||
retryDeploy: values.retryDeploy,
|
||||
webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '',
|
||||
envVars: values.envVars,
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
|
|||
try {
|
||||
await getSelfSubjectAccessReview(endpoint.Id, 'default');
|
||||
} catch (e) {
|
||||
throw new Error(`The environment named ${endpoint.Name} is unreachable.`);
|
||||
throw new Error('Environment is unreachable.');
|
||||
}
|
||||
} catch (e) {
|
||||
let params = {};
|
||||
|
|
|
@ -59,94 +59,7 @@
|
|||
<div class="col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal mt-4" name="kubernetesApplicationCreationForm" autocomplete="off" novalidate>
|
||||
<div ng-if="ctrl.isExternalApplication()">
|
||||
<div class="col-sm-12 form-section-title" ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"> Namespace </div>
|
||||
<!-- #region NAMESPACE -->
|
||||
<div class="form-group" ng-if="ctrl.formValues.ResourcePool">
|
||||
<label for="resource-pool-selector" class="col-sm-1 control-label text-left">Namespace</label>
|
||||
<div class="col-sm-11">
|
||||
<select
|
||||
class="form-control"
|
||||
id="resource-pool-selector"
|
||||
ng-model="ctrl.formValues.ResourcePool"
|
||||
ng-options="resourcePool.Namespace.Name for resourcePool in ctrl.resourcePools"
|
||||
ng-change="ctrl.onResourcePoolSelectionChange()"
|
||||
ng-disabled="ctrl.state.isEdit"
|
||||
data-cy="k8sAppCreate-nsSelect"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool">
|
||||
<div class="col-sm-12 small text-danger">
|
||||
<pr-icon icon="'alert-circle'" mode="'danger'"></pr-icon>
|
||||
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the
|
||||
namespace.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
|
||||
<div class="col-sm-12 small text-muted">
|
||||
<pr-icon icon="'alert-circle'" mode="'warning'"></pr-icon>
|
||||
You do not have access to any namespace. Contact your administrator to get access to a namespace.
|
||||
</div>
|
||||
</div>
|
||||
<!-- kubernetes services options -->
|
||||
<div ng-if="ctrl.formValues.ResourcePool">
|
||||
<kube-services-form
|
||||
on-change="(ctrl.onServicesChange)"
|
||||
values="ctrl.formValues.Services"
|
||||
app-name="ctrl.formValues.Name"
|
||||
selector="ctrl.formValues.Selector"
|
||||
validation-data="{nodePortServices: ctrl.state.nodePortServices, formServices: ctrl.formValues.Services, ingressPaths: ctrl.ingressPaths, originalIngressPaths: ctrl.originalIngressPaths}"
|
||||
is-edit-mode="ctrl.state.isEdit"
|
||||
namespace="ctrl.formValues.ResourcePool.Namespace.Name"
|
||||
></kube-services-form>
|
||||
</div>
|
||||
<!-- kubernetes services options -->
|
||||
<kubernetes-summary-view form-values="ctrl.formValues" old-form-values="ctrl.savedFormValues"></kubernetes-summary-view>
|
||||
<!-- #region ACTIONS -->
|
||||
<div class="col-sm-12 form-section-title !mt-6" ng-if="ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.GIT" ng-hide="ctrl.stack.IsComposeFormat"> Actions </div>
|
||||
<div class="form-group" ng-hide="ctrl.stack.IsComposeFormat">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.imageValidityIsValid() || ctrl.hasPortErrors() || !ctrl.formValues.ResourcePool"
|
||||
ng-click="ctrl.deployApplication()"
|
||||
button-spinner="ctrl.state.actionInProgress"
|
||||
data-cy="k8sAppCreate-deployButton"
|
||||
>
|
||||
<span ng-show="!ctrl.state.isEdit && !ctrl.state.actionInProgress">Deploy application</span>
|
||||
<span ng-show="!ctrl.state.isEdit && ctrl.state.actionInProgress">Deployment in progress...</span>
|
||||
<span ng-show="ctrl.state.isEdit && !ctrl.state.actionInProgress">Update application</span>
|
||||
<span ng-show="ctrl.state.isEdit && ctrl.state.actionInProgress">Update in progress...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="ctrl.state.isEdit && !ctrl.state.actionInProgress && ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-sm btn-default"
|
||||
ui-sref="kubernetes.applications.application({ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool })"
|
||||
data-cy="k8sAppCreate-appCancelButton"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<!-- #Web editor buttons -->
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="ctrl.updateApplicationViaWebEditor()"
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT || ctrl.state.updateWebEditorInProgress"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || !ctrl.state.isEditorDirty || ctrl.state.updateWebEditorInProgress"
|
||||
style="margin-top: 7px; margin-left: 0"
|
||||
button-spinner="ctrl.state.updateWebEditorInProgress"
|
||||
>
|
||||
<span ng-show="!ctrl.state.updateWebEditorInProgress">Update the application</span>
|
||||
<span ng-show="ctrl.state.updateWebEditorInProgress">Update in progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #endregion -->
|
||||
</div>
|
||||
<form class="form-horizontal mt-4" name="kubernetesApplicationCreationForm" autocomplete="off">
|
||||
<div ng-if="!ctrl.isExternalApplication()">
|
||||
<git-form-info-panel
|
||||
ng-if="ctrl.state.appType == ctrl.KubernetesDeploymentTypes.GIT"
|
||||
|
@ -1402,54 +1315,105 @@
|
|||
></kube-services-form>
|
||||
</div>
|
||||
<!-- kubernetes services options -->
|
||||
|
||||
<!-- summary -->
|
||||
<kubernetes-summary-view
|
||||
ng-if="!(!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.state.pullImageValidity)"
|
||||
form-values="ctrl.formValues"
|
||||
old-form-values="ctrl.savedFormValues"
|
||||
></kubernetes-summary-view>
|
||||
</div>
|
||||
<!-- #region ACTIONS -->
|
||||
<div class="col-sm-12 form-section-title !mt-6" ng-if="ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.GIT" ng-hide="ctrl.stack.IsComposeFormat"> Actions </div>
|
||||
<div class="form-group" ng-hide="ctrl.stack.IsComposeFormat">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.imageValidityIsValid() || ctrl.hasPortErrors() || !ctrl.formValues.ResourcePool"
|
||||
ng-click="ctrl.deployApplication()"
|
||||
button-spinner="ctrl.state.actionInProgress"
|
||||
data-cy="k8sAppCreate-deployButton"
|
||||
>
|
||||
<span ng-show="!ctrl.state.isEdit && !ctrl.state.actionInProgress">Deploy application</span>
|
||||
<span ng-show="!ctrl.state.isEdit && ctrl.state.actionInProgress">Deployment in progress...</span>
|
||||
<span ng-show="ctrl.state.isEdit && !ctrl.state.actionInProgress">Update application</span>
|
||||
<span ng-show="ctrl.state.isEdit && ctrl.state.actionInProgress">Update in progress...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="ctrl.state.isEdit && !ctrl.state.actionInProgress && ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-sm btn-default"
|
||||
ui-sref="kubernetes.applications.application({ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool })"
|
||||
data-cy="k8sAppCreate-appCancelButton"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<!-- #Web editor buttons -->
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="ctrl.updateApplicationViaWebEditor()"
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT || ctrl.state.updateWebEditorInProgress"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || !ctrl.state.isEditorDirty || ctrl.state.updateWebEditorInProgress"
|
||||
style="margin-top: 7px; margin-left: 0"
|
||||
button-spinner="ctrl.state.updateWebEditorInProgress"
|
||||
>
|
||||
<span ng-show="!ctrl.state.updateWebEditorInProgress">Update the application</span>
|
||||
<span ng-show="ctrl.state.updateWebEditorInProgress">Update in progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div ng-if="ctrl.isExternalApplication()">
|
||||
<div class="col-sm-12 form-section-title" ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"> Namespace </div>
|
||||
<!-- #region NAMESPACE -->
|
||||
<div class="form-group" ng-if="ctrl.formValues.ResourcePool">
|
||||
<label for="resource-pool-selector" class="col-sm-1 control-label text-left">Namespace</label>
|
||||
<div class="col-sm-11">
|
||||
<select
|
||||
class="form-control"
|
||||
id="resource-pool-selector"
|
||||
ng-model="ctrl.formValues.ResourcePool"
|
||||
ng-options="resourcePool.Namespace.Name for resourcePool in ctrl.resourcePools"
|
||||
ng-change="ctrl.onResourcePoolSelectionChange()"
|
||||
ng-disabled="ctrl.state.isEdit"
|
||||
data-cy="k8sAppCreate-nsSelect"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool">
|
||||
<div class="col-sm-12 small text-danger">
|
||||
<pr-icon icon="'alert-circle'" mode="'danger'"></pr-icon>
|
||||
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the
|
||||
namespace.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
|
||||
<div class="col-sm-12 small text-muted">
|
||||
<pr-icon icon="'alert-circle'" mode="'warning'"></pr-icon>
|
||||
You do not have access to any namespace. Contact your administrator to get access to a namespace.
|
||||
</div>
|
||||
</div>
|
||||
<!-- kubernetes services options -->
|
||||
<div ng-if="ctrl.formValues.ResourcePool">
|
||||
<kube-services-form
|
||||
on-change="(ctrl.onServicesChange)"
|
||||
values="ctrl.formValues.Services"
|
||||
app-name="ctrl.formValues.Name"
|
||||
selector="ctrl.formValues.Selector"
|
||||
validation-data="{nodePortServices: ctrl.state.nodePortServices, formServices: ctrl.formValues.Services, ingressPaths: ctrl.ingressPaths, originalIngressPaths: ctrl.originalIngressPaths}"
|
||||
is-edit-mode="ctrl.state.isEdit"
|
||||
namespace="ctrl.formValues.ResourcePool.Namespace.Name"
|
||||
></kube-services-form>
|
||||
</div>
|
||||
<!-- kubernetes services options -->
|
||||
</div>
|
||||
|
||||
<!-- kubernetes summary for external application -->
|
||||
<kubernetes-summary-view ng-if="ctrl.isExternalApplication()" form-values="ctrl.formValues" old-form-values="ctrl.savedFormValues"></kubernetes-summary-view>
|
||||
<!-- kubernetes summary for external application -->
|
||||
<div class="col-sm-12 form-section-title !mt-6" ng-if="ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.GIT" ng-hide="ctrl.stack.IsComposeFormat"> Actions </div>
|
||||
<!-- #region ACTIONS -->
|
||||
<div class="form-group" ng-hide="ctrl.stack.IsComposeFormat">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || ctrl.isDeployUpdateButtonDisabled() || !ctrl.imageValidityIsValid() || ctrl.hasPortErrors() || !ctrl.formValues.ResourcePool"
|
||||
ng-click="ctrl.deployApplication()"
|
||||
button-spinner="ctrl.state.actionInProgress"
|
||||
data-cy="k8sAppCreate-deployButton"
|
||||
>
|
||||
<span ng-show="!ctrl.state.isEdit && !ctrl.state.actionInProgress">Deploy application</span>
|
||||
<span ng-show="!ctrl.state.isEdit && ctrl.state.actionInProgress">Deployment in progress...</span>
|
||||
<span ng-show="ctrl.state.isEdit && !ctrl.state.actionInProgress">Update application</span>
|
||||
<span ng-show="ctrl.state.isEdit && ctrl.state.actionInProgress">Update in progress...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="ctrl.state.isEdit && !ctrl.state.actionInProgress && ctrl.state.appType === ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
|
||||
type="button"
|
||||
class="btn btn-sm btn-default"
|
||||
ui-sref="kubernetes.applications.application({ name: ctrl.application.Name, namespace: ctrl.application.ResourcePool })"
|
||||
data-cy="k8sAppCreate-appCancelButton"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<!-- #Web editor buttons -->
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="ctrl.updateApplicationViaWebEditor()"
|
||||
ng-if="ctrl.state.appType === ctrl.KubernetesDeploymentTypes.CONTENT || ctrl.state.updateWebEditorInProgress"
|
||||
ng-disabled="!kubernetesApplicationCreationForm.$valid || !ctrl.state.isEditorDirty || ctrl.state.updateWebEditorInProgress"
|
||||
style="margin-top: 7px; margin-left: 0"
|
||||
button-spinner="ctrl.state.updateWebEditorInProgress"
|
||||
>
|
||||
<span ng-show="!ctrl.state.updateWebEditorInProgress">Update the application</span>
|
||||
<span ng-show="ctrl.state.updateWebEditorInProgress">Update in progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #endregion -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
|
|
@ -1114,10 +1114,10 @@ class KubernetesCreateApplicationController {
|
|||
});
|
||||
|
||||
if (this.resourcePools.length) {
|
||||
this.namespaceWithQuota = await this.KubernetesResourcePoolService.get(this.resourcePools[0].Namespace.Name);
|
||||
this.formValues.ResourcePool.Quota = this.namespaceWithQuota.Quota;
|
||||
this.updateNamespaceLimits(this.namespaceWithQuota);
|
||||
this.updateSliders(this.namespaceWithQuota);
|
||||
const namespaceWithQuota = await this.KubernetesResourcePoolService.get(this.resourcePools[0].Namespace.Name);
|
||||
this.formValues.ResourcePool.Quota = namespaceWithQuota.Quota;
|
||||
this.updateNamespaceLimits(namespaceWithQuota);
|
||||
this.updateSliders(namespaceWithQuota);
|
||||
}
|
||||
this.formValues.ResourcePool = this.resourcePools[0];
|
||||
if (!this.formValues.ResourcePool) {
|
||||
|
@ -1140,8 +1140,6 @@ class KubernetesCreateApplicationController {
|
|||
this.nodesLabels,
|
||||
this.ingresses
|
||||
);
|
||||
|
||||
this.formValues.Services = this.formValues.Services || [];
|
||||
this.originalServicePorts = structuredClone(this.formValues.Services.flatMap((service) => service.Ports));
|
||||
this.originalIngressPaths = structuredClone(this.originalServicePorts.flatMap((port) => port.ingressPaths).filter((ingressPath) => ingressPath.Host));
|
||||
|
||||
|
@ -1162,8 +1160,6 @@ class KubernetesCreateApplicationController {
|
|||
this.formValues.OriginalIngresses = this.ingresses;
|
||||
this.formValues.ImageModel = await this.parseImageConfiguration(this.formValues.ImageModel);
|
||||
this.savedFormValues = angular.copy(this.formValues);
|
||||
this.updateNamespaceLimits(this.namespaceWithQuota);
|
||||
this.updateSliders(this.namespaceWithQuota);
|
||||
delete this.formValues.ApplicationType;
|
||||
|
||||
if (this.application.ApplicationType !== KubernetesApplicationTypes.STATEFULSET) {
|
||||
|
|
|
@ -194,7 +194,7 @@ class KubernetesDeployController {
|
|||
this.state.templateContent = await this.CustomTemplateService.customTemplateFile(templateId, template.GitConfig !== null);
|
||||
this.onChangeFileContent(this.state.templateContent);
|
||||
|
||||
this.state.isEditorReadOnly = false;
|
||||
this.state.isEditorReadOnly = true;
|
||||
} catch (err) {
|
||||
this.state.templateLoadFailed = true;
|
||||
throw err;
|
||||
|
|
|
@ -144,18 +144,18 @@
|
|||
<table-column-header
|
||||
col-title="'Created'"
|
||||
can-sort="true"
|
||||
is-sorted="$ctrl.state.orderBy === 'CreationDate'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'CreationDate' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('CreationDate')"
|
||||
is-sorted="$ctrl.state.orderBy === 'ResourceControl.CreationDate'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'ResourceControl.CreationDate' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('ResourceControl.CreationDate')"
|
||||
></table-column-header>
|
||||
</th>
|
||||
<th ng-if="$ctrl.columnVisibility.columns.updated.display">
|
||||
<table-column-header
|
||||
col-title="'Updated'"
|
||||
can-sort="true"
|
||||
is-sorted="$ctrl.state.orderBy === 'UpdateDate'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'UpdateDate' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('UpdateDate')"
|
||||
is-sorted="$ctrl.state.orderBy === 'ResourceControl.UpdateDate'"
|
||||
is-sorted-desc="$ctrl.state.orderBy === 'ResourceControl.UpdateDate' && $ctrl.state.reverseOrder"
|
||||
ng-click="$ctrl.changeOrderBy('ResourceControl.UpdateDate')"
|
||||
></table-column-header>
|
||||
</th>
|
||||
<th>
|
||||
|
|
|
@ -52,7 +52,7 @@ export default class GitFormAuthFieldsetController {
|
|||
...newValues,
|
||||
};
|
||||
this.onChange?.(value);
|
||||
await this.runGitValidation(value, this.isAuthEdit);
|
||||
await this.runGitValidation(value, false);
|
||||
}
|
||||
|
||||
async runGitValidation(value: GitAuthModel, isAuthEdit: boolean) {
|
||||
|
|
|
@ -179,14 +179,6 @@ class StackRedeployGitFormController {
|
|||
});
|
||||
}
|
||||
|
||||
disablePullAndRedeployButton() {
|
||||
return this.isSubmitButtonDisabled() || this.state.hasUnsavedChanges || !this.redeployGitForm.$valid;
|
||||
}
|
||||
|
||||
disableSaveSettingsButton() {
|
||||
return this.isSubmitButtonDisabled() || !this.state.hasUnsavedChanges || !this.redeployGitForm.$valid;
|
||||
}
|
||||
|
||||
isSubmitButtonDisabled() {
|
||||
return this.state.inProgress || this.state.redeployInProgress;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="$ctrl.submit()"
|
||||
ng-disabled="$ctrl.disablePullAndRedeployButton()"
|
||||
ng-disabled="$ctrl.isSubmitButtonDisabled() || $ctrl.state.hasUnsavedChanges || !$ctrl.redeployGitForm.$valid"
|
||||
style="margin-top: 7px; margin-left: 0"
|
||||
button-spinner="$ctrl.state.redeployInProgress"
|
||||
analytics-on
|
||||
|
@ -92,7 +92,7 @@
|
|||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="$ctrl.saveGitSettings()"
|
||||
ng-disabled="$ctrl.disableSaveSettingsButton()"
|
||||
ng-disabled="$ctrl.isSubmitButtonDisabled() || !$ctrl.state.hasUnsavedChanges || !$ctrl.redeployGitForm.$valid"
|
||||
style="margin-top: 7px; margin-left: 0"
|
||||
button-spinner="$ctrl.state.inProgress"
|
||||
analytics-on
|
||||
|
|
|
@ -13,9 +13,9 @@ export function StackViewModel(data) {
|
|||
this.ResourceControl = new ResourceControlViewModel(data.ResourceControl);
|
||||
}
|
||||
this.Status = data.Status;
|
||||
this.CreationDate = data.CreationDate || undefined; // set undefined for angular sorting
|
||||
this.CreationDate = data.CreationDate;
|
||||
this.CreatedBy = data.CreatedBy;
|
||||
this.UpdateDate = data.UpdateDate || undefined; // set undefined for angular sorting
|
||||
this.UpdateDate = data.UpdateDate;
|
||||
this.UpdatedBy = data.UpdatedBy;
|
||||
this.Regular = true;
|
||||
this.External = false;
|
||||
|
@ -30,7 +30,7 @@ export function StackViewModel(data) {
|
|||
export function ExternalStackViewModel(name, type, creationDate) {
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
this.CreationDate = creationDate || undefined; // set undefined for angular sorting
|
||||
this.CreationDate = creationDate;
|
||||
|
||||
this.Regular = false;
|
||||
this.External = true;
|
||||
|
@ -50,9 +50,9 @@ export function OrphanedStackViewModel(data) {
|
|||
this.ResourceControl = new ResourceControlViewModel(data.ResourceControl);
|
||||
}
|
||||
this.Status = data.Status;
|
||||
this.CreationDate = data.CreationDate || undefined; // set undefined for angular sorting
|
||||
this.CreationDate = data.CreationDate;
|
||||
this.CreatedBy = data.CreatedBy;
|
||||
this.UpdateDate = data.UpdateDate || undefined; // set undefined for angular sorting
|
||||
this.UpdateDate = data.UpdateDate;
|
||||
this.UpdatedBy = data.UpdatedBy;
|
||||
|
||||
this.Regular = false;
|
||||
|
|
|
@ -306,7 +306,7 @@ angular
|
|||
$scope.state.templateContent = await this.CustomTemplateService.customTemplateFile(templateId, template.GitConfig !== null);
|
||||
onChangeFileContent($scope.state.templateContent);
|
||||
|
||||
$scope.state.isEditorReadOnly = false;
|
||||
$scope.state.isEditorReadOnly = true;
|
||||
} catch (err) {
|
||||
$scope.state.templateLoadFailed = true;
|
||||
throw err;
|
||||
|
|
|
@ -4,14 +4,14 @@ import { DockerImage } from '@/react/docker/images/types';
|
|||
|
||||
import { columnHelper } from './helper';
|
||||
|
||||
export const tags = columnHelper.accessor((item) => item.RepoTags.join(','), {
|
||||
export const tags = columnHelper.accessor('RepoTags', {
|
||||
id: 'tags',
|
||||
header: 'Tags',
|
||||
cell: Cell,
|
||||
});
|
||||
|
||||
function Cell({ row: { original: item } }: CellContext<DockerImage, unknown>) {
|
||||
const repoTags = item.RepoTags;
|
||||
function Cell({ getValue }: CellContext<DockerImage, string[]>) {
|
||||
const repoTags = getValue();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -9,7 +9,6 @@ import UpToDate from '@/assets/ico/icon_up-to-date.svg?c';
|
|||
import { isoDateFromTimestamp } from '@/portainer/filters/filters';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { getDashboardRoute } from '@/react/portainer/environments/utils';
|
||||
import { cleanGitRepoUrl } from '@/react/portainer/gitops/utils';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -188,9 +187,7 @@ function TargetVersionCell({
|
|||
{row.original.TargetCommitHash ? (
|
||||
<div>
|
||||
<a
|
||||
href={`${cleanGitRepoUrl(row.original.GitConfigURL)}/commit/${
|
||||
row.original.TargetCommitHash
|
||||
}`}
|
||||
href={`${row.original.GitConfigURL}/commit/${row.original.TargetCommitHash}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
@ -239,9 +236,7 @@ function DeployedVersionCell({
|
|||
<div>
|
||||
{statusIcon}
|
||||
<a
|
||||
href={`${cleanGitRepoUrl(row.original.GitConfigURL)}/commit/${
|
||||
row.original.TargetCommitHash
|
||||
}`}
|
||||
href={`${row.original.GitConfigURL}/commit/${row.original.TargetCommitHash}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
|
|
@ -3,7 +3,6 @@ import _ from 'lodash';
|
|||
|
||||
import { isoDateFromTimestamp } from '@/portainer/filters/filters';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { cleanGitRepoUrl } from '@/react/portainer/gitops/utils';
|
||||
|
||||
import { buildNameColumn } from '@@/datatables/NameCell';
|
||||
import { Link } from '@@/Link';
|
||||
|
@ -152,9 +151,7 @@ export const columns = _.compact([
|
|||
<div className="text-center">
|
||||
<a
|
||||
target="_blank"
|
||||
href={`${cleanGitRepoUrl(item.GitConfig.URL)}/commit/${
|
||||
item.GitConfig.ConfigHash
|
||||
}`}
|
||||
href={`${item.GitConfig.URL}/commit/${item.GitConfig.ConfigHash}`}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{item.GitConfig.ConfigHash.slice(0, 7)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Pod } from 'kubernetes-types/core/v1';
|
||||
import { EnvVar, Pod } from 'kubernetes-types/core/v1';
|
||||
import { Asterisk, File, FileCode, Key, Lock } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -44,7 +44,7 @@ export function ApplicationEnvVarsTable({ namespace, app }: Props) {
|
|||
{envVar.isInitContainer && (
|
||||
<span>
|
||||
<Icon icon={Asterisk} className="!ml-1" />
|
||||
{envVar.fieldPath} (
|
||||
{envVar.valueFrom?.fieldRef?.fieldPath} (
|
||||
<a
|
||||
href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/"
|
||||
target="_blank"
|
||||
|
@ -56,13 +56,13 @@ export function ApplicationEnvVarsTable({ namespace, app }: Props) {
|
|||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td data-cy="k8sAppDetail-envVarName">{envVar.key || '-'}</td>
|
||||
<td data-cy="k8sAppDetail-envVarName">{envVar.name}</td>
|
||||
<td data-cy="k8sAppDetail-envVarValue">
|
||||
{envVar.value && <span>{envVar.value}</span>}
|
||||
{envVar.fieldPath && (
|
||||
{envVar.valueFrom?.fieldRef?.fieldPath && (
|
||||
<span>
|
||||
<Icon icon={Asterisk} className="!ml-1" />
|
||||
{envVar.fieldPath} (
|
||||
{envVar.valueFrom.fieldRef.fieldPath} (
|
||||
<a
|
||||
href="https://kubernetes.io/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/"
|
||||
target="_blank"
|
||||
|
@ -73,36 +73,50 @@ export function ApplicationEnvVarsTable({ namespace, app }: Props) {
|
|||
)
|
||||
</span>
|
||||
)}
|
||||
{envVar.key ? (
|
||||
{envVar.valueFrom?.secretKeyRef?.key && (
|
||||
<span className="flex items-center">
|
||||
<Icon icon={Key} className="!mr-1" />
|
||||
{envVar.key}
|
||||
{envVar.valueFrom.secretKeyRef.key}
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
{envVar.valueFrom?.configMapKeyRef?.key && (
|
||||
<span className="flex items-center">
|
||||
<Icon icon={Key} className="!mr-1" />
|
||||
{envVar.valueFrom.configMapKeyRef.key}
|
||||
</span>
|
||||
)}
|
||||
{!envVar.value && !envVar.valueFrom && <span>-</span>}
|
||||
</td>
|
||||
<td data-cy="k8sAppDetail-configName">
|
||||
{!envVar.resourseName && <span>-</span>}
|
||||
{envVar.resourseName && (
|
||||
{!envVar.valueFrom?.configMapKeyRef?.name &&
|
||||
!envVar.valueFrom?.secretKeyRef?.name && <span>-</span>}
|
||||
{envVar.valueFrom?.configMapKeyRef && (
|
||||
<span>
|
||||
<Link
|
||||
to={
|
||||
envVar.type === 'configMap'
|
||||
? 'kubernetes.configmaps.configmap'
|
||||
: 'kubernetes.secrets.secret'
|
||||
}
|
||||
to="kubernetes.configmaps.configmap"
|
||||
params={{
|
||||
name: envVar.resourseName,
|
||||
name: envVar.valueFrom.configMapKeyRef.name,
|
||||
namespace,
|
||||
}}
|
||||
className="flex items-center"
|
||||
>
|
||||
<Icon
|
||||
icon={envVar.type === 'configMap' ? FileCode : Lock}
|
||||
className="!mr-1"
|
||||
/>
|
||||
{envVar.resourseName}
|
||||
<Icon icon={FileCode} className="!mr-1" />
|
||||
{envVar.valueFrom.configMapKeyRef.name}
|
||||
</Link>
|
||||
</span>
|
||||
)}
|
||||
{envVar.valueFrom?.secretKeyRef && (
|
||||
<span>
|
||||
<Link
|
||||
to="kubernetes.secrets.secret"
|
||||
params={{
|
||||
name: envVar.valueFrom.secretKeyRef.name,
|
||||
namespace,
|
||||
}}
|
||||
className="flex items-center"
|
||||
>
|
||||
<Icon icon={Lock} className="!mr-1" />
|
||||
{envVar.valueFrom.secretKeyRef.name}
|
||||
</Link>
|
||||
</span>
|
||||
)}
|
||||
|
@ -116,14 +130,9 @@ export function ApplicationEnvVarsTable({ namespace, app }: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
interface ContainerEnvVar {
|
||||
key?: string;
|
||||
value?: string;
|
||||
fieldPath?: string;
|
||||
interface ContainerEnvVar extends EnvVar {
|
||||
containerName: string;
|
||||
isInitContainer: boolean;
|
||||
type: 'configMap' | 'secret';
|
||||
resourseName: string;
|
||||
}
|
||||
|
||||
function getApplicationEnvironmentVariables(
|
||||
|
@ -141,60 +150,23 @@ function getApplicationEnvironmentVariables(
|
|||
|
||||
// get all the environment variables for each container
|
||||
const appContainersEnvVars =
|
||||
appContainers?.flatMap((container) => {
|
||||
const containerEnvVars: ContainerEnvVar[] =
|
||||
appContainers?.flatMap(
|
||||
(container) =>
|
||||
container?.env?.map((envVar) => ({
|
||||
key: envVar?.name,
|
||||
fieldPath: envVar?.valueFrom?.fieldRef?.fieldPath,
|
||||
...envVar,
|
||||
containerName: container.name,
|
||||
isInitContainer: false,
|
||||
type: envVar?.valueFrom?.configMapKeyRef ? 'configMap' : 'secret',
|
||||
resourseName:
|
||||
envVar?.valueFrom?.configMapKeyRef?.name ||
|
||||
envVar?.valueFrom?.secretKeyRef?.name ||
|
||||
'',
|
||||
})) || [];
|
||||
|
||||
const containerEnvFroms: ContainerEnvVar[] =
|
||||
container?.envFrom?.map((envFrom) => ({
|
||||
name: '',
|
||||
resourseName:
|
||||
envFrom?.configMapRef?.name || envFrom?.secretRef?.name || '',
|
||||
containerName: container.name,
|
||||
isInitContainer: false,
|
||||
type: envFrom?.configMapRef ? 'configMap' : 'secret',
|
||||
})) || [];
|
||||
|
||||
return [...containerEnvVars, ...containerEnvFroms];
|
||||
}) || [];
|
||||
|
||||
})) || []
|
||||
) || [];
|
||||
const appInitContainersEnvVars =
|
||||
appInitContainers?.flatMap((container) => {
|
||||
const containerEnvVars: ContainerEnvVar[] =
|
||||
appInitContainers?.flatMap(
|
||||
(container) =>
|
||||
container?.env?.map((envVar) => ({
|
||||
key: envVar?.name,
|
||||
fieldPath: envVar?.valueFrom?.fieldRef?.fieldPath,
|
||||
...envVar,
|
||||
containerName: container.name,
|
||||
isInitContainer: false,
|
||||
type: envVar?.valueFrom?.configMapKeyRef ? 'configMap' : 'secret',
|
||||
resourseName:
|
||||
envVar?.valueFrom?.configMapKeyRef?.name ||
|
||||
envVar?.valueFrom?.secretKeyRef?.name ||
|
||||
'',
|
||||
})) || [];
|
||||
|
||||
const containerEnvFroms: ContainerEnvVar[] =
|
||||
container?.envFrom?.map((envFrom) => ({
|
||||
name: '',
|
||||
resourseName:
|
||||
envFrom?.configMapRef?.name || envFrom?.secretRef?.name || '',
|
||||
containerName: container.name,
|
||||
isInitContainer: false,
|
||||
type: envFrom?.configMapRef ? 'configMap' : 'secret',
|
||||
})) || [];
|
||||
|
||||
return [...containerEnvVars, ...containerEnvFroms];
|
||||
}) || [];
|
||||
isInitContainer: true,
|
||||
})) || []
|
||||
) || [];
|
||||
|
||||
return [...appContainersEnvVars, ...appInitContainersEnvVars];
|
||||
}
|
||||
|
|
|
@ -250,12 +250,7 @@ async function getApplicationsByKind<T extends ApplicationList>(
|
|||
const { data } = await axios.get<T>(
|
||||
buildUrl(environmentId, namespace, `${appKind}s`)
|
||||
);
|
||||
const items = (data.items || []).map((app) => ({
|
||||
...app,
|
||||
kind: appKind,
|
||||
apiVersion: data.apiVersion,
|
||||
}));
|
||||
return items as T['items'];
|
||||
return data.items as T['items'];
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, `Unable to retrieve ${appKind}s`);
|
||||
}
|
||||
|
|
|
@ -19,15 +19,7 @@ export async function getNamespacePods(
|
|||
},
|
||||
}
|
||||
);
|
||||
const items = (data.items || []).map(
|
||||
(pod) =>
|
||||
<Pod>{
|
||||
...pod,
|
||||
kind: 'Pod',
|
||||
apiVersion: data.apiVersion,
|
||||
}
|
||||
);
|
||||
return items;
|
||||
return data.items;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve pods');
|
||||
}
|
||||
|
|
|
@ -14,16 +14,12 @@ export function getIsConfigMapInUse(
|
|||
? app?.spec
|
||||
: app?.spec?.template?.spec;
|
||||
|
||||
const hasEnvVarReference = appSpec?.containers.some((container) => {
|
||||
const valueFromEnv = container.env?.some(
|
||||
const hasEnvVarReference = appSpec?.containers.some((container) =>
|
||||
container.env?.some(
|
||||
(envVar) =>
|
||||
envVar.valueFrom?.configMapKeyRef?.name === configMap.metadata?.name
|
||||
);
|
||||
const envFromEnv = container.envFrom?.some(
|
||||
(envVar) => envVar.configMapRef?.name === configMap.metadata?.name
|
||||
);
|
||||
return valueFromEnv || envFromEnv;
|
||||
});
|
||||
)
|
||||
);
|
||||
const hasVolumeReference = appSpec?.volumes?.some(
|
||||
(volume) => volume.configMap?.name === configMap.metadata?.name
|
||||
);
|
||||
|
|
|
@ -11,16 +11,12 @@ export function getIsSecretInUse(secret: Secret, applications: Application[]) {
|
|||
? app?.spec
|
||||
: app?.spec?.template?.spec;
|
||||
|
||||
const hasEnvVarReference = appSpec?.containers.some((container) => {
|
||||
const valueFromEnv = container.env?.some(
|
||||
const hasEnvVarReference = appSpec?.containers.some((container) =>
|
||||
container.env?.some(
|
||||
(envVar) =>
|
||||
envVar.valueFrom?.secretKeyRef?.name === secret.metadata?.name
|
||||
);
|
||||
const envFromEnv = container.envFrom?.some(
|
||||
(envVar) => envVar.secretRef?.name === secret.metadata?.name
|
||||
);
|
||||
return valueFromEnv || envFromEnv;
|
||||
});
|
||||
)
|
||||
);
|
||||
const hasVolumeReference = appSpec?.volumes?.some(
|
||||
(volume) => volume.secret?.secretName === secret.metadata?.name
|
||||
);
|
||||
|
|
|
@ -165,8 +165,7 @@ export function gitAuthValidation(
|
|||
NewCredentialName: string()
|
||||
.default('')
|
||||
.when(['RepositoryAuthentication', 'SaveCredential'], {
|
||||
is: (RepositoryAuthentication: boolean, SaveCredential: boolean) =>
|
||||
RepositoryAuthentication && SaveCredential && !isAuthEdit,
|
||||
is: true,
|
||||
then: string()
|
||||
.required('Name is required')
|
||||
.test(
|
||||
|
|
|
@ -32,10 +32,3 @@ export function confirmEnableTLSVerify() {
|
|||
'Enabling the verification of TLS certificates without ensuring the correct configuration of your Certificate Authority (CA) for self-signed certificates can result in deployment failures.',
|
||||
});
|
||||
}
|
||||
|
||||
export function cleanGitRepoUrl(url: string) {
|
||||
return url
|
||||
.trim() // remove leading and trailing whitespace
|
||||
.replace(/\/$/, '') // if there's a trailing slash, remove it
|
||||
.replace(/\.git$/, ''); // if there's a trailing .git extension, remove it
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Download } from 'lucide-react';
|
||||
|
||||
import { useLocalStorage } from '@/react/hooks/useLocalStorage';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Widget, WidgetBody, WidgetTitle } from '@@/Widget';
|
||||
import { FormSection } from '@@/form-components/FormSection';
|
||||
|
@ -11,14 +10,11 @@ import { BackupFileForm } from './BackupFileForm';
|
|||
import { BackupS3Form } from './BackupS3Form';
|
||||
|
||||
export function BackupSettingsPanel() {
|
||||
const [backupType, setBackupType] = useLocalStorage(
|
||||
'settings_backup_type',
|
||||
options[0].value
|
||||
);
|
||||
const [backupType, setBackupType] = useState(options[0].value);
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetTitle icon={Download} title="Back up Portainer" />
|
||||
<WidgetTitle icon={Download} title="Backup up Portainer" />
|
||||
<WidgetBody>
|
||||
<div className="form-horizontal">
|
||||
<FormSection title="Backup configuration">
|
||||
|
|
|
@ -20,8 +20,8 @@ import { useUpdateSSLConfigMutation } from '../useUpdateSSLConfigMutation';
|
|||
import { useSSLSettings } from '../../queries/useSSLSettings';
|
||||
|
||||
interface FormValues {
|
||||
certFile?: File;
|
||||
keyFile?: File;
|
||||
certFile: File | null;
|
||||
keyFile: File | null;
|
||||
forceHTTPS: boolean;
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,8 @@ function SSLSettingsPanel() {
|
|||
}
|
||||
|
||||
const initialValues: FormValues = {
|
||||
certFile: undefined,
|
||||
keyFile: undefined,
|
||||
certFile: null,
|
||||
keyFile: null,
|
||||
forceHTTPS: !settingsQuery.data.httpEnabled,
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ function SSLSettingsPanel() {
|
|||
validationSchema={validation}
|
||||
validateOnMount
|
||||
>
|
||||
{({ values, setFieldValue, isValid, errors, dirty }) => (
|
||||
{({ values, setFieldValue, isValid, errors }) => (
|
||||
<Form className="form-horizontal">
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
|
@ -92,6 +92,7 @@ function SSLSettingsPanel() {
|
|||
errors={errors.certFile}
|
||||
>
|
||||
<FileUploadField
|
||||
required={typeof errors.certFile !== 'undefined'}
|
||||
inputId="ca-cert-field"
|
||||
name="certFile"
|
||||
onChange={(file) => setFieldValue('certFile', file)}
|
||||
|
@ -106,6 +107,7 @@ function SSLSettingsPanel() {
|
|||
errors={errors.keyFile}
|
||||
>
|
||||
<FileUploadField
|
||||
required={typeof errors.keyFile !== 'undefined'}
|
||||
inputId="ca-cert-field"
|
||||
name="keyFile"
|
||||
onChange={(file) => setFieldValue('keyFile', file)}
|
||||
|
@ -117,7 +119,7 @@ function SSLSettingsPanel() {
|
|||
<div className="col-sm-12">
|
||||
<LoadingButton
|
||||
isLoading={mutation.isLoading || reloadingPage}
|
||||
disabled={!dirty || !isValid}
|
||||
disabled={!isValid}
|
||||
loadingText={reloadingPage ? 'Reloading' : 'Saving'}
|
||||
className="!ml-0"
|
||||
>
|
||||
|
@ -133,12 +135,16 @@ function SSLSettingsPanel() {
|
|||
);
|
||||
|
||||
function handleSubmit({ certFile, forceHTTPS, keyFile }: FormValues) {
|
||||
if (!certFile || !keyFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutation.mutate(
|
||||
{ certFile, httpEnabled: !forceHTTPS, keyFile },
|
||||
{
|
||||
async onSuccess() {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 10000);
|
||||
setTimeout(resolve, 5000);
|
||||
});
|
||||
window.location.reload();
|
||||
setReloadingPage(true);
|
||||
|
@ -150,13 +156,10 @@ function SSLSettingsPanel() {
|
|||
|
||||
function validation(): SchemaOf<FormValues> {
|
||||
return object({
|
||||
certFile: withFileExtension(file(), [
|
||||
'pem',
|
||||
'crt',
|
||||
'cer',
|
||||
'cert',
|
||||
]).optional(),
|
||||
keyFile: withFileExtension(file(), ['pem', 'key']).optional(),
|
||||
certFile: withFileExtension(file(), ['pem', 'crt', 'cer', 'cert']).required(
|
||||
''
|
||||
),
|
||||
keyFile: withFileExtension(file(), ['pem', 'key']).required(''),
|
||||
forceHTTPS: bool().required(),
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue