mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
console license page improvements and fixes (#647)
- fixed issue when deploying tenant with tls disabled - applied new design for tenant details and license screens - added license refresh job to operator console - added new refresh license endpoint - console operator not longer store CONSOLE_ACCESS_KEY and CONSOLE_SECRET_KEY values in the tenant-console-secret Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/jessevdk/go-flags"
|
||||
@@ -135,6 +136,28 @@ func startServer(ctx *cli.Context) error {
|
||||
|
||||
server.ConfigureAPI()
|
||||
|
||||
// subnet license refresh process
|
||||
go func() {
|
||||
failedAttempts := 0
|
||||
for {
|
||||
if err := restapi.RefreshLicense(); err != nil {
|
||||
log.Println(err)
|
||||
failedAttempts++
|
||||
// end license refresh after 3 consecutive failed attempts
|
||||
if failedAttempts >= 3 {
|
||||
return
|
||||
}
|
||||
// wait 5 minutes and retry again
|
||||
time.Sleep(time.Minute * 5)
|
||||
continue
|
||||
}
|
||||
// if license refreshed successfully reset the counter
|
||||
failedAttempts = 0
|
||||
// try to refresh license every 24 hrs
|
||||
time.Sleep(time.Hour * 24)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := server.Serve(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -21,7 +21,7 @@ require (
|
||||
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243
|
||||
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f
|
||||
github.com/minio/minio-go/v7 v7.0.10
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4
|
||||
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4
|
||||
github.com/minio/selfupdate v0.3.1
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
|
||||
6
go.sum
6
go.sum
@@ -895,7 +895,7 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/minio/cli v1.22.0 h1:VTQm7lmXm3quxO917X3p+el1l0Ca5X3S4PM2ruUYO68=
|
||||
github.com/minio/cli v1.22.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
|
||||
github.com/minio/controller-tools v0.4.6/go.mod h1:xES4iNis9dGrLQuP6nquTZZNg2T0/EM8wduC4/GWfZ0=
|
||||
github.com/minio/controller-tools v0.4.7/go.mod h1:xES4iNis9dGrLQuP6nquTZZNg2T0/EM8wduC4/GWfZ0=
|
||||
github.com/minio/direct-csi v1.2.8 h1:jOpefwTGZYUIzouz5McSQ8EqeXO7Qu5aCJIwstbc6vs=
|
||||
github.com/minio/direct-csi v1.2.8/go.mod h1:+Zw8NjMQ5rQqySezan6G9DsQjHSHQCPWJli3v9dP80o=
|
||||
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
|
||||
@@ -917,8 +917,8 @@ github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c/go.mod h1:pEZB
|
||||
github.com/minio/minio-go/v7 v7.0.8-0.20210127003153-c40722862654/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
|
||||
github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94=
|
||||
github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo=
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4 h1:2TtnWOrVkMC8N/wLWwlnsEIMOHpZOIsF8JZ0cPDI1m0=
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4/go.mod h1:xjLK0CsJr9Zo0AUdgpsnBbTjHyM5XXr15JM/MHNBppI=
|
||||
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd h1:ibM7ebC9WdtgJh6p3mqbU0W1vpoFXqJgTHDBKJWGRQI=
|
||||
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd/go.mod h1:1Bpqm6g8f30YeePauZfSet7EWrWeyqu1eJhY5/4sn28=
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4 h1:7HNd0WPMFcQzQbgPs7VQJfiXVm8xjuxnS3/1yi4twwM=
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4/go.mod h1:ngzK3RurLvshJ4XmJ6eP4WTOIc9Vu1HQNq0Hm6XOZmw=
|
||||
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
||||
|
||||
@@ -32,5 +32,6 @@ const (
|
||||
// Subnet endpoints
|
||||
publicKey = "/downloads/license-pubkey.pem"
|
||||
loginEndpoint = "/api/auth/login"
|
||||
refreshLicenseKeyEndpoint = "/api/auth/subscription/renew-license"
|
||||
licenseKeyEndpoint = "/api/auth/subscription/license-key"
|
||||
)
|
||||
|
||||
@@ -67,6 +67,41 @@ type subnetLicenseResponse struct {
|
||||
Metadata LicenseMetadata `json:"metadata"`
|
||||
}
|
||||
|
||||
// subnetLoginRequest body request for subnet login
|
||||
type subnetRefreshRequest struct {
|
||||
License string `json:"license"`
|
||||
}
|
||||
|
||||
// getNewLicenseFromExistingLicense will perform license refresh based on the provided license key
|
||||
func getNewLicenseFromExistingLicense(client cluster.HTTPClientI, licenseKey string) (string, error) {
|
||||
request := subnetRefreshRequest{
|
||||
License: licenseKey,
|
||||
}
|
||||
// http body for login request
|
||||
payloadBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetURL := GetSubnetURL()
|
||||
url := fmt.Sprintf("%s%s", subnetURL, refreshLicenseKeyEndpoint)
|
||||
resp, err := client.Post(url, "application/json", bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetLicense := &subnetLicenseResponse{}
|
||||
// Parse subnet login response
|
||||
err = json.Unmarshal(bodyBytes, subnetLicense)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return subnetLicense.License, nil
|
||||
}
|
||||
|
||||
// getLicenseFromCredentials will perform authentication against subnet using
|
||||
// user provided credentials and return the current subnet license key
|
||||
func getLicenseFromCredentials(client cluster.HTTPClientI, username, password string) (string, error) {
|
||||
@@ -171,3 +206,18 @@ func ValidateLicense(client cluster.HTTPClientI, licenseKey, email, password str
|
||||
}
|
||||
return licInfo, license, nil
|
||||
}
|
||||
|
||||
func RefreshLicense(client cluster.HTTPClientI, licenseKey string) (licInfo *licverifier.LicenseInfo, license string, err error) {
|
||||
if licenseKey != "" {
|
||||
license, err = getNewLicenseFromExistingLicense(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
licenseInfo, rawLicense, err := ValidateLicense(client, license, "", "")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return licenseInfo, rawLicense, nil
|
||||
}
|
||||
return nil, "", errors.New("invalid license")
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"static/js/2.33fe03f5.chunk.js": "/static/js/2.33fe03f5.chunk.js",
|
||||
"static/js/2.33fe03f5.chunk.js.map": "/static/js/2.33fe03f5.chunk.js.map",
|
||||
"index.html": "/index.html",
|
||||
"static/css/2.76b14b73.chunk.css.map": "/static/css/2.76b14b73.chunk.css.map",
|
||||
"static/css/2.f324abd6.chunk.css.map": "/static/css/2.f324abd6.chunk.css.map",
|
||||
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
|
||||
"static/js/2.33fe03f5.chunk.js.LICENSE.txt": "/static/js/2.33fe03f5.chunk.js.LICENSE.txt",
|
||||
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
.ReactVirtualized__Table__headerRow{font-weight:700;text-transform:uppercase}.ReactVirtualized__Table__headerRow,.ReactVirtualized__Table__row{display:flex;flex-direction:row;align-items:center}.ReactVirtualized__Table__headerTruncatedText{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ReactVirtualized__Table__headerColumn,.ReactVirtualized__Table__rowColumn{margin-right:10px;min-width:0}.ReactVirtualized__Table__rowColumn{text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn:first-of-type,.ReactVirtualized__Table__rowColumn:first-of-type{margin-left:10px}.ReactVirtualized__Table__sortableHeaderColumn{cursor:pointer}.ReactVirtualized__Table__sortableHeaderIconContainer{display:flex;align-items:center}.ReactVirtualized__Table__sortableHeaderIcon{flex:0 0 24px;height:1em;width:1em;fill:currentColor}.react-grid-layout{position:relative;transition:height .2s ease}.react-grid-item{transition:all .2s ease;transition-property:left,top}.react-grid-item img{pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.react-grid-item.cssTransforms{transition-property:transform}.react-grid-item.resizing{z-index:1;will-change:width,height}.react-grid-item.react-draggable-dragging{transition:none;z-index:3;will-change:transform}.react-grid-item.dropping{visibility:hidden}.react-grid-item.react-grid-placeholder{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.react-grid-item>.react-resizable-handle{position:absolute;width:20px;height:20px}.react-grid-item>.react-resizable-handle:after{content:"";position:absolute;right:3px;bottom:3px;width:5px;height:5px;border-right:2px solid rgba(0,0,0,.4);border-bottom:2px solid rgba(0,0,0,.4)}.react-resizable-hide>.react-resizable-handle{display:none}.react-grid-item>.react-resizable-handle.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e,.react-grid-item>.react-resizable-handle.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-n,.react-grid-item>.react-resizable-handle.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}.react-resizable{position:relative}.react-resizable-handle{position:absolute;width:20px;height:20px;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgd2lkdGg9IjYiIGhlaWdodD0iNiI+PHBhdGggZD0iTTYgNkgwVjQuMmg0LjJWMEg2djZ6IiBvcGFjaXR5PSIuMzAyIi8+PC9zdmc+");background-position:100% 100%;padding:0 3px 3px 0}.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-resizable-handle-e,.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-resizable-handle-n,.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}
|
||||
/*# sourceMappingURL=2.76b14b73.chunk.css.map */
|
||||
/*# sourceMappingURL=2.f324abd6.chunk.css.map */
|
||||
File diff suppressed because one or more lines are too long
3
portal-ui/build/static/js/2.5bd1362b.chunk.js
Normal file
3
portal-ui/build/static/js/2.5bd1362b.chunk.js
Normal file
File diff suppressed because one or more lines are too long
271
portal-ui/build/static/js/2.5bd1362b.chunk.js.LICENSE.txt
Normal file
271
portal-ui/build/static/js/2.5bd1362b.chunk.js.LICENSE.txt
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2017 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Chart.js v2.9.4
|
||||
* https://www.chartjs.org
|
||||
* (c) 2020 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* cookie
|
||||
* Copyright(c) 2012-2014 Roman Shtylman
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*! Conditions:: INITIAL */
|
||||
|
||||
/*! Moment Duration Format v2.2.2
|
||||
* https://github.com/jsmreese/moment-duration-format
|
||||
* Date: 2018-02-16
|
||||
*
|
||||
* Duration format plugin function for the Moment.js library
|
||||
* http://momentjs.com/
|
||||
*
|
||||
* Copyright 2018 John Madhavan-Reese
|
||||
* Released under the MIT license
|
||||
*/
|
||||
|
||||
/*! Production:: $accept : expression $end */
|
||||
|
||||
/*! Production:: css_value : ANGLE */
|
||||
|
||||
/*! Production:: css_value : CHS */
|
||||
|
||||
/*! Production:: css_value : EMS */
|
||||
|
||||
/*! Production:: css_value : EXS */
|
||||
|
||||
/*! Production:: css_value : FREQ */
|
||||
|
||||
/*! Production:: css_value : LENGTH */
|
||||
|
||||
/*! Production:: css_value : PERCENTAGE */
|
||||
|
||||
/*! Production:: css_value : REMS */
|
||||
|
||||
/*! Production:: css_value : RES */
|
||||
|
||||
/*! Production:: css_value : SUB css_value */
|
||||
|
||||
/*! Production:: css_value : TIME */
|
||||
|
||||
/*! Production:: css_value : VHS */
|
||||
|
||||
/*! Production:: css_value : VMAXS */
|
||||
|
||||
/*! Production:: css_value : VMINS */
|
||||
|
||||
/*! Production:: css_value : VWS */
|
||||
|
||||
/*! Production:: css_variable : CSS_VAR LPAREN CSS_CPROP COMMA math_expression RPAREN */
|
||||
|
||||
/*! Production:: css_variable : CSS_VAR LPAREN CSS_CPROP RPAREN */
|
||||
|
||||
/*! Production:: expression : math_expression EOF */
|
||||
|
||||
/*! Production:: math_expression : LPAREN math_expression RPAREN */
|
||||
|
||||
/*! Production:: math_expression : NESTED_CALC LPAREN math_expression RPAREN */
|
||||
|
||||
/*! Production:: math_expression : SUB PREFIX SUB NESTED_CALC LPAREN math_expression RPAREN */
|
||||
|
||||
/*! Production:: math_expression : css_value */
|
||||
|
||||
/*! Production:: math_expression : css_variable */
|
||||
|
||||
/*! Production:: math_expression : math_expression ADD math_expression */
|
||||
|
||||
/*! Production:: math_expression : math_expression DIV math_expression */
|
||||
|
||||
/*! Production:: math_expression : math_expression MUL math_expression */
|
||||
|
||||
/*! Production:: math_expression : math_expression SUB math_expression */
|
||||
|
||||
/*! Production:: math_expression : value */
|
||||
|
||||
/*! Production:: value : NUMBER */
|
||||
|
||||
/*! Production:: value : SUB NUMBER */
|
||||
|
||||
/*! Rule:: $ */
|
||||
|
||||
/*! Rule:: (--[0-9a-z-A-Z-]*) */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)% */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)Hz\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ch\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)cm\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)deg\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dpcm\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dpi\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)dppx\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)em\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ex\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)grad\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)in\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)kHz\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)mm\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)ms\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)pc\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)pt\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)px\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)rad\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)rem\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)s\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)turn\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vh\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vmax\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vmin\b */
|
||||
|
||||
/*! Rule:: ([0-9]+(\.[0-9]*)?|\.[0-9]+)vw\b */
|
||||
|
||||
/*! Rule:: ([a-z]+) */
|
||||
|
||||
/*! Rule:: (calc) */
|
||||
|
||||
/*! Rule:: (var) */
|
||||
|
||||
/*! Rule:: , */
|
||||
|
||||
/*! Rule:: - */
|
||||
|
||||
/*! Rule:: \( */
|
||||
|
||||
/*! Rule:: \) */
|
||||
|
||||
/*! Rule:: \* */
|
||||
|
||||
/*! Rule:: \+ */
|
||||
|
||||
/*! Rule:: \/ */
|
||||
|
||||
/*! Rule:: \s+ */
|
||||
|
||||
/*! decimal.js-light v2.5.1 https://github.com/MikeMcl/decimal.js-light/LICENCE */
|
||||
|
||||
/**
|
||||
* A better abstraction over CSS.
|
||||
*
|
||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
||||
* @website https://github.com/cssinjs/jss
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v0.20.1
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.1
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.1
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.1
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.1-lts
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
//! moment.js
|
||||
1
portal-ui/build/static/js/2.5bd1362b.chunk.js.map
Normal file
1
portal-ui/build/static/js/2.5bd1362b.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/main.6229c36f.chunk.js
Normal file
2
portal-ui/build/static/js/main.6229c36f.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/main.6229c36f.chunk.js.map
Normal file
1
portal-ui/build/static/js/main.6229c36f.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/verified.svg
Normal file
1
portal-ui/build/verified.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.3 KiB |
1
portal-ui/public/verified.svg
Normal file
1
portal-ui/public/verified.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.3 KiB |
@@ -28,7 +28,7 @@ import { planDetails, planItems, planButtons } from "./utils";
|
||||
import ActivationModal from "./ActivationModal";
|
||||
import api from "../../../common/api";
|
||||
import { LicenseInfo } from "./types";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import { CircularProgress, LinearProgress } from "@material-ui/core";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
@@ -50,6 +50,7 @@ const styles = (theme: Theme) =>
|
||||
padding: "20px 52px 20px 28px",
|
||||
},
|
||||
licenseContainer: {
|
||||
position: "relative",
|
||||
padding: "20px 52px 0px 28px",
|
||||
background: "#032F51",
|
||||
boxShadow: "0px 3px 7px #00000014",
|
||||
@@ -94,7 +95,7 @@ const styles = (theme: Theme) =>
|
||||
marginBottom: 26,
|
||||
paddingTop: 18,
|
||||
},
|
||||
currentPlan: {
|
||||
activePlanHeader: {
|
||||
fontWeight: 700,
|
||||
background: "#D5DDE5",
|
||||
borderRadius: "3px 3px 0px 0px",
|
||||
@@ -103,6 +104,12 @@ const styles = (theme: Theme) =>
|
||||
borderTop: "1px solid #D5DDE5",
|
||||
marginTop: -2,
|
||||
},
|
||||
planHeader: {
|
||||
background: "#FFFFFF",
|
||||
borderRadius: "3px 3px 0px 0px",
|
||||
padding: 8,
|
||||
borderTop: "1px solid #D5DDE5",
|
||||
},
|
||||
detailsPrice: {
|
||||
fontSize: 13,
|
||||
fontWeight: 700,
|
||||
@@ -198,6 +205,16 @@ const styles = (theme: Theme) =>
|
||||
textDecoration: "underline",
|
||||
cursor: "pointer",
|
||||
},
|
||||
subnetRefreshLicenseLink: {
|
||||
color: "#1C5A8D",
|
||||
fontWeight: "bold",
|
||||
clear: "both",
|
||||
background: "none",
|
||||
border: "none",
|
||||
textDecoration: "underline",
|
||||
cursor: "pointer",
|
||||
fontSize: 13,
|
||||
},
|
||||
fullWidth: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
@@ -213,14 +230,16 @@ const styles = (theme: Theme) =>
|
||||
height: "100%",
|
||||
borderRadius: "0px 3px 0px 0px !important",
|
||||
},
|
||||
licenseInfo: { color: "#FFFFFF" },
|
||||
licenseInfo: { color: "#FFFFFF", position: "relative" },
|
||||
licenseInfoTitle: {
|
||||
textTransform: "none",
|
||||
color: "#FFFFFF",
|
||||
color: "#BFBFBF",
|
||||
fontSize: 11,
|
||||
},
|
||||
licenseInfoValue: {
|
||||
textTransform: "none",
|
||||
fontSize: 17,
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
licenseDescription: {
|
||||
background: "#032F51",
|
||||
@@ -228,6 +247,10 @@ const styles = (theme: Theme) =>
|
||||
borderTop: "1px solid #e2e5e4",
|
||||
borderLeft: "1px solid #e2e5e4",
|
||||
borderRight: "1px solid #e2e5e4",
|
||||
bottom: 0,
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
position: "absolute",
|
||||
},
|
||||
currentPlanBG: {
|
||||
background: "#022A4A 0% 0% no-repeat padding-box",
|
||||
@@ -244,6 +267,18 @@ const styles = (theme: Theme) =>
|
||||
planItemsPadding: {
|
||||
padding: "23px 33px",
|
||||
},
|
||||
subnetSubTitle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
verifiedIcon: {
|
||||
width: 96,
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
bottom: 29,
|
||||
},
|
||||
loadingLoginStrategy: {
|
||||
textAlign: "center",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
@@ -278,6 +313,27 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
setLoadingLicenseInfo(false);
|
||||
});
|
||||
};
|
||||
const refreshLicense = () => {
|
||||
setLoadingRefreshLicense(true);
|
||||
api
|
||||
.invoke("POST", `/api/v1/subscription/refresh`, {})
|
||||
.then((res: LicenseInfo) => {
|
||||
if (res) {
|
||||
if (res.plan === "STANDARD") {
|
||||
setCurrentPlanID(1);
|
||||
} else if (res.plan === "ENTERPRISE") {
|
||||
setCurrentPlanID(2);
|
||||
} else {
|
||||
setCurrentPlanID(1);
|
||||
}
|
||||
setLicenseInfo(res);
|
||||
}
|
||||
setLoadingRefreshLicense(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoadingRefreshLicense(false);
|
||||
});
|
||||
};
|
||||
|
||||
const [activateProductModal, setActivateProductModal] = useState<boolean>(
|
||||
false
|
||||
@@ -286,6 +342,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo>();
|
||||
const [currentPlanID, setCurrentPlanID] = useState<number>(0);
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(true);
|
||||
const [loadingRefreshLicense, setLoadingRefreshLicense] = useState<boolean>(
|
||||
false
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchLicenseInfo();
|
||||
@@ -303,11 +362,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
<React.Fragment>
|
||||
<PageHeader label="License" />
|
||||
<Grid container>
|
||||
<Grid item xs={operatorMode ? 12 : 6} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Paper
|
||||
className={`${classes.licenseContainer} ${
|
||||
operatorMode ? classes.midWidth : classes.fullWidth
|
||||
}`}
|
||||
className={`${classes.licenseContainer} ${classes.midWidth}`}
|
||||
>
|
||||
{licenseInfo ? (
|
||||
<React.Fragment>
|
||||
@@ -378,7 +435,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
<Moment>{licenseInfo.expires_at}</Moment>
|
||||
<Moment format="YYYY-MM-DD">
|
||||
{licenseInfo.expires_at}
|
||||
</Moment>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
@@ -415,11 +474,15 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
{licenseInfo.email}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<img
|
||||
className={classes.verifiedIcon}
|
||||
src={"/verified.svg"}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<img src="agpl.svg" height={40} alt="agpl" />
|
||||
<img src="/agpl.svg" height={40} alt="agpl" />
|
||||
<Typography component="h2" variant="h6">
|
||||
GNU Affero General Public License
|
||||
</Typography>
|
||||
@@ -441,8 +504,57 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Paper>
|
||||
{
|
||||
<Paper className={`${classes.paper} ${classes.smallWidth}`}>
|
||||
{licenseInfo ? (
|
||||
<React.Fragment>
|
||||
{" "}
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
className={classes.pageTitle}
|
||||
>
|
||||
Login to MinIO SUBNET !
|
||||
</Typography>
|
||||
<Typography component="h6" className={classes.subnetSubTitle}>
|
||||
It combines a commercial license with a support experience
|
||||
unlike any other.
|
||||
</Typography>
|
||||
<br />
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://subnet.min.io/support/?ref=op"
|
||||
>
|
||||
Login to SUBNET
|
||||
</Button>
|
||||
{operatorMode && (
|
||||
<React.Fragment>
|
||||
{" "}
|
||||
<br />
|
||||
<br />
|
||||
<button
|
||||
className={classes.subnetRefreshLicenseLink}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
refreshLicense();
|
||||
}}
|
||||
>
|
||||
Refresh Licence
|
||||
</button>
|
||||
{loadingRefreshLicense && (
|
||||
<CircularProgress
|
||||
size={16}
|
||||
className={classes.loadingLoginStrategy}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
@@ -451,12 +563,12 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
Choosing between GNU AGPL v3 and Commercial License
|
||||
</Typography>
|
||||
<Typography component="h6">
|
||||
If you are building proprietary applications, you may want to
|
||||
choose the commercial license included as part of the Standard
|
||||
and Enterprise subscription plans. Applications must otherwise
|
||||
comply with all the GNU AGPLv3 License & Trademark
|
||||
obligations. Follow the links below to learn more about the
|
||||
compliance policy.
|
||||
If you are building proprietary applications, you may want
|
||||
to choose the commercial license included as part of the
|
||||
Standard and Enterprise subscription plans. Applications
|
||||
must otherwise comply with all the GNU AGPLv3 License &
|
||||
Trademark obligations. Follow the links below to learn more
|
||||
about the compliance policy.
|
||||
</Typography>
|
||||
<br />
|
||||
<a
|
||||
@@ -477,14 +589,11 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
>
|
||||
Trademark Policy
|
||||
</a>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Paper>
|
||||
}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={clsx(classes.container, classes.planItemsPadding)}
|
||||
>
|
||||
<Grid item xs={12} className={clsx(classes.planItemsPadding)}>
|
||||
<Paper
|
||||
className={classes.paper}
|
||||
style={{ borderRadius: "0px 0px 3px 3px" }}
|
||||
@@ -521,11 +630,17 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
currentPlan ? classes.currentPlanBG : ""
|
||||
)}
|
||||
>
|
||||
{currentPlan ? (
|
||||
<Grid item xs={12} className={classes.currentPlan}>
|
||||
Current Plan
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={
|
||||
currentPlan
|
||||
? classes.activePlanHeader
|
||||
: classes.planHeader
|
||||
}
|
||||
>
|
||||
{currentPlan ? "Current Plan" : "\u00A0"}
|
||||
</Grid>
|
||||
) : null}
|
||||
<Grid item xs={12} className={classes.detailsTitle}>
|
||||
{details.title}
|
||||
</Grid>
|
||||
|
||||
@@ -148,6 +148,7 @@ const AddTenant = ({
|
||||
const gcpPrivateKeyID = fields.encryption.gcpPrivateKeyID;
|
||||
const gcpPrivateKey = fields.encryption.gcpPrivateKey;
|
||||
const enableAutoCert = fields.security.enableAutoCert;
|
||||
const enableTLS = fields.security.enableTLS;
|
||||
const ecParity = fields.tenantSize.ecParity;
|
||||
const distribution = fields.tenantSize.distribution;
|
||||
const memorySize = fields.tenantSize.memorySize;
|
||||
@@ -167,7 +168,7 @@ const AddTenant = ({
|
||||
namespace: namespace,
|
||||
access_key: "",
|
||||
secret_key: "",
|
||||
enable_tls: enableAutoCert,
|
||||
enable_tls: enableTLS && enableAutoCert,
|
||||
enable_console: true,
|
||||
enable_prometheus: true,
|
||||
service_name: "",
|
||||
@@ -221,7 +222,7 @@ const AddTenant = ({
|
||||
};
|
||||
}
|
||||
|
||||
if (minioCertificates.length > 0) {
|
||||
if (enableTLS && minioCertificates.length > 0) {
|
||||
tenantCerts = {
|
||||
minio: minioCertificates
|
||||
.map((keyPair: KeyPair) => ({
|
||||
@@ -233,6 +234,7 @@ const AddTenant = ({
|
||||
}
|
||||
|
||||
if (
|
||||
enableTLS &&
|
||||
consoleCertificate.encoded_cert !== "" &&
|
||||
consoleCertificate.encoded_key !== ""
|
||||
) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import PencilIcon from "../../Common/TableWrapper/TableActionIcons/PencilIcon";
|
||||
import { LicenseInfo } from "../../License/types";
|
||||
import { Link } from "react-router-dom";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import Moment from "react-moment";
|
||||
|
||||
interface ITenantDetailsProps {
|
||||
classes: any;
|
||||
@@ -118,6 +119,44 @@ const styles = (theme: Theme) =>
|
||||
poolLabel: {
|
||||
color: "#666666",
|
||||
},
|
||||
licenseContainer: {
|
||||
position: "relative",
|
||||
padding: "20px 52px 0px 28px",
|
||||
background: "#032F51",
|
||||
boxShadow: "0px 3px 7px #00000014",
|
||||
"& h2": {
|
||||
color: "#FFF",
|
||||
marginBottom: 67,
|
||||
},
|
||||
"& a": {
|
||||
textDecoration: "none",
|
||||
},
|
||||
"& h3": {
|
||||
color: "#FFFFFF",
|
||||
marginBottom: "30px",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
"& h6": {
|
||||
color: "#FFFFFF !important",
|
||||
},
|
||||
},
|
||||
licenseInfo: { color: "#FFFFFF", position: "relative" },
|
||||
licenseInfoTitle: {
|
||||
textTransform: "none",
|
||||
color: "#BFBFBF",
|
||||
fontSize: 11,
|
||||
},
|
||||
licenseInfoValue: {
|
||||
textTransform: "none",
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
verifiedIcon: {
|
||||
width: 96,
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
bottom: 29,
|
||||
},
|
||||
...modalBasic,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
@@ -432,34 +471,130 @@ const TenantDetails = ({
|
||||
<React.Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
{tenant && tenant.subnet_license ? (
|
||||
<Grid className={classes.paperContainer}>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
className={classes.pageTitle}
|
||||
<Paper
|
||||
className={
|
||||
tenant && tenant.subnet_license
|
||||
? classes.licenseContainer
|
||||
: ""
|
||||
}
|
||||
>
|
||||
Subscription Information
|
||||
{tenant && tenant.subnet_license ? (
|
||||
<React.Fragment>
|
||||
<Grid container className={classes.licenseInfo}>
|
||||
<Grid item xs={6}>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
License
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
Commercial License
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
Organization
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{tenant.subnet_license.organization}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
Registered Capacity
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{niceBytes(
|
||||
(
|
||||
tenant.subnet_license.storage_capacity *
|
||||
1099511627776
|
||||
) // 1 Terabyte = 1099511627776 Bytes
|
||||
.toString(10)
|
||||
)}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
Expiry Date
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
<Moment format="YYYY-MM-DD">
|
||||
{tenant.subnet_license.expires_at}
|
||||
</Moment>
|
||||
</Typography>
|
||||
Account ID: {tenant.subnet_license.account_id}
|
||||
<br />
|
||||
<br />
|
||||
Email: {tenant.subnet_license.email}
|
||||
<br />
|
||||
<br />
|
||||
Plan: {tenant.subnet_license.plan}
|
||||
<br />
|
||||
<br />
|
||||
Organization: {tenant.subnet_license.organization}
|
||||
<br />
|
||||
<br />
|
||||
Storage Capacity:{" "}
|
||||
{tenant.subnet_license.storage_capacity}
|
||||
<br />
|
||||
<br />
|
||||
Expiration: {tenant.subnet_license.expires_at}
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
Subscription Plan
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{tenant.subnet_license.plan}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="button"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoTitle}
|
||||
>
|
||||
Requester
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="overline"
|
||||
display="block"
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{tenant.subnet_license.email}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<img
|
||||
className={classes.verifiedIcon}
|
||||
src={"/verified.svg"}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
!loadingLicenseInfo && (
|
||||
<Grid className={classes.paperContainer}>
|
||||
|
||||
@@ -19,6 +19,7 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
@@ -63,6 +64,40 @@ func registerSubscriptionHandlers(api *operations.ConsoleAPI) {
|
||||
}
|
||||
return admin_api.NewSubscriptionInfoOK().WithPayload(license)
|
||||
})
|
||||
// Refresh license for k8s cluster
|
||||
api.AdminAPISubscriptionRefreshHandler = admin_api.SubscriptionRefreshHandlerFunc(func(params admin_api.SubscriptionRefreshParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionRefreshResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewSubscriptionRefreshDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubscriptionRefreshOK().WithPayload(license)
|
||||
})
|
||||
}
|
||||
|
||||
// retrieveLicense returns license from K8S secrets (If console is deployed in operator mode) or from
|
||||
// the configured CONSOLE_SUBNET_LICENSE environment variable
|
||||
func retrieveLicense(ctx context.Context, sessionToken string) (string, error) {
|
||||
var license string
|
||||
// If Console is running in operator mode retrieve License stored in K8s secrets
|
||||
if acl.GetOperatorMode() {
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(sessionToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err = getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// If Console is running in Tenant Admin mode retrieve license from env variable
|
||||
license = GetSubnetLicense()
|
||||
}
|
||||
return license, nil
|
||||
}
|
||||
|
||||
// addSubscriptionLicenseToTenant replace existing console tenant secret and adds the subnet license key
|
||||
@@ -175,6 +210,47 @@ func saveSubscriptionLicense(ctx context.Context, clientSet K8sClientI, license
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateTenantLicenseAndRestartConsole
|
||||
func updateTenantLicenseAndRestartConsole(ctx context.Context, clientSet K8sClientI, license, namespace, tenantName string) error {
|
||||
consoleSelector := fmt.Sprintf("%s-console", tenantName)
|
||||
consoleSecretName := fmt.Sprintf("%s-secret", consoleSelector)
|
||||
// read current console configuration from k8s secrets
|
||||
currentConsoleSecret, err := clientSet.getSecret(ctx, namespace, consoleSecretName, metav1.GetOptions{})
|
||||
if err != nil || currentConsoleSecret == nil {
|
||||
return err
|
||||
}
|
||||
secretData := currentConsoleSecret.Data
|
||||
secretData[ConsoleSubnetLicense] = []byte(license)
|
||||
// delete existing console configuration from k8s secrets
|
||||
err = clientSet.deleteSecret(ctx, namespace, consoleSecretName, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// log the error if any and continue
|
||||
log.Println(err)
|
||||
}
|
||||
// Save subnet license in k8s secrets
|
||||
imm := true
|
||||
consoleConfigSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: consoleSecretName,
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: secretData,
|
||||
}
|
||||
_, err = clientSet.createSecret(ctx, namespace, consoleConfigSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// restart Console pods based on label:
|
||||
// v1.min.io/console: TENANT-console
|
||||
err = clientSet.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("%s=%s%s", miniov2.ConsoleTenantLabel, tenantName, miniov2.ConsoleName),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// subscriptionValidate will validate the provided jwt license against the subnet public key
|
||||
func subscriptionValidate(client cluster.HTTPClientI, license, email, password string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.ValidateLicense(client, license, email, password)
|
||||
@@ -237,37 +313,123 @@ func getSubscriptionLicense(ctx context.Context, clientSet K8sClientI, namespace
|
||||
|
||||
// getSubscriptionInfoResponse returns information about the current configured subnet license for Console
|
||||
func getSubscriptionInfoResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
var licenseInfo *models.License
|
||||
var license string
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleSTSClient(),
|
||||
}
|
||||
// If Console is running in operator mode retrieve License stored in K8s secrets
|
||||
if acl.GetOperatorMode() {
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidLicense, nil, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err = getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
licenseKey, err := retrieveLicense(context.Background(), session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
} else {
|
||||
// If Console is running in Tenant Admin mode retrieve license from env variable
|
||||
license = GetSubnetLicense()
|
||||
}
|
||||
// validate license key and obtain license info
|
||||
licenseInfo, _, err := subscriptionValidate(client, license, "", "")
|
||||
licenseInfo, _, err = subscriptionValidate(client, licenseKey, "", "")
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
|
||||
func subscriptionRefresh(httpClient *cluster.HTTPClient, license string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.RefreshLicense(httpClient, license)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
func getSubscriptionRefreshResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleSTSClient(),
|
||||
}
|
||||
licenseKey, err := retrieveLicense(context.Background(), session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
newLicenseInfo, licenseRaw, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// save license key to k8s and restart all console pods
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, licenseRaw); err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
// update license for all existing tenants
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
tenants, err := listTenants(ctx, opClient, "", nil)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// iterate over all tenants, update console configuration and restart console pods
|
||||
for _, tenant := range tenants.Tenants {
|
||||
if err := updateTenantLicenseAndRestartConsole(ctx, &k8sClient, licenseRaw, tenant.Namespace, tenant.Name); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
return newLicenseInfo, nil
|
||||
}
|
||||
|
||||
// RefreshLicense will check current subnet license and try to renew it
|
||||
func RefreshLicense() error {
|
||||
// Get current license
|
||||
saK8SToken := getK8sSAToken()
|
||||
licenseKey, err := retrieveLicense(context.Background(), saK8SToken)
|
||||
if licenseKey == "" {
|
||||
return errors.New("no license present")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleSTSClient(),
|
||||
}
|
||||
// Attempt to refresh license
|
||||
_, refreshedLicenseKey, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// store new license in memory for console ui
|
||||
LicenseKey = refreshedLicenseKey
|
||||
// Update in memory license and update k8s secret
|
||||
if refreshedLicenseKey != "" {
|
||||
if acl.GetOperatorMode() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
clientSet, err := cluster.K8sClient(saK8SToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, refreshedLicenseKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -739,8 +739,8 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
}
|
||||
// optionals are set below
|
||||
var consoleAccess string
|
||||
var consoleSecret string
|
||||
var tenantUserAccessKey string
|
||||
var tenantUserSecretKey string
|
||||
|
||||
enableConsole := true
|
||||
if tenantReq.EnableConsole != nil && *tenantReq.EnableConsole {
|
||||
@@ -748,16 +748,24 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
|
||||
if enableConsole {
|
||||
// provision initial user for tenant
|
||||
tenantUserAccessKey = RandomCharString(16)
|
||||
tenantUserSecretKey = RandomCharString(32)
|
||||
consoleUserSecretName := fmt.Sprintf("%s-user-secret", tenantName)
|
||||
consoleUserSecretData := map[string][]byte{
|
||||
"CONSOLE_ACCESS_KEY": []byte(tenantUserAccessKey),
|
||||
"CONSOLE_SECRET_KEY": []byte(tenantUserSecretKey),
|
||||
}
|
||||
_, err := createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{{Name: consoleUserSecretName, Content: consoleUserSecretData}}, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
minInst.Spec.Users = []*corev1.LocalObjectReference{{Name: consoleUserSecretName}}
|
||||
consoleSelector := fmt.Sprintf("%s-console", tenantName)
|
||||
consoleSecretName := fmt.Sprintf("%s-secret", consoleSelector)
|
||||
consoleAccess = RandomCharString(16)
|
||||
consoleSecret = RandomCharString(32)
|
||||
|
||||
consoleSecretData := map[string][]byte{
|
||||
"CONSOLE_PBKDF_PASSPHRASE": []byte(RandomCharString(16)),
|
||||
"CONSOLE_PBKDF_SALT": []byte(RandomCharString(8)),
|
||||
"CONSOLE_ACCESS_KEY": []byte(consoleAccess),
|
||||
"CONSOLE_SECRET_KEY": []byte(consoleSecret),
|
||||
}
|
||||
// If Subnet License is present in k8s secrets, copy that to the CONSOLE_SUBNET_LICENSE env variable
|
||||
// of the console tenant
|
||||
@@ -928,8 +936,8 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
// Attach Console Credentials
|
||||
if enableConsole {
|
||||
response.Console = &models.CreateTenantResponseConsole{
|
||||
AccessKey: consoleAccess,
|
||||
SecretKey: consoleSecret,
|
||||
AccessKey: tenantUserAccessKey,
|
||||
SecretKey: tenantUserSecretKey,
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
|
||||
@@ -47,6 +47,9 @@ var (
|
||||
|
||||
// SessionDuration cookie validity duration
|
||||
SessionDuration = 45 * time.Minute
|
||||
|
||||
// LicenseKey in memory license key used by console ui
|
||||
LicenseKey = ""
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -257,7 +260,13 @@ func getPrometheusURL() string {
|
||||
|
||||
// GetSubnetLicense returns the current subnet jwt license
|
||||
func GetSubnetLicense() string {
|
||||
return env.Get(ConsoleSubnetLicense, "")
|
||||
// if we have a license key in memory return that
|
||||
if LicenseKey != "" {
|
||||
return LicenseKey
|
||||
}
|
||||
// return license configured via environment variable
|
||||
LicenseKey = env.Get(ConsoleSubnetLicense, "")
|
||||
return LicenseKey
|
||||
}
|
||||
|
||||
func initVars() {
|
||||
|
||||
@@ -2876,12 +2876,35 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/refresh": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Refresh existing subscription license",
|
||||
"operationId": "SubscriptionRefresh",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/license"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/validate": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Validate a provided subscription license",
|
||||
"summary": "Validates subscription license",
|
||||
"operationId": "SubscriptionValidate",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -8570,12 +8593,35 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/refresh": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Refresh existing subscription license",
|
||||
"operationId": "SubscriptionRefresh",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/license"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/validate": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Validate a provided subscription license",
|
||||
"summary": "Validates subscription license",
|
||||
"operationId": "SubscriptionValidate",
|
||||
"parameters": [
|
||||
{
|
||||
|
||||
90
restapi/operations/admin_api/subscription_refresh.go
Normal file
90
restapi/operations/admin_api/subscription_refresh.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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/console/models"
|
||||
)
|
||||
|
||||
// SubscriptionRefreshHandlerFunc turns a function with the right signature into a subscription refresh handler
|
||||
type SubscriptionRefreshHandlerFunc func(SubscriptionRefreshParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubscriptionRefreshHandlerFunc) Handle(params SubscriptionRefreshParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubscriptionRefreshHandler interface for that can handle valid subscription refresh params
|
||||
type SubscriptionRefreshHandler interface {
|
||||
Handle(SubscriptionRefreshParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubscriptionRefresh creates a new http.Handler for the subscription refresh operation
|
||||
func NewSubscriptionRefresh(ctx *middleware.Context, handler SubscriptionRefreshHandler) *SubscriptionRefresh {
|
||||
return &SubscriptionRefresh{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*SubscriptionRefresh swagger:route POST /subscription/refresh AdminAPI subscriptionRefresh
|
||||
|
||||
Refresh existing subscription license
|
||||
|
||||
*/
|
||||
type SubscriptionRefresh struct {
|
||||
Context *middleware.Context
|
||||
Handler SubscriptionRefreshHandler
|
||||
}
|
||||
|
||||
func (o *SubscriptionRefresh) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewSubscriptionRefreshParams()
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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"
|
||||
)
|
||||
|
||||
// NewSubscriptionRefreshParams creates a new SubscriptionRefreshParams object
|
||||
// no default values defined in spec.
|
||||
func NewSubscriptionRefreshParams() SubscriptionRefreshParams {
|
||||
|
||||
return SubscriptionRefreshParams{}
|
||||
}
|
||||
|
||||
// SubscriptionRefreshParams contains all the bound params for the subscription refresh operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubscriptionRefresh
|
||||
type SubscriptionRefreshParams 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 NewSubscriptionRefreshParams() beforehand.
|
||||
func (o *SubscriptionRefreshParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/admin_api/subscription_refresh_responses.go
Normal file
133
restapi/operations/admin_api/subscription_refresh_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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/console/models"
|
||||
)
|
||||
|
||||
// SubscriptionRefreshOKCode is the HTTP code returned for type SubscriptionRefreshOK
|
||||
const SubscriptionRefreshOKCode int = 200
|
||||
|
||||
/*SubscriptionRefreshOK A successful response.
|
||||
|
||||
swagger:response subscriptionRefreshOK
|
||||
*/
|
||||
type SubscriptionRefreshOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.License `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubscriptionRefreshOK creates SubscriptionRefreshOK with default headers values
|
||||
func NewSubscriptionRefreshOK() *SubscriptionRefreshOK {
|
||||
|
||||
return &SubscriptionRefreshOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subscription refresh o k response
|
||||
func (o *SubscriptionRefreshOK) WithPayload(payload *models.License) *SubscriptionRefreshOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subscription refresh o k response
|
||||
func (o *SubscriptionRefreshOK) SetPayload(payload *models.License) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubscriptionRefreshOK) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*SubscriptionRefreshDefault Generic error response.
|
||||
|
||||
swagger:response subscriptionRefreshDefault
|
||||
*/
|
||||
type SubscriptionRefreshDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubscriptionRefreshDefault creates SubscriptionRefreshDefault with default headers values
|
||||
func NewSubscriptionRefreshDefault(code int) *SubscriptionRefreshDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubscriptionRefreshDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subscription refresh default response
|
||||
func (o *SubscriptionRefreshDefault) WithStatusCode(code int) *SubscriptionRefreshDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subscription refresh default response
|
||||
func (o *SubscriptionRefreshDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subscription refresh default response
|
||||
func (o *SubscriptionRefreshDefault) WithPayload(payload *models.Error) *SubscriptionRefreshDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subscription refresh default response
|
||||
func (o *SubscriptionRefreshDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubscriptionRefreshDefault) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/admin_api/subscription_refresh_urlbuilder.go
Normal file
104
restapi/operations/admin_api/subscription_refresh_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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"
|
||||
)
|
||||
|
||||
// SubscriptionRefreshURL generates an URL for the subscription refresh operation
|
||||
type SubscriptionRefreshURL 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 *SubscriptionRefreshURL) WithBasePath(bp string) *SubscriptionRefreshURL {
|
||||
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 *SubscriptionRefreshURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubscriptionRefreshURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subscription/refresh"
|
||||
|
||||
_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 *SubscriptionRefreshURL) 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 *SubscriptionRefreshURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubscriptionRefreshURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubscriptionRefreshURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubscriptionRefreshURL")
|
||||
}
|
||||
|
||||
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 *SubscriptionRefreshURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func NewSubscriptionValidate(ctx *middleware.Context, handler SubscriptionValida
|
||||
|
||||
/*SubscriptionValidate swagger:route POST /subscription/validate AdminAPI subscriptionValidate
|
||||
|
||||
Validate a provided subscription license
|
||||
Validates subscription license
|
||||
|
||||
*/
|
||||
type SubscriptionValidate struct {
|
||||
|
||||
@@ -307,6 +307,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPISubscriptionInfoHandler: admin_api.SubscriptionInfoHandlerFunc(func(params admin_api.SubscriptionInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubscriptionInfo has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubscriptionRefreshHandler: admin_api.SubscriptionRefreshHandlerFunc(func(params admin_api.SubscriptionRefreshParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubscriptionRefresh has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubscriptionValidateHandler: admin_api.SubscriptionValidateHandlerFunc(func(params admin_api.SubscriptionValidateParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubscriptionValidate has not yet been implemented")
|
||||
}),
|
||||
@@ -551,6 +554,8 @@ type ConsoleAPI struct {
|
||||
AdminAPISubscriptionActivateHandler admin_api.SubscriptionActivateHandler
|
||||
// AdminAPISubscriptionInfoHandler sets the operation handler for the subscription info operation
|
||||
AdminAPISubscriptionInfoHandler admin_api.SubscriptionInfoHandler
|
||||
// AdminAPISubscriptionRefreshHandler sets the operation handler for the subscription refresh operation
|
||||
AdminAPISubscriptionRefreshHandler admin_api.SubscriptionRefreshHandler
|
||||
// AdminAPISubscriptionValidateHandler sets the operation handler for the subscription validate operation
|
||||
AdminAPISubscriptionValidateHandler admin_api.SubscriptionValidateHandler
|
||||
// AdminAPITenantAddPoolHandler sets the operation handler for the tenant add pool operation
|
||||
@@ -890,6 +895,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPISubscriptionInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubscriptionInfoHandler")
|
||||
}
|
||||
if o.AdminAPISubscriptionRefreshHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubscriptionRefreshHandler")
|
||||
}
|
||||
if o.AdminAPISubscriptionValidateHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubscriptionValidateHandler")
|
||||
}
|
||||
@@ -1349,6 +1357,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/subscription/refresh"] = admin_api.NewSubscriptionRefresh(o.context, o.AdminAPISubscriptionRefreshHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/subscription/validate"] = admin_api.NewSubscriptionValidate(o.context, o.AdminAPISubscriptionValidateHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
|
||||
17
swagger.yml
17
swagger.yml
@@ -1495,7 +1495,7 @@ paths:
|
||||
|
||||
/subscription/validate:
|
||||
post:
|
||||
summary: Validate a provided subscription license
|
||||
summary: Validates subscription license
|
||||
operationId: SubscriptionValidate
|
||||
parameters:
|
||||
- name: body
|
||||
@@ -1515,6 +1515,21 @@ paths:
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/subscription/refresh:
|
||||
post:
|
||||
summary: Refresh existing subscription license
|
||||
operationId: SubscriptionRefresh
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/license"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/subscription/namespaces/{namespace}/tenants/{tenant}/activate:
|
||||
post:
|
||||
|
||||
Reference in New Issue
Block a user