Add: Allow to configure env variables during tenant creation (#2322)

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2022-09-20 16:58:31 -07:00
committed by GitHub
parent fdd5a94074
commit 0af36a5757
9 changed files with 399 additions and 6 deletions

View File

@@ -58,6 +58,9 @@ type CreateTenantRequest struct {
// encryption
Encryption *EncryptionConfiguration `json:"encryption,omitempty"`
// environment variables
EnvironmentVariables []*EnvironmentVariable `json:"environmentVariables"`
// erasure coding parity
ErasureCodingParity int64 `json:"erasureCodingParity,omitempty"`
@@ -123,6 +126,10 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateEnvironmentVariables(formats); err != nil {
res = append(res, err)
}
if err := m.validateIdp(formats); err != nil {
res = append(res, err)
}
@@ -199,6 +206,32 @@ func (m *CreateTenantRequest) validateEncryption(formats strfmt.Registry) error
return nil
}
func (m *CreateTenantRequest) validateEnvironmentVariables(formats strfmt.Registry) error {
if swag.IsZero(m.EnvironmentVariables) { // not required
return nil
}
for i := 0; i < len(m.EnvironmentVariables); i++ {
if swag.IsZero(m.EnvironmentVariables[i]) { // not required
continue
}
if m.EnvironmentVariables[i] != nil {
if err := m.EnvironmentVariables[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *CreateTenantRequest) validateIdp(formats strfmt.Registry) error {
if swag.IsZero(m.Idp) { // not required
return nil
@@ -355,6 +388,10 @@ func (m *CreateTenantRequest) ContextValidate(ctx context.Context, formats strfm
res = append(res, err)
}
if err := m.contextValidateEnvironmentVariables(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateIdp(ctx, formats); err != nil {
res = append(res, err)
}
@@ -417,6 +454,26 @@ func (m *CreateTenantRequest) contextValidateEncryption(ctx context.Context, for
return nil
}
func (m *CreateTenantRequest) contextValidateEnvironmentVariables(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.EnvironmentVariables); i++ {
if m.EnvironmentVariables[i] != nil {
if err := m.EnvironmentVariables[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *CreateTenantRequest) contextValidateIdp(ctx context.Context, formats strfmt.Registry) error {
if m.Idp != nil {

View File

@@ -2620,6 +2620,12 @@ func init() {
"type": "object",
"$ref": "#/definitions/encryptionConfiguration"
},
"environmentVariables": {
"type": "array",
"items": {
"$ref": "#/definitions/environmentVariable"
}
},
"erasureCodingParity": {
"type": "integer"
},
@@ -8575,6 +8581,12 @@ func init() {
"type": "object",
"$ref": "#/definitions/encryptionConfiguration"
},
"environmentVariables": {
"type": "array",
"items": {
"$ref": "#/definitions/environmentVariable"
}
},
"erasureCodingParity": {
"type": "integer"
},

View File

@@ -537,6 +537,11 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
Console: tenantReq.ExposeConsole,
}
// set custom environment variables in configuration file
for _, envVar := range tenantReq.EnvironmentVariables {
tenantConfigurationENV[envVar.Key] = envVar.Value
}
// write tenant configuration to secret that contains config.env
tenantConfigurationName := fmt.Sprintf("%s-env-configuration", tenantName)
_, err = createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{

View File

@@ -732,3 +732,166 @@ export const getClientOS = (): string => {
return getPlatform;
};
export const MinIOEnvironmentVariables = [
"MINIO_ACCESS_KEY",
"MINIO_ACCESS_KEY_OLD",
"MINIO_AUDIT_WEBHOOK_AUTH_TOKEN",
"MINIO_AUDIT_WEBHOOK_CLIENT_CERT",
"MINIO_AUDIT_WEBHOOK_CLIENT_KEY",
"MINIO_AUDIT_WEBHOOK_ENABLE",
"MINIO_AUDIT_WEBHOOK_ENDPOINT",
"MINIO_BROWSER",
"MINIO_BROWSER_REDIRECT_URL",
"MINIO_IDENTITY_LDAP_COMMENT",
"MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN",
"MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER",
"MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN",
"MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD",
"MINIO_IDENTITY_LDAP_SERVER_ADDR",
"MINIO_IDENTITY_LDAP_SERVER_INSECURE",
"MINIO_IDENTITY_LDAP_SERVER_STARTTLS",
"MINIO_IDENTITY_LDAP_STS_EXPIRY",
"MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY",
"MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN",
"MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER",
"MINIO_IDENTITY_LDAP_USERNAME_FORMAT",
"MINIO_IDENTITY_OPENID_CLAIM_NAME",
"MINIO_IDENTITY_OPENID_CLAIM_PREFIX",
"MINIO_IDENTITY_OPENID_CLIENT_ID",
"MINIO_IDENTITY_OPENID_CLIENT_SECRET",
"MINIO_IDENTITY_OPENID_COMMENT",
"MINIO_IDENTITY_OPENID_CONFIG_URL",
"MINIO_IDENTITY_OPENID_REDIRECT_URI",
"MINIO_IDENTITY_OPENID_SCOPES",
"MINIO_KMS_AUTO_ENCRYPTION",
"MINIO_KMS_KES_CERT_FILE",
"MINIO_KMS_KES_ENDPOINT",
"MINIO_KMS_KES_KEY_FILE",
"MINIO_KMS_KES_KEY_NAME",
"MINIO_KMS_SECRET_KEY",
"MINIO_LOGGER_WEBHOOK_AUTH_TOKEN",
"MINIO_LOGGER_WEBHOOK_ENABLE",
"MINIO_LOGGER_WEBHOOK_ENDPOINT",
"MINIO_LOG_QUERY_URL",
"MINIO_NOTIFY_AMQP_AUTO_DELETED",
"MINIO_NOTIFY_AMQP_COMMENT",
"MINIO_NOTIFY_AMQP_DELIVERY_MODE",
"MINIO_NOTIFY_AMQP_DURABLE",
"MINIO_NOTIFY_AMQP_ENABLE",
"MINIO_NOTIFY_AMQP_EXCHANGE",
"MINIO_NOTIFY_AMQP_EXCHANGE_TYPE",
"MINIO_NOTIFY_AMQP_INTERNAL",
"MINIO_NOTIFY_AMQP_MANDATORY",
"MINIO_NOTIFY_AMQP_NO_WAIT",
"MINIO_NOTIFY_AMQP_QUEUE_DIR",
"MINIO_NOTIFY_AMQP_QUEUE_LIMIT",
"MINIO_NOTIFY_AMQP_ROUTING_KEY",
"MINIO_NOTIFY_AMQP_URL",
"MINIO_NOTIFY_ELASTICSEARCH_COMMENT",
"MINIO_NOTIFY_ELASTICSEARCH_ENABLE",
"MINIO_NOTIFY_ELASTICSEARCH_FORMAT",
"MINIO_NOTIFY_ELASTICSEARCH_INDEX",
"MINIO_NOTIFY_ELASTICSEARCH_PASSWORD",
"MINIO_NOTIFY_ELASTICSEARCH_QUEUE_DIR",
"MINIO_NOTIFY_ELASTICSEARCH_QUEUE_LIMIT",
"MINIO_NOTIFY_ELASTICSEARCH_URL",
"MINIO_NOTIFY_ELASTICSEARCH_USERNAME",
"MINIO_NOTIFY_KAFKA_BROKERS",
"MINIO_NOTIFY_KAFKA_CLIENT_TLS_CERT",
"MINIO_NOTIFY_KAFKA_CLIENT_TLS_KEY",
"MINIO_NOTIFY_KAFKA_COMMENT",
"MINIO_NOTIFY_KAFKA_ENABLE",
"MINIO_NOTIFY_KAFKA_QUEUE_DIR",
"MINIO_NOTIFY_KAFKA_QUEUE_LIMIT",
"MINIO_NOTIFY_KAFKA_SASL",
"MINIO_NOTIFY_KAFKA_SASL_MECHANISM",
"MINIO_NOTIFY_KAFKA_SASL_PASSWORD",
"MINIO_NOTIFY_KAFKA_SASL_USERNAME",
"MINIO_NOTIFY_KAFKA_TLS",
"MINIO_NOTIFY_KAFKA_TLS_CLIENT_AUTH",
"MINIO_NOTIFY_KAFKA_TLS_SKIP_VERIFY",
"MINIO_NOTIFY_KAFKA_TOPIC",
"MINIO_NOTIFY_KAFKA_VERSION",
"MINIO_NOTIFY_MQTT_BROKER",
"MINIO_NOTIFY_MQTT_COMMENT",
"MINIO_NOTIFY_MQTT_ENABLE",
"MINIO_NOTIFY_MQTT_KEEP_ALIVE_INTERVAL",
"MINIO_NOTIFY_MQTT_PASSWORD",
"MINIO_NOTIFY_MQTT_QOS",
"MINIO_NOTIFY_MQTT_QUEUE_DIR",
"MINIO_NOTIFY_MQTT_QUEUE_LIMIT",
"MINIO_NOTIFY_MQTT_RECONNECT_INTERVAL",
"MINIO_NOTIFY_MQTT_TOPIC",
"MINIO_NOTIFY_MQTT_USERNAME",
"MINIO_NOTIFY_MYSQL_COMMENT",
"MINIO_NOTIFY_MYSQL_DSN_STRING",
"MINIO_NOTIFY_MYSQL_ENABLE",
"MINIO_NOTIFY_MYSQL_FORMAT",
"MINIO_NOTIFY_MYSQL_MAX_OPEN_CONNECTIONS",
"MINIO_NOTIFY_MYSQL_QUEUE_DIR",
"MINIO_NOTIFY_MYSQL_QUEUE_LIMIT",
"MINIO_NOTIFY_MYSQL_TABLE",
"MINIO_NOTIFY_NATS_ADDRESS",
"MINIO_NOTIFY_NATS_CERT_AUTHORITY",
"MINIO_NOTIFY_NATS_CLIENT_CERT",
"MINIO_NOTIFY_NATS_CLIENT_KEY",
"MINIO_NOTIFY_NATS_COMMENT",
"MINIO_NOTIFY_NATS_ENABLE",
"MINIO_NOTIFY_NATS_PASSWORD",
"MINIO_NOTIFY_NATS_PING_INTERVAL",
"MINIO_NOTIFY_NATS_QUEUE_DIR",
"MINIO_NOTIFY_NATS_QUEUE_LIMIT",
"MINIO_NOTIFY_NATS_STREAMING",
"MINIO_NOTIFY_NATS_STREAMING_ASYNC",
"MINIO_NOTIFY_NATS_STREAMING_CLUSTER_ID",
"MINIO_NOTIFY_NATS_STREAMING_MAX_PUB_ACKS_IN_FLIGHT",
"MINIO_NOTIFY_NATS_SUBJECT",
"MINIO_NOTIFY_NATS_TLS",
"MINIO_NOTIFY_NATS_TLS_SKIP_VERIFY",
"MINIO_NOTIFY_NATS_TOKEN",
"MINIO_NOTIFY_NATS_USERNAME",
"MINIO_NOTIFY_NSQ_COMMENT",
"MINIO_NOTIFY_NSQ_ENABLE",
"MINIO_NOTIFY_NSQ_NSQD_ADDRESS",
"MINIO_NOTIFY_NSQ_QUEUE_DIR",
"MINIO_NOTIFY_NSQ_QUEUE_LIMIT",
"MINIO_NOTIFY_NSQ_TLS",
"MINIO_NOTIFY_NSQ_TLS_SKIP_VERIFY",
"MINIO_NOTIFY_NSQ_TOPIC",
"MINIO_NOTIFY_POSTGRESQL_COMMENT",
"MINIO_NOTIFY_POSTGRESQL_CONNECTION_STRING",
"MINIO_NOTIFY_POSTGRESQL_ENABLE",
"MINIO_NOTIFY_POSTGRESQL_FORMAT",
"MINIO_NOTIFY_POSTGRESQL_MAX_OPEN_CONNECTIONS",
"MINIO_NOTIFY_POSTGRESQL_QUEUE_DIR",
"MINIO_NOTIFY_POSTGRESQL_QUEUE_LIMIT",
"MINIO_NOTIFY_POSTGRESQL_TABLE",
"MINIO_NOTIFY_REDIS_ADDRESS",
"MINIO_NOTIFY_REDIS_COMMENT",
"MINIO_NOTIFY_REDIS_ENABLE",
"MINIO_NOTIFY_REDIS_FORMAT",
"MINIO_NOTIFY_REDIS_KEY",
"MINIO_NOTIFY_REDIS_PASSWORD",
"MINIO_NOTIFY_REDIS_QUEUE_DIR",
"MINIO_NOTIFY_REDIS_QUEUE_LIMIT",
"MINIO_NOTIFY_WEBHOOK_AUTH_TOKEN",
"MINIO_NOTIFY_WEBHOOK_CLIENT_CERT",
"MINIO_NOTIFY_WEBHOOK_CLIENT_KEY",
"MINIO_NOTIFY_WEBHOOK_COMMENT",
"MINIO_NOTIFY_WEBHOOK_ENABLE",
"MINIO_NOTIFY_WEBHOOK_ENDPOINT",
"MINIO_NOTIFY_WEBHOOK_QUEUE_DIR",
"MINIO_NOTIFY_WEBHOOK_QUEUE_LIMIT",
"MINIO_PROMETHEUS_AUTH_TYPE",
"MINIO_PROMETHEUS_JOB_ID",
"MINIO_PROMETHEUS_URL",
"MINIO_ROOT_PASSWORD",
"MINIO_ROOT_USER",
"MINIO_SECRET_KEY",
"MINIO_SECRET_KEY_OLD",
"MINIO_SERVER_URL",
"MINIO_STORAGE_CLASS_COMMENT",
"MINIO_STORAGE_CLASS_RRS",
"MINIO_STORAGE_CLASS_STANDARD",
];

View File

@@ -19,11 +19,18 @@ import { useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Grid, IconButton, Paper, SelectChangeEvent } from "@mui/material";
import {
Divider,
Grid,
IconButton,
Paper,
SelectChangeEvent,
} from "@mui/material";
import {
createTenantCommon,
modalBasic,
wizardCommon,
formFieldStyles,
} from "../../../Common/FormComponents/common/styleLibrary";
import { AppState, useAppDispatch } from "../../../../../store";
@@ -40,6 +47,7 @@ import {
addNewMinIODomain,
isPageValid,
removeMinIODomain,
setEnvVars,
updateAddField,
} from "../createTenantSlice";
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
@@ -86,13 +94,44 @@ const styles = (theme: Theme) =>
display: "flex",
marginBottom: 15,
},
overlayAction: {
marginLeft: 10,
envVarRow: {
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
"&:last-child": {
borderBottom: 0,
},
"@media (max-width: 900px)": {
flex: 1,
"& div label": {
minWidth: 50,
},
},
},
fileItem: {
marginRight: 10,
display: "flex",
"& div label": {
minWidth: 50,
},
"@media (max-width: 900px)": {
flexFlow: "column",
},
},
rowActions: {
display: "flex",
justifyContent: "flex-end",
"@media (max-width: 900px)": {
flex: 1,
},
},
overlayAction: {
marginLeft: 10,
"& svg": {
width: 15,
height: 15,
maxWidth: 15,
maxHeight: 15,
},
"& button": {
background: "#EAEAEA",
@@ -100,6 +139,7 @@ const styles = (theme: Theme) =>
},
...modalBasic,
...wizardCommon,
...formFieldStyles,
});
const Configure = ({ classes }: IConfigureProps) => {
@@ -123,6 +163,9 @@ const Configure = ({ classes }: IConfigureProps) => {
const tenantCustom = useSelector(
(state: AppState) => state.createTenant.fields.configure.tenantCustom
);
const tenantEnvVars = useSelector(
(state: AppState) => state.createTenant.fields.configure.envVars
);
const tenantSecurityContext = useSelector(
(state: AppState) =>
state.createTenant.fields.configure.tenantSecurityContext
@@ -242,7 +285,7 @@ const Configure = ({ classes }: IConfigureProps) => {
</span>
</div>
<div className={classes.headerElement}>
<h3 className={classes.h3Section}>Services</h3>
<h4 className={classes.h3Section}>Services</h4>
<span className={classes.descriptionText}>
Whether the tenant's services should request an external IP via
LoadBalancer service type.
@@ -518,6 +561,99 @@ const Configure = ({ classes }: IConfigureProps) => {
</fieldset>
</Grid>
)}
<Divider />
<div className={classes.headerElement}>
<h3 className={classes.h3Section}>Additional Environment Variables</h3>
<span className={classes.descriptionText}>
Define additional environment variables to be used by your MinIO pods
</span>
</div>
<Grid container>
{tenantEnvVars.map((envVar, index) => (
<Grid
item
xs={12}
className={`${classes.formFieldRow} ${classes.envVarRow}`}
key={`tenant-envVar-${index.toString()}`}
>
<Grid item xs={5} className={classes.fileItem}>
<InputBoxWrapper
id="env_var_key"
name="env_var_key"
label="Key"
value={envVar.key}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const existingEnvVars = [...tenantEnvVars];
dispatch(
setEnvVars(
existingEnvVars.map((keyPair, i) =>
i === index
? { key: e.target.value, value: keyPair.value }
: keyPair
)
)
);
}}
index={index}
key={`env_var_key_${index.toString()}`}
/>
</Grid>
<Grid item xs={5} className={classes.fileItem}>
<InputBoxWrapper
id="env_var_value"
name="env_var_value"
label="Value"
value={envVar.value}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const existingEnvVars = [...tenantEnvVars];
dispatch(
setEnvVars(
existingEnvVars.map((keyPair, i) =>
i === index
? { key: keyPair.key, value: e.target.value }
: keyPair
)
)
);
}}
index={index}
key={`env_var_value_${index.toString()}`}
/>
</Grid>
<Grid item xs={2} className={classes.rowActions}>
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
const existingEnvVars = [...tenantEnvVars];
existingEnvVars.push({ key: "", value: "" });
dispatch(setEnvVars(existingEnvVars));
}}
disabled={index !== tenantEnvVars.length - 1}
>
<AddIcon />
</IconButton>
</div>
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
const existingEnvVars = tenantEnvVars.filter(
(item, fIndex) => fIndex !== index
);
dispatch(setEnvVars(existingEnvVars));
}}
disabled={tenantEnvVars.length <= 1}
>
<RemoveIcon />
</IconButton>
</div>
</Grid>
</Grid>
))}
</Grid>
</Paper>
);
};

View File

@@ -103,6 +103,7 @@ const initialState: ICreateTenant = {
exposeMinIO: true,
exposeConsole: true,
tenantCustom: false,
envVars: [{ key: "", value: "" }],
logSearchEnabled: true,
prometheusEnabled: true,
logSearchVolumeSize: "5",
@@ -746,6 +747,9 @@ export const createTenantSlice = createSlice({
setKeyValuePairs: (state, action: PayloadAction<LabelKeyPair[]>) => {
state.nodeSelectorPairs = action.payload;
},
setEnvVars: (state, action: PayloadAction<LabelKeyPair[]>) => {
state.fields.configure.envVars = action.payload;
},
setTolerationInfo: (
state,
action: PayloadAction<{
@@ -1049,6 +1053,7 @@ export const {
addFileGemaltoCa,
resetAddTenantForm,
setKeyValuePairs,
setEnvVars,
setTolerationInfo,
addNewToleration,
removeToleration,

View File

@@ -137,6 +137,7 @@ export const createTenantAsync = createAsyncThunk(
const setDomains = fields.configure.setDomains;
const minioDomains = fields.configure.minioDomains;
const consoleDomain = fields.configure.consoleDomain;
const environmentVariables = fields.configure.envVars;
let tolerations = state.createTenant.tolerations;
let namespace = state.createTenant.fields.nameTenant.namespace;
@@ -547,6 +548,7 @@ export const createTenantAsync = createAsyncThunk(
let domains: any = {};
let sendDomain: any = {};
let sendEnvironmentVariables: any = {};
if (setDomains) {
if (consoleDomain !== "") {
@@ -564,9 +566,17 @@ export const createTenantAsync = createAsyncThunk(
}
}
sendEnvironmentVariables.environmentVariables = environmentVariables
.map((envVar) => ({
key: envVar.key.trim(),
value: envVar.value.trim(),
}))
.filter((envVar) => envVar.key !== "");
dataSend = {
...dataSend,
...sendDomain,
...sendEnvironmentVariables,
idp: { ...dataIDP },
};

View File

@@ -155,6 +155,7 @@ export interface IConfigureFields {
exposeConsole: boolean;
prometheusEnabled: boolean;
tenantCustom: boolean;
envVars: LabelKeyPair[];
logSearchEnabled: boolean;
logSearchVolumeSize: string;
logSearchSizeFactor: string;

View File

@@ -1991,6 +1991,10 @@ definitions:
domains:
type: object
$ref: "#/definitions/domainsConfiguration"
environmentVariables:
type: array
items:
$ref: "#/definitions/environmentVariable"
metadataFields:
type: object