Compare commits

...

11 Commits

Author SHA1 Message Date
César Nieto
1aec2d879e Remove unused swagger autogenerated files (#180) 2020-06-22 20:56:52 -07:00
Alex
f77770bb6e Changed create tenant form to be a wizard (#179) 2020-06-22 12:21:19 -05:00
Alex
34ff3d7157 Implemented validation in create tenant form (#177)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-06-19 11:16:16 -07:00
Alex
4b6700d4ac Fixed usability issues on IAM Policies module (#175)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-06-15 21:38:42 -07:00
Alex
f2c8f15fbf Added delete option to multi-zone selector (#167)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-06-15 16:29:43 -07:00
Anton Huck
991204cd46 Remove trailing comma in policy (#160)
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
Co-authored-by: César Nieto <ces.nietor@gmail.com>
Co-authored-by: Alex <33497058+bexsoft@users.noreply.github.com>
Co-authored-by: Lenin Alevski <alevsk.8772@gmail.com>
2020-06-08 19:24:51 -07:00
Justin Hutchings
4bac7040a1 Add CodeQL security scanning (#157)
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-06-08 17:44:22 -07:00
dependabot[bot]
a247bf6a0c Bump websocket-extensions from 0.1.3 to 0.1.4 in /portal-ui (#165)
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-08 14:04:18 -07:00
Daniel Valdivia
f4d08a7502 Tenant Details (#162) 2020-06-08 13:37:14 -07:00
Daniel Valdivia
fb59e8c353 Remove Policy OPA (#164) 2020-06-04 18:37:59 -07:00
Daniel Valdivia
6e6ed300b7 Listen to all ips by default (#161) 2020-06-04 15:57:13 -07:00
38 changed files with 1386 additions and 2122 deletions

52
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: "Code scanning - action"
on:
push:
pull_request:
schedule:
- cron: '0 19 * * 0'
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -31,6 +31,8 @@ install: mcs
swagger-gen: swagger-gen:
@echo "Generating swagger server code from yaml" @echo "Generating swagger server code from yaml"
@rm -rf models
@rm -rf restapi/operations
@swagger generate server -A mcs --main-package=mcs --exclude-main -P models.Principal -f ./swagger.yml -r NOTICE @swagger generate server -A mcs --main-package=mcs --exclude-main -P models.Principal -f ./swagger.yml -r NOTICE
assets: assets:

View File

@@ -62,7 +62,7 @@ Additionally, you can create policies to limit the privileges for `mcs` users, f
"Version": "2012-10-17", "Version": "2012-10-17",
"Statement": [{ "Statement": [{
"Action": [ "Action": [
"admin:ServerInfo", "admin:ServerInfo"
], ],
"Effect": "Allow", "Effect": "Allow",
"Sid": "" "Sid": ""

View File

@@ -1,147 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
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 (
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// AddNotificationEndpoint add notification endpoint
//
// swagger:model addNotificationEndpoint
type AddNotificationEndpoint struct {
// account
Account string `json:"account,omitempty"`
// properties
Properties map[string]string `json:"properties,omitempty"`
// service
// Enum: [webhook amqp kafka mqtt nats nsq mysql postgres elasticsearch redis]
Service string `json:"service,omitempty"`
}
// Validate validates this add notification endpoint
func (m *AddNotificationEndpoint) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateService(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var addNotificationEndpointTypeServicePropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["webhook","amqp","kafka","mqtt","nats","nsq","mysql","postgres","elasticsearch","redis"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
addNotificationEndpointTypeServicePropEnum = append(addNotificationEndpointTypeServicePropEnum, v)
}
}
const (
// AddNotificationEndpointServiceWebhook captures enum value "webhook"
AddNotificationEndpointServiceWebhook string = "webhook"
// AddNotificationEndpointServiceAmqp captures enum value "amqp"
AddNotificationEndpointServiceAmqp string = "amqp"
// AddNotificationEndpointServiceKafka captures enum value "kafka"
AddNotificationEndpointServiceKafka string = "kafka"
// AddNotificationEndpointServiceMqtt captures enum value "mqtt"
AddNotificationEndpointServiceMqtt string = "mqtt"
// AddNotificationEndpointServiceNats captures enum value "nats"
AddNotificationEndpointServiceNats string = "nats"
// AddNotificationEndpointServiceNsq captures enum value "nsq"
AddNotificationEndpointServiceNsq string = "nsq"
// AddNotificationEndpointServiceMysql captures enum value "mysql"
AddNotificationEndpointServiceMysql string = "mysql"
// AddNotificationEndpointServicePostgres captures enum value "postgres"
AddNotificationEndpointServicePostgres string = "postgres"
// AddNotificationEndpointServiceElasticsearch captures enum value "elasticsearch"
AddNotificationEndpointServiceElasticsearch string = "elasticsearch"
// AddNotificationEndpointServiceRedis captures enum value "redis"
AddNotificationEndpointServiceRedis string = "redis"
)
// prop value enum
func (m *AddNotificationEndpoint) validateServiceEnum(path, location string, value string) error {
if err := validate.Enum(path, location, value, addNotificationEndpointTypeServicePropEnum); err != nil {
return err
}
return nil
}
func (m *AddNotificationEndpoint) validateService(formats strfmt.Registry) error {
if swag.IsZero(m.Service) { // not required
return nil
}
// value enum
if err := m.validateServiceEnum("service", "body", m.Service); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *AddNotificationEndpoint) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AddNotificationEndpoint) UnmarshalBinary(b []byte) error {
var res AddNotificationEndpoint
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -1,72 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
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 (
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// PolicyStatement policy statement
//
// swagger:model policyStatement
type PolicyStatement struct {
// action
Action []string `json:"Action"`
// condition
Condition string `json:"Condition,omitempty"`
// effect
Effect string `json:"Effect,omitempty"`
// resource
Resource string `json:"Resource,omitempty"`
// sid
Sid string `json:"Sid,omitempty"`
}
// Validate validates this policy statement
func (m *PolicyStatement) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *PolicyStatement) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PolicyStatement) UnmarshalBinary(b []byte) error {
var res PolicyStatement
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -1,62 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
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 (
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// PolicyStatements policy statements
//
// swagger:model policyStatements
type PolicyStatements []*PolicyStatement
// Validate validates this policy statements
func (m PolicyStatements) Validate(formats strfmt.Registry) error {
var res []error
for i := 0; i < len(m); i++ {
if swag.IsZero(m[i]) { // not required
continue
}
if m[i] != nil {
if err := m[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName(strconv.Itoa(i))
}
return err
}
}
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

File diff suppressed because one or more lines are too long

View File

@@ -14181,14 +14181,6 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==" "integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
}, },
"react-google-charts": {
"version": "3.0.15",
"resolved": "https://registry.npmjs.org/react-google-charts/-/react-google-charts-3.0.15.tgz",
"integrity": "sha512-78s5xOQOJvL+jIewrWQZEHtlVk+5Yh4zZy+ODA1on1o1FaRjKWXxoo4n4JQl1XuqkF/A9NWque3KqM6pMggjzQ==",
"requires": {
"react-load-script": "^0.0.6"
}
},
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -14199,11 +14191,6 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"react-load-script": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/react-load-script/-/react-load-script-0.0.6.tgz",
"integrity": "sha512-aRGxDGP9VoLxcsaYvKWIW+LRrMOzz2eEcubTS4NvQPPugjk2VvMhow0wWTkSl7RxookomD1MwcP4l5UStg5ShQ=="
},
"react-moment": { "react-moment": {
"version": "0.9.7", "version": "0.9.7",
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.9.7.tgz", "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.9.7.tgz",
@@ -17438,9 +17425,9 @@
} }
}, },
"websocket-extensions": { "websocket-extensions": {
"version": "0.1.3", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
"integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
}, },
"whatwg-encoding": { "whatwg-encoding": {
"version": "1.0.5", "version": "1.0.5",

View File

@@ -44,6 +44,9 @@ interface InputBoxProps {
tooltip?: string; tooltip?: string;
autoComplete?: string; autoComplete?: string;
index?: number; index?: number;
error?: string;
required?: boolean;
placeholder?: string;
} }
const styles = (theme: Theme) => const styles = (theme: Theme) =>
@@ -52,6 +55,14 @@ const styles = (theme: Theme) =>
...tooltipHelper, ...tooltipHelper,
textBoxContainer: { textBoxContainer: {
flexGrow: 1, flexGrow: 1,
position: "relative",
},
errorState: {
color: "#b53b4b",
fontSize: 14,
position: "absolute",
top: 7,
right: 7,
}, },
}); });
@@ -66,6 +77,10 @@ const inputStyles = makeStyles((theme: Theme) =>
color: "#393939", color: "#393939",
fontSize: 14, fontSize: 14,
}, },
error: {
color: "#b53b4b",
boxShadow: "inset 0px 0px 1px 1px #b53b4b",
},
}) })
); );
@@ -92,14 +107,31 @@ const InputBoxWrapper = ({
multiline = false, multiline = false,
tooltip = "", tooltip = "",
index = 0, index = 0,
error = "",
required = false,
placeholder = "",
classes, classes,
}: InputBoxProps) => { }: InputBoxProps) => {
return ( return (
<React.Fragment> <React.Fragment>
<Grid item xs={12} className={classes.fieldContainer}> <Grid
item
xs={12}
className={`${classes.fieldContainer} ${
error !== "" ? classes.errorInField : ""
}`}
>
{label !== "" && ( {label !== "" && (
<InputLabel htmlFor={id} className={classes.inputLabel}> <InputLabel
<span>{label}</span> htmlFor={id}
className={`${error !== "" ? classes.fieldLabelError : ""} ${
classes.inputLabel
}`}
>
<span>
{label}
{required ? "*" : ""}
</span>
{tooltip !== "" && ( {tooltip !== "" && (
<div className={classes.tooltipContainer}> <div className={classes.tooltipContainer}>
<Tooltip title={tooltip} placement="top-start"> <Tooltip title={tooltip} placement="top-start">
@@ -112,7 +144,6 @@ const InputBoxWrapper = ({
<div className={classes.textBoxContainer}> <div className={classes.textBoxContainer}>
<InputField <InputField
className={classes.boxDesign}
id={id} id={id}
name={name} name={name}
variant="outlined" variant="outlined"
@@ -124,6 +155,9 @@ const InputBoxWrapper = ({
multiline={multiline} multiline={multiline}
autoComplete={autoComplete} autoComplete={autoComplete}
inputProps={{ "data-index": index }} inputProps={{ "data-index": index }}
error={error !== ""}
helperText={error}
placeholder={placeholder}
/> />
</div> </div>
</Grid> </Grid>

View File

@@ -33,6 +33,9 @@ export const fieldBasic = {
alignItems: "center", alignItems: "center",
}, },
}, },
fieldLabelError: {
paddingBottom: 22,
},
fieldContainer: { fieldContainer: {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",

View File

@@ -0,0 +1,128 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
import React, { useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { IWizardMain } from "./types";
import WizardPage from "./WizardPage";
const styles = (theme: Theme) =>
createStyles({
wizardMain: {
display: "flex",
width: "100%",
flexGrow: 1,
},
wizardSteps: {
marginRight: 10,
"& ul": {
padding: 15,
"& li": {
listStyle: "lower-roman",
marginBottom: 12,
},
},
},
buttonList: {
backgroundColor: "transparent",
border: "none",
cursor: "pointer",
"&:not(:disabled):hover": {
textDecoration: "underline",
},
"&:selected, &:active, &:focus, &:focus:active": {
border: "none",
outline: 0,
boxShadow: "none",
},
},
wizardContainer: {
flexGrow: 1,
},
});
const GenericWizard = ({ classes, wizardSteps }: IWizardMain) => {
const [currentStep, setCurrentStep] = useState<number>(0);
const pageChange = (toElement: string | number) => {
const lastPage = wizardSteps.length - 1;
if (toElement === "++") {
let nextPage = currentStep + 1;
if (nextPage > lastPage) {
nextPage = lastPage;
}
setCurrentStep(nextPage);
}
if (toElement === "--") {
let prevPage = currentStep - 1;
if (prevPage < 0) {
prevPage = 0;
}
setCurrentStep(prevPage);
}
if (typeof toElement === "number") {
let pg = toElement;
if (toElement < 0) {
pg = 0;
}
if (toElement > lastPage) {
pg = lastPage;
}
setCurrentStep(pg);
}
};
if (wizardSteps.length === 0) {
return null;
}
return (
<div className={classes.wizardMain}>
<div className={classes.wizardSteps}>
<ul>
{wizardSteps.map((step, index) => {
return (
<li key={`wizard-${index.toString()}`}>
<button
onClick={() => pageChange(index)}
disabled={index > currentStep}
className={classes.buttonList}
>
{step.label}
</button>
</li>
);
})}
</ul>
</div>
<div className={classes.wizardContainer}>
<WizardPage page={wizardSteps[currentStep]} pageChange={pageChange} />
</div>
</div>
);
};
export default withStyles(styles)(GenericWizard);

View File

@@ -0,0 +1,89 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { IWizardButton, IWizardPage } from "./types";
import { Button } from "@material-ui/core";
const styles = (theme: Theme) =>
createStyles({
wizardStepContainer: {
display: "flex",
flexDirection: "column",
},
wizardComponent: {
height: 375,
overflowY: "auto",
marginBottom: 10,
},
buttonsContainer: {
display: "flex",
flexDirection: "row",
justifyContent: "flex-end" as const,
"& button": {
marginLeft: 10,
},
},
});
const WizardPage = ({ classes, page, pageChange }: IWizardPage) => {
const buttonAction = (btn: IWizardButton) => {
switch (btn.type) {
case "next":
pageChange("++");
break;
case "back":
pageChange("--");
break;
case "to":
pageChange(btn.toPage || 0);
default:
}
if (btn.action) {
btn.action();
}
};
console.log("buttons", page);
return (
<div className={classes.wizardStepContainer}>
<div className={classes.wizardComponent}>{page.componentRender}</div>
<div className={classes.buttonsContainer}>
{page.buttons.map((btn) => {
return (
<Button
variant="contained"
color="primary"
size="small"
onClick={() => {
buttonAction(btn);
}}
disabled={!btn.enabled}
key={`button-${page.label}-${btn.label}`}
>
{btn.label}
</Button>
);
})}
</div>
</div>
);
};
export default withStyles(styles)(WizardPage);

View File

@@ -0,0 +1,40 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
export interface IWizardButton {
label: string;
type: string;
action?: () => void;
enabled?: boolean;
toPage?: number;
}
export interface IWizardElement {
label: string;
componentRender: any;
buttons: IWizardButton[];
}
export interface IWizardMain {
classes: any;
wizardSteps: IWizardElement[];
}
export interface IWizardPage {
classes: any;
page: IWizardElement;
pageChange: (to: string | number) => void;
}

View File

@@ -42,10 +42,6 @@ export const configurationElements: IConfigurationElement[] = [
configuration_id: "identity_ldap", configuration_id: "identity_ldap",
configuration_label: "Identity LDAP Configuration", configuration_label: "Identity LDAP Configuration",
}, },
{
configuration_id: "policy_opa",
configuration_label: "Policy OPA Configuration",
},
{ {
configuration_id: "kms_vault", configuration_id: "kms_vault",
configuration_label: "KMS Vault Configuration", configuration_label: "KMS Vault Configuration",
@@ -312,26 +308,6 @@ export const fieldsConfigurations: any = {
multiline: true, multiline: true,
}, },
], ],
policy_opa: [
{
name: "url",
required: true,
label: "OPA URL",
type: "string",
},
{
name: "auth_token",
required: true,
label: "Auth Token",
type: "string",
},
{
name: "policy_opa",
required: true,
label: "Policy OPA",
type: "string",
},
],
kms_vault: [], kms_vault: [],
kms_kes: [], kms_kes: [],
logger_webhook: [ logger_webhook: [

View File

@@ -307,7 +307,7 @@ const Console = ({
}, },
{ {
component: TenantDetails, component: TenantDetails,
path: "/clusters/:clusterName", path: "/tenants/:tenantName",
}, },
]; ];
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]); const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);

View File

@@ -51,7 +51,7 @@ const styles = (theme: Theme) =>
interface IAddPolicyProps { interface IAddPolicyProps {
classes: any; classes: any;
open: boolean; open: boolean;
closeModalAndRefresh: () => void; closeModalAndRefresh: (refresh: boolean) => void;
policyEdit: Policy; policyEdit: Policy;
} }
@@ -89,7 +89,7 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
addError: "", addError: "",
}, },
() => { () => {
this.props.closeModalAndRefresh(); this.props.closeModalAndRefresh(true);
} }
); );
}) })
@@ -120,7 +120,7 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
modalOpen={open} modalOpen={open}
onClose={() => { onClose={() => {
this.setState({ addError: "" }, () => { this.setState({ addError: "" }, () => {
this.props.closeModalAndRefresh(); this.props.closeModalAndRefresh(false);
}); });
}} }}
title={`${policyEdit ? "Info" : "Create"} Policy`} title={`${policyEdit ? "Info" : "Create"} Policy`}

View File

@@ -14,114 +14,114 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react"; import React, { useState, useEffect } from "react";
import get from "lodash/get";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import { Button } from "@material-ui/core"; import { Button } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import { Policy, PolicyList } from "./types"; import { Policy, PolicyList } from "./types";
import AddPolicy from "./AddPolicy";
import DeletePolicy from "./DeletePolicy";
import api from "../../../common/api";
import { CreateIcon } from "../../../icons"; import { CreateIcon } from "../../../icons";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions"; import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import AddPolicy from "./AddPolicy";
import DeletePolicy from "./DeletePolicy";
import TableWrapper from "../Common/TableWrapper/TableWrapper"; import TableWrapper from "../Common/TableWrapper/TableWrapper";
import api from "../../../common/api";
const styles = (theme: Theme) => const styles = (theme: Theme) =>
createStyles({ createStyles({
seeMore: { seeMore: {
marginTop: theme.spacing(3) marginTop: theme.spacing(3),
}, },
paper: { paper: {
display: "flex", display: "flex",
overflow: "auto", overflow: "auto",
flexDirection: "column" flexDirection: "column",
}, },
addSideBar: { addSideBar: {
width: "320px", width: "320px",
padding: "20px" padding: "20px",
}, },
errorBlock: { errorBlock: {
color: "red" color: "red",
}, },
tableToolbar: { tableToolbar: {
paddingLeft: theme.spacing(2), paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(0) paddingRight: theme.spacing(0),
}, },
minTableHeader: { minTableHeader: {
color: "#393939", color: "#393939",
"& tr": { "& tr": {
"& th": { "& th": {
fontWeight: "bold" fontWeight: "bold",
} },
} },
}, },
actionsTray: { actionsTray: {
textAlign: "right", textAlign: "right",
"& button": { "& button": {
marginLeft: 10 marginLeft: 10,
} },
}, },
searchField: { searchField: {
background: "#FFFFFF", background: "#FFFFFF",
padding: 12, padding: 12,
borderRadius: 5, borderRadius: 5,
boxShadow: "0px 3px 6px #00000012" boxShadow: "0px 3px 6px #00000012",
} },
}); });
interface IPoliciesProps { interface IPoliciesProps {
classes: any; classes: any;
} }
interface IPoliciesState { const Policies = ({ classes }: IPoliciesProps) => {
records: Policy[]; const [records, setRecords] = useState<Policy[]>([]);
totalRecords: number; const [totalRecords, setTotalRecords] = useState<number>(0);
loading: boolean; const [loading, setLoading] = useState<boolean>(false);
error: string; const [error, setError] = useState<string>("");
deleteError: string; const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
addScreenOpen: boolean; const [page, setPage] = useState<number>(0);
page: number; const [rowsPerPage, setRowsPerPage] = useState<number>(10);
rowsPerPage: number; const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
deleteOpen: boolean; const [selectedPolicy, setSelectedPolicy] = useState<string>("");
selectedPolicy: string; const [filterPolicies, setFilterPolicies] = useState<string>("");
filterPolicies: string; const [policyEdit, setPolicyEdit] = useState<any>(null);
policyEdit: any;
}
class Policies extends React.Component<IPoliciesProps, IPoliciesState> { useEffect(() => {
state: IPoliciesState = { fetchRecords();
records: [], }, []);
totalRecords: 0,
loading: false,
error: "",
deleteError: "",
addScreenOpen: false,
page: 0,
rowsPerPage: 10,
deleteOpen: false,
selectedPolicy: "",
filterPolicies: "",
policyEdit: null
};
fetchRecords() { useEffect(() => {
this.setState({ loading: true }, () => { if (loading) {
const { page, rowsPerPage } = this.state;
const offset = page * rowsPerPage; const offset = page * rowsPerPage;
api api
.invoke("GET", `/api/v1/policies?offset=${offset}&limit=${rowsPerPage}`) .invoke("GET", `/api/v1/policies?offset=${offset}&limit=${rowsPerPage}`)
.then((res: PolicyList) => { .then((res: PolicyList) => {
this.setState({ const policies = get(res, "policies", []);
loading: false, const total = get(res, "total", 0);
records: res.policies,
totalRecords: res.total, policies.sort((pa, pb) => {
error: "" if (pa.name > pb.name) {
return 1;
}
if (pa.name < pb.name) {
return -1;
}
return 0;
}); });
setLoading(false);
setRecords(policies);
setTotalRecords(total);
setError("");
// if we get 0 results, and page > 0 , go down 1 page // if we get 0 results, and page > 0 , go down 1 page
if ( if (
(res.policies === undefined || (res.policies === undefined ||
@@ -130,187 +130,166 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
page > 0 page > 0
) { ) {
const newPage = page - 1; const newPage = page - 1;
this.setState({ page: newPage }, () => {
this.fetchRecords(); setPage(newPage);
}); fetchRecords();
} }
}) })
.catch(err => { .catch((err) => {
this.setState({ loading: false, error: err }); setLoading(false);
setError(err);
}); });
}); }
} }, [
loading,
setLoading,
setRecords,
setTotalRecords,
setError,
setPage,
setError,
]);
closeAddModalAndRefresh() { const fetchRecords = () => {
this.setState({ addScreenOpen: false }, () => { setLoading(true);
this.fetchRecords(); };
});
}
closeDeleteModalAndRefresh(refresh: boolean) { const closeAddModalAndRefresh = (refresh: boolean) => {
this.setState({ deleteOpen: false }, () => { setAddScreenOpen(false);
if (refresh) {
this.fetchRecords();
}
});
}
policyFilter(): void {} if (refresh) {
fetchRecords();
}
};
componentDidMount(): void { const closeDeleteModalAndRefresh = (refresh: boolean) => {
this.fetchRecords(); setDeleteOpen(false);
}
render() { if (refresh) {
const { classes } = this.props; fetchRecords();
const { }
records, };
totalRecords,
addScreenOpen,
loading,
page,
rowsPerPage,
deleteOpen,
selectedPolicy,
filterPolicies,
policyEdit
} = this.state;
const offset = page * rowsPerPage; const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangePage = (event: unknown, newPage: number) => { const handleChangeRowsPerPage = (
this.setState({ page: newPage }); event: React.ChangeEvent<HTMLInputElement>
}; ) => {
const rPP = parseInt(event.target.value, 10);
setPage(0);
setRowsPerPage(rPP);
};
const handleChangeRowsPerPage = ( const confirmDeletePolicy = (policy: string) => {
event: React.ChangeEvent<HTMLInputElement> setDeleteOpen(true);
) => { setSelectedPolicy(policy);
const rPP = parseInt(event.target.value, 10); };
this.setState({ page: 0, rowsPerPage: rPP });
};
const confirmDeletePolicy = (policy: string) => { const viewAction = (row: any) => {
this.setState({ deleteOpen: true, selectedPolicy: policy }); setAddScreenOpen(true);
}; setPolicyEdit(row);
};
const viewAction = (row: any) => { const tableActions = [
this.setState({ { type: "view", onClick: viewAction },
addScreenOpen: true, { type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true },
policyEdit: row ];
});
};
const tableActions = [ const filteredRecords = records.filter((elementItem) =>
{ type: "view", onClick: viewAction }, elementItem.name.includes(filterPolicies)
{ type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true } );
];
const filteredRecords = records const beginRecord = page * rowsPerPage;
.slice(offset, offset + rowsPerPage) const endRecords = beginRecord + rowsPerPage;
.filter((b: Policy) => {
if (filterPolicies === "") {
return true;
} else {
if (b.name.indexOf(filterPolicies) >= 0) {
return true;
} else {
return false;
}
}
});
return ( const paginatedRecords = filteredRecords.slice(beginRecord, endRecords);
<React.Fragment>
{addScreenOpen && ( return (
<AddPolicy <React.Fragment>
open={addScreenOpen} {addScreenOpen && (
closeModalAndRefresh={() => { <AddPolicy
this.closeAddModalAndRefresh(); open={addScreenOpen}
}} closeModalAndRefresh={closeAddModalAndRefresh}
policyEdit={policyEdit} policyEdit={policyEdit}
/> />
)} )}
{deleteOpen && ( {deleteOpen && (
<DeletePolicy <DeletePolicy
deleteOpen={deleteOpen} deleteOpen={deleteOpen}
selectedPolicy={selectedPolicy} selectedPolicy={selectedPolicy}
closeDeleteModalAndRefresh={(refresh: boolean) => { closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
this.closeDeleteModalAndRefresh(refresh); />
}} )}
/> <Grid container>
)} <Grid item xs={12}>
<Grid container> <Typography variant="h6">IAM Policies</Typography>
<Grid item xs={12}>
<Typography variant="h6">IAM Policies</Typography>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Search Policies"
className={classes.searchField}
id="search-resource"
label=""
onChange={val => {
this.setState({
filterPolicies: val.target.value
});
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
)
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
onClick={() => {
this.setState({
addScreenOpen: true,
policyEdit: null
});
}}
>
Create Policy
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[{ label: "Name", elementKey: "name" }]}
isLoading={loading}
records={filteredRecords}
entityName="Policies"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: totalRecords,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions
}}
/>
</Grid>
</Grid> </Grid>
</React.Fragment> <Grid item xs={12}>
); <br />
} </Grid>
} <Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Search Policies"
className={classes.searchField}
id="search-resource"
label=""
onChange={(val) => {
setPage(0);
setFilterPolicies(val.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
onClick={() => {
setAddScreenOpen(true);
setPolicyEdit(null);
}}
>
Create Policy
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[{ label: "Name", elementKey: "name" }]}
isLoading={loading}
records={paginatedRecords}
entityName="Policies"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>
</React.Fragment>
);
};
export default withStyles(styles)(Policies); export default withStyles(styles)(Policies);

View File

@@ -19,7 +19,11 @@ import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button, LinearProgress } from "@material-ui/core"; import { LinearProgress } from "@material-ui/core";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableRow from "@material-ui/core/TableRow";
import api from "../../../../common/api"; import api from "../../../../common/api";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary"; import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
@@ -28,6 +32,12 @@ import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/Checkbo
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper"; import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import { k8sfactorForDropdown } from "../../../../common/utils"; import { k8sfactorForDropdown } from "../../../../common/utils";
import ZonesMultiSelector from "./ZonesMultiSelector"; import ZonesMultiSelector from "./ZonesMultiSelector";
import {
commonFormValidation,
IValidation,
} from "../../../../utils/validationFunctions";
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
import { IWizardElement } from "../../Common/GenericWizard/types";
interface IAddTenantProps { interface IAddTenantProps {
open: boolean; open: boolean;
@@ -50,10 +60,27 @@ const styles = (theme: Theme) =>
}, },
sizeFactorContainer: { sizeFactorContainer: {
marginLeft: 8, marginLeft: 8,
alignSelf: "flex-start" as const,
},
headerElement: {
position: "sticky",
top: 0,
paddingTop: 5,
marginBottom: 10,
backgroundColor: "#fff",
},
tableTitle: {
fontWeight: 700,
width: "30%",
}, },
...modalBasic, ...modalBasic,
}); });
interface Opts {
label: string;
value: string;
}
const AddTenant = ({ const AddTenant = ({
open, open,
closeModalAndRefresh, closeModalAndRefresh,
@@ -72,15 +99,89 @@ const AddTenant = ({
const [mountPath, setMountPath] = useState<string>(""); const [mountPath, setMountPath] = useState<string>("");
const [accessKey, setAccessKey] = useState<string>(""); const [accessKey, setAccessKey] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>(""); const [secretKey, setSecretKey] = useState<string>("");
const [enableMCS, setEnableMCS] = useState<boolean>(false); const [enableMCS, setEnableMCS] = useState<boolean>(true);
const [enableSSL, setEnableSSL] = useState<boolean>(false); const [enableSSL, setEnableSSL] = useState<boolean>(false);
const [sizeFactor, setSizeFactor] = useState<string>("Gi"); const [sizeFactor, setSizeFactor] = useState<string>("Gi");
const [storageClasses, setStorageClassesList] = useState<string[]>([]); const [storageClasses, setStorageClassesList] = useState<Opts[]>([]);
const [validationErrors, setValidationErrors] = useState<any>({});
const [namespace, setNamespace] = useState<string>("");
const [nameTenantValid, setNameTenantValid] = useState<boolean>(false);
const [configValid, setConfigValid] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
fetchStorageClassList(); fetchStorageClassList();
}, []); }, []);
useEffect(() => {
const commonValidation = commonFormValidation([validationElements[0]]);
setNameTenantValid(!("tenant-name" in commonValidation));
setValidationErrors(commonValidation);
}, [tenantName]);
useEffect(() => {
const commonValidation = commonFormValidation(
validationElements.slice(1, 3)
);
setConfigValid(
!("volumes_per_server" in commonValidation) &&
!("volume_size" in commonValidation)
);
setValidationErrors(commonValidation);
}, [volumesPerServer, volumeConfiguration]);
const validationElements: IValidation[] = [
{
fieldKey: "tenant-name",
required: true,
pattern: /^[a-z0-9-]{3,63}$/,
customPatternMessage:
"Name only can contain lowercase letters, numbers and '-'. Min. Length: 3",
value: tenantName,
},
{
fieldKey: "volumes_per_server",
required: true,
value: volumesPerServer.toString(10),
},
{
fieldKey: "volume_size",
required: true,
value: volumeConfiguration.size,
},
{
fieldKey: "image",
required: false,
value: imageName,
},
{
fieldKey: "service_name",
required: false,
value: serviceName,
},
{
fieldKey: "access_key",
required: false,
value: accessKey,
},
{
fieldKey: "secret_key",
required: false,
value: secretKey,
},
];
const clearValidationError = (fieldKey: string) => {
const newValidationElement = { ...validationErrors };
delete newValidationElement[fieldKey];
setValidationErrors(newValidationElement);
};
useEffect(() => { useEffect(() => {
if (addSending) { if (addSending) {
let cleanZones: IZone[] = []; let cleanZones: IZone[] = [];
@@ -90,31 +191,40 @@ const AddTenant = ({
} }
} }
api const commonValidation = commonFormValidation(validationElements);
.invoke("POST", `/api/v1/mkube/tenants`, {
name: tenantName, setValidationErrors(commonValidation);
service_name: tenantName,
image: imageName, if (Object.keys(commonValidation).length === 0) {
enable_ssl: enableSSL, api
enable_mcs: enableMCS, .invoke("POST", `/api/v1/mkube/tenants`, {
access_key: accessKey, name: tenantName,
secret_key: secretKey, service_name: tenantName,
volumes_per_server: volumesPerServer, image: imageName,
volume_configuration: { enable_ssl: enableSSL,
size: `${volumeConfiguration.size}${sizeFactor}`, enable_mcs: enableMCS,
storage_class: volumeConfiguration.storage_class, access_key: accessKey,
}, secret_key: secretKey,
zones: cleanZones, volumes_per_server: volumesPerServer,
}) volume_configuration: {
.then(() => { size: `${volumeConfiguration.size}${sizeFactor}`,
setAddSending(false); storage_class: volumeConfiguration.storage_class,
setAddError(""); },
closeModalAndRefresh(true); zones: cleanZones,
}) })
.catch((err) => { .then(() => {
setAddSending(false); setAddSending(false);
setAddError(err); setAddError("");
}); closeModalAndRefresh(true);
})
.catch((err) => {
setAddSending(false);
setAddError(err);
});
} else {
setAddSending(false);
setAddError("Please fix the errors in the form and try again");
}
} }
}, [addSending]); }, [addSending]);
@@ -136,17 +246,447 @@ const AddTenant = ({
if (res !== null) { if (res !== null) {
classes = res; classes = res;
} }
setStorageClassesList(classes); setStorageClassesList(
classes.map((s: string) => ({
label: s,
value: s,
}))
);
const newStorage = { ...volumeConfiguration };
newStorage.storage_class = res[0];
setVolumeConfiguration(newStorage);
}) })
.catch((err: any) => { .catch((err: any) => {
console.log(err); console.log(err);
}); });
}; };
const storageClassesList = storageClasses.map((s: string) => ({ const cancelButton = {
label: s, label: "Cancel",
value: s, type: "other",
})); enabled: true,
action: () => {
closeModalAndRefresh(false);
},
};
const wizardSteps: IWizardElement[] = [
{
label: "Name Tenant",
componentRender: (
<Grid item xs={12}>
<div className={classes.headerElement}>
<h3>Name Tenant</h3>
<span>How would you like to name this new tenant?</span>
</div>
<InputBoxWrapper
id="tenant-name"
name="tenant-name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTenantName(e.target.value);
clearValidationError("tenant-name");
}}
label="Tenant Name"
value={tenantName}
required
error={validationErrors["tenant-name"] || ""}
/>
</Grid>
),
buttons: [
cancelButton,
{ label: "Next", type: "next", enabled: nameTenantValid },
],
},
{
label: "Configure",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Configure</h3>
<span>Basic configurations for tenant management</span>
</div>
Please enter your access & secret keys
<Grid item xs={12}>
<InputBoxWrapper
id="access_key"
name="access_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
clearValidationError("access_key");
}}
label="Access Key"
value={accessKey}
error={validationErrors["access_key"] || ""}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="secret_key"
name="secret_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
clearValidationError("secret_key");
}}
label="Secret Key"
value={secretKey}
error={validationErrors["secret_key"] || ""}
/>
</Grid>
Please enter the MinIO image from dockerhub
<Grid item xs={12}>
<InputBoxWrapper
id="image"
name="image"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setImageName(e.target.value);
clearValidationError("image");
}}
label="MinIO Image"
value={imageName}
error={validationErrors["image"] || ""}
placeholder="Eg. minio/minio:RELEASE.2020-05-08T02-40-49Z"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="service_name"
name="service_name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setServiceName(e.target.value);
clearValidationError("service_name");
}}
label="Service Name"
value={serviceName}
error={validationErrors["service_name"] || ""}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="namespace"
name="namespace"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNamespace(e.target.value);
clearValidationError("namespace");
}}
label="Namespace"
value={namespace}
error={validationErrors["namespace"] || ""}
/>
</Grid>
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{ label: "Next", type: "next", enabled: true },
],
},
{
label: "Storage Class",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Choose your prefered Storage Class</h3>
<span>
Review the storage classes available in the tenant and decide
which one to allocate the tenant to
</span>
</div>
<Grid item xs={12}>
<SelectWrapper
id="storage_class"
name="storage_class"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setVolumeConfig("storage_class", e.target.value as string);
}}
label="Storage Class"
value={volumeConfiguration.storage_class}
options={storageClasses}
/>
</Grid>
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{ label: "Next", type: "next", enabled: true },
],
},
{
label: "Server Configuration",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Server Configuration</h3>
<span>Define the server configuration</span>
</div>
<Grid item xs={12}>
<InputBoxWrapper
id="mount_path"
name="mount_path"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setMountPath(e.target.value);
}}
label="Mount Path"
value={mountPath}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="volumes_per_server"
name="volumes_per_server"
type="number"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumesPerServer(parseInt(e.target.value));
clearValidationError("volumes_per_server");
}}
label="Volumes per Server"
value={volumesPerServer.toString(10)}
required
error={validationErrors["volumes_per_server"] || ""}
/>
</Grid>
<Grid item xs={12}>
<div className={classes.multiContainer}>
<div>
<InputBoxWrapper
id="volume_size"
name="volume_size"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumeConfig("size", e.target.value);
clearValidationError("volume_size");
}}
label="Size"
value={volumeConfiguration.size}
required
error={validationErrors["volume_size"] || ""}
/>
</div>
<div className={classes.sizeFactorContainer}>
<SelectWrapper
label=""
id="size_factor"
name="size_factor"
value={sizeFactor}
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setSizeFactor(e.target.value as string);
}}
options={k8sfactorForDropdown()}
/>
</div>
</div>
</Grid>
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{ label: "Next", type: "next", enabled: configValid },
],
},
{
label: "Zones Definition",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Zones Definition</h3>
<span>Define the size of the tenant by defining the zone size</span>
</div>
<Grid item xs={12}>
<div>
<ZonesMultiSelector
label="Zones"
name="zones_selector"
onChange={(elements: IZone[]) => {
setZones(elements);
}}
elements={zones}
/>
</div>
</Grid>
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{ label: "Next", type: "next", enabled: true },
],
},
{
label: "Extra Configurations",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Extra Configurations</h3>
</div>
<Grid item xs={12}>
<CheckboxWrapper
value="enabled_mcs"
id="enabled_mcs"
name="enabled_mcs"
checked={enableMCS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableMCS(checked);
}}
label={"Enable mcs"}
/>
</Grid>
<Grid item xs={12}>
<CheckboxWrapper
value="enable_ssl"
id="enable_ssl"
name="enable_ssl"
checked={enableSSL}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableSSL(checked);
}}
label={"Enable SSL"}
/>
</Grid>
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{ label: "Next", type: "next", enabled: true },
],
},
{
label: "Review",
componentRender: (
<React.Fragment>
<div className={classes.headerElement}>
<h3>Review</h3>
<span>Review the details of the new tenant</span>
</div>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Table size="small">
<TableBody>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Tenant Name
</TableCell>
<TableCell>{tenantName}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Access Key
</TableCell>
<TableCell>{accessKey}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Secret Key
</TableCell>
<TableCell>{secretKey}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
MinIO Image
</TableCell>
<TableCell>{imageName}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Service Name
</TableCell>
<TableCell>{serviceName}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Namespace
</TableCell>
<TableCell>{namespace}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Storage Class
</TableCell>
<TableCell>{volumeConfiguration.storage_class}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Mount Path
</TableCell>
<TableCell>{mountPath}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Volumes per Server
</TableCell>
<TableCell>{volumesPerServer}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Volume Size
</TableCell>
<TableCell>
{volumeConfiguration.size} {sizeFactor}
</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Total Zones
</TableCell>
<TableCell>{zones.length}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Enable SSL
</TableCell>
<TableCell>{enableSSL ? "Enabled" : "Disabled"}</TableCell>
</TableRow>
<TableRow>
<TableCell align="right" className={classes.tableTitle}>
Enable MCS
</TableCell>
<TableCell>{enableMCS ? "Enabled" : "Disabled"}</TableCell>
</TableRow>
</TableBody>
</Table>
{addSending && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</React.Fragment>
),
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{
label: "Save",
type: "submit",
enabled: !addSending,
action: () => {
console.log("Save");
setAddSending(true);
},
},
],
},
];
return ( return (
<ModalWrapper <ModalWrapper
@@ -159,210 +699,7 @@ const AddTenant = ({
aria-labelledby="alert-dialog-title" aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
> >
<form <GenericWizard wizardSteps={wizardSteps} />
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setAddSending(true);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid item xs={12}>
<InputBoxWrapper
id="tenant-name"
name="tenant-name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTenantName(e.target.value);
}}
label="Tenant Name"
value={tenantName}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="image"
name="image"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setImageName(e.target.value);
}}
label="MinIO Image"
value={imageName}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="service_name"
name="service_name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setServiceName(e.target.value);
}}
label="Service Name"
value={serviceName}
/>
</Grid>
<Grid item xs={12}>
<div>
<ZonesMultiSelector
label="Zones"
name="zones_selector"
onChange={(elements: IZone[]) => {
setZones(elements);
}}
elements={zones}
/>
</div>
</Grid>
<Grid item xs={12}>
<Typography component="h3">Volume Configuration</Typography>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="volumes_per_server"
name="volumes_per_server"
type="number"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumesPerServer(parseInt(e.target.value));
}}
label="Volumes per Server"
value={volumesPerServer.toString(10)}
/>
</Grid>
<Grid item xs={12}>
<div className={classes.multiContainer}>
<div>
<InputBoxWrapper
id="volume_size"
name="volume_size"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumeConfig("size", e.target.value);
}}
label="Size"
value={volumeConfiguration.size}
/>
</div>
<div className={classes.sizeFactorContainer}>
<SelectWrapper
label=""
id="size_factor"
name="size_factor"
value={sizeFactor}
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setSizeFactor(e.target.value as string);
}}
options={k8sfactorForDropdown()}
/>
</div>
</div>
</Grid>
<Grid item xs={12}>
<SelectWrapper
id="storage_class"
name="storage_class"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setVolumeConfig("storage_class", e.target.value as string);
}}
label="Storage Class"
value={volumeConfiguration.storage_class}
options={storageClassesList}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="mount_path"
name="mount_path"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setMountPath(e.target.value);
}}
label="Mount Path"
value={mountPath}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="access_key"
name="access_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
}}
label="Access Key"
value={accessKey}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="secret_key"
name="secret_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
}}
label="Secret Key"
value={secretKey}
/>
</Grid>
<Grid item xs={12}>
<CheckboxWrapper
value="enabled_mcs"
id="enabled_mcs"
name="enabled_mcs"
checked={enableMCS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableMCS(checked);
}}
label={"Enable mcs"}
/>
</Grid>
<Grid item xs={12}>
<CheckboxWrapper
value="enable_ssl"
id="enable_ssl"
name="enable_ssl"
checked={enableSSL}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableSSL(checked);
}}
label={"Enable SSL"}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addSending}
>
Save
</Button>
</Grid>
{addSending && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper> </ModalWrapper>
); );
}; };

View File

@@ -18,12 +18,14 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import get from "lodash/get"; import get from "lodash/get";
import { InputLabel, Tooltip } from "@material-ui/core"; import { InputLabel, Tooltip } from "@material-ui/core";
import IconButton from "@material-ui/core/IconButton";
import HelpIcon from "@material-ui/icons/Help"; import HelpIcon from "@material-ui/icons/Help";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { import {
fieldBasic, fieldBasic,
tooltipHelper, tooltipHelper,
} from "../../Common/FormComponents/common/styleLibrary"; } from "../../Common/FormComponents/common/styleLibrary";
import DeleteIcon from "../../../../icons/DeleteIcon";
import { IZone } from "./types"; import { IZone } from "./types";
interface IZonesMultiSelector { interface IZonesMultiSelector {
@@ -37,7 +39,7 @@ interface IZonesMultiSelector {
const gridBasic = { const gridBasic = {
display: "grid", display: "grid",
gridTemplateColumns: "calc(50% - 4px) calc(50% - 4px)", gridTemplateColumns: "calc(50% - 20px) calc(50% - 20px) 30px",
gridGap: 8, gridGap: 8,
}; };
@@ -66,6 +68,13 @@ const styles = (theme: Theme) =>
...gridBasic, ...gridBasic,
marginBottom: 5, marginBottom: 5,
}, },
deleteIconContainer: {
alignSelf: "center",
"& button": {
padding: 0,
marginBottom: 10,
},
},
}); });
const ZonesMultiSelector = ({ const ZonesMultiSelector = ({
@@ -76,7 +85,7 @@ const ZonesMultiSelector = ({
onChange, onChange,
classes, classes,
}: IZonesMultiSelector) => { }: IZonesMultiSelector) => {
const defaultZone: IZone = { name: "", servers: 0 }; const defaultZone: IZone = { name: "", servers: 0, capacity: "", volumes: 0 };
const [currentElements, setCurrentElements] = useState<IZone[]>([ const [currentElements, setCurrentElements] = useState<IZone[]>([
{ ...defaultZone }, { ...defaultZone },
@@ -122,6 +131,16 @@ const ZonesMultiSelector = ({
setCurrentElements(updatedElement); setCurrentElements(updatedElement);
}; };
const deleteElement = (zoneToRemove: number) => {
const zonesClone = [...currentElements];
const newArray = zonesClone
.slice(0, zoneToRemove)
.concat(zonesClone.slice(zoneToRemove + 1, zonesClone.length));
setCurrentElements(newArray);
};
const inputs = currentElements.map((element, index) => { const inputs = currentElements.map((element, index) => {
return ( return (
<React.Fragment key={`zone-${name}-${index.toString()}`}> <React.Fragment key={`zone-${name}-${index.toString()}`}>
@@ -148,6 +167,17 @@ const ZonesMultiSelector = ({
key={`Zones-${name}-${index.toString()}-servers`} key={`Zones-${name}-${index.toString()}-servers`}
/> />
</div> </div>
<div className={classes.deleteIconContainer}>
<IconButton
color="primary"
onClick={() => {
deleteElement(index);
}}
disabled={index === currentElements.length - 1}
>
<DeleteIcon />
</IconButton>
</div>
</React.Fragment> </React.Fragment>
); );
}); });

View File

@@ -17,6 +17,9 @@
export interface IZone { export interface IZone {
name: string; name: string;
servers: number; servers: number;
// computed
capacity: string;
volumes: number;
} }
export interface IVolumeConfiguration { export interface IVolumeConfiguration {
@@ -32,6 +35,8 @@ export interface ITenant {
creation_date: Date; creation_date: Date;
volume_size: number; volume_size: number;
volume_count: number; volume_count: number;
volumes_per_server: number;
zones: IZone[];
// computed // computed
capacity: string; capacity: string;
} }

View File

@@ -5,13 +5,19 @@ import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper"; import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import { factorForDropdown, getTotalSize } from "../../../../common/utils"; import {
factorForDropdown,
getTotalSize,
niceBytes,
} from "../../../../common/utils";
import { Button, LinearProgress } from "@material-ui/core"; import { Button, LinearProgress } from "@material-ui/core";
interface IAddZoneProps { interface IAddZoneProps {
classes: any; classes: any;
open: boolean; open: boolean;
onCloseZoneAndReload: (shouldReload: boolean) => void; onCloseZoneAndReload: (shouldReload: boolean) => void;
volumesPerInstance: number;
volumeSize: number;
} }
const styles = (theme: Theme) => const styles = (theme: Theme) =>
@@ -60,17 +66,14 @@ const AddZoneModal = ({
classes, classes,
open, open,
onCloseZoneAndReload, onCloseZoneAndReload,
volumesPerInstance,
volumeSize,
}: IAddZoneProps) => { }: IAddZoneProps) => {
const [addSending, setAddSending] = useState<boolean>(false); const [addSending, setAddSending] = useState<boolean>(false);
const [zoneName, setZoneName] = useState<string>(""); const [zoneName, setZoneName] = useState<string>("");
const [numberOfInstances, setNumberOfInstances] = useState<number>(0); const [numberOfInstances, setNumberOfInstances] = useState<number>(0);
const [volumesPerInstance, setVolumesPerInstance] = useState<number>(0);
const [sizeFactor, setSizeFactor] = useState<string>("GiB");
const [volumeConfiguration, setVolumeConfig] = useState<string>("");
const [storageClass, setStorageClass] = useState<string>("");
const instanceCapacity: number = const instanceCapacity: number = volumeSize * volumesPerInstance;
parseFloat(volumeConfiguration) * volumesPerInstance;
const totalCapacity: number = instanceCapacity * numberOfInstances; const totalCapacity: number = instanceCapacity * numberOfInstances;
return ( return (
@@ -112,66 +115,17 @@ const AddZoneModal = ({
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<InputBoxWrapper
id="volumes_per_instance"
name="volumes_per_instance"
type="number"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumesPerInstance(parseInt(e.target.value));
}}
label="Volumes per Instance"
value={volumesPerInstance.toString(10)}
/>
</Grid>
<Grid item xs={12}>
<div className={classes.multiContainer}>
<div>
<InputBoxWrapper
id="volume_size"
name="volume_size"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumeConfig(e.target.value);
}}
label="Size"
value={volumeConfiguration}
/>
</div>
<div className={classes.sizeFactorContainer}>
<SelectWrapper
label=""
id="size_factor"
name="size_factor"
value={sizeFactor}
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setSizeFactor(e.target.value as string);
}}
options={factorForDropdown()}
/>
</div>
</div>
<Grid item xs={12}>
<InputBoxWrapper
id="storage_class"
name="storage_class"
type="string"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setStorageClass(e.target.value);
}}
label="Volumes per Server"
value={storageClass}
/>
</Grid>
<Grid item xs={12} className={classes.bottomContainer}> <Grid item xs={12} className={classes.bottomContainer}>
<div className={classes.factorElements}> <div className={classes.factorElements}>
<div> <div>
<div className={classes.sizeNumber}> <div className={classes.sizeNumber}>
{getTotalSize(instanceCapacity.toString(10), sizeFactor)} {niceBytes(instanceCapacity.toString(10))}
</div> </div>
<div className={classes.sizeDescription}>Instance Capacity</div> <div className={classes.sizeDescription}>Instance Capacity</div>
</div> </div>
<div> <div>
<div className={classes.sizeNumber}> <div className={classes.sizeNumber}>
{getTotalSize(totalCapacity.toString(10), sizeFactor)} {niceBytes(totalCapacity.toString(10))}
</div> </div>
<div className={classes.sizeDescription}>Total Capacity</div> <div className={classes.sizeDescription}>Total Capacity</div>
</div> </div>

View File

@@ -30,6 +30,9 @@ import { niceBytes } from "../../../../common/utils";
import AddZoneModal from "./AddZoneModal"; import AddZoneModal from "./AddZoneModal";
import AddBucket from "../../Buckets/ListBuckets/AddBucket"; import AddBucket from "../../Buckets/ListBuckets/AddBucket";
import ReplicationSetup from "./ReplicationSetup"; import ReplicationSetup from "./ReplicationSetup";
import api from "../../../../common/api";
import { BucketInfo } from "../../Buckets/types";
import { ITenant, IZone } from "../ListTenants/types";
interface ITenantDetailsProps { interface ITenantDetailsProps {
classes: any; classes: any;
@@ -106,12 +109,16 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
const [capacity, setCapacity] = useState<number>(0); const [capacity, setCapacity] = useState<number>(0);
const [externalIDP, setExternalIDP] = useState<boolean>(false); const [externalIDP, setExternalIDP] = useState<boolean>(false);
const [externalKMS, setExternalKMS] = useState<boolean>(false); const [externalKMS, setExternalKMS] = useState<boolean>(false);
const [zones, setZones] = useState<number>(0); const [zoneCount, setZoneCount] = useState<number>(0);
const [zones, setZones] = useState<IZone[]>([]);
const [instances, setInstances] = useState<number>(0); const [instances, setInstances] = useState<number>(0);
const [drives, setDrives] = useState<number>(0); const [volumes, setVolumes] = useState<number>(0);
const [addZoneOpen, setAddZone] = useState<boolean>(false); const [addZoneOpen, setAddZone] = useState<boolean>(false);
const [addBucketOpen, setAddBucketOpen] = useState<boolean>(false); const [addBucketOpen, setAddBucketOpen] = useState<boolean>(false);
const [addReplicationOpen, setAddReplicationOpen] = useState<boolean>(false); const [addReplicationOpen, setAddReplicationOpen] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const [tenant, setTenant] = useState<ITenant | null>(null);
const onCloseZoneAndRefresh = (reload: boolean) => { const onCloseZoneAndRefresh = (reload: boolean) => {
setAddZone(false); setAddZone(false);
@@ -133,12 +140,50 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
} }
}; };
const loadInfo = () => {
const tenantName = match.params["tenantName"];
setLoading(true);
api
.invoke("GET", `/api/v1/mkube/tenants/${tenantName}`)
.then((res: ITenant) => {
const total = res.volume_count * res.volume_size;
setCapacity(total);
setZoneCount(res.zone_count);
setVolumes(res.volume_count);
setInstances(res.instance_count);
const resZones = !res.zones ? [] : res.zones;
for (let zone of resZones) {
zone.volumes = res.volumes_per_server;
const cap = res.volumes_per_server * res.volume_size * zone.servers;
zone.capacity = niceBytes(cap + "");
}
setZones(resZones);
setTenant(res);
setError("");
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
};
useEffect(() => {
loadInfo();
}, []);
return ( return (
<React.Fragment> <React.Fragment>
{addZoneOpen && ( {addZoneOpen && tenant !== null && (
<AddZoneModal <AddZoneModal
open={addZoneOpen} open={addZoneOpen}
onCloseZoneAndReload={onCloseZoneAndRefresh} onCloseZoneAndReload={onCloseZoneAndRefresh}
volumeSize={tenant.volume_size}
volumesPerInstance={tenant.volumes_per_server}
/> />
)} )}
{addBucketOpen && ( {addBucketOpen && (
@@ -156,7 +201,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
<Grid container> <Grid container>
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h6"> <Typography variant="h6">
Tenant > {match.params["clusterName"]} Tenant > {match.params["tenantName"]}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
@@ -168,7 +213,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
<div>Capacity:</div> <div>Capacity:</div>
<div>{niceBytes(capacity.toString(10))}</div> <div>{niceBytes(capacity.toString(10))}</div>
<div>Zones:</div> <div>Zones:</div>
<div>{zones}</div> <div>{zoneCount}</div>
<div>External IDP:</div> <div>External IDP:</div>
<div> <div>
{externalIDP ? "Yes" : "No"}&nbsp;&nbsp; {externalIDP ? "Yes" : "No"}&nbsp;&nbsp;
@@ -184,19 +229,9 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
<div>Instances:</div> <div>Instances:</div>
<div>{instances}</div> <div>{instances}</div>
<div>External KMS:</div> <div>External KMS:</div>
<div> <div>{externalKMS ? "Yes" : "No"}&nbsp;&nbsp;</div>
{externalKMS ? "Yes" : "No"}&nbsp;&nbsp; <div>Volumes:</div>
<Button <div>{volumes}</div>
variant="contained"
color="primary"
size="small"
onClick={() => {}}
>
Edit
</Button>
</div>
<div>Drives:</div>
<div>{drives}</div>
</div> </div>
</Paper> </Paper>
<div className={classes.masterActions}> <div className={classes.masterActions}>
@@ -210,16 +245,6 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
Warp Warp
</Button> </Button>
</div> </div>
<div>
<Button
variant="contained"
color="primary"
fullWidth
onClick={() => {}}
>
See as deployment
</Button>
</div>
</div> </div>
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
@@ -303,17 +328,13 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
}, },
]} ]}
columns={[ columns={[
{
label: "Status",
elementKey: "status",
},
{ label: "Name", elementKey: "name" }, { label: "Name", elementKey: "name" },
{ label: "Capacity", elementKey: "capacity" }, { label: "Capacity", elementKey: "capacity" },
{ label: "# of Instances", elementKey: "instances" }, { label: "# of Instances", elementKey: "servers" },
{ label: "# of Drives", elementKey: "drives" }, { label: "# of Drives", elementKey: "volumes" },
]} ]}
isLoading={false} isLoading={false}
records={[]} records={zones}
entityName="Zones" entityName="Zones"
idField="name" idField="name"
paginatorConfig={{ paginatorConfig={{

View File

@@ -0,0 +1,44 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
export interface IValidation {
fieldKey: string;
required: boolean;
pattern?: RegExp;
customPatternMessage?: string;
value: string;
}
export const commonFormValidation = (fieldsValidate: IValidation[]) => {
let returnErrors: any = {};
fieldsValidate.forEach((field) => {
if (field.required && field.value.trim() === "") {
returnErrors[field.fieldKey] = "Field cannot be empty";
return;
}
if (field.pattern && field.customPatternMessage) {
const rgx = new RegExp(field.pattern, "g");
if (!field.value.match(rgx)) {
returnErrors[field.fieldKey] = field.customPatternMessage;
}
}
});
return returnErrors;
};

View File

@@ -12656,9 +12656,9 @@ websocket-driver@>=0.5.1:
websocket-extensions ">=0.1.1" websocket-extensions ">=0.1.1"
websocket-extensions@>=0.1.1: websocket-extensions@>=0.1.1:
version "0.1.3" version "0.1.4"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
websocket@^1.0.31: websocket@^1.0.31:
version "1.0.31" version "1.0.31"

View File

@@ -74,10 +74,10 @@ func registerUsersHandlers(api *operations.McsAPI) {
sessionID := string(*principal) sessionID := string(*principal)
userInfoResponse, err := getUserInfoResponse(sessionID, params) userInfoResponse, err := getUserInfoResponse(sessionID, params)
if err != nil { if err != nil {
return admin_api.NewGetUserDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())}) return admin_api.NewGetUserInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
} }
return admin_api.NewGetUserOK().WithPayload(userInfoResponse) return admin_api.NewGetUserInfoOK().WithPayload(userInfoResponse)
}) })
// Update User // Update User
api.AdminAPIUpdateUserInfoHandler = admin_api.UpdateUserInfoHandlerFunc(func(params admin_api.UpdateUserInfoParams, principal *models.Principal) middleware.Responder { api.AdminAPIUpdateUserInfoHandler = admin_api.UpdateUserInfoHandlerFunc(func(params admin_api.UpdateUserInfoParams, principal *models.Principal) middleware.Responder {

View File

@@ -28,10 +28,10 @@ import (
var Port = "9090" var Port = "9090"
// Hostname mcs hostname // Hostname mcs hostname
var Hostname = "localhost" var Hostname = "0.0.0.0"
// TLSHostname mcs tls hostname // TLSHostname mcs tls hostname
var TLSHostname = "localhost" var TLSHostname = "0.0.0.0"
// TLSPort mcs tls port // TLSPort mcs tls port
var TLSPort = "9443" var TLSPort = "9443"

View File

@@ -1,90 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/mcs/models"
)
// GetUserHandlerFunc turns a function with the right signature into a get user handler
type GetUserHandlerFunc func(GetUserParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn GetUserHandlerFunc) Handle(params GetUserParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// GetUserHandler interface for that can handle valid get user params
type GetUserHandler interface {
Handle(GetUserParams, *models.Principal) middleware.Responder
}
// NewGetUser creates a new http.Handler for the get user operation
func NewGetUser(ctx *middleware.Context, handler GetUserHandler) *GetUser {
return &GetUser{Context: ctx, Handler: handler}
}
/*GetUser swagger:route GET /users/{name} AdminAPI getUser
Get User
*/
type GetUser struct {
Context *middleware.Context
Handler GetUserHandler
}
func (o *GetUser) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewGetUserParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -1,89 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
)
// NewGetUserParams creates a new GetUserParams object
// no default values defined in spec.
func NewGetUserParams() GetUserParams {
return GetUserParams{}
}
// GetUserParams contains all the bound params for the get user operation
// typically these are obtained from a http.Request
//
// swagger:parameters GetUser
type GetUserParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: path
*/
Name string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewGetUserParams() beforehand.
func (o *GetUserParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
rName, rhkName, _ := route.Params.GetOK("name")
if err := o.bindName(rName, rhkName, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindName binds and validates parameter Name from path.
func (o *GetUserParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.Name = raw
return nil
}

View File

@@ -1,133 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/mcs/models"
)
// GetUserOKCode is the HTTP code returned for type GetUserOK
const GetUserOKCode int = 200
/*GetUserOK A successful response.
swagger:response getUserOK
*/
type GetUserOK struct {
/*
In: Body
*/
Payload *models.User `json:"body,omitempty"`
}
// NewGetUserOK creates GetUserOK with default headers values
func NewGetUserOK() *GetUserOK {
return &GetUserOK{}
}
// WithPayload adds the payload to the get user o k response
func (o *GetUserOK) WithPayload(payload *models.User) *GetUserOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get user o k response
func (o *GetUserOK) SetPayload(payload *models.User) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetUserOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
/*GetUserDefault Generic error response.
swagger:response getUserDefault
*/
type GetUserDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewGetUserDefault creates GetUserDefault with default headers values
func NewGetUserDefault(code int) *GetUserDefault {
if code <= 0 {
code = 500
}
return &GetUserDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the get user default response
func (o *GetUserDefault) WithStatusCode(code int) *GetUserDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the get user default response
func (o *GetUserDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the get user default response
func (o *GetUserDefault) WithPayload(payload *models.Error) *GetUserDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get user default response
func (o *GetUserDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetUserDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -1,116 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
)
// GetUserURL generates an URL for the get user operation
type GetUserURL struct {
Name string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *GetUserURL) WithBasePath(bp string) *GetUserURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *GetUserURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *GetUserURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/users/{name}"
name := o.Name
if name != "" {
_path = strings.Replace(_path, "{name}", name, -1)
} else {
return nil, errors.New("name is required on GetUserURL")
}
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *GetUserURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *GetUserURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *GetUserURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on GetUserURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on GetUserURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *GetUserURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -1,75 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// LoginOauth2CallbackHandlerFunc turns a function with the right signature into a login oauth2 callback handler
type LoginOauth2CallbackHandlerFunc func(LoginOauth2CallbackParams) middleware.Responder
// Handle executing the request and returning a response
func (fn LoginOauth2CallbackHandlerFunc) Handle(params LoginOauth2CallbackParams) middleware.Responder {
return fn(params)
}
// LoginOauth2CallbackHandler interface for that can handle valid login oauth2 callback params
type LoginOauth2CallbackHandler interface {
Handle(LoginOauth2CallbackParams) middleware.Responder
}
// NewLoginOauth2Callback creates a new http.Handler for the login oauth2 callback operation
func NewLoginOauth2Callback(ctx *middleware.Context, handler LoginOauth2CallbackHandler) *LoginOauth2Callback {
return &LoginOauth2Callback{Context: ctx, Handler: handler}
}
/*LoginOauth2Callback swagger:route GET /login/oauth2/callback UserAPI loginOauth2Callback
Identity Provider oauth2 callback endpoint.
*/
type LoginOauth2Callback struct {
Context *middleware.Context
Handler LoginOauth2CallbackHandler
}
func (o *LoginOauth2Callback) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewLoginOauth2CallbackParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -1,62 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewLoginOauth2CallbackParams creates a new LoginOauth2CallbackParams object
// no default values defined in spec.
func NewLoginOauth2CallbackParams() LoginOauth2CallbackParams {
return LoginOauth2CallbackParams{}
}
// LoginOauth2CallbackParams contains all the bound params for the login oauth2 callback operation
// typically these are obtained from a http.Request
//
// swagger:parameters LoginOauth2Callback
type LoginOauth2CallbackParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewLoginOauth2CallbackParams() beforehand.
func (o *LoginOauth2CallbackParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -1,113 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/mcs/models"
)
// LoginOauth2CallbackOKCode is the HTTP code returned for type LoginOauth2CallbackOK
const LoginOauth2CallbackOKCode int = 200
/*LoginOauth2CallbackOK A successful response.
swagger:response loginOauth2CallbackOK
*/
type LoginOauth2CallbackOK struct {
}
// NewLoginOauth2CallbackOK creates LoginOauth2CallbackOK with default headers values
func NewLoginOauth2CallbackOK() *LoginOauth2CallbackOK {
return &LoginOauth2CallbackOK{}
}
// WriteResponse to the client
func (o *LoginOauth2CallbackOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
/*LoginOauth2CallbackDefault Generic error response.
swagger:response loginOauth2CallbackDefault
*/
type LoginOauth2CallbackDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewLoginOauth2CallbackDefault creates LoginOauth2CallbackDefault with default headers values
func NewLoginOauth2CallbackDefault(code int) *LoginOauth2CallbackDefault {
if code <= 0 {
code = 500
}
return &LoginOauth2CallbackDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the login oauth2 callback default response
func (o *LoginOauth2CallbackDefault) WithStatusCode(code int) *LoginOauth2CallbackDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the login oauth2 callback default response
func (o *LoginOauth2CallbackDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the login oauth2 callback default response
func (o *LoginOauth2CallbackDefault) WithPayload(payload *models.Error) *LoginOauth2CallbackDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the login oauth2 callback default response
func (o *LoginOauth2CallbackDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *LoginOauth2CallbackDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -1,104 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// LoginOauth2CallbackURL generates an URL for the login oauth2 callback operation
type LoginOauth2CallbackURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *LoginOauth2CallbackURL) WithBasePath(bp string) *LoginOauth2CallbackURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *LoginOauth2CallbackURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *LoginOauth2CallbackURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/login/oauth2/callback"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *LoginOauth2CallbackURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *LoginOauth2CallbackURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *LoginOauth2CallbackURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on LoginOauth2CallbackURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on LoginOauth2CallbackURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *LoginOauth2CallbackURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -1,75 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// Oauth2CallbackHandlerFunc turns a function with the right signature into a oauth2 callback handler
type Oauth2CallbackHandlerFunc func(Oauth2CallbackParams) middleware.Responder
// Handle executing the request and returning a response
func (fn Oauth2CallbackHandlerFunc) Handle(params Oauth2CallbackParams) middleware.Responder {
return fn(params)
}
// Oauth2CallbackHandler interface for that can handle valid oauth2 callback params
type Oauth2CallbackHandler interface {
Handle(Oauth2CallbackParams) middleware.Responder
}
// NewOauth2Callback creates a new http.Handler for the oauth2 callback operation
func NewOauth2Callback(ctx *middleware.Context, handler Oauth2CallbackHandler) *Oauth2Callback {
return &Oauth2Callback{Context: ctx, Handler: handler}
}
/*Oauth2Callback swagger:route GET /login/oauth2/callback UserAPI oauth2Callback
Identity Provider oauth2 callback endpoint.
*/
type Oauth2Callback struct {
Context *middleware.Context
Handler Oauth2CallbackHandler
}
func (o *Oauth2Callback) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewOauth2CallbackParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -1,62 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewOauth2CallbackParams creates a new Oauth2CallbackParams object
// no default values defined in spec.
func NewOauth2CallbackParams() Oauth2CallbackParams {
return Oauth2CallbackParams{}
}
// Oauth2CallbackParams contains all the bound params for the oauth2 callback operation
// typically these are obtained from a http.Request
//
// swagger:parameters Oauth2Callback
type Oauth2CallbackParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewOauth2CallbackParams() beforehand.
func (o *Oauth2CallbackParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -1,113 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/mcs/models"
)
// Oauth2CallbackOKCode is the HTTP code returned for type Oauth2CallbackOK
const Oauth2CallbackOKCode int = 200
/*Oauth2CallbackOK A successful response.
swagger:response oauth2CallbackOK
*/
type Oauth2CallbackOK struct {
}
// NewOauth2CallbackOK creates Oauth2CallbackOK with default headers values
func NewOauth2CallbackOK() *Oauth2CallbackOK {
return &Oauth2CallbackOK{}
}
// WriteResponse to the client
func (o *Oauth2CallbackOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
/*Oauth2CallbackDefault Generic error response.
swagger:response oauth2CallbackDefault
*/
type Oauth2CallbackDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewOauth2CallbackDefault creates Oauth2CallbackDefault with default headers values
func NewOauth2CallbackDefault(code int) *Oauth2CallbackDefault {
if code <= 0 {
code = 500
}
return &Oauth2CallbackDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the oauth2 callback default response
func (o *Oauth2CallbackDefault) WithStatusCode(code int) *Oauth2CallbackDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the oauth2 callback default response
func (o *Oauth2CallbackDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the oauth2 callback default response
func (o *Oauth2CallbackDefault) WithPayload(payload *models.Error) *Oauth2CallbackDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the oauth2 callback default response
func (o *Oauth2CallbackDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *Oauth2CallbackDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -1,104 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
//
package user_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// Oauth2CallbackURL generates an URL for the oauth2 callback operation
type Oauth2CallbackURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *Oauth2CallbackURL) WithBasePath(bp string) *Oauth2CallbackURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *Oauth2CallbackURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *Oauth2CallbackURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/login/oauth2/callback"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *Oauth2CallbackURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *Oauth2CallbackURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *Oauth2CallbackURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on Oauth2CallbackURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on Oauth2CallbackURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *Oauth2CallbackURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}