mirror of https://github.com/portainer/portainer
feat(deployment): enforce deployment options EE-4416 (#7974)
parent
e0f3a8c0a2
commit
d012a4efc4
|
@ -72,7 +72,6 @@
|
||||||
<a href="https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/" target="_blank">privacy policy</a>.
|
<a href="https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/" target="_blank">privacy policy</a>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- login screen banner -->
|
<!-- login screen banner -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<por-switch-field
|
<por-switch-field
|
||||||
|
@ -86,7 +85,6 @@
|
||||||
></por-switch-field>
|
></por-switch-field>
|
||||||
</div>
|
</div>
|
||||||
<!-- !login screen banner -->
|
<!-- !login screen banner -->
|
||||||
|
|
||||||
<!-- templates -->
|
<!-- templates -->
|
||||||
<div class="col-sm-12 form-section-title"> App Templates </div>
|
<div class="col-sm-12 form-section-title"> App Templates </div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -112,6 +110,35 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !templates -->
|
<!-- !templates -->
|
||||||
|
<!-- actions -->
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
ng-click="saveApplicationSettings()"
|
||||||
|
ng-disabled="state.actionInProgress || !settings.TemplatesURL"
|
||||||
|
button-spinner="state.actionInProgress"
|
||||||
|
data-cy="settings-saveSettingsButton"
|
||||||
|
>
|
||||||
|
<span ng-hide="state.actionInProgress">Save application settings</span>
|
||||||
|
<span ng-show="state.actionInProgress">Saving...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- !actions -->
|
||||||
|
</form>
|
||||||
|
</rd-widget-body>
|
||||||
|
</rd-widget>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<rd-widget>
|
||||||
|
<rd-widget-header icon="svg-kube" feather-icon="true" title-text="Kubernetes settings"></rd-widget-header>
|
||||||
|
<rd-widget-body>
|
||||||
|
<form class="form-horizontal">
|
||||||
<!-- helm charts -->
|
<!-- helm charts -->
|
||||||
<div class="col-sm-12 form-section-title"> Helm Repository </div>
|
<div class="col-sm-12 form-section-title"> Helm Repository </div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -129,7 +156,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !helm charts -->
|
<!-- !helm charts -->
|
||||||
<!-- host-filesystem -->
|
|
||||||
<!-- kube -->
|
<!-- kube -->
|
||||||
<div class="col-sm-12 form-section-title"> Kubernetes </div>
|
<div class="col-sm-12 form-section-title"> Kubernetes </div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -138,25 +164,40 @@
|
||||||
<select
|
<select
|
||||||
id="kubeconfig_expiry"
|
id="kubeconfig_expiry"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
ng-model="settings.KubeconfigExpiry"
|
ng-model="formValues.KubeconfigExpiry"
|
||||||
ng-options="opt.value as opt.key for opt in state.availableKubeconfigExpiryOptions"
|
ng-options="opt.value as opt.key for opt in state.availableKubeconfigExpiryOptions"
|
||||||
></select>
|
></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- ! kube -->
|
<!-- ! kube -->
|
||||||
|
<!-- deployment options -->
|
||||||
|
<div class="col-sm-12 form-section-title"> Deployment Options </div>
|
||||||
|
<div class="form-group">
|
||||||
|
<por-switch-field
|
||||||
|
label="'Enforce code-based deployment'"
|
||||||
|
name="'toggle_hideAddWithForm'"
|
||||||
|
feature-id="enforceDeploymentOptions"
|
||||||
|
disabled="true"
|
||||||
|
checked="false"
|
||||||
|
field-class="'col-sm-12'"
|
||||||
|
label-class="'col-sm-2'"
|
||||||
|
tooltip="'Hides the \'Add with form\' buttons and prevents adding/editing of resources via forms'"
|
||||||
|
></por-switch-field>
|
||||||
|
</div>
|
||||||
|
<!-- !deployment options -->
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
ng-click="saveApplicationSettings()"
|
ng-click="saveKubernetesSettings()"
|
||||||
ng-disabled="state.actionInProgress || !settings.TemplatesURL"
|
ng-disabled="state.kubeSettingsActionInProgress"
|
||||||
button-spinner="state.actionInProgress"
|
button-spinner="state.kubeSettingsActionInProgress"
|
||||||
data-cy="settings-saveSettingsButton"
|
data-cy="settings-saveSettingsButton"
|
||||||
>
|
>
|
||||||
<span ng-hide="state.actionInProgress">Save settings</span>
|
<span ng-hide="state.kubeSettingsActionInProgress">Save Kubernetes settings</span>
|
||||||
<span ng-show="state.actionInProgress">Saving...</span>
|
<span ng-show="state.kubeSettingsActionInProgress">Saving...</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,7 +243,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="label in settings.BlackListedLabels">
|
<tr ng-repeat="label in formValues.BlackListedLabels">
|
||||||
<td>{{ label.name }}</td>
|
<td>{{ label.name }}</td>
|
||||||
<td>{{ label.value }}</td>
|
<td>{{ label.value }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -211,10 +252,10 @@
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="settings.BlackListedLabels.length === 0">
|
<tr ng-if="formValues.BlackListedLabels.length === 0">
|
||||||
<td colspan="3" class="text-center text-muted">No filter available.</td>
|
<td colspan="3" class="text-center text-muted">No filter available.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="!settings.BlackListedLabels">
|
<tr ng-if="!formValues.BlackListedLabels">
|
||||||
<td colspan="3" class="text-center text-muted">Loading...</td>
|
<td colspan="3" class="text-center text-muted">Loading...</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -15,6 +15,7 @@ angular.module('portainer.app').controller('SettingsController', [
|
||||||
function ($scope, $state, Notifications, SettingsService, StateManager, BackupService, FileSaver) {
|
function ($scope, $state, Notifications, SettingsService, StateManager, BackupService, FileSaver) {
|
||||||
$scope.customBannerFeatureId = FeatureId.CUSTOM_LOGIN_BANNER;
|
$scope.customBannerFeatureId = FeatureId.CUSTOM_LOGIN_BANNER;
|
||||||
$scope.s3BackupFeatureId = FeatureId.S3_BACKUP_SETTING;
|
$scope.s3BackupFeatureId = FeatureId.S3_BACKUP_SETTING;
|
||||||
|
$scope.enforceDeploymentOptions = FeatureId.ENFORCE_DEPLOYMENT_OPTIONS;
|
||||||
|
|
||||||
$scope.backupOptions = options;
|
$scope.backupOptions = options;
|
||||||
|
|
||||||
|
@ -52,6 +53,9 @@ angular.module('portainer.app').controller('SettingsController', [
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
customLogo: false,
|
customLogo: false,
|
||||||
|
KubeconfigExpiry: undefined,
|
||||||
|
HelmRepositoryURL: undefined,
|
||||||
|
BlackListedLabels: [],
|
||||||
labelName: '',
|
labelName: '',
|
||||||
labelValue: '',
|
labelValue: '',
|
||||||
enableTelemetry: false,
|
enableTelemetry: false,
|
||||||
|
@ -91,21 +95,20 @@ angular.module('portainer.app').controller('SettingsController', [
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeFilteredContainerLabel = function (index) {
|
$scope.removeFilteredContainerLabel = function (index) {
|
||||||
var settings = $scope.settings;
|
const filteredSettings = $scope.formValues.BlackListedLabels.filter((_, i) => i !== index);
|
||||||
settings.BlackListedLabels.splice(index, 1);
|
const filteredSettingsPayload = { BlackListedLabels: filteredSettings };
|
||||||
|
updateSettings(filteredSettingsPayload, 'Hidden container settings updated');
|
||||||
updateSettings(settings);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.addFilteredContainerLabel = function () {
|
$scope.addFilteredContainerLabel = function () {
|
||||||
var settings = $scope.settings;
|
|
||||||
var label = {
|
var label = {
|
||||||
name: $scope.formValues.labelName,
|
name: $scope.formValues.labelName,
|
||||||
value: $scope.formValues.labelValue,
|
value: $scope.formValues.labelValue,
|
||||||
};
|
};
|
||||||
settings.BlackListedLabels.push(label);
|
|
||||||
|
|
||||||
updateSettings(settings);
|
const filteredSettings = [...$scope.formValues.BlackListedLabels, label];
|
||||||
|
const filteredSettingsPayload = { BlackListedLabels: filteredSettings };
|
||||||
|
updateSettings(filteredSettingsPayload, 'Hidden container settings updated');
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.downloadBackup = function () {
|
$scope.downloadBackup = function () {
|
||||||
|
@ -130,32 +133,45 @@ angular.module('portainer.app').controller('SettingsController', [
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// only update the values from the app settings widget. In future separate the api endpoints
|
||||||
$scope.saveApplicationSettings = function () {
|
$scope.saveApplicationSettings = function () {
|
||||||
var settings = $scope.settings;
|
const appSettingsPayload = {
|
||||||
|
SnapshotInterval: $scope.settings.SnapshotInterval,
|
||||||
if (!$scope.formValues.customLogo) {
|
LogoURL: $scope.formValues.customLogo ? $scope.settings.LogoURL : '',
|
||||||
settings.LogoURL = '';
|
EnableTelemetry: $scope.formValues.enableTelemetry,
|
||||||
}
|
TemplatesURL: $scope.settings.TemplatesURL,
|
||||||
|
};
|
||||||
settings.EnableTelemetry = $scope.formValues.enableTelemetry;
|
|
||||||
|
|
||||||
$scope.state.actionInProgress = true;
|
$scope.state.actionInProgress = true;
|
||||||
updateSettings(settings);
|
updateSettings(appSettingsPayload, 'Application settings updated');
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateSettings(settings) {
|
// only update the values from the kube settings widget. In future separate the api endpoints
|
||||||
|
$scope.saveKubernetesSettings = function () {
|
||||||
|
const kubeSettingsPayload = {
|
||||||
|
KubeconfigExpiry: $scope.formValues.KubeconfigExpiry,
|
||||||
|
HelmRepositoryURL: $scope.formValues.HelmRepositoryURL,
|
||||||
|
GlobalDeploymentOptions: $scope.formValues.GlobalDeploymentOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.state.kubeSettingsActionInProgress = true;
|
||||||
|
updateSettings(kubeSettingsPayload, 'Kubernetes settings updated');
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateSettings(settings, successMessage = 'Settings updated') {
|
||||||
SettingsService.update(settings)
|
SettingsService.update(settings)
|
||||||
.then(function success() {
|
.then(function success(response) {
|
||||||
Notifications.success('Success', 'Settings updated');
|
Notifications.success('Success', successMessage);
|
||||||
StateManager.updateLogo(settings.LogoURL);
|
StateManager.updateLogo(settings.LogoURL);
|
||||||
StateManager.updateSnapshotInterval(settings.SnapshotInterval);
|
StateManager.updateSnapshotInterval(settings.SnapshotInterval);
|
||||||
StateManager.updateEnableTelemetry(settings.EnableTelemetry);
|
StateManager.updateEnableTelemetry(settings.EnableTelemetry);
|
||||||
$state.reload();
|
$scope.formValues.BlackListedLabels = response.BlackListedLabels;
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to update settings');
|
Notifications.error('Failure', err, 'Unable to update settings');
|
||||||
})
|
})
|
||||||
.finally(function final() {
|
.finally(function final() {
|
||||||
|
$scope.state.kubeSettingsActionInProgress = false;
|
||||||
$scope.state.actionInProgress = false;
|
$scope.state.actionInProgress = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -172,7 +188,11 @@ angular.module('portainer.app').controller('SettingsController', [
|
||||||
if (settings.LogoURL !== '') {
|
if (settings.LogoURL !== '') {
|
||||||
$scope.formValues.customLogo = true;
|
$scope.formValues.customLogo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.formValues.enableTelemetry = settings.EnableTelemetry;
|
$scope.formValues.enableTelemetry = settings.EnableTelemetry;
|
||||||
|
$scope.formValues.KubeconfigExpiry = settings.KubeconfigExpiry;
|
||||||
|
$scope.formValues.HelmRepositoryURL = settings.HelmRepositoryURL;
|
||||||
|
$scope.formValues.BlackListedLabels = settings.BlackListedLabels;
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to retrieve application settings');
|
Notifications.error('Failure', err, 'Unable to retrieve application settings');
|
||||||
|
|
|
@ -53,6 +53,7 @@ import upload from '@/assets/ico/upload.svg?c';
|
||||||
import url from '@/assets/ico/url.svg?c';
|
import url from '@/assets/ico/url.svg?c';
|
||||||
import usercircle from '@/assets/ico/user-circle.svg?c';
|
import usercircle from '@/assets/ico/user-circle.svg?c';
|
||||||
import userlock from '@/assets/ico/user-lock.svg?c';
|
import userlock from '@/assets/ico/user-lock.svg?c';
|
||||||
|
import kube from '@/assets/ico/kube.svg?c';
|
||||||
import Placeholder from '@/assets/ico/placeholder.svg?c'; // Placeholder is used when an icon name cant be matched
|
import Placeholder from '@/assets/ico/placeholder.svg?c'; // Placeholder is used when an icon name cant be matched
|
||||||
// vendor icons
|
// vendor icons
|
||||||
import aws from '@/assets/ico/vendor/aws.svg?c';
|
import aws from '@/assets/ico/vendor/aws.svg?c';
|
||||||
|
@ -152,6 +153,7 @@ export const SvgIcons = {
|
||||||
proget,
|
proget,
|
||||||
quay,
|
quay,
|
||||||
internal,
|
internal,
|
||||||
|
kube,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SvgProps {
|
interface SvgProps {
|
||||||
|
|
|
@ -32,5 +32,6 @@ export enum FeatureId {
|
||||||
POD_SECURITY_POLICY_CONSTRAINT = 'pod-security-policy-constraint',
|
POD_SECURITY_POLICY_CONSTRAINT = 'pod-security-policy-constraint',
|
||||||
HIDE_DOCKER_HUB_ANONYMOUS = 'hide-docker-hub-anonymous',
|
HIDE_DOCKER_HUB_ANONYMOUS = 'hide-docker-hub-anonymous',
|
||||||
CUSTOM_LOGIN_BANNER = 'custom-login-banner',
|
CUSTOM_LOGIN_BANNER = 'custom-login-banner',
|
||||||
|
ENFORCE_DEPLOYMENT_OPTIONS = 'k8s-enforce-deployment-options',
|
||||||
K8S_ADM_ONLY_USR_INGRESS_DEPLY = 'k8s-admin-only-ingress-deploy',
|
K8S_ADM_ONLY_USR_INGRESS_DEPLY = 'k8s-admin-only-ingress-deploy',
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export async function init(edition: Edition) {
|
||||||
[FeatureId.POD_SECURITY_POLICY_CONSTRAINT]: Edition.BE,
|
[FeatureId.POD_SECURITY_POLICY_CONSTRAINT]: Edition.BE,
|
||||||
[FeatureId.HIDE_DOCKER_HUB_ANONYMOUS]: Edition.BE,
|
[FeatureId.HIDE_DOCKER_HUB_ANONYMOUS]: Edition.BE,
|
||||||
[FeatureId.CUSTOM_LOGIN_BANNER]: Edition.BE,
|
[FeatureId.CUSTOM_LOGIN_BANNER]: Edition.BE,
|
||||||
|
[FeatureId.ENFORCE_DEPLOYMENT_OPTIONS]: Edition.BE,
|
||||||
[FeatureId.K8S_ADM_ONLY_USR_INGRESS_DEPLY]: Edition.BE,
|
[FeatureId.K8S_ADM_ONLY_USR_INGRESS_DEPLY]: Edition.BE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue