Implement License page (#324)
* Implement License page Fixes #320 * License Assets * Fix endpoint tests Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
This commit is contained in:
@@ -42,6 +42,7 @@ var (
|
||||
replication = "/replication"
|
||||
objectBrowser = "/object-browser/:bucket?"
|
||||
mainObjectBrowser = "/object-browser"
|
||||
license = "/license"
|
||||
)
|
||||
|
||||
type ConfigurationActionSet struct {
|
||||
@@ -236,6 +237,12 @@ var objectBrowserActionSet = ConfigurationActionSet{
|
||||
actions: iampolicy.NewActionSet(),
|
||||
}
|
||||
|
||||
// licenseActionSet no actions needed for this module to work
|
||||
var licenseActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(),
|
||||
actions: iampolicy.NewActionSet(),
|
||||
}
|
||||
|
||||
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
||||
var endpointRules = map[string]ConfigurationActionSet{
|
||||
configuration: configurationActionSet,
|
||||
@@ -256,6 +263,7 @@ var endpointRules = map[string]ConfigurationActionSet{
|
||||
replication: replicationActionSet,
|
||||
objectBrowser: objectBrowserActionSet,
|
||||
mainObjectBrowser: objectBrowserActionSet,
|
||||
license: licenseActionSet,
|
||||
}
|
||||
|
||||
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
args: args{
|
||||
[]string{"admin:ServerInfo"},
|
||||
},
|
||||
want: 4,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "policies endpoint",
|
||||
@@ -63,7 +63,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"admin:ListUserPolicies",
|
||||
},
|
||||
},
|
||||
want: 4,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "all admin endpoints",
|
||||
@@ -72,7 +72,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"admin:*",
|
||||
},
|
||||
},
|
||||
want: 15,
|
||||
want: 16,
|
||||
},
|
||||
{
|
||||
name: "all s3 endpoints",
|
||||
@@ -81,7 +81,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 6,
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
name: "all admin and s3 endpoints",
|
||||
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 18,
|
||||
want: 19,
|
||||
},
|
||||
{
|
||||
name: "no endpoints",
|
||||
|
||||
File diff suppressed because one or more lines are too long
47
portal-ui/src/icons/LicenseIcon.tsx
Normal file
47
portal-ui/src/icons/LicenseIcon.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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 { SvgIcon } from "@material-ui/core";
|
||||
|
||||
class LicenseIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
|
||||
<path fill="#fff" d="M11 11H0V2h11v9zM2 8v1h7V8zm0-3v1h5V5z"></path>
|
||||
<g
|
||||
fill="#07274a"
|
||||
stroke="#fdfdfd"
|
||||
strokeWidth="0.5"
|
||||
transform="translate(7)"
|
||||
>
|
||||
<circle cx="3" cy="3" r="3" stroke="none"></circle>
|
||||
<circle cx="3" cy="3" r="2.75" fill="none"></circle>
|
||||
</g>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
strokeWidth="0.5"
|
||||
d="M8.73 2.794l.954.953 1.471-1.471"
|
||||
></path>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LicenseIcon;
|
||||
@@ -69,6 +69,7 @@ import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
|
||||
import { clearSession } from "../../common/utils";
|
||||
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||
import ListObjects from "./Buckets/ListBuckets/Objects/ListObjects/ListObjects";
|
||||
import License from "./License/License";
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
@@ -322,6 +323,10 @@ const Console = ({
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||
},
|
||||
{
|
||||
component: License,
|
||||
path: "/license",
|
||||
},
|
||||
];
|
||||
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
||||
|
||||
|
||||
335
portal-ui/src/screens/Console/License/License.tsx
Normal file
335
portal-ui/src/screens/Console/License/License.tsx
Normal file
@@ -0,0 +1,335 @@
|
||||
// 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 clsx from "clsx";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { planDetails, planItems, planButtons } from "./utils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
pageTitle: {
|
||||
fontSize: 18,
|
||||
marginBottom: 20,
|
||||
},
|
||||
paper: {
|
||||
padding: "20px 52px 20px 28px",
|
||||
},
|
||||
tableContainer: {
|
||||
marginLeft: 28,
|
||||
},
|
||||
detailsContainer: {
|
||||
textAlign: "center",
|
||||
paddingTop: 18,
|
||||
paddingBottom: 12,
|
||||
borderRadius: "3px 3px 0 0",
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
detailsContainerBorder: {
|
||||
border: "1px solid #e2e2e2",
|
||||
borderBottom: 0,
|
||||
},
|
||||
detailsContainerBorderHighlighted: {
|
||||
border: "1px solid #9a93ad",
|
||||
borderBottom: 0,
|
||||
},
|
||||
detailsTitle: {
|
||||
fontSize: 17,
|
||||
fontWeight: 700,
|
||||
marginBottom: 26,
|
||||
},
|
||||
detailsPrice: {
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
marginBottom: 8,
|
||||
},
|
||||
detailsCapacityMax: {
|
||||
minHeight: 28,
|
||||
fontSize: 10,
|
||||
fontWeight: 700,
|
||||
marginBottom: 12,
|
||||
padding: "0% 15%",
|
||||
color: "#474747",
|
||||
},
|
||||
detailsCapacityMin: {
|
||||
fontSize: 10,
|
||||
},
|
||||
itemContainer: {
|
||||
height: 36,
|
||||
borderTop: "1px solid #e5e5e5",
|
||||
},
|
||||
itemContainerDetail: {
|
||||
height: 48,
|
||||
borderTop: "1px solid #e5e5e5",
|
||||
},
|
||||
item: {
|
||||
height: "100%",
|
||||
borderLeft: "1px solid #e2e2e2",
|
||||
borderRight: "1px solid #e2e2e2",
|
||||
textAlign: "center",
|
||||
fontSize: 10,
|
||||
fontWeight: 700,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
alignContent: "center",
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
itemFirst: {
|
||||
borderLeft: 0,
|
||||
borderRight: 0,
|
||||
},
|
||||
itemHighlighted: {
|
||||
borderLeft: "1px solid #9a93ad",
|
||||
borderRight: "1px solid #9a93ad",
|
||||
},
|
||||
field: {
|
||||
textAlign: "left",
|
||||
fontWeight: 400,
|
||||
},
|
||||
checkIcon: {
|
||||
height: 12,
|
||||
color:
|
||||
"transparent linear-gradient(90deg, #073052 0%, #081c42 100%) 0% 0% no-repeat padding-box",
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingTop: 8,
|
||||
paddingBottom: 24,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
borderRadius: "0 0 3px 3px",
|
||||
border: "1px solid #e2e2e2",
|
||||
borderTop: 0,
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
buttonContainerBlank: {
|
||||
border: 0,
|
||||
},
|
||||
buttonContainerHighlighted: {
|
||||
border: "1px solid #9a93ad",
|
||||
borderTop: 0,
|
||||
},
|
||||
button: {
|
||||
textTransform: "none",
|
||||
fontSize: 15,
|
||||
fontWeight: 700,
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface ILicense {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const License = ({ classes }: ILicense) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="License" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
className={classes.pageTitle}
|
||||
>
|
||||
Upgrade to commercial license
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid container item xs={12} className={classes.tableContainer}>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={3} className={classes.detailsContainer} />
|
||||
{planDetails.map((details: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.detailsContainer,
|
||||
classes.detailsContainerBorder,
|
||||
{
|
||||
[classes.detailsContainerBorderHighlighted]:
|
||||
details.title !== "Community",
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Grid item xs={12} className={classes.detailsTitle}>
|
||||
{details.title}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.detailsPrice}>
|
||||
{details.price}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.detailsCapacityMax}
|
||||
>
|
||||
{details.capacityMax || ""}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.detailsCapacityMin}
|
||||
>
|
||||
{details.capacityMin}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
{planItems.map((item: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={12}
|
||||
className={clsx(
|
||||
classes.itemContainer,
|
||||
item.communityDetail && classes.itemContainerDetail
|
||||
)}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.item,
|
||||
classes.field,
|
||||
classes.itemFirst
|
||||
)}
|
||||
>
|
||||
{item.field}
|
||||
</Grid>
|
||||
<Grid container item xs={3} className={classes.item}>
|
||||
<Grid item xs={12}>
|
||||
{item.community === "N/A" ? (
|
||||
""
|
||||
) : item.community === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.community
|
||||
)}
|
||||
</Grid>
|
||||
{item.communityDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.communityDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.item, classes.itemHighlighted)}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
{item.standard === "N/A" ? (
|
||||
""
|
||||
) : item.standard === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.standard
|
||||
)}
|
||||
</Grid>
|
||||
{item.standardDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.standardDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.item, classes.itemHighlighted)}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
{item.enterprise === "N/A" ? (
|
||||
""
|
||||
) : item.enterprise === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.enterprise
|
||||
)}
|
||||
</Grid>
|
||||
{item.enterpriseDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.enterpriseDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
<Grid container item xs={12}>
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.buttonContainer,
|
||||
classes.buttonContainerBlank
|
||||
)}
|
||||
/>
|
||||
{planButtons.map((button: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.buttonContainer, {
|
||||
[classes.buttonContainerHighlighted]:
|
||||
button.text === "Subscribe",
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
variant={
|
||||
button.text === "Subscribe"
|
||||
? "contained"
|
||||
: "outlined"
|
||||
}
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
target="_blank"
|
||||
href={button.link}
|
||||
>
|
||||
{button.text}
|
||||
</Button>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(License);
|
||||
119
portal-ui/src/screens/Console/License/utils.ts
Normal file
119
portal-ui/src/screens/Console/License/utils.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
// 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 const planDetails = [
|
||||
{
|
||||
title: "Community",
|
||||
price: "Free",
|
||||
capacityMin: "(No minimum)",
|
||||
},
|
||||
{
|
||||
title: "Standard",
|
||||
price: "$10/TB/month",
|
||||
capacityMax: "Up to 10PB. No additional charges for capacity over 10PB",
|
||||
capacityMin: "(25TB minimum)",
|
||||
},
|
||||
{
|
||||
title: "Enterprise",
|
||||
price: "$20/TB/month",
|
||||
capacityMax: "Up to 5PB. No additional charges for capacity over 5PB",
|
||||
capacityMin: "(100TB minimum)",
|
||||
},
|
||||
];
|
||||
|
||||
export const planItems = [
|
||||
{
|
||||
field: "License",
|
||||
community: "100% Open Source",
|
||||
communityDetail: "Apache License v2, GNU AGPL v3",
|
||||
standard: "Dual License",
|
||||
standardDetail: "Commercial + Open Source",
|
||||
enterprise: "Dual License",
|
||||
enterpriseDetail: "Commercial + Open Source",
|
||||
},
|
||||
{
|
||||
field: "Software Release",
|
||||
community: "Update to latest",
|
||||
standard: "1 Year Long Term Support",
|
||||
enterprise: "5 Years Long Term Support",
|
||||
},
|
||||
{
|
||||
field: "SLA",
|
||||
community: "No SLA",
|
||||
standard: "<24 hours",
|
||||
enterprise: "<1 hour",
|
||||
},
|
||||
{
|
||||
field: "Support",
|
||||
community: "Community:",
|
||||
communityDetail: "Public Slack Channel + Github Issues",
|
||||
standard: "24x7 L4 direct engineering",
|
||||
standardDetail: "Support via SUBNET",
|
||||
enterprise: "24x7 L4 direct engineering",
|
||||
enterpriseDetail: "Support via SUBNET",
|
||||
},
|
||||
{
|
||||
field: "Security Updates & Critical Bugs",
|
||||
community: "Self Update",
|
||||
standard: "Guided Update",
|
||||
enterprise: "Guided Update",
|
||||
},
|
||||
{
|
||||
field: "Panic Button",
|
||||
community: "N/A",
|
||||
standard: "1 per year",
|
||||
enterprise: "Unlimited",
|
||||
},
|
||||
{
|
||||
field: "Annual Architecture Review",
|
||||
community: "N/A",
|
||||
standard: "Yes",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Annual Performance Review",
|
||||
community: "N/A",
|
||||
standard: "Yes",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Indemnification",
|
||||
community: "N/A",
|
||||
standard: "N/A",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Security + Policy Review",
|
||||
community: "N/A",
|
||||
standard: "N/A",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
];
|
||||
|
||||
export const planButtons = [
|
||||
{
|
||||
text: "Slack Community",
|
||||
link: "https://slack.min.io",
|
||||
},
|
||||
{
|
||||
text: "Subscribe",
|
||||
link: "https://min.io/pricing",
|
||||
},
|
||||
{
|
||||
text: "Subscribe",
|
||||
link: "https://min.io/pricing",
|
||||
},
|
||||
];
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
import { clearSession } from "../../../common/utils";
|
||||
import HealIcon from "../../../icons/HealIcon";
|
||||
import ConsoleIcon from "../../../icons/ConsoleIcon";
|
||||
import LicenseIcon from "../../../icons/LicenseIcon";
|
||||
import LogoutIcon from "../../../icons/LogoutIcon";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -318,6 +319,14 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
name: "Warp",
|
||||
icon: <WarpIcon />,
|
||||
},
|
||||
{
|
||||
group: "License",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/license",
|
||||
name: "License",
|
||||
icon: <LicenseIcon />,
|
||||
},
|
||||
];
|
||||
|
||||
const allowedPages = pages.reduce((result: any, item: any, index: any) => {
|
||||
|
||||
@@ -20,4 +20,5 @@ export const menuGroups = [
|
||||
{ label: "Admin", group: "Admin", collapsible: true },
|
||||
{ label: "Tools", group: "Tools", collapsible: true },
|
||||
{ label: "Operator", group: "Operator", collapsible: true },
|
||||
{ label: "", group: "License", collapsible: false },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user