{title}
diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx
index fd875e0fd..8c517e623 100644
--- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx
+++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx
@@ -44,6 +44,7 @@ import TenantLicense from "./TenantLicense";
import PoolsSummary from "./PoolsSummary";
import PodsSummary from "./PodsSummary";
import { AppState } from "../../../../store";
+import TenantMetrics from "./TenantMetrics";
interface ITenantDetailsProps {
classes: any;
@@ -153,6 +154,7 @@ const TenantDetails = ({
switch (section) {
case "pools":
case "pods":
+ case "metrics":
case "license":
setTenantTab(section);
break;
@@ -232,6 +234,7 @@ const TenantDetails = ({
scrollButtons="auto"
>
+
@@ -244,6 +247,10 @@ const TenantDetails = ({
path="/namespaces/:tenantNamespace/tenants/:tenantName/summary"
component={TenantSummary}
/>
+
.
+
+import React, { Fragment, useCallback, useEffect, useState } from "react";
+import { connect } from "react-redux";
+import get from "lodash/get";
+import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
+import {
+ containerForHeader,
+ tenantDetailsStyles,
+} from "../../Common/FormComponents/common/styleLibrary";
+import Grid from "@material-ui/core/Grid";
+import { LinearProgress } from "@material-ui/core";
+import api from "../../../../common/api";
+import { ITenant } from "../ListTenants/types";
+import { setErrorSnackMessage } from "../../../../actions";
+import { AppState } from "../../../../store";
+import { Usage } from "../../Dashboard/types";
+import PrDashboard from "../../Dashboard/Prometheus/PrDashboard";
+import BasicDashboard from "../../Dashboard/BasicDashboard/BasicDashboard";
+
+interface ITenantMetrics {
+ classes: any;
+ match: any;
+ tenant: ITenant | null;
+
+ setErrorSnackMessage: typeof setErrorSnackMessage;
+}
+
+const styles = (theme: Theme) =>
+ createStyles({
+ ...tenantDetailsStyles,
+ redState: {
+ color: theme.palette.error.main,
+ },
+ yellowState: {
+ color: theme.palette.warning.main,
+ },
+ greenState: {
+ color: theme.palette.success.main,
+ },
+ greyState: {
+ color: "grey",
+ },
+ centerAlign: {
+ textAlign: "center",
+ },
+ ...containerForHeader(theme.spacing(4)),
+ });
+
+const TenantMetrics = ({
+ classes,
+ match,
+ tenant,
+ setErrorSnackMessage,
+}: ITenantMetrics) => {
+ const [loadingWidgets, setLoadingWidgets] = useState
(true);
+ const [basicResult, setBasicResult] = useState(null);
+
+ const tenantName = match.params["tenantName"];
+ const tenantNamespace = match.params["tenantNamespace"];
+
+ const fetchWidgets = useCallback(() => {
+ api
+ .invoke(
+ "GET",
+ `/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/info`
+ )
+ .then((res: Usage) => {
+ setBasicResult(res);
+ setLoadingWidgets(false);
+ })
+ .catch((err) => {
+ setErrorSnackMessage(err);
+ setLoadingWidgets(false);
+ });
+ }, [
+ setBasicResult,
+ setLoadingWidgets,
+ setErrorSnackMessage,
+ tenantNamespace,
+ tenantName,
+ ]);
+
+ useEffect(() => {
+ if (loadingWidgets) {
+ fetchWidgets();
+ }
+ }, [loadingWidgets, fetchWidgets, setErrorSnackMessage]);
+
+ const widgets = get(basicResult, "widgets", null);
+
+ return (
+
+
+ {loadingWidgets ? (
+
+
+
+ ) : (
+
+ {widgets !== null ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ );
+};
+
+const mapState = (state: AppState) => ({
+ loadingTenant: state.tenants.tenantDetails.loadingTenant,
+ selectedTenant: state.tenants.tenantDetails.currentTenant,
+ tenant: state.tenants.tenantDetails.tenantInfo,
+ logEnabled: get(state.tenants.tenantDetails.tenantInfo, "logEnabled", false),
+ monitoringEnabled: get(
+ state.tenants.tenantDetails.tenantInfo,
+ "monitoringEnabled",
+ false
+ ),
+ encryptionEnabled: get(
+ state.tenants.tenantDetails.tenantInfo,
+ "encryptionEnabled",
+ false
+ ),
+ adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false),
+ oicEnabled: get(
+ state.tenants.tenantDetails.tenantInfo,
+ "idpOicEnabled",
+ false
+ ),
+});
+
+const connector = connect(mapState, {
+ setErrorSnackMessage,
+});
+
+export default withStyles(styles)(connector(TenantMetrics));
diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx
index 86ad2a723..4972dc769 100644
--- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx
+++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx
@@ -30,7 +30,6 @@ import api from "../../../../common/api";
import { ITenant } from "../ListTenants/types";
import UsageBarWrapper from "../../Common/UsageBarWrapper/UsageBarWrapper";
import UpdateTenantModal from "./UpdateTenantModal";
-import { setErrorSnackMessage } from "../../../../actions";
import { AppState } from "../../../../store";
interface ITenantsSummary {
@@ -43,7 +42,6 @@ interface ITenantsSummary {
adEnabled: boolean;
oicEnabled: boolean;
loadingTenant: boolean;
- setErrorSnackMessage: typeof setErrorSnackMessage;
}
interface ITenantUsage {
@@ -82,7 +80,6 @@ const TenantSummary = ({
adEnabled,
oicEnabled,
loadingTenant,
- setErrorSnackMessage,
}: ITenantsSummary) => {
const [capacity, setCapacity] = useState(0);
const [poolCount, setPoolCount] = useState(0);
@@ -441,8 +438,6 @@ const mapState = (state: AppState) => ({
),
});
-const connector = connect(mapState, {
- setErrorSnackMessage,
-});
+const connector = connect(mapState, null);
export default withStyles(styles)(connector(TenantSummary));
diff --git a/portal-ui/src/screens/Console/Users/DeleteUserString.tsx b/portal-ui/src/screens/Console/Users/DeleteUserString.tsx
index d435ef8bd..096db4e0d 100644
--- a/portal-ui/src/screens/Console/Users/DeleteUserString.tsx
+++ b/portal-ui/src/screens/Console/Users/DeleteUserString.tsx
@@ -26,7 +26,7 @@ import {
LinearProgress,
} from "@material-ui/core";
import api from "../../../common/api";
-import { User, UsersList } from "./types";
+import { UsersList } from "./types";
import { setErrorSnackMessage } from "../../../actions";
import history from "../../../history";
diff --git a/restapi/admin_info.go b/restapi/admin_info.go
index 05d3d718f..4b735071a 100644
--- a/restapi/admin_info.go
+++ b/restapi/admin_info.go
@@ -27,6 +27,8 @@ import (
"strings"
"time"
+ "github.com/minio/madmin-go"
+
"github.com/go-openapi/swag"
"github.com/go-openapi/runtime/middleware"
@@ -788,12 +790,21 @@ type LabelResults struct {
// getAdminInfoResponse returns the response containing total buckets, objects and usage.
func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse, *models.Error) {
prometheusURL := getPrometheusURL()
+ mAdmin, err := newAdminClient(session)
+ if err != nil {
+ return nil, prepareError(err)
+ }
+ sessionResp, err2 := getUsageWidgetsForDeployment(prometheusURL, mAdmin)
+ if err2 != nil {
+ return nil, err2
+ }
+
+ return sessionResp, nil
+}
+
+func getUsageWidgetsForDeployment(prometheusURL string, mAdmin *madmin.AdminClient) (*models.AdminInfoResponse, *models.Error) {
if prometheusURL == "" {
- mAdmin, err := newAdminClient(session)
- if err != nil {
- return nil, prepareError(err)
- }
// create a minioClient interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
@@ -837,7 +848,6 @@ func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse,
sessionResp := &models.AdminInfoResponse{}
sessionResp.Widgets = wdgts
-
return sessionResp, nil
}
@@ -872,6 +882,10 @@ func getAdminInfoWidgetResponse(params admin_api.DashboardWidgetDetailsParams) (
prometheusURL := getPrometheusURL()
prometheusJobID := getPrometheusJobID()
+ return getWidgetDetails(prometheusURL, prometheusJobID, params.WidgetID, params.Step, params.Start, params.End)
+}
+
+func getWidgetDetails(prometheusURL string, prometheusJobID string, widgetID int32, step *int32, start *int64, end *int64) (*models.WidgetDetails, *models.Error) {
labelResultsCh := make(chan LabelResults)
for _, lbl := range labels {
@@ -907,14 +921,14 @@ LabelsWaitLoop:
// launch a goroutines per widget
for _, m := range widgets {
- if m.ID != params.WidgetID {
+ if m.ID != widgetID {
continue
}
targetResults := make(chan *models.ResultTarget)
// for each target we will launch another goroutine to fetch the values
for _, target := range m.Targets {
- go func(target Target, params admin_api.DashboardWidgetDetailsParams) {
+ go func(target Target, inStep *int32, inStart *int64, inEnd *int64) {
apiType := "query_range"
now := time.Now()
@@ -924,15 +938,15 @@ LabelsWaitLoop:
if target.Step > 0 {
step = target.Step
}
- if params.Step != nil && *params.Step > 0 {
- step = *params.Step
+ if inStep != nil && *inStep > 0 {
+ step = *inStep
}
if step > 0 {
extraParamters = fmt.Sprintf("%s&step=%d", extraParamters, step)
}
- if params.Start != nil && params.End != nil {
- extraParamters = fmt.Sprintf("&start=%d&end=%d&step=%d", *params.Start, *params.End, *params.Step)
+ if inStart != nil && inEnd != nil {
+ extraParamters = fmt.Sprintf("&start=%d&end=%d&step=%d", *inStart, *inEnd, *inStep)
}
// replace the `$__interval` global for step with unit (s for seconds)
@@ -969,7 +983,7 @@ LabelsWaitLoop:
targetResults <- &targetResult
- }(target, params)
+ }(target, step, start, end)
}
wdgtResult := models.WidgetDetails{
diff --git a/restapi/admin_tenants.go b/restapi/admin_tenants.go
index b00aef368..e8edba4db 100644
--- a/restapi/admin_tenants.go
+++ b/restapi/admin_tenants.go
@@ -91,12 +91,12 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
})
// Detail Tenant
- api.AdminAPITenantInfoHandler = admin_api.TenantInfoHandlerFunc(func(params admin_api.TenantInfoParams, session *models.Principal) middleware.Responder {
- resp, err := getTenantInfoResponse(session, params)
+ api.AdminAPITenantDetailsHandler = admin_api.TenantDetailsHandlerFunc(func(params admin_api.TenantDetailsParams, session *models.Principal) middleware.Responder {
+ resp, err := getTenantDetailsResponse(session, params)
if err != nil {
- return admin_api.NewTenantInfoDefault(int(err.Code)).WithPayload(err)
+ return admin_api.NewTenantDetailsDefault(int(err.Code)).WithPayload(err)
}
- return admin_api.NewTenantInfoOK().WithPayload(resp)
+ return admin_api.NewTenantDetailsOK().WithPayload(resp)
})
@@ -367,7 +367,7 @@ func getTenantInfo(tenant *miniov2.Tenant) *models.Tenant {
}
}
-func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInfoParams) (*models.Tenant, *models.Error) {
+func getTenantDetailsResponse(session *models.Principal, params admin_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
// 5 seconds timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@@ -408,7 +408,9 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
// detect if OpenID is enabled
oicEnabled := false
- consoleSecret, err := clientSet.CoreV1().Secrets(minTenant.Namespace).Get(ctx, minTenant.Name, metav1.GetOptions{})
+ consoleSelector := fmt.Sprintf("%s-console", minTenant.Name)
+ consoleSecretName := fmt.Sprintf("%s-secret", consoleSelector)
+ consoleSecret, err := clientSet.CoreV1().Secrets(minTenant.Namespace).Get(ctx, consoleSecretName, metav1.GetOptions{})
// we can tolerate not getting this secret
if err != nil {
LogError("unable to fetch existing secrets for %s: %v", minTenant.Name, err)
@@ -418,6 +420,13 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
oicEnabled = true
}
}
+ if minTenant.HasConsoleEnabled() {
+ for _, env := range minTenant.Spec.Console.Env {
+ if env.Name == "CONSOLE_IDP_URL" {
+ oicEnabled = true
+ }
+ }
+ }
info.LogEnabled = minTenant.HasLogEnabled()
info.MonitoringEnabled = minTenant.HasPrometheusEnabled()
diff --git a/restapi/configure_console.go b/restapi/configure_console.go
index 5d3dfdc60..7c04ba37f 100644
--- a/restapi/configure_console.go
+++ b/restapi/configure_console.go
@@ -117,6 +117,8 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
// Operator Console
// Register tenant handlers
registerTenantHandlers(api)
+ // Register admin info handlers
+ registerOperatorTenantInfoHandlers(api)
// Register ResourceQuota handlers
registerResourceQuotaHandlers(api)
// Register Nodes' handlers
diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go
index c4f54822c..364800351 100644
--- a/restapi/embedded_spec.go
+++ b/restapi/embedded_spec.go
@@ -2560,8 +2560,8 @@ func init() {
"tags": [
"AdminAPI"
],
- "summary": "Tenant Info",
- "operationId": "TenantInfo",
+ "summary": "Tenant Details",
+ "operationId": "TenantDetails",
"parameters": [
{
"type": "string",
@@ -2755,6 +2755,103 @@ func init() {
}
}
},
+ "/namespaces/{namespace}/tenants/{tenant}/info": {
+ "get": {
+ "tags": [
+ "AdminAPI"
+ ],
+ "summary": "Tenant Info",
+ "operationId": "TenantInfo",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "namespace",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "tenant",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/adminInfoResponse"
+ }
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
+ }
+ },
+ "/namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId}": {
+ "get": {
+ "tags": [
+ "AdminAPI"
+ ],
+ "summary": "Returns information about a tenant deployment",
+ "operationId": "TenantWidgetDetails",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "namespace",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "tenant",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "name": "widgetId",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "name": "start",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "name": "end",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "name": "step",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/widgetDetails"
+ }
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
+ }
+ },
"/namespaces/{namespace}/tenants/{tenant}/pods": {
"get": {
"tags": [
@@ -9925,8 +10022,8 @@ func init() {
"tags": [
"AdminAPI"
],
- "summary": "Tenant Info",
- "operationId": "TenantInfo",
+ "summary": "Tenant Details",
+ "operationId": "TenantDetails",
"parameters": [
{
"type": "string",
@@ -10120,6 +10217,103 @@ func init() {
}
}
},
+ "/namespaces/{namespace}/tenants/{tenant}/info": {
+ "get": {
+ "tags": [
+ "AdminAPI"
+ ],
+ "summary": "Tenant Info",
+ "operationId": "TenantInfo",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "namespace",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "tenant",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/adminInfoResponse"
+ }
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
+ }
+ },
+ "/namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId}": {
+ "get": {
+ "tags": [
+ "AdminAPI"
+ ],
+ "summary": "Returns information about a tenant deployment",
+ "operationId": "TenantWidgetDetails",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "namespace",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "tenant",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "name": "widgetId",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "name": "start",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "name": "end",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "name": "step",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/widgetDetails"
+ }
+ },
+ "default": {
+ "description": "Generic error response.",
+ "schema": {
+ "$ref": "#/definitions/error"
+ }
+ }
+ }
+ }
+ },
"/namespaces/{namespace}/tenants/{tenant}/pods": {
"get": {
"tags": [
diff --git a/restapi/operations/admin_api/tenant_details.go b/restapi/operations/admin_api/tenant_details.go
new file mode 100644
index 000000000..979ee5d80
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_details.go
@@ -0,0 +1,88 @@
+// 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 .
+//
+
+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"
+)
+
+// TenantDetailsHandlerFunc turns a function with the right signature into a tenant details handler
+type TenantDetailsHandlerFunc func(TenantDetailsParams, *models.Principal) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn TenantDetailsHandlerFunc) Handle(params TenantDetailsParams, principal *models.Principal) middleware.Responder {
+ return fn(params, principal)
+}
+
+// TenantDetailsHandler interface for that can handle valid tenant details params
+type TenantDetailsHandler interface {
+ Handle(TenantDetailsParams, *models.Principal) middleware.Responder
+}
+
+// NewTenantDetails creates a new http.Handler for the tenant details operation
+func NewTenantDetails(ctx *middleware.Context, handler TenantDetailsHandler) *TenantDetails {
+ return &TenantDetails{Context: ctx, Handler: handler}
+}
+
+/* TenantDetails swagger:route GET /namespaces/{namespace}/tenants/{tenant} AdminAPI tenantDetails
+
+Tenant Details
+
+*/
+type TenantDetails struct {
+ Context *middleware.Context
+ Handler TenantDetailsHandler
+}
+
+func (o *TenantDetails) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewTenantDetailsParams()
+ 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)
+
+}
diff --git a/restapi/operations/admin_api/tenant_details_parameters.go b/restapi/operations/admin_api/tenant_details_parameters.go
new file mode 100644
index 000000000..67d97d460
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_details_parameters.go
@@ -0,0 +1,112 @@
+// 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 .
+//
+
+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"
+)
+
+// NewTenantDetailsParams creates a new TenantDetailsParams object
+//
+// There are no default values defined in the spec.
+func NewTenantDetailsParams() TenantDetailsParams {
+
+ return TenantDetailsParams{}
+}
+
+// TenantDetailsParams contains all the bound params for the tenant details operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters TenantDetails
+type TenantDetailsParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*
+ Required: true
+ In: path
+ */
+ Namespace string
+ /*
+ Required: true
+ In: path
+ */
+ Tenant 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 NewTenantDetailsParams() beforehand.
+func (o *TenantDetailsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ rNamespace, rhkNamespace, _ := route.Params.GetOK("namespace")
+ if err := o.bindNamespace(rNamespace, rhkNamespace, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ rTenant, rhkTenant, _ := route.Params.GetOK("tenant")
+ if err := o.bindTenant(rTenant, rhkTenant, route.Formats); err != nil {
+ res = append(res, err)
+ }
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindNamespace binds and validates parameter Namespace from path.
+func (o *TenantDetailsParams) bindNamespace(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.Namespace = raw
+
+ return nil
+}
+
+// bindTenant binds and validates parameter Tenant from path.
+func (o *TenantDetailsParams) bindTenant(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.Tenant = raw
+
+ return nil
+}
diff --git a/restapi/operations/admin_api/tenant_details_responses.go b/restapi/operations/admin_api/tenant_details_responses.go
new file mode 100644
index 000000000..bde73c4e7
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_details_responses.go
@@ -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 .
+//
+
+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"
+)
+
+// TenantDetailsOKCode is the HTTP code returned for type TenantDetailsOK
+const TenantDetailsOKCode int = 200
+
+/*TenantDetailsOK A successful response.
+
+swagger:response tenantDetailsOK
+*/
+type TenantDetailsOK struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.Tenant `json:"body,omitempty"`
+}
+
+// NewTenantDetailsOK creates TenantDetailsOK with default headers values
+func NewTenantDetailsOK() *TenantDetailsOK {
+
+ return &TenantDetailsOK{}
+}
+
+// WithPayload adds the payload to the tenant details o k response
+func (o *TenantDetailsOK) WithPayload(payload *models.Tenant) *TenantDetailsOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the tenant details o k response
+func (o *TenantDetailsOK) SetPayload(payload *models.Tenant) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *TenantDetailsOK) 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
+ }
+ }
+}
+
+/*TenantDetailsDefault Generic error response.
+
+swagger:response tenantDetailsDefault
+*/
+type TenantDetailsDefault struct {
+ _statusCode int
+
+ /*
+ In: Body
+ */
+ Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewTenantDetailsDefault creates TenantDetailsDefault with default headers values
+func NewTenantDetailsDefault(code int) *TenantDetailsDefault {
+ if code <= 0 {
+ code = 500
+ }
+
+ return &TenantDetailsDefault{
+ _statusCode: code,
+ }
+}
+
+// WithStatusCode adds the status to the tenant details default response
+func (o *TenantDetailsDefault) WithStatusCode(code int) *TenantDetailsDefault {
+ o._statusCode = code
+ return o
+}
+
+// SetStatusCode sets the status to the tenant details default response
+func (o *TenantDetailsDefault) SetStatusCode(code int) {
+ o._statusCode = code
+}
+
+// WithPayload adds the payload to the tenant details default response
+func (o *TenantDetailsDefault) WithPayload(payload *models.Error) *TenantDetailsDefault {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the tenant details default response
+func (o *TenantDetailsDefault) SetPayload(payload *models.Error) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *TenantDetailsDefault) 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
+ }
+ }
+}
diff --git a/restapi/operations/admin_api/tenant_details_urlbuilder.go b/restapi/operations/admin_api/tenant_details_urlbuilder.go
new file mode 100644
index 000000000..f137096d1
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_details_urlbuilder.go
@@ -0,0 +1,124 @@
+// 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 .
+//
+
+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"
+)
+
+// TenantDetailsURL generates an URL for the tenant details operation
+type TenantDetailsURL struct {
+ Namespace string
+ Tenant 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 *TenantDetailsURL) WithBasePath(bp string) *TenantDetailsURL {
+ 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 *TenantDetailsURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *TenantDetailsURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/namespaces/{namespace}/tenants/{tenant}"
+
+ namespace := o.Namespace
+ if namespace != "" {
+ _path = strings.Replace(_path, "{namespace}", namespace, -1)
+ } else {
+ return nil, errors.New("namespace is required on TenantDetailsURL")
+ }
+
+ tenant := o.Tenant
+ if tenant != "" {
+ _path = strings.Replace(_path, "{tenant}", tenant, -1)
+ } else {
+ return nil, errors.New("tenant is required on TenantDetailsURL")
+ }
+
+ _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 *TenantDetailsURL) 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 *TenantDetailsURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *TenantDetailsURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on TenantDetailsURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on TenantDetailsURL")
+ }
+
+ 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 *TenantDetailsURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/restapi/operations/admin_api/tenant_info.go b/restapi/operations/admin_api/tenant_info.go
index f67b472ae..6fe5b8f5a 100644
--- a/restapi/operations/admin_api/tenant_info.go
+++ b/restapi/operations/admin_api/tenant_info.go
@@ -48,7 +48,7 @@ func NewTenantInfo(ctx *middleware.Context, handler TenantInfoHandler) *TenantIn
return &TenantInfo{Context: ctx, Handler: handler}
}
-/* TenantInfo swagger:route GET /namespaces/{namespace}/tenants/{tenant} AdminAPI tenantInfo
+/* TenantInfo swagger:route GET /namespaces/{namespace}/tenants/{tenant}/info AdminAPI tenantInfo
Tenant Info
diff --git a/restapi/operations/admin_api/tenant_info_responses.go b/restapi/operations/admin_api/tenant_info_responses.go
index d9373f1b0..e0a20d1fc 100644
--- a/restapi/operations/admin_api/tenant_info_responses.go
+++ b/restapi/operations/admin_api/tenant_info_responses.go
@@ -42,7 +42,7 @@ type TenantInfoOK struct {
/*
In: Body
*/
- Payload *models.Tenant `json:"body,omitempty"`
+ Payload *models.AdminInfoResponse `json:"body,omitempty"`
}
// NewTenantInfoOK creates TenantInfoOK with default headers values
@@ -52,13 +52,13 @@ func NewTenantInfoOK() *TenantInfoOK {
}
// WithPayload adds the payload to the tenant info o k response
-func (o *TenantInfoOK) WithPayload(payload *models.Tenant) *TenantInfoOK {
+func (o *TenantInfoOK) WithPayload(payload *models.AdminInfoResponse) *TenantInfoOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the tenant info o k response
-func (o *TenantInfoOK) SetPayload(payload *models.Tenant) {
+func (o *TenantInfoOK) SetPayload(payload *models.AdminInfoResponse) {
o.Payload = payload
}
diff --git a/restapi/operations/admin_api/tenant_info_urlbuilder.go b/restapi/operations/admin_api/tenant_info_urlbuilder.go
index 042cdc0ed..bd4330b27 100644
--- a/restapi/operations/admin_api/tenant_info_urlbuilder.go
+++ b/restapi/operations/admin_api/tenant_info_urlbuilder.go
@@ -58,7 +58,7 @@ func (o *TenantInfoURL) SetBasePath(bp string) {
func (o *TenantInfoURL) Build() (*url.URL, error) {
var _result url.URL
- var _path = "/namespaces/{namespace}/tenants/{tenant}"
+ var _path = "/namespaces/{namespace}/tenants/{tenant}/info"
namespace := o.Namespace
if namespace != "" {
diff --git a/restapi/operations/admin_api/tenant_widget_details.go b/restapi/operations/admin_api/tenant_widget_details.go
new file mode 100644
index 000000000..75f1a8ca4
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_widget_details.go
@@ -0,0 +1,88 @@
+// 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 .
+//
+
+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"
+)
+
+// TenantWidgetDetailsHandlerFunc turns a function with the right signature into a tenant widget details handler
+type TenantWidgetDetailsHandlerFunc func(TenantWidgetDetailsParams, *models.Principal) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn TenantWidgetDetailsHandlerFunc) Handle(params TenantWidgetDetailsParams, principal *models.Principal) middleware.Responder {
+ return fn(params, principal)
+}
+
+// TenantWidgetDetailsHandler interface for that can handle valid tenant widget details params
+type TenantWidgetDetailsHandler interface {
+ Handle(TenantWidgetDetailsParams, *models.Principal) middleware.Responder
+}
+
+// NewTenantWidgetDetails creates a new http.Handler for the tenant widget details operation
+func NewTenantWidgetDetails(ctx *middleware.Context, handler TenantWidgetDetailsHandler) *TenantWidgetDetails {
+ return &TenantWidgetDetails{Context: ctx, Handler: handler}
+}
+
+/* TenantWidgetDetails swagger:route GET /namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId} AdminAPI tenantWidgetDetails
+
+Returns information about a tenant deployment
+
+*/
+type TenantWidgetDetails struct {
+ Context *middleware.Context
+ Handler TenantWidgetDetailsHandler
+}
+
+func (o *TenantWidgetDetails) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewTenantWidgetDetailsParams()
+ 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)
+
+}
diff --git a/restapi/operations/admin_api/tenant_widget_details_parameters.go b/restapi/operations/admin_api/tenant_widget_details_parameters.go
new file mode 100644
index 000000000..2769c4bf6
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_widget_details_parameters.go
@@ -0,0 +1,241 @@
+// 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 .
+//
+
+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"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// NewTenantWidgetDetailsParams creates a new TenantWidgetDetailsParams object
+//
+// There are no default values defined in the spec.
+func NewTenantWidgetDetailsParams() TenantWidgetDetailsParams {
+
+ return TenantWidgetDetailsParams{}
+}
+
+// TenantWidgetDetailsParams contains all the bound params for the tenant widget details operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters TenantWidgetDetails
+type TenantWidgetDetailsParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*
+ In: query
+ */
+ End *int64
+ /*
+ Required: true
+ In: path
+ */
+ Namespace string
+ /*
+ In: query
+ */
+ Start *int64
+ /*
+ In: query
+ */
+ Step *int32
+ /*
+ Required: true
+ In: path
+ */
+ Tenant string
+ /*
+ Required: true
+ In: path
+ */
+ WidgetID int32
+}
+
+// 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 NewTenantWidgetDetailsParams() beforehand.
+func (o *TenantWidgetDetailsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ qs := runtime.Values(r.URL.Query())
+
+ qEnd, qhkEnd, _ := qs.GetOK("end")
+ if err := o.bindEnd(qEnd, qhkEnd, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ rNamespace, rhkNamespace, _ := route.Params.GetOK("namespace")
+ if err := o.bindNamespace(rNamespace, rhkNamespace, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qStart, qhkStart, _ := qs.GetOK("start")
+ if err := o.bindStart(qStart, qhkStart, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qStep, qhkStep, _ := qs.GetOK("step")
+ if err := o.bindStep(qStep, qhkStep, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ rTenant, rhkTenant, _ := route.Params.GetOK("tenant")
+ if err := o.bindTenant(rTenant, rhkTenant, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ rWidgetID, rhkWidgetID, _ := route.Params.GetOK("widgetId")
+ if err := o.bindWidgetID(rWidgetID, rhkWidgetID, route.Formats); err != nil {
+ res = append(res, err)
+ }
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindEnd binds and validates parameter End from query.
+func (o *TenantWidgetDetailsParams) bindEnd(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt64(raw)
+ if err != nil {
+ return errors.InvalidType("end", "query", "int64", raw)
+ }
+ o.End = &value
+
+ return nil
+}
+
+// bindNamespace binds and validates parameter Namespace from path.
+func (o *TenantWidgetDetailsParams) bindNamespace(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.Namespace = raw
+
+ return nil
+}
+
+// bindStart binds and validates parameter Start from query.
+func (o *TenantWidgetDetailsParams) bindStart(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt64(raw)
+ if err != nil {
+ return errors.InvalidType("start", "query", "int64", raw)
+ }
+ o.Start = &value
+
+ return nil
+}
+
+// bindStep binds and validates parameter Step from query.
+func (o *TenantWidgetDetailsParams) bindStep(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt32(raw)
+ if err != nil {
+ return errors.InvalidType("step", "query", "int32", raw)
+ }
+ o.Step = &value
+
+ return nil
+}
+
+// bindTenant binds and validates parameter Tenant from path.
+func (o *TenantWidgetDetailsParams) bindTenant(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.Tenant = raw
+
+ return nil
+}
+
+// bindWidgetID binds and validates parameter WidgetID from path.
+func (o *TenantWidgetDetailsParams) bindWidgetID(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
+
+ value, err := swag.ConvertInt32(raw)
+ if err != nil {
+ return errors.InvalidType("widgetId", "path", "int32", raw)
+ }
+ o.WidgetID = value
+
+ return nil
+}
diff --git a/restapi/operations/admin_api/tenant_widget_details_responses.go b/restapi/operations/admin_api/tenant_widget_details_responses.go
new file mode 100644
index 000000000..c6f9d5f0f
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_widget_details_responses.go
@@ -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 .
+//
+
+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"
+)
+
+// TenantWidgetDetailsOKCode is the HTTP code returned for type TenantWidgetDetailsOK
+const TenantWidgetDetailsOKCode int = 200
+
+/*TenantWidgetDetailsOK A successful response.
+
+swagger:response tenantWidgetDetailsOK
+*/
+type TenantWidgetDetailsOK struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.WidgetDetails `json:"body,omitempty"`
+}
+
+// NewTenantWidgetDetailsOK creates TenantWidgetDetailsOK with default headers values
+func NewTenantWidgetDetailsOK() *TenantWidgetDetailsOK {
+
+ return &TenantWidgetDetailsOK{}
+}
+
+// WithPayload adds the payload to the tenant widget details o k response
+func (o *TenantWidgetDetailsOK) WithPayload(payload *models.WidgetDetails) *TenantWidgetDetailsOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the tenant widget details o k response
+func (o *TenantWidgetDetailsOK) SetPayload(payload *models.WidgetDetails) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *TenantWidgetDetailsOK) 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
+ }
+ }
+}
+
+/*TenantWidgetDetailsDefault Generic error response.
+
+swagger:response tenantWidgetDetailsDefault
+*/
+type TenantWidgetDetailsDefault struct {
+ _statusCode int
+
+ /*
+ In: Body
+ */
+ Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewTenantWidgetDetailsDefault creates TenantWidgetDetailsDefault with default headers values
+func NewTenantWidgetDetailsDefault(code int) *TenantWidgetDetailsDefault {
+ if code <= 0 {
+ code = 500
+ }
+
+ return &TenantWidgetDetailsDefault{
+ _statusCode: code,
+ }
+}
+
+// WithStatusCode adds the status to the tenant widget details default response
+func (o *TenantWidgetDetailsDefault) WithStatusCode(code int) *TenantWidgetDetailsDefault {
+ o._statusCode = code
+ return o
+}
+
+// SetStatusCode sets the status to the tenant widget details default response
+func (o *TenantWidgetDetailsDefault) SetStatusCode(code int) {
+ o._statusCode = code
+}
+
+// WithPayload adds the payload to the tenant widget details default response
+func (o *TenantWidgetDetailsDefault) WithPayload(payload *models.Error) *TenantWidgetDetailsDefault {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the tenant widget details default response
+func (o *TenantWidgetDetailsDefault) SetPayload(payload *models.Error) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *TenantWidgetDetailsDefault) 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
+ }
+ }
+}
diff --git a/restapi/operations/admin_api/tenant_widget_details_urlbuilder.go b/restapi/operations/admin_api/tenant_widget_details_urlbuilder.go
new file mode 100644
index 000000000..5f322664f
--- /dev/null
+++ b/restapi/operations/admin_api/tenant_widget_details_urlbuilder.go
@@ -0,0 +1,166 @@
+// 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 .
+//
+
+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"
+
+ "github.com/go-openapi/swag"
+)
+
+// TenantWidgetDetailsURL generates an URL for the tenant widget details operation
+type TenantWidgetDetailsURL struct {
+ Namespace string
+ Tenant string
+ WidgetID int32
+
+ End *int64
+ Start *int64
+ Step *int32
+
+ _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 *TenantWidgetDetailsURL) WithBasePath(bp string) *TenantWidgetDetailsURL {
+ 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 *TenantWidgetDetailsURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *TenantWidgetDetailsURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId}"
+
+ namespace := o.Namespace
+ if namespace != "" {
+ _path = strings.Replace(_path, "{namespace}", namespace, -1)
+ } else {
+ return nil, errors.New("namespace is required on TenantWidgetDetailsURL")
+ }
+
+ tenant := o.Tenant
+ if tenant != "" {
+ _path = strings.Replace(_path, "{tenant}", tenant, -1)
+ } else {
+ return nil, errors.New("tenant is required on TenantWidgetDetailsURL")
+ }
+
+ widgetID := swag.FormatInt32(o.WidgetID)
+ if widgetID != "" {
+ _path = strings.Replace(_path, "{widgetId}", widgetID, -1)
+ } else {
+ return nil, errors.New("widgetId is required on TenantWidgetDetailsURL")
+ }
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ qs := make(url.Values)
+
+ var endQ string
+ if o.End != nil {
+ endQ = swag.FormatInt64(*o.End)
+ }
+ if endQ != "" {
+ qs.Set("end", endQ)
+ }
+
+ var startQ string
+ if o.Start != nil {
+ startQ = swag.FormatInt64(*o.Start)
+ }
+ if startQ != "" {
+ qs.Set("start", startQ)
+ }
+
+ var stepQ string
+ if o.Step != nil {
+ stepQ = swag.FormatInt32(*o.Step)
+ }
+ if stepQ != "" {
+ qs.Set("step", stepQ)
+ }
+
+ _result.RawQuery = qs.Encode()
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *TenantWidgetDetailsURL) 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 *TenantWidgetDetailsURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *TenantWidgetDetailsURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on TenantWidgetDetailsURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on TenantWidgetDetailsURL")
+ }
+
+ 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 *TenantWidgetDetailsURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/restapi/operations/console_api.go b/restapi/operations/console_api.go
index e7f24c696..b3c3e16b2 100644
--- a/restapi/operations/console_api.go
+++ b/restapi/operations/console_api.go
@@ -384,6 +384,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
AdminAPITenantAddPoolHandler: admin_api.TenantAddPoolHandlerFunc(func(params admin_api.TenantAddPoolParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantAddPool has not yet been implemented")
}),
+ AdminAPITenantDetailsHandler: admin_api.TenantDetailsHandlerFunc(func(params admin_api.TenantDetailsParams, principal *models.Principal) middleware.Responder {
+ return middleware.NotImplemented("operation admin_api.TenantDetails has not yet been implemented")
+ }),
AdminAPITenantInfoHandler: admin_api.TenantInfoHandlerFunc(func(params admin_api.TenantInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantInfo has not yet been implemented")
}),
@@ -396,6 +399,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
AdminAPITenantUpdatePoolsHandler: admin_api.TenantUpdatePoolsHandlerFunc(func(params admin_api.TenantUpdatePoolsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantUpdatePools has not yet been implemented")
}),
+ AdminAPITenantWidgetDetailsHandler: admin_api.TenantWidgetDetailsHandlerFunc(func(params admin_api.TenantWidgetDetailsParams, principal *models.Principal) middleware.Responder {
+ return middleware.NotImplemented("operation admin_api.TenantWidgetDetails has not yet been implemented")
+ }),
AdminAPITiersListHandler: admin_api.TiersListHandlerFunc(func(params admin_api.TiersListParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TiersList has not yet been implemented")
}),
@@ -681,6 +687,8 @@ type ConsoleAPI struct {
AdminAPISubscriptionValidateHandler admin_api.SubscriptionValidateHandler
// AdminAPITenantAddPoolHandler sets the operation handler for the tenant add pool operation
AdminAPITenantAddPoolHandler admin_api.TenantAddPoolHandler
+ // AdminAPITenantDetailsHandler sets the operation handler for the tenant details operation
+ AdminAPITenantDetailsHandler admin_api.TenantDetailsHandler
// AdminAPITenantInfoHandler sets the operation handler for the tenant info operation
AdminAPITenantInfoHandler admin_api.TenantInfoHandler
// AdminAPITenantUpdateCertificateHandler sets the operation handler for the tenant update certificate operation
@@ -689,6 +697,8 @@ type ConsoleAPI struct {
AdminAPITenantUpdateEncryptionHandler admin_api.TenantUpdateEncryptionHandler
// AdminAPITenantUpdatePoolsHandler sets the operation handler for the tenant update pools operation
AdminAPITenantUpdatePoolsHandler admin_api.TenantUpdatePoolsHandler
+ // AdminAPITenantWidgetDetailsHandler sets the operation handler for the tenant widget details operation
+ AdminAPITenantWidgetDetailsHandler admin_api.TenantWidgetDetailsHandler
// AdminAPITiersListHandler sets the operation handler for the tiers list operation
AdminAPITiersListHandler admin_api.TiersListHandler
// UserAPIUpdateBucketLifecycleHandler sets the operation handler for the update bucket lifecycle operation
@@ -1106,6 +1116,9 @@ func (o *ConsoleAPI) Validate() error {
if o.AdminAPITenantAddPoolHandler == nil {
unregistered = append(unregistered, "admin_api.TenantAddPoolHandler")
}
+ if o.AdminAPITenantDetailsHandler == nil {
+ unregistered = append(unregistered, "admin_api.TenantDetailsHandler")
+ }
if o.AdminAPITenantInfoHandler == nil {
unregistered = append(unregistered, "admin_api.TenantInfoHandler")
}
@@ -1118,6 +1131,9 @@ func (o *ConsoleAPI) Validate() error {
if o.AdminAPITenantUpdatePoolsHandler == nil {
unregistered = append(unregistered, "admin_api.TenantUpdatePoolsHandler")
}
+ if o.AdminAPITenantWidgetDetailsHandler == nil {
+ unregistered = append(unregistered, "admin_api.TenantWidgetDetailsHandler")
+ }
if o.AdminAPITiersListHandler == nil {
unregistered = append(unregistered, "admin_api.TiersListHandler")
}
@@ -1665,7 +1681,11 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
- o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}"] = admin_api.NewTenantInfo(o.context, o.AdminAPITenantInfoHandler)
+ o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}"] = admin_api.NewTenantDetails(o.context, o.AdminAPITenantDetailsHandler)
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
+ o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/info"] = admin_api.NewTenantInfo(o.context, o.AdminAPITenantInfoHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
@@ -1681,6 +1701,10 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
+ o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId}"] = admin_api.NewTenantWidgetDetails(o.context, o.AdminAPITenantWidgetDetailsHandler)
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
o.handlers["GET"]["/admin/tiers"] = admin_api.NewTiersList(o.context, o.AdminAPITiersListHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
diff --git a/restapi/operator_info.go b/restapi/operator_info.go
new file mode 100644
index 000000000..e58f42c4b
--- /dev/null
+++ b/restapi/operator_info.go
@@ -0,0 +1,138 @@
+// 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 .
+
+package restapi
+
+import (
+ "fmt"
+
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/minio/console/cluster"
+ "github.com/minio/console/models"
+ "github.com/minio/console/restapi/operations"
+ "github.com/minio/console/restapi/operations/admin_api"
+ miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
+)
+
+func registerOperatorTenantInfoHandlers(api *operations.ConsoleAPI) {
+ // return usage stats
+ api.AdminAPITenantInfoHandler = admin_api.TenantInfoHandlerFunc(func(params admin_api.TenantInfoParams, session *models.Principal) middleware.Responder {
+ infoResp, err := getTenantInfoResponse(session, params)
+ if err != nil {
+ return admin_api.NewTenantInfoDefault(int(err.Code)).WithPayload(err)
+ }
+ return admin_api.NewTenantInfoOK().WithPayload(infoResp)
+ })
+ // return single widget results
+ api.AdminAPITenantWidgetDetailsHandler = admin_api.TenantWidgetDetailsHandlerFunc(func(params admin_api.TenantWidgetDetailsParams, session *models.Principal) middleware.Responder {
+ infoResp, err := getTenantWidgetResponse(session, params)
+ if err != nil {
+ return admin_api.NewDashboardWidgetDetailsDefault(int(err.Code)).WithPayload(err)
+ }
+ return admin_api.NewDashboardWidgetDetailsOK().WithPayload(infoResp)
+ })
+}
+
+func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInfoParams) (*models.AdminInfoResponse, *models.Error) {
+ opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+ clientSet, err := cluster.K8sClient(session.STSSessionToken)
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+
+ opClient := &operatorClient{
+ client: opClientClientSet,
+ }
+ k8sClient := &k8sClient{
+ client: clientSet,
+ }
+
+ tenant, err := getTenant(params.HTTPRequest.Context(), opClient, params.Namespace, params.Tenant)
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+ tenant.EnsureDefaults()
+
+ svcURL := GetTenantServiceURL(tenant)
+ // getTenantAdminClient will use all certificates under ~/.console/certs/CAs to trust the TLS connections with MinIO tenants
+ mAdmin, err := getTenantAdminClient(
+ params.HTTPRequest.Context(),
+ k8sClient,
+ tenant,
+ svcURL,
+ )
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+
+ prometheusURL := getPrometheusURLForTenant(tenant)
+
+ sessionResp, err2 := getUsageWidgetsForDeployment(prometheusURL, mAdmin)
+ if err2 != nil {
+ return nil, err2
+ }
+
+ return sessionResp, nil
+}
+
+func getTenantWidgetResponse(session *models.Principal, params admin_api.TenantWidgetDetailsParams) (*models.WidgetDetails, *models.Error) {
+
+ opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+
+ opClient := &operatorClient{
+ client: opClientClientSet,
+ }
+
+ tenant, err := getTenant(params.HTTPRequest.Context(), opClient, params.Namespace, params.Tenant)
+ if err != nil {
+ return nil, prepareError(err, errorUnableToGetTenantUsage)
+ }
+ tenant.EnsureDefaults()
+
+ prometheusURL := getPrometheusURLForTenant(tenant)
+ prometheusJobID := getPrometheusJobID()
+ // check for special values
+ if tenant.HasConsoleEnabled() {
+ for _, env := range tenant.Spec.Console.Env {
+ if env.Name == "CONSOLE_PROMETHEUS_JOB_ID" {
+ prometheusJobID = env.Value
+ }
+ }
+
+ }
+
+ return getWidgetDetails(prometheusURL, prometheusJobID, params.WidgetID, params.Step, params.Start, params.End)
+}
+
+func getPrometheusURLForTenant(tenant *miniov2.Tenant) string {
+ prometheusURL := fmt.Sprintf("http://%s.%s:%d", tenant.PrometheusHLServiceName(), tenant.Namespace, miniov2.PrometheusAPIPort)
+ // check for special values
+ if tenant.HasConsoleEnabled() {
+ for _, env := range tenant.Spec.Console.Env {
+ if env.Name == "CONSOLE_PROMETHEUS_URL" {
+ prometheusURL = env.Value
+ }
+ }
+
+ }
+ return prometheusURL
+}
diff --git a/swagger.yml b/swagger.yml
index dc5a72112..b6d29aa74 100644
--- a/swagger.yml
+++ b/swagger.yml
@@ -2229,8 +2229,8 @@ paths:
/namespaces/{namespace}/tenants/{tenant}:
get:
- summary: Tenant Info
- operationId: TenantInfo
+ summary: Tenant Details
+ operationId: TenantDetails
parameters:
- name: namespace
in: path
@@ -2303,6 +2303,70 @@ paths:
$ref: "#/definitions/error"
tags:
- AdminAPI
+ /namespaces/{namespace}/tenants/{tenant}/info:
+ get:
+ summary: Tenant Info
+ operationId: TenantInfo
+ parameters:
+ - name: namespace
+ in: path
+ required: true
+ type: string
+ - name: tenant
+ in: path
+ required: true
+ type: string
+ responses:
+ 200:
+ description: A successful response.
+ schema:
+ $ref: "#/definitions/adminInfoResponse"
+ default:
+ description: Generic error response.
+ schema:
+ $ref: "#/definitions/error"
+ tags:
+ - AdminAPI
+
+ /namespaces/{namespace}/tenants/{tenant}/info/widgets/{widgetId}:
+ get:
+ summary: Returns information about a tenant deployment
+ operationId: TenantWidgetDetails
+ parameters:
+ - name: namespace
+ in: path
+ required: true
+ type: string
+ - name: tenant
+ in: path
+ required: true
+ type: string
+ - name: widgetId
+ in: path
+ type: integer
+ format: int32
+ required: true
+ - name: start
+ in: query
+ type: integer
+ - name: end
+ in: query
+ type: integer
+ - name: step
+ in: query
+ type: integer
+ format: int32
+ responses:
+ 200:
+ description: A successful response.
+ schema:
+ $ref: "#/definitions/widgetDetails"
+ default:
+ description: Generic error response.
+ schema:
+ $ref: "#/definitions/error"
+ tags:
+ - AdminAPI
/namespaces/{namespace}/tenants/{tenant}/pools:
post: