1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

refactor(app): migrate configmap and secret form sections [EE-6233] (#10528)

* refactor(app): migrate configmap and secret form sections [EE-6233]
This commit is contained in:
Ali 2024-01-03 09:07:11 +13:00 committed by GitHub
parent 391b85da41
commit 7a2412b1be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 631 additions and 447 deletions

View file

@ -381,7 +381,7 @@
<!-- #region ENVIRONMENT VARIABLES -->
<div class="form-group">
<div class="col-sm-12 vertical-center">
<label class="control-label !pt-0 text-left">Environment variables</label>
<label class="control-label !pt-0 text-left !text-sm">Environment variables</label>
</div>
<div class="col-sm-11 col-lg-10 mt-2">
<kube-environment-variables-fieldset
@ -394,313 +394,26 @@
<!-- #endregion -->
<!-- #region CONFIGMAPS -->
<div class="form-group">
<div class="col-sm-12 vertical-center">
<label class="control-label !pt-0 text-left">ConfigMaps</label>
</div>
<div class="col-sm-12 small text-muted vertical-center" style="margin-top: 15px" ng-if="ctrl.formValues.ConfigMaps.length">
<pr-icon icon="'info'" mode="'primary'"></pr-icon>
Portainer will automatically expose all the keys of a ConfigMap as environment variables. This behavior can be overridden to filesystem mounts for each key
via the override option.
</div>
</div>
<!-- config-element -->
<div class="form-inline clearfix" ng-repeat="(index, config) in ctrl.formValues.ConfigMaps">
<div class="col-sm-12 !p-0">
<div class="input-group input-group-sm !mr-1">
<span class="input-group-addon">name</span>
<select
class="form-control col-sm-6"
ng-model="config.SelectedConfiguration"
ng-options="c as c.Name for c in ctrl.configMaps track by c.Name"
ng-change="ctrl.resetConfigMap(index)"
ng-disabled="ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-addConfigSelect_{{ $index }}"
></select>
</div>
<div class="input-group btn-group btn-group-sm">
<label
class="btn btn-md btn-light vertical-center !ml-0"
type="button"
ng-click="ctrl.resetConfigMap(index)"
ng-disabled="ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-configAutoButton_{{ $index }}"
uib-btn-radio="false"
ng-model="config.Overriden"
>
<pr-icon icon="'rotate-cw'" size="'md'"></pr-icon> Auto
</label>
<label
class="btn btn-md btn-light vertical-center !ml-0"
ng-click="ctrl.overrideConfigMap(index)"
ng-disabled="!config.SelectedConfiguration || ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-configOverrideButton_{{ $index }}"
uib-btn-radio="true"
ng-model="config.Overriden"
>
<pr-icon icon="'list'" size="'md'"></pr-icon> Override
</label>
</div>
<button
class="btn btn-md btn-dangerlight btn-only-icon vertical-center"
type="button"
ng-click="ctrl.removeConfigMap(index)"
ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-configRemoveButton"
>
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
</button>
</div>
<!-- no-override -->
<div class="row clearfix" ng-if="config.SelectedConfiguration && !config.Overriden">
<div class="col-sm-9 small text-muted !mt-2 !p-0">
The following keys will be loaded from the <code>{{ config.SelectedConfiguration.Name }}</code>
ConfigMap as environment variables:
<span ng-repeat="(key, _) in config.SelectedConfiguration.Data">
<code>{{ key }}</code
>{{ $last ? '' : ', ' }}
</span>
</div>
</div>
<!-- !no-override -->
<!-- has-override -->
<div class="col-sm-12 !mt-2 !mb-4 !p-0" ng-if="config.Overriden" ng-repeat="(keyIndex, overridenKey) in config.OverridenKeys" style="margin-top: 2px">
<div class="input-group input-group-sm !mr-1">
<span class="input-group-addon">key</span>
<input type="text" class="form-control" ng-value="overridenKey.Key" disabled />
</div>
<div class="input-group btn-group btn-group-sm !mr-1">
<label class="btn btn-light" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.ENVIRONMENT">
<pr-icon icon="'list'"></pr-icon> Environment
</label>
<label class="btn btn-light" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<pr-icon icon="'file-text'"></pr-icon> Filesystem
</label>
</div>
<div class="form-group !ml-0 !mr-0 !align-top" ng-if="overridenKey.Type === ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<div class="input-group input-group-sm">
<span class="input-group-addon required">path on disk</span>
<input
type="text"
class="form-control"
ng-model="overridenKey.Path"
placeholder="/etc/myapp/conf.d"
name="overriden_key_configmap_path_{{ index }}_{{ keyIndex }}"
ng-disabled="ctrl.formValues.Containers.length > 1"
required
ng-change="ctrl.onChangeConfigMapPath()"
data-cy="k8sAppCreate-pathOnDiskInput"
/>
</div>
<div
class="small"
ng-show="
kubernetesApplicationCreationForm['overriden_key_configmap_path_' + index + '_' + keyIndex].$invalid ||
ctrl.state.duplicates.configMapPaths.refs[index + '_' + keyIndex] !== undefined
"
>
<div class="text-warning" ng-if="overridenKey.Type === ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<div
ng-show="
kubernetesApplicationCreationForm['overriden_key_configmap_path_' + index + '_' + keyIndex].$invalid ||
ctrl.state.duplicates.configMapPaths.refs[index + '_' + keyIndex] !== undefined
"
>
<ng-messages for="kubernetesApplicationCreationForm['overriden_key_configmap_path_' + index + '_' + keyIndex].$error">
<p class="vertical-center" ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Path is required.</p>
</ng-messages>
<p class="vertical-center" ng-if="ctrl.state.duplicates.configMapPaths.refs[index + '_' + keyIndex] !== undefined">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This path is already used.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- !has-override -->
</div>
<!-- !config-element -->
<div class="col-sm-12 !p-0">
<button
type="button"
class="btn btn-primary btn-sm btn btn-sm btn-light mb-2 !ml-0"
ng-disabled="ctrl.configMaps.length === 0"
ng-click="ctrl.addConfigMap()"
ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-addConfigButton"
>
<pr-icon icon="'plus'" size="'sm'"></pr-icon> Add ConfigMap
</button>
</div>
<div class="my-2 w-full">
<p class="vertical-center text-warning text-xs" ng-if="ctrl.configMaps.length === 0"
><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> There are no ConfigMaps available in this namespace.</p
>
</div>
<config-maps-form-section
values="ctrl.formValues.ConfigMaps"
on-change="(ctrl.onConfigMapsChange)"
namespace="ctrl.formValues.ResourcePool.Namespace.Name"
validation-data="ctrl.formValues.ConfigMaps"
></config-maps-form-section>
<!-- #region SECRETS -->
<div class="form-group">
<div class="col-sm-12 vertical-center pt-2.5">
<label class="control-label !pt-0 text-left">Secrets</label>
</div>
<div class="col-sm-12 small text-muted vertical-center" style="margin-top: 15px" ng-if="ctrl.formValues.Secrets.length">
<pr-icon icon="'info'" mode="'primary'"></pr-icon>
Portainer will automatically expose all the keys of a Secret as environment variables. This behavior can be overridden to filesystem mounts for each key via
the override option.
</div>
</div>
<!-- config-element -->
<div class="form-inline clearfix" ng-repeat="(index, config) in ctrl.formValues.Secrets">
<div class="col-sm-12 !p-0">
<div class="input-group input-group-sm !mr-1">
<span class="input-group-addon">name</span>
<select
class="form-control col-sm-6"
ng-model="config.SelectedConfiguration"
ng-options="c as c.Name for c in ctrl.secrets track by c.Name"
ng-change="ctrl.resetSecret(index)"
ng-disabled="ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-addSecretSelect_{{ $index }}"
></select>
</div>
<div class="input-group btn-group btn-group-sm">
<label
class="btn btn-md btn-light vertical-center !ml-0"
type="button"
ng-click="ctrl.resetSecret(index)"
ng-disabled="ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-secretAutoButton_{{ $index }}"
uib-btn-radio="false"
ng-model="config.Overriden"
>
<pr-icon icon="'rotate-cw'" size="'md'"></pr-icon> Auto
</label>
<label
class="btn btn-md btn-light vertical-center !ml-0"
ng-click="ctrl.overrideSecret(index)"
ng-disabled="!config.SelectedConfiguration || ctrl.formValues.Containers.length > 1"
data-cy="k8sAppCreate-secretOverrideButton_{{ $index }}"
uib-btn-radio="true"
ng-model="config.Overriden"
>
<pr-icon icon="'list'" size="'md'"></pr-icon> Override
</label>
</div>
<button
class="btn btn-md btn-dangerlight btn-only-icon vertical-center"
type="button"
ng-click="ctrl.removeSecret(index)"
ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-secretRemoveButton"
>
<pr-icon icon="'trash-2'" size="'md'"></pr-icon>
</button>
</div>
<!-- no-override -->
<div class="row clearfix" ng-if="config.SelectedConfiguration && !config.Overriden">
<div class="col-sm-9 small text-muted !mt-2 !p-0">
The following keys will be loaded from the <code>{{ config.SelectedConfiguration.Name }}</code> Secret as environment variables:
<span ng-repeat="(key, _) in config.SelectedConfiguration.Data">
<code>{{ key }}</code
>{{ $last ? '' : ', ' }}
</span>
</div>
</div>
<!-- !no-override -->
<!-- has-override -->
<div class="col-sm-12 !mt-2 !mb-4 !p-0" ng-if="config.Overriden" ng-repeat="(keyIndex, overridenKey) in config.OverridenKeys" style="margin-top: 2px">
<div class="input-group input-group-sm !mr-1">
<span class="input-group-addon">key</span>
<input type="text" class="form-control" ng-value="overridenKey.Key" disabled />
</div>
<div class="input-group btn-group btn-group-sm !mr-1">
<label class="btn btn-light" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.ENVIRONMENT">
<pr-icon icon="'list'"></pr-icon> Environment
</label>
<label class="btn btn-light" ng-model="overridenKey.Type" uib-btn-radio="ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<pr-icon icon="'file-text'"></pr-icon> Filesystem
</label>
</div>
<div class="form-group !ml-0 !mr-0 !align-top" ng-if="overridenKey.Type === ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<div class="input-group input-group-sm">
<span class="input-group-addon required">path on disk</span>
<input
type="text"
class="form-control"
ng-model="overridenKey.Path"
placeholder="/etc/myapp/conf.d"
name="overriden_key_secret_path_{{ index }}_{{ keyIndex }}"
ng-disabled="ctrl.formValues.Containers.length > 1"
required
ng-change="ctrl.onChangeSecretPath()"
data-cy="k8sAppCreate-secretPathOnDiskInput"
/>
</div>
<div
class="small"
ng-show="
kubernetesApplicationCreationForm['overriden_key_secret_path_' + index + '_' + keyIndex].$invalid ||
ctrl.state.duplicates.secretPaths.refs[index + '_' + keyIndex] !== undefined
"
>
<div class="text-warning" ng-if="overridenKey.Type === ctrl.ApplicationConfigurationFormValueOverridenKeyTypes.FILESYSTEM">
<div
ng-show="
kubernetesApplicationCreationForm['overriden_key_secret_path_' + index + '_' + keyIndex].$invalid ||
ctrl.state.duplicates.secretPaths.refs[index + '_' + keyIndex] !== undefined
"
>
<ng-messages for="kubernetesApplicationCreationForm['overriden_key_secret_path_' + index + '_' + keyIndex].$error">
<p class="vertical-center" ng-message="required"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Path is required.</p>
</ng-messages>
<p class="vertical-center" ng-if="ctrl.state.duplicates.secretPaths.refs[index + '_' + keyIndex] !== undefined"
><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This path is already used.</p
>
</div>
</div>
</div>
</div>
</div>
<!-- !has-override -->
</div>
<div class="my-2 w-full">
<p class="vertical-center text-muted text-xs" ng-if="ctrl.secrets.length === 0">
<pr-icon icon="'info'" mode="'primary'"></pr-icon>
There are no secrets available in this namespace.
</p>
</div>
<!-- !config-element -->
<div class="col-sm-12 !p-0">
<button
type="button"
ng-disabled="ctrl.secrets.length === 0"
class="btn btn-primary btn-sm btn btn-sm btn-light mb-2 !ml-0"
ng-click="ctrl.addSecret()"
ng-if="ctrl.formValues.Containers.length <= 1"
data-cy="k8sAppCreate-addSecretButton"
>
<pr-icon icon="'plus'" size="'sm'"></pr-icon> Add Secret
</button>
</div>
<secrets-form-section
values="ctrl.formValues.Secrets"
on-change="(ctrl.onSecretsChange)"
namespace="ctrl.formValues.ResourcePool.Namespace.Name"
validation-data="ctrl.formValues.Secrets"
></secrets-form-section>
<!-- #endregion -->
<!-- #region PERSISTED FOLDERS -->
<div class="form-group">
<div class="col-sm-12 vertical-center mb-2 pt-2.5" style="margin-top: 5px">
<label class="control-label !pt-0 text-left">Persisted folders</label>
<label class="control-label !pt-0 text-left !text-sm">Persisted folders</label>
</div>
<div class="col-sm-12 small text-muted vertical-center mt-1" ng-if="!ctrl.storageClassAvailable()">

View file

@ -17,9 +17,6 @@ import {
KubernetesDeploymentTypes,
} from 'Kubernetes/models/application/models';
import {
KubernetesApplicationConfigurationFormValue,
KubernetesApplicationConfigurationFormValueOverridenKey,
KubernetesApplicationConfigurationFormValueOverridenKeyTypes,
KubernetesApplicationEnvironmentVariableFormValue,
KubernetesApplicationFormValues,
KubernetesApplicationPersistedFolderFormValue,
@ -85,7 +82,6 @@ class KubernetesCreateApplicationController {
this.ApplicationPublishingTypes = KubernetesApplicationPublishingTypes;
this.ApplicationPlacementTypes = KubernetesApplicationPlacementTypes;
this.ApplicationTypes = KubernetesApplicationTypes;
this.ApplicationConfigurationFormValueOverridenKeyTypes = KubernetesApplicationConfigurationFormValueOverridenKeyTypes;
this.ServiceTypes = KubernetesServiceTypes;
this.KubernetesDeploymentTypes = KubernetesDeploymentTypes;
@ -155,6 +151,8 @@ class KubernetesCreateApplicationController {
this.onChangePlacementType = this.onChangePlacementType.bind(this);
this.onServicesChange = this.onServicesChange.bind(this);
this.onEnvironmentVariableChange = this.onEnvironmentVariableChange.bind(this);
this.onConfigMapsChange = this.onConfigMapsChange.bind(this);
this.onSecretsChange = this.onSecretsChange.bind(this);
}
/* #endregion */
@ -240,34 +238,12 @@ class KubernetesCreateApplicationController {
/* #endregion */
/* #region CONFIGMAP UI MANAGEMENT */
addConfigMap() {
let config = new KubernetesApplicationConfigurationFormValue();
config.SelectedConfiguration = this.configMaps[0];
this.formValues.ConfigMaps.push(config);
}
removeConfigMap(index) {
this.formValues.ConfigMaps.splice(index, 1);
this.onChangeConfigMapPath();
}
overrideConfigMap(index) {
const config = this.formValues.ConfigMaps[index];
config.Overriden = true;
config.OverridenKeys = _.map(_.keys(config.SelectedConfiguration.Data), (key) => {
const res = new KubernetesApplicationConfigurationFormValueOverridenKey();
res.Key = key;
return res;
onConfigMapsChange(configMaps) {
return this.$async(async () => {
this.formValues.ConfigMaps = configMaps;
});
}
resetConfigMap(index) {
const config = this.formValues.ConfigMaps[index];
config.Overriden = false;
config.OverridenKeys = [];
this.onChangeConfigMapPath();
}
clearConfigMaps() {
this.formValues.ConfigMaps = [];
}
@ -278,7 +254,7 @@ class KubernetesCreateApplicationController {
const paths = _.reduce(
this.formValues.ConfigMaps,
(result, config) => {
const uniqOverridenKeysPath = _.uniq(_.map(config.OverridenKeys, 'Path'));
const uniqOverridenKeysPath = _.uniq(_.map(config.overridenKeys, 'path'));
return _.concat(result, uniqOverridenKeysPath);
},
[]
@ -287,8 +263,8 @@ class KubernetesCreateApplicationController {
const duplicatePaths = KubernetesFormValidationHelper.getDuplicates(paths);
_.forEach(this.formValues.ConfigMaps, (config, index) => {
_.forEach(config.OverridenKeys, (overridenKey, keyIndex) => {
const findPath = _.find(duplicatePaths, (path) => path === overridenKey.Path);
_.forEach(config.overridenKeys, (overridenKey, keyIndex) => {
const findPath = _.find(duplicatePaths, (path) => path === overridenKey.path);
if (findPath) {
this.state.duplicates.configMapPaths.refs[index + '_' + keyIndex] = findPath;
}
@ -300,63 +276,15 @@ class KubernetesCreateApplicationController {
/* #endregion */
/* #region SECRET UI MANAGEMENT */
addSecret() {
let secret = new KubernetesApplicationConfigurationFormValue();
secret.SelectedConfiguration = this.secrets[0];
this.formValues.Secrets.push(secret);
}
removeSecret(index) {
this.formValues.Secrets.splice(index, 1);
this.onChangeSecretPath();
}
overrideSecret(index) {
const secret = this.formValues.Secrets[index];
secret.Overriden = true;
secret.OverridenKeys = _.map(_.keys(secret.SelectedConfiguration.Data), (key) => {
const res = new KubernetesApplicationConfigurationFormValueOverridenKey();
res.Key = key;
return res;
onSecretsChange(secrets) {
return this.$async(async () => {
this.formValues.Secrets = secrets;
});
}
resetSecret(index) {
const secret = this.formValues.Secrets[index];
secret.Overriden = false;
secret.OverridenKeys = [];
this.onChangeSecretPath();
}
clearSecrets() {
this.formValues.Secrets = [];
}
onChangeSecretPath() {
this.state.duplicates.secretPaths.refs = [];
const paths = _.reduce(
this.formValues.Secrets,
(result, secret) => {
const uniqOverridenKeysPath = _.uniq(_.map(secret.OverridenKeys, 'Path'));
return _.concat(result, uniqOverridenKeysPath);
},
[]
);
const duplicatePaths = KubernetesFormValidationHelper.getDuplicates(paths);
_.forEach(this.formValues.Secrets, (secret, index) => {
_.forEach(secret.OverridenKeys, (overridenKey, keyIndex) => {
const findPath = _.find(duplicatePaths, (path) => path === overridenKey.Path);
if (findPath) {
this.state.duplicates.secretPaths.refs[index + '_' + keyIndex] = findPath;
}
});
});
this.state.duplicates.secretPaths.hasRefs = Object.keys(this.state.duplicates.secretPaths.refs).length > 0;
}
/* #endregion */
/* #region ENVIRONMENT UI MANAGEMENT */
@ -920,7 +848,7 @@ class KubernetesCreateApplicationController {
try {
this.formValues.ApplicationOwner = this.Authentication.getUserDetails().username;
// combine the secrets and configmap form values when submitting the form
_.remove(this.formValues.Configurations, (item) => item.SelectedConfiguration === undefined);
_.remove(this.formValues.Configurations, (item) => item.selectedConfiguration === undefined);
await this.KubernetesApplicationService.create(this.formValues, this.originalServicePorts, this.deploymentOptions.hideStacksFunctionality);
this.Notifications.success('Request to deploy application successfully submitted', this.formValues.Name);
this.$state.go('kubernetes.applications');