diff --git a/models/configuration_k_v.go b/models/configuration_k_v.go
index c6421982c..68c296a35 100644
--- a/models/configuration_k_v.go
+++ b/models/configuration_k_v.go
@@ -25,6 +25,7 @@ package models
import (
"context"
+ "github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
@@ -34,6 +35,9 @@ import (
// swagger:model configurationKV
type ConfigurationKV struct {
+ // env override
+ EnvOverride *EnvOverride `json:"env_override,omitempty"`
+
// key
Key string `json:"key,omitempty"`
@@ -43,11 +47,64 @@ type ConfigurationKV struct {
// Validate validates this configuration k v
func (m *ConfigurationKV) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateEnvOverride(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
return nil
}
-// ContextValidate validates this configuration k v based on context it is used
+func (m *ConfigurationKV) validateEnvOverride(formats strfmt.Registry) error {
+ if swag.IsZero(m.EnvOverride) { // not required
+ return nil
+ }
+
+ if m.EnvOverride != nil {
+ if err := m.EnvOverride.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("env_override")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("env_override")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+// ContextValidate validate this configuration k v based on the context it is used
func (m *ConfigurationKV) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.contextValidateEnvOverride(ctx, formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ConfigurationKV) contextValidateEnvOverride(ctx context.Context, formats strfmt.Registry) error {
+
+ if m.EnvOverride != nil {
+ if err := m.EnvOverride.ContextValidate(ctx, formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("env_override")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("env_override")
+ }
+ return err
+ }
+ }
+
return nil
}
diff --git a/models/env_override.go b/models/env_override.go
new file mode 100644
index 000000000..79cb2903c
--- /dev/null
+++ b/models/env_override.go
@@ -0,0 +1,70 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 MinIO, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// EnvOverride env override
+//
+// swagger:model envOverride
+type EnvOverride struct {
+
+ // name
+ Name string `json:"name,omitempty"`
+
+ // value
+ Value string `json:"value,omitempty"`
+}
+
+// Validate validates this env override
+func (m *EnvOverride) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this env override based on context it is used
+func (m *EnvOverride) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *EnvOverride) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *EnvOverride) UnmarshalBinary(b []byte) error {
+ var res EnvOverride
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/portal-ui/src/screens/Console/Configurations/types.ts b/portal-ui/src/screens/Console/Configurations/types.ts
index 3abc113b4..0e89df450 100644
--- a/portal-ui/src/screens/Console/Configurations/types.ts
+++ b/portal-ui/src/screens/Console/Configurations/types.ts
@@ -49,9 +49,15 @@ export interface IConfigurationElement {
url?: string;
}
+export interface IEnvOverride {
+ name: string;
+ value: string;
+}
+
export interface IElementValue {
key: string;
value: string;
+ env_override?: IEnvOverride;
}
export interface IConfigurationSys {
@@ -65,3 +71,12 @@ export interface IElement {
icon?: any;
disabled?: boolean;
}
+
+export interface OverrideValue {
+ value: string;
+ overrideEnv: string;
+}
+
+export interface IOverrideEnv {
+ [key: string]: OverrideValue;
+}
diff --git a/portal-ui/src/screens/Console/Configurations/utils.tsx b/portal-ui/src/screens/Console/Configurations/utils.tsx
index 5a0575e4c..7cecb77d4 100644
--- a/portal-ui/src/screens/Console/Configurations/utils.tsx
+++ b/portal-ui/src/screens/Console/Configurations/utils.tsx
@@ -22,7 +22,7 @@ import FindReplaceIcon from "@mui/icons-material/FindReplace";
import VpnKeyIcon from "@mui/icons-material/VpnKey";
import PendingActionsIcon from "@mui/icons-material/PendingActions";
import CallToActionIcon from "@mui/icons-material/CallToAction";
-import { IElement, IElementValue } from "./types";
+import { IElement, IElementValue, IOverrideEnv, OverrideValue } from "./types";
export const configurationElements: IElement[] = [
{
@@ -309,3 +309,21 @@ export const selectSAs = (
setSelectedSAs(elements);
return elements;
};
+
+export const overrideFields = (formFields: IElementValue[]): IOverrideEnv => {
+ let overrideReturn: IOverrideEnv = {};
+
+ formFields.forEach((envItem) => {
+ // it has override values, we construct the value
+ if (envItem.env_override) {
+ const value: OverrideValue = {
+ value: envItem.env_override.value,
+ overrideEnv: envItem.env_override.name,
+ };
+
+ overrideReturn = { ...overrideReturn, [envItem.key]: value };
+ }
+ });
+
+ return overrideReturn;
+};
diff --git a/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx b/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx
index 98b50b773..202379054 100644
--- a/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx
+++ b/portal-ui/src/screens/Console/EventDestinations/ConfTargetGeneric.tsx
@@ -19,7 +19,7 @@ import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
-import { IElementValue, KVField } from "../Configurations/types";
+import { IElementValue, IOverrideEnv, KVField } from "../Configurations/types";
import {
formFieldStyles,
modalBasic,
@@ -28,11 +28,14 @@ import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWr
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
import CommentBoxWrapper from "../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+import PredefinedList from "../Common/FormComponents/PredefinedList/PredefinedList";
+import { ConsoleIcon, Tooltip } from "mds";
interface IConfGenericProps {
onChange: (newValue: IElementValue[]) => void;
fields: KVField[];
defaultVals?: IElementValue[];
+ overrideEnv?: IOverrideEnv;
classes: any;
}
@@ -69,6 +72,7 @@ const ConfTargetGeneric = ({
onChange,
fields,
defaultVals,
+ overrideEnv,
classes,
}: IConfGenericProps) => {
const [valueHolder, setValueHolder] = useState([]);
@@ -102,9 +106,42 @@ const ConfTargetGeneric = ({
};
const fieldDefinition = (field: KVField, item: number) => {
+ const holderItem = valueHolder[item];
+
+ if (holderItem) {
+ // Override Value with env var, we display generic string component
+ const override = overrideEnv?.[`${holderItem.key}`];
+
+ if (override) {
+ return (
+
+
+
+
+
+ }
+ />
+ );
+ }
+ }
+
switch (field.type) {
case "on|off":
- const value = valueHolder[item] ? valueHolder[item].value : "off";
+ const value = holderItem ? holderItem.value : "off";
return (
{
@@ -141,7 +178,7 @@ const ConfTargetGeneric = ({
name={field.name}
label={field.label}
tooltip={field.tooltip}
- value={valueHolder[item] ? valueHolder[item].value : ""}
+ value={holderItem ? holderItem.value : ""}
onChange={(e: React.ChangeEvent) =>
setValueElement(field.name, e.target.value, item)
}
@@ -155,7 +192,7 @@ const ConfTargetGeneric = ({
name={field.name}
label={field.label}
tooltip={field.tooltip}
- value={valueHolder[item] ? valueHolder[item].value : ""}
+ value={holderItem ? holderItem.value : ""}
onChange={(e: React.ChangeEvent) =>
setValueElement(field.name, e.target.value, item)
}
diff --git a/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx b/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx
index acee4aaa6..4ca81c430 100644
--- a/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx
+++ b/portal-ui/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.tsx
@@ -32,12 +32,14 @@ import {
} from "../../Common/FormComponents/common/styleLibrary";
import {
fieldsConfigurations,
+ overrideFields,
removeEmptyFields,
} from "../../Configurations/utils";
import {
IConfigurationElement,
IConfigurationSys,
IElementValue,
+ IOverrideEnv,
} from "../../Configurations/types";
import { ErrorResponseHandler } from "../../../../common/types";
import ResetConfigurationModal from "./ResetConfigurationModal";
@@ -89,6 +91,7 @@ const EditConfiguration = ({
);
const [resetConfigurationOpen, setResetConfigurationOpen] =
useState(false);
+ const [overrideEnvs, setOverrideEnvs] = useState({});
const loadingConfig = useSelector(
(state: AppState) => state.system.loadingConfigurations
@@ -109,6 +112,7 @@ const EditConfiguration = ({
setConfigSubsysList(res);
const keyVals = get(res[0], "key_values", []);
setConfigValues(keyVals);
+ setOverrideEnvs(overrideFields(keyVals));
dispatch(configurationIsLoading(false));
})
.catch((err: ErrorResponseHandler) => {
@@ -221,6 +225,7 @@ const EditConfiguration = ({
}
onChange={onValueChange}
defaultVals={configValues}
+ overrideEnv={overrideEnvs}
/>
.
import React, { Fragment, useEffect, useState } from "react";
-import { Button, Grid } from "mds";
+import { Button, ConsoleIcon, Grid, Tooltip } from "mds";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import { Webhook } from "@mui/icons-material";
@@ -34,6 +34,8 @@ import { useAppDispatch } from "../../../../store";
import { LinearProgress } from "@mui/material";
import { IConfigurationSys } from "../../Configurations/types";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+import PredefinedList from "../../Common/FormComponents/PredefinedList/PredefinedList";
+import { overrideFields } from "../../Configurations/utils";
interface IEndpointModal {
open: boolean;
@@ -166,6 +168,11 @@ const EditEndpointModal = ({
};
const defaultWH = !name.includes(":");
+ const hasOverride = endpointInfo.key_values.filter(
+ (itm) => !!itm.env_override
+ );
+
+ const overrideValues = overrideFields(hasOverride);
let title = "Edit Webhook";
let icon = ;
@@ -181,6 +188,10 @@ const EditEndpointModal = ({
break;
}
+ if (hasOverride.length > 0) {
+ title = "View env variable Webhook";
+ }
+
return (
-
- ) => {
- const value = e.target.checked ? "on" : "off";
- setEndpointState(value);
- }}
- id={"endpoint_enabled"}
- name={"endpoint_enabled"}
- label={"Enabled"}
- value={"switch_on"}
- checked={endpointState === "on"}
- />
-
-
- ) => {
- setEndpoint(event.target.value);
- validateInput("endpoint", event.target.validity.valid);
- }}
- error={
- invalidInputs.includes("endpoint") ? "Invalid Endpoint set" : ""
- }
- label="Endpoint"
- value={endpoint}
- pattern={
- "^(https?):\\/\\/([a-zA-Z0-9\\-.]+)(:[0-9]+)?(\\/[a-zA-Z0-9\\-.\\/]*)?$"
- }
- required
- />
-
-
- ) => {
- setAuthToken(event.target.value);
- }}
- label="Auth Token"
- value={authToken}
- />
-
- {saving && (
-
-
-
+ {hasOverride.length > 0 ? (
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ }
+ />
+
+
+ ) : (
+
+
+ ) => {
+ const value = e.target.checked ? "on" : "off";
+ setEndpointState(value);
+ }}
+ id={"endpoint_enabled"}
+ name={"endpoint_enabled"}
+ label={"Enabled"}
+ value={"switch_on"}
+ checked={endpointState === "on"}
+ />
+
+
+ ) => {
+ setEndpoint(event.target.value);
+ validateInput("endpoint", event.target.validity.valid);
+ }}
+ error={
+ invalidInputs.includes("endpoint")
+ ? "Invalid Endpoint set"
+ : ""
+ }
+ label="Endpoint"
+ value={endpoint}
+ pattern={
+ "^(https?):\\/\\/([a-zA-Z0-9\\-.]+)(:[0-9]+)?(\\/[a-zA-Z0-9\\-.\\/]*)?$"
+ }
+ required
+ />
+
+
+ ) => {
+ setAuthToken(event.target.value);
+ }}
+ label="Auth Token"
+ value={authToken}
+ />
+
+ {saving && (
+
+
+
+ )}
+
+
+
+
+
)}
-
-
-
-
);
diff --git a/portal-ui/src/screens/Console/EventDestinations/WebhookSettings/WebhookSettings.tsx b/portal-ui/src/screens/Console/EventDestinations/WebhookSettings/WebhookSettings.tsx
index ba5f5d185..65561a455 100644
--- a/portal-ui/src/screens/Console/EventDestinations/WebhookSettings/WebhookSettings.tsx
+++ b/portal-ui/src/screens/Console/EventDestinations/WebhookSettings/WebhookSettings.tsx
@@ -16,7 +16,14 @@
import React, { Fragment, useState } from "react";
import { IConfigurationSys, IElementValue } from "../../Configurations/types";
-import { Button, DataTable, Grid, TierOfflineIcon, TierOnlineIcon } from "mds";
+import {
+ Button,
+ ConsoleIcon,
+ DataTable,
+ Grid,
+ TierOfflineIcon,
+ TierOnlineIcon,
+} from "mds";
import AddEndpointModal from "./AddEndpointModal";
import DeleteWebhookEndpoint from "./DeleteWebhookEndpoint";
import EditWebhookEndpoint from "./EditWebhookEndpoint";
@@ -43,6 +50,10 @@ const WebhookSettings = ({
const endpointFilter = item.find((itm) => itm.key === "endpoint");
if (endpointFilter) {
+ if (endpointFilter.env_override) {
+ return endpointFilter.env_override.value;
+ }
+
return endpointFilter.value;
}
@@ -52,8 +63,32 @@ const WebhookSettings = ({
const renderWebhookStatus = (item: IElementValue[]) => {
const EnableFilter = item.find((itm) => itm.key === "enable");
+ if (EnableFilter?.env_override) {
+ const overrideEnabled =
+ !EnableFilter?.env_override.value ||
+ EnableFilter?.env_override.value === "on" ||
+ !EnableFilter?.env_override.value
+ ? "Enabled"
+ : "Disabled";
+ return (
+
+
+ {overrideEnabled ? "Enabled" : "Disabled"}
+
+ );
+ }
+
// If enable is not set, then enabled by default
- if (!EnableFilter || EnableFilter.value === "on") {
+ if (!EnableFilter || EnableFilter.value === "on" || !EnableFilter.value) {
return (
{
+ const wHook = WebhookSettingslist.find(
+ (element) => element.name === item
+ );
+
+ if (wHook) {
+ const hasOverride = wHook.key_values.filter(
+ (itm) => !!itm.env_override
+ );
+
+ // Has override values, we cannot delete.
+ if (hasOverride.length > 0) {
+ return true;
+ }
+
+ return false;
+ }
+ return false;
+ },
},
];
-
return (
{newEndpointOpen && (
@@ -187,5 +240,4 @@ const WebhookSettings = ({
);
};
-
export default WebhookSettings;
diff --git a/restapi/admin_config.go b/restapi/admin_config.go
index 82cc8ddf4..2ff077d67 100644
--- a/restapi/admin_config.go
+++ b/restapi/admin_config.go
@@ -129,9 +129,16 @@ func getConfig(ctx context.Context, client MinioAdmin, name string) ([]*models.C
}
var confkv []*models.ConfigurationKV
for _, kv := range scfg.KV {
- // FIXME: Ignoring env-overrides for now as support for this
- // needs to be added for presentation.
- confkv = append(confkv, &models.ConfigurationKV{Key: kv.Key, Value: kv.Value})
+ var envOverride *models.EnvOverride
+
+ if kv.EnvOverride != nil {
+ envOverride = &models.EnvOverride{
+ Name: kv.EnvOverride.Name,
+ Value: kv.EnvOverride.Value,
+ }
+ }
+
+ confkv = append(confkv, &models.ConfigurationKV{Key: kv.Key, Value: kv.Value, EnvOverride: envOverride})
}
if len(confkv) == 0 {
continue
diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go
index d9c63b57a..ee6315aec 100644
--- a/restapi/embedded_spec.go
+++ b/restapi/embedded_spec.go
@@ -5870,6 +5870,9 @@ func init() {
"configurationKV": {
"type": "object",
"properties": {
+ "env_override": {
+ "$ref": "#/definitions/envOverride"
+ },
"key": {
"type": "string"
},
@@ -5939,6 +5942,17 @@ func init() {
}
}
},
+ "envOverride": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"environmentConstants": {
"type": "object",
"properties": {
@@ -14554,6 +14568,9 @@ func init() {
"configurationKV": {
"type": "object",
"properties": {
+ "env_override": {
+ "$ref": "#/definitions/envOverride"
+ },
"key": {
"type": "string"
},
@@ -14623,6 +14640,17 @@ func init() {
}
}
},
+ "envOverride": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"environmentConstants": {
"type": "object",
"properties": {
diff --git a/swagger-console.yml b/swagger-console.yml
index f8e7c044b..3dc1433d3 100644
--- a/swagger-console.yml
+++ b/swagger-console.yml
@@ -3894,6 +3894,15 @@ definitions:
type: string
value:
type: string
+ env_override:
+ $ref: "#/definitions/envOverride"
+ envOverride:
+ type: object
+ properties:
+ name:
+ type: string
+ value:
+ type: string
configuration:
type: object
properties: