diff --git a/models/annotation.go b/models/annotation.go new file mode 100644 index 000000000..32d1fd8da --- /dev/null +++ b/models/annotation.go @@ -0,0 +1,70 @@ +// 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 models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Annotation annotation +// +// swagger:model annotation +type Annotation struct { + + // key + Key string `json:"key,omitempty"` + + // value + Value string `json:"value,omitempty"` +} + +// Validate validates this annotation +func (m *Annotation) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this annotation based on context it is used +func (m *Annotation) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Annotation) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Annotation) UnmarshalBinary(b []byte) error { + var res Annotation + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/configure_tenant_request.go b/models/configure_tenant_request.go new file mode 100644 index 000000000..bfccc77b0 --- /dev/null +++ b/models/configure_tenant_request.go @@ -0,0 +1,67 @@ +// 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 models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ConfigureTenantRequest configure tenant request +// +// swagger:model configureTenantRequest +type ConfigureTenantRequest struct { + + // prometheus enabled + PrometheusEnabled bool `json:"prometheusEnabled,omitempty"` +} + +// Validate validates this configure tenant request +func (m *ConfigureTenantRequest) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this configure tenant request based on context it is used +func (m *ConfigureTenantRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *ConfigureTenantRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ConfigureTenantRequest) UnmarshalBinary(b []byte) error { + var res ConfigureTenantRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/label.go b/models/label.go new file mode 100644 index 000000000..bdfa59fee --- /dev/null +++ b/models/label.go @@ -0,0 +1,70 @@ +// 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 models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Label label +// +// swagger:model label +type Label struct { + + // key + Key string `json:"key,omitempty"` + + // value + Value string `json:"value,omitempty"` +} + +// Validate validates this label +func (m *Label) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this label based on context it is used +func (m *Label) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Label) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Label) UnmarshalBinary(b []byte) error { + var res Label + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/node_selector.go b/models/node_selector.go new file mode 100644 index 000000000..bfd68c49e --- /dev/null +++ b/models/node_selector.go @@ -0,0 +1,70 @@ +// 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 models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NodeSelector node selector +// +// swagger:model nodeSelector +type NodeSelector struct { + + // key + Key string `json:"key,omitempty"` + + // value + Value string `json:"value,omitempty"` +} + +// Validate validates this node selector +func (m *NodeSelector) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this node selector based on context it is used +func (m *NodeSelector) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *NodeSelector) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NodeSelector) UnmarshalBinary(b []byte) error { + var res NodeSelector + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/tenant_monitoring_info.go b/models/tenant_monitoring_info.go new file mode 100644 index 000000000..4e1d82067 --- /dev/null +++ b/models/tenant_monitoring_info.go @@ -0,0 +1,259 @@ +// 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 models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// TenantMonitoringInfo tenant monitoring info +// +// swagger:model tenantMonitoringInfo +type TenantMonitoringInfo struct { + + // annotations + Annotations []*Annotation `json:"annotations"` + + // disk capacity g b + DiskCapacityGB string `json:"diskCapacityGB,omitempty"` + + // image + Image string `json:"image,omitempty"` + + // init image + InitImage string `json:"initImage,omitempty"` + + // labels + Labels []*Label `json:"labels"` + + // node selector + NodeSelector []*NodeSelector `json:"nodeSelector"` + + // prometheus enabled + PrometheusEnabled bool `json:"prometheusEnabled,omitempty"` + + // service account name + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // sidecar image + SidecarImage string `json:"sidecarImage,omitempty"` + + // storage class name + StorageClassName string `json:"storageClassName,omitempty"` + + // toggle + Toggle bool `json:"toggle,omitempty"` +} + +// Validate validates this tenant monitoring info +func (m *TenantMonitoringInfo) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAnnotations(formats); err != nil { + res = append(res, err) + } + + if err := m.validateLabels(formats); err != nil { + res = append(res, err) + } + + if err := m.validateNodeSelector(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *TenantMonitoringInfo) validateAnnotations(formats strfmt.Registry) error { + if swag.IsZero(m.Annotations) { // not required + return nil + } + + for i := 0; i < len(m.Annotations); i++ { + if swag.IsZero(m.Annotations[i]) { // not required + continue + } + + if m.Annotations[i] != nil { + if err := m.Annotations[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("annotations" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *TenantMonitoringInfo) validateLabels(formats strfmt.Registry) error { + if swag.IsZero(m.Labels) { // not required + return nil + } + + for i := 0; i < len(m.Labels); i++ { + if swag.IsZero(m.Labels[i]) { // not required + continue + } + + if m.Labels[i] != nil { + if err := m.Labels[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("labels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *TenantMonitoringInfo) validateNodeSelector(formats strfmt.Registry) error { + if swag.IsZero(m.NodeSelector) { // not required + return nil + } + + for i := 0; i < len(m.NodeSelector); i++ { + if swag.IsZero(m.NodeSelector[i]) { // not required + continue + } + + if m.NodeSelector[i] != nil { + if err := m.NodeSelector[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("nodeSelector" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this tenant monitoring info based on the context it is used +func (m *TenantMonitoringInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateAnnotations(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateLabels(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateNodeSelector(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *TenantMonitoringInfo) contextValidateAnnotations(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Annotations); i++ { + + if m.Annotations[i] != nil { + if err := m.Annotations[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("annotations" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *TenantMonitoringInfo) contextValidateLabels(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Labels); i++ { + + if m.Labels[i] != nil { + if err := m.Labels[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("labels" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *TenantMonitoringInfo) contextValidateNodeSelector(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.NodeSelector); i++ { + + if m.NodeSelector[i] != nil { + if err := m.NodeSelector[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("nodeSelector" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *TenantMonitoringInfo) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *TenantMonitoringInfo) UnmarshalBinary(b []byte) error { + var res TenantMonitoringInfo + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/operatorapi/embedded_spec.go b/operatorapi/embedded_spec.go index 41afbd101..a1c8cba65 100644 --- a/operatorapi/embedded_spec.go +++ b/operatorapi/embedded_spec.go @@ -704,6 +704,83 @@ func init() { } } }, + "/namespaces/{namespace}/tenants/{tenant}/monitoring": { + "get": { + "tags": [ + "OperatorAPI" + ], + "summary": "Get Prometheus Monitoring config info For The Tenant", + "operationId": "GetTenantMonitoring", + "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/tenantMonitoringInfo" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, + "put": { + "tags": [ + "OperatorAPI" + ], + "summary": "Set Prometheus monitoring fields for tenant", + "operationId": "SetTenantMonitoring", + "parameters": [ + { + "type": "string", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "tenant", + "in": "path", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tenantMonitoringInfo" + } + } + ], + "responses": { + "201": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/namespaces/{namespace}/tenants/{tenant}/pods": { "get": { "tags": [ @@ -1434,6 +1511,17 @@ func init() { } } }, + "annotation": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "awsConfiguration": { "type": "object", "required": [ @@ -1537,6 +1625,14 @@ func init() { } } }, + "configureTenantRequest": { + "type": "object", + "properties": { + "prometheusEnabled": { + "type": "boolean" + } + } + }, "createTenantRequest": { "type": "object", "required": [ @@ -2091,6 +2187,17 @@ func init() { } } }, + "label": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "license": { "type": "object", "properties": { @@ -2294,6 +2401,17 @@ func init() { } } }, + "nodeSelector": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "nodeSelectorTerm": { "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", "type": "object", @@ -2965,6 +3083,53 @@ func init() { } } }, + "tenantMonitoringInfo": { + "type": "object", + "properties": { + "annotations": { + "type": "array", + "items": { + "$ref": "#/definitions/annotation" + } + }, + "diskCapacityGB": { + "type": "string" + }, + "image": { + "type": "string" + }, + "initImage": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/label" + } + }, + "nodeSelector": { + "type": "array", + "items": { + "$ref": "#/definitions/nodeSelector" + } + }, + "prometheusEnabled": { + "type": "boolean" + }, + "serviceAccountName": { + "type": "string" + }, + "sidecarImage": { + "type": "string" + }, + "storageClassName": { + "type": "string" + }, + "toggle": { + "type": "boolean" + } + } + }, "tenantPod": { "type": "object", "required": [ @@ -3910,6 +4075,83 @@ func init() { } } }, + "/namespaces/{namespace}/tenants/{tenant}/monitoring": { + "get": { + "tags": [ + "OperatorAPI" + ], + "summary": "Get Prometheus Monitoring config info For The Tenant", + "operationId": "GetTenantMonitoring", + "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/tenantMonitoringInfo" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, + "put": { + "tags": [ + "OperatorAPI" + ], + "summary": "Set Prometheus monitoring fields for tenant", + "operationId": "SetTenantMonitoring", + "parameters": [ + { + "type": "string", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "tenant", + "in": "path", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tenantMonitoringInfo" + } + } + ], + "responses": { + "201": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + } + }, "/namespaces/{namespace}/tenants/{tenant}/pods": { "get": { "tags": [ @@ -5365,6 +5607,17 @@ func init() { } } }, + "annotation": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "awsConfiguration": { "type": "object", "required": [ @@ -5468,6 +5721,14 @@ func init() { } } }, + "configureTenantRequest": { + "type": "object", + "properties": { + "prometheusEnabled": { + "type": "boolean" + } + } + }, "createTenantRequest": { "type": "object", "required": [ @@ -6010,6 +6271,17 @@ func init() { } } }, + "label": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "license": { "type": "object", "properties": { @@ -6213,6 +6485,17 @@ func init() { } } }, + "nodeSelector": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, "nodeSelectorTerm": { "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", "type": "object", @@ -6749,6 +7032,53 @@ func init() { } } }, + "tenantMonitoringInfo": { + "type": "object", + "properties": { + "annotations": { + "type": "array", + "items": { + "$ref": "#/definitions/annotation" + } + }, + "diskCapacityGB": { + "type": "string" + }, + "image": { + "type": "string" + }, + "initImage": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "$ref": "#/definitions/label" + } + }, + "nodeSelector": { + "type": "array", + "items": { + "$ref": "#/definitions/nodeSelector" + } + }, + "prometheusEnabled": { + "type": "boolean" + }, + "serviceAccountName": { + "type": "string" + }, + "sidecarImage": { + "type": "string" + }, + "storageClassName": { + "type": "string" + }, + "toggle": { + "type": "boolean" + } + } + }, "tenantPod": { "type": "object", "required": [ diff --git a/operatorapi/operations/operator_api.go b/operatorapi/operations/operator_api.go index b2f5dde07..aefaa9180 100644 --- a/operatorapi/operations/operator_api.go +++ b/operatorapi/operations/operator_api.go @@ -102,6 +102,9 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI { OperatorAPIGetResourceQuotaHandler: operator_api.GetResourceQuotaHandlerFunc(func(params operator_api.GetResourceQuotaParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation operator_api.GetResourceQuota has not yet been implemented") }), + OperatorAPIGetTenantMonitoringHandler: operator_api.GetTenantMonitoringHandlerFunc(func(params operator_api.GetTenantMonitoringParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation operator_api.GetTenantMonitoring has not yet been implemented") + }), OperatorAPIGetTenantPodsHandler: operator_api.GetTenantPodsHandlerFunc(func(params operator_api.GetTenantPodsParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation operator_api.GetTenantPods has not yet been implemented") }), @@ -144,6 +147,9 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI { UserAPISessionCheckHandler: user_api.SessionCheckHandlerFunc(func(params user_api.SessionCheckParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user_api.SessionCheck has not yet been implemented") }), + OperatorAPISetTenantMonitoringHandler: operator_api.SetTenantMonitoringHandlerFunc(func(params operator_api.SetTenantMonitoringParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation operator_api.SetTenantMonitoring has not yet been implemented") + }), OperatorAPISubscriptionActivateHandler: operator_api.SubscriptionActivateHandlerFunc(func(params operator_api.SubscriptionActivateParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation operator_api.SubscriptionActivate has not yet been implemented") }), @@ -255,6 +261,8 @@ type OperatorAPI struct { OperatorAPIGetPodLogsHandler operator_api.GetPodLogsHandler // OperatorAPIGetResourceQuotaHandler sets the operation handler for the get resource quota operation OperatorAPIGetResourceQuotaHandler operator_api.GetResourceQuotaHandler + // OperatorAPIGetTenantMonitoringHandler sets the operation handler for the get tenant monitoring operation + OperatorAPIGetTenantMonitoringHandler operator_api.GetTenantMonitoringHandler // OperatorAPIGetTenantPodsHandler sets the operation handler for the get tenant pods operation OperatorAPIGetTenantPodsHandler operator_api.GetTenantPodsHandler // OperatorAPIGetTenantUsageHandler sets the operation handler for the get tenant usage operation @@ -283,6 +291,8 @@ type OperatorAPI struct { OperatorAPIPutTenantYAMLHandler operator_api.PutTenantYAMLHandler // UserAPISessionCheckHandler sets the operation handler for the session check operation UserAPISessionCheckHandler user_api.SessionCheckHandler + // OperatorAPISetTenantMonitoringHandler sets the operation handler for the set tenant monitoring operation + OperatorAPISetTenantMonitoringHandler operator_api.SetTenantMonitoringHandler // OperatorAPISubscriptionActivateHandler sets the operation handler for the subscription activate operation OperatorAPISubscriptionActivateHandler operator_api.SubscriptionActivateHandler // OperatorAPISubscriptionInfoHandler sets the operation handler for the subscription info operation @@ -427,6 +437,9 @@ func (o *OperatorAPI) Validate() error { if o.OperatorAPIGetResourceQuotaHandler == nil { unregistered = append(unregistered, "operator_api.GetResourceQuotaHandler") } + if o.OperatorAPIGetTenantMonitoringHandler == nil { + unregistered = append(unregistered, "operator_api.GetTenantMonitoringHandler") + } if o.OperatorAPIGetTenantPodsHandler == nil { unregistered = append(unregistered, "operator_api.GetTenantPodsHandler") } @@ -469,6 +482,9 @@ func (o *OperatorAPI) Validate() error { if o.UserAPISessionCheckHandler == nil { unregistered = append(unregistered, "user_api.SessionCheckHandler") } + if o.OperatorAPISetTenantMonitoringHandler == nil { + unregistered = append(unregistered, "operator_api.SetTenantMonitoringHandler") + } if o.OperatorAPISubscriptionActivateHandler == nil { unregistered = append(unregistered, "operator_api.SubscriptionActivateHandler") } @@ -658,6 +674,10 @@ func (o *OperatorAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/monitoring"] = operator_api.NewGetTenantMonitoring(o.context, o.OperatorAPIGetTenantMonitoringHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/pods"] = operator_api.NewGetTenantPods(o.context, o.OperatorAPIGetTenantPodsHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) @@ -711,6 +731,10 @@ func (o *OperatorAPI) initHandlerCache() { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/session"] = user_api.NewSessionCheck(o.context, o.UserAPISessionCheckHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } + o.handlers["PUT"]["/namespaces/{namespace}/tenants/{tenant}/monitoring"] = operator_api.NewSetTenantMonitoring(o.context, o.OperatorAPISetTenantMonitoringHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } diff --git a/operatorapi/operations/operator_api/get_tenant_monitoring.go b/operatorapi/operations/operator_api/get_tenant_monitoring.go new file mode 100644 index 000000000..86587e548 --- /dev/null +++ b/operatorapi/operations/operator_api/get_tenant_monitoring.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 operator_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" +) + +// GetTenantMonitoringHandlerFunc turns a function with the right signature into a get tenant monitoring handler +type GetTenantMonitoringHandlerFunc func(GetTenantMonitoringParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetTenantMonitoringHandlerFunc) Handle(params GetTenantMonitoringParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetTenantMonitoringHandler interface for that can handle valid get tenant monitoring params +type GetTenantMonitoringHandler interface { + Handle(GetTenantMonitoringParams, *models.Principal) middleware.Responder +} + +// NewGetTenantMonitoring creates a new http.Handler for the get tenant monitoring operation +func NewGetTenantMonitoring(ctx *middleware.Context, handler GetTenantMonitoringHandler) *GetTenantMonitoring { + return &GetTenantMonitoring{Context: ctx, Handler: handler} +} + +/* GetTenantMonitoring swagger:route GET /namespaces/{namespace}/tenants/{tenant}/monitoring OperatorAPI getTenantMonitoring + +Get Prometheus Monitoring config info For The Tenant + +*/ +type GetTenantMonitoring struct { + Context *middleware.Context + Handler GetTenantMonitoringHandler +} + +func (o *GetTenantMonitoring) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetTenantMonitoringParams() + 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/operatorapi/operations/operator_api/get_tenant_monitoring_parameters.go b/operatorapi/operations/operator_api/get_tenant_monitoring_parameters.go new file mode 100644 index 000000000..8d92c94b2 --- /dev/null +++ b/operatorapi/operations/operator_api/get_tenant_monitoring_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 operator_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" +) + +// NewGetTenantMonitoringParams creates a new GetTenantMonitoringParams object +// +// There are no default values defined in the spec. +func NewGetTenantMonitoringParams() GetTenantMonitoringParams { + + return GetTenantMonitoringParams{} +} + +// GetTenantMonitoringParams contains all the bound params for the get tenant monitoring operation +// typically these are obtained from a http.Request +// +// swagger:parameters GetTenantMonitoring +type GetTenantMonitoringParams 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 NewGetTenantMonitoringParams() beforehand. +func (o *GetTenantMonitoringParams) 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 *GetTenantMonitoringParams) 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 *GetTenantMonitoringParams) 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/operatorapi/operations/operator_api/get_tenant_monitoring_responses.go b/operatorapi/operations/operator_api/get_tenant_monitoring_responses.go new file mode 100644 index 000000000..3835f5174 --- /dev/null +++ b/operatorapi/operations/operator_api/get_tenant_monitoring_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 operator_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" +) + +// GetTenantMonitoringOKCode is the HTTP code returned for type GetTenantMonitoringOK +const GetTenantMonitoringOKCode int = 200 + +/*GetTenantMonitoringOK A successful response. + +swagger:response getTenantMonitoringOK +*/ +type GetTenantMonitoringOK struct { + + /* + In: Body + */ + Payload *models.TenantMonitoringInfo `json:"body,omitempty"` +} + +// NewGetTenantMonitoringOK creates GetTenantMonitoringOK with default headers values +func NewGetTenantMonitoringOK() *GetTenantMonitoringOK { + + return &GetTenantMonitoringOK{} +} + +// WithPayload adds the payload to the get tenant monitoring o k response +func (o *GetTenantMonitoringOK) WithPayload(payload *models.TenantMonitoringInfo) *GetTenantMonitoringOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get tenant monitoring o k response +func (o *GetTenantMonitoringOK) SetPayload(payload *models.TenantMonitoringInfo) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTenantMonitoringOK) 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 + } + } +} + +/*GetTenantMonitoringDefault Generic error response. + +swagger:response getTenantMonitoringDefault +*/ +type GetTenantMonitoringDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetTenantMonitoringDefault creates GetTenantMonitoringDefault with default headers values +func NewGetTenantMonitoringDefault(code int) *GetTenantMonitoringDefault { + if code <= 0 { + code = 500 + } + + return &GetTenantMonitoringDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get tenant monitoring default response +func (o *GetTenantMonitoringDefault) WithStatusCode(code int) *GetTenantMonitoringDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get tenant monitoring default response +func (o *GetTenantMonitoringDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get tenant monitoring default response +func (o *GetTenantMonitoringDefault) WithPayload(payload *models.Error) *GetTenantMonitoringDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get tenant monitoring default response +func (o *GetTenantMonitoringDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetTenantMonitoringDefault) 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/operatorapi/operations/operator_api/get_tenant_monitoring_urlbuilder.go b/operatorapi/operations/operator_api/get_tenant_monitoring_urlbuilder.go new file mode 100644 index 000000000..934a5c47d --- /dev/null +++ b/operatorapi/operations/operator_api/get_tenant_monitoring_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 operator_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" +) + +// GetTenantMonitoringURL generates an URL for the get tenant monitoring operation +type GetTenantMonitoringURL 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 *GetTenantMonitoringURL) WithBasePath(bp string) *GetTenantMonitoringURL { + 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 *GetTenantMonitoringURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetTenantMonitoringURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/namespaces/{namespace}/tenants/{tenant}/monitoring" + + namespace := o.Namespace + if namespace != "" { + _path = strings.Replace(_path, "{namespace}", namespace, -1) + } else { + return nil, errors.New("namespace is required on GetTenantMonitoringURL") + } + + tenant := o.Tenant + if tenant != "" { + _path = strings.Replace(_path, "{tenant}", tenant, -1) + } else { + return nil, errors.New("tenant is required on GetTenantMonitoringURL") + } + + _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 *GetTenantMonitoringURL) 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 *GetTenantMonitoringURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetTenantMonitoringURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetTenantMonitoringURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetTenantMonitoringURL") + } + + 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 *GetTenantMonitoringURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/operatorapi/operations/operator_api/set_tenant_monitoring.go b/operatorapi/operations/operator_api/set_tenant_monitoring.go new file mode 100644 index 000000000..1d5ba5951 --- /dev/null +++ b/operatorapi/operations/operator_api/set_tenant_monitoring.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 operator_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" +) + +// SetTenantMonitoringHandlerFunc turns a function with the right signature into a set tenant monitoring handler +type SetTenantMonitoringHandlerFunc func(SetTenantMonitoringParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn SetTenantMonitoringHandlerFunc) Handle(params SetTenantMonitoringParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// SetTenantMonitoringHandler interface for that can handle valid set tenant monitoring params +type SetTenantMonitoringHandler interface { + Handle(SetTenantMonitoringParams, *models.Principal) middleware.Responder +} + +// NewSetTenantMonitoring creates a new http.Handler for the set tenant monitoring operation +func NewSetTenantMonitoring(ctx *middleware.Context, handler SetTenantMonitoringHandler) *SetTenantMonitoring { + return &SetTenantMonitoring{Context: ctx, Handler: handler} +} + +/* SetTenantMonitoring swagger:route PUT /namespaces/{namespace}/tenants/{tenant}/monitoring OperatorAPI setTenantMonitoring + +Set Prometheus monitoring fields for tenant + +*/ +type SetTenantMonitoring struct { + Context *middleware.Context + Handler SetTenantMonitoringHandler +} + +func (o *SetTenantMonitoring) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewSetTenantMonitoringParams() + 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/operatorapi/operations/operator_api/set_tenant_monitoring_parameters.go b/operatorapi/operations/operator_api/set_tenant_monitoring_parameters.go new file mode 100644 index 000000000..21216fac3 --- /dev/null +++ b/operatorapi/operations/operator_api/set_tenant_monitoring_parameters.go @@ -0,0 +1,151 @@ +// 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 operator_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "io" + "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/validate" + + "github.com/minio/console/models" +) + +// NewSetTenantMonitoringParams creates a new SetTenantMonitoringParams object +// +// There are no default values defined in the spec. +func NewSetTenantMonitoringParams() SetTenantMonitoringParams { + + return SetTenantMonitoringParams{} +} + +// SetTenantMonitoringParams contains all the bound params for the set tenant monitoring operation +// typically these are obtained from a http.Request +// +// swagger:parameters SetTenantMonitoring +type SetTenantMonitoringParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Data *models.TenantMonitoringInfo + /* + 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 NewSetTenantMonitoringParams() beforehand. +func (o *SetTenantMonitoringParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.TenantMonitoringInfo + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("data", "body", "")) + } else { + res = append(res, errors.NewParseError("data", "body", "", err)) + } + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(context.Background()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Data = &body + } + } + } else { + res = append(res, errors.Required("data", "body", "")) + } + + 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 *SetTenantMonitoringParams) 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 *SetTenantMonitoringParams) 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/operatorapi/operations/operator_api/set_tenant_monitoring_responses.go b/operatorapi/operations/operator_api/set_tenant_monitoring_responses.go new file mode 100644 index 000000000..830c5c610 --- /dev/null +++ b/operatorapi/operations/operator_api/set_tenant_monitoring_responses.go @@ -0,0 +1,113 @@ +// 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 operator_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" +) + +// SetTenantMonitoringCreatedCode is the HTTP code returned for type SetTenantMonitoringCreated +const SetTenantMonitoringCreatedCode int = 201 + +/*SetTenantMonitoringCreated A successful response. + +swagger:response setTenantMonitoringCreated +*/ +type SetTenantMonitoringCreated struct { +} + +// NewSetTenantMonitoringCreated creates SetTenantMonitoringCreated with default headers values +func NewSetTenantMonitoringCreated() *SetTenantMonitoringCreated { + + return &SetTenantMonitoringCreated{} +} + +// WriteResponse to the client +func (o *SetTenantMonitoringCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(201) +} + +/*SetTenantMonitoringDefault Generic error response. + +swagger:response setTenantMonitoringDefault +*/ +type SetTenantMonitoringDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewSetTenantMonitoringDefault creates SetTenantMonitoringDefault with default headers values +func NewSetTenantMonitoringDefault(code int) *SetTenantMonitoringDefault { + if code <= 0 { + code = 500 + } + + return &SetTenantMonitoringDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the set tenant monitoring default response +func (o *SetTenantMonitoringDefault) WithStatusCode(code int) *SetTenantMonitoringDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the set tenant monitoring default response +func (o *SetTenantMonitoringDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the set tenant monitoring default response +func (o *SetTenantMonitoringDefault) WithPayload(payload *models.Error) *SetTenantMonitoringDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the set tenant monitoring default response +func (o *SetTenantMonitoringDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *SetTenantMonitoringDefault) 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/operatorapi/operations/operator_api/set_tenant_monitoring_urlbuilder.go b/operatorapi/operations/operator_api/set_tenant_monitoring_urlbuilder.go new file mode 100644 index 000000000..014c09c5e --- /dev/null +++ b/operatorapi/operations/operator_api/set_tenant_monitoring_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 operator_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" +) + +// SetTenantMonitoringURL generates an URL for the set tenant monitoring operation +type SetTenantMonitoringURL 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 *SetTenantMonitoringURL) WithBasePath(bp string) *SetTenantMonitoringURL { + 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 *SetTenantMonitoringURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *SetTenantMonitoringURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/namespaces/{namespace}/tenants/{tenant}/monitoring" + + namespace := o.Namespace + if namespace != "" { + _path = strings.Replace(_path, "{namespace}", namespace, -1) + } else { + return nil, errors.New("namespace is required on SetTenantMonitoringURL") + } + + tenant := o.Tenant + if tenant != "" { + _path = strings.Replace(_path, "{tenant}", tenant, -1) + } else { + return nil, errors.New("tenant is required on SetTenantMonitoringURL") + } + + _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 *SetTenantMonitoringURL) 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 *SetTenantMonitoringURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *SetTenantMonitoringURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on SetTenantMonitoringURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on SetTenantMonitoringURL") + } + + 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 *SetTenantMonitoringURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/operatorapi/operator_tenants.go b/operatorapi/operator_tenants.go index 9e9bc139a..ee0a5b7b0 100644 --- a/operatorapi/operator_tenants.go +++ b/operatorapi/operator_tenants.go @@ -201,6 +201,23 @@ func registerTenantHandlers(api *operations.OperatorAPI) { return operator_api.NewGetPodEventsOK().WithPayload(payload) }) + //Get tenant monitoring info + api.OperatorAPIGetTenantMonitoringHandler = operator_api.GetTenantMonitoringHandlerFunc(func(params operator_api.GetTenantMonitoringParams, session *models.Principal) middleware.Responder { + payload, err := getTenantMonitoringResponse(session, params) + if err != nil { + return operator_api.NewGetTenantMonitoringDefault(int(err.Code)).WithPayload(err) + } + return operator_api.NewGetTenantMonitoringOK().WithPayload(payload) + }) + //Set configuration fields for Prometheus monitoring on a tenant + api.OperatorAPISetTenantMonitoringHandler = operator_api.SetTenantMonitoringHandlerFunc(func(params operator_api.SetTenantMonitoringParams, session *models.Principal) middleware.Responder { + _, err := setTenantMonitoringResponse(session, params) + if err != nil { + return operator_api.NewSetTenantMonitoringDefault(int(err.Code)).WithPayload(err) + } + return operator_api.NewSetTenantMonitoringCreated() + }) + // Update Tenant Pools api.OperatorAPITenantUpdatePoolsHandler = operator_api.TenantUpdatePoolsHandlerFunc(func(params operator_api.TenantUpdatePoolsParams, session *models.Principal) middleware.Responder { resp, err := getTenantUpdatePoolResponse(session, params) @@ -1773,6 +1790,152 @@ func getPodEventsResponse(session *models.Principal, params operator_api.GetPodE return retval, nil } +//get values for prometheus metrics +func getTenantMonitoringResponse(session *models.Principal, params operator_api.GetTenantMonitoringParams) (*models.TenantMonitoringInfo, *models.Error) { + ctx := context.Background() + + opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken) + if err != nil { + return nil, prepareError(err) + } + + opClient := &operatorClient{ + client: opClientClientSet, + } + + minInst, err := opClient.TenantGet(ctx, params.Namespace, params.Tenant, metav1.GetOptions{}) + if err != nil { + return nil, prepareError(err) + } + + var storageClassName string + monitoringInfo := &models.TenantMonitoringInfo{} + + if minInst.Spec.Prometheus == nil { + monitoringInfo := &models.TenantMonitoringInfo{ + PrometheusEnabled: (false), + } + return monitoringInfo, nil + } + + if minInst.Spec.Prometheus.StorageClassName != nil { + storageClassName = *minInst.Spec.Prometheus.StorageClassName + } + + mLabels := []*models.Label{} + for k, v := range minInst.Spec.Prometheus.Labels { + mLabels = append(mLabels, &models.Label{Key: k, Value: v}) + } + mAnnotations := []*models.Annotation{} + for k, v := range minInst.Spec.Prometheus.Annotations { + mAnnotations = append(mAnnotations, &models.Annotation{Key: k, Value: v}) + } + mNodeSelector := []*models.NodeSelector{} + for k, v := range minInst.Spec.Prometheus.NodeSelector { + mNodeSelector = append(mNodeSelector, &models.NodeSelector{Key: k, Value: v}) + } + + if minInst.Spec.Prometheus != nil { + monitoringInfo = &models.TenantMonitoringInfo{ + PrometheusEnabled: (true), + Annotations: mAnnotations, + DiskCapacityGB: strconv.Itoa(*minInst.Spec.Prometheus.DiskCapacityDB), + Image: minInst.Spec.Prometheus.Image, + InitImage: minInst.Spec.Prometheus.InitImage, + Labels: mLabels, + NodeSelector: mNodeSelector, + ServiceAccountName: minInst.Spec.Prometheus.ServiceAccountName, + SidecarImage: minInst.Spec.Prometheus.SideCarImage, + StorageClassName: storageClassName, + } + return monitoringInfo, nil + } + return monitoringInfo, nil +} + +//sets tenant Prometheus monitoring cofiguration fields to values provided +func setTenantMonitoringResponse(session *models.Principal, params operator_api.SetTenantMonitoringParams) (bool, *models.Error) { + // 30 seconds timeout + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken) + if err != nil { + return false, prepareError(err, errorUnableToGetTenantUsage) + } + + opClient := &operatorClient{ + client: opClientClientSet, + } + + minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant) + if err != nil { + return false, prepareError(err, errorUnableToGetTenantUsage) + } + + if params.Data.Toggle { + if params.Data.PrometheusEnabled { + minTenant.Spec.Prometheus = nil + } else { + promDiskSpaceGB := 5 + promImage := "" + minTenant.Spec.Prometheus = &miniov2.PrometheusConfig{ + DiskCapacityDB: swag.Int(promDiskSpaceGB), + Image: promImage, + } + } + _, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{}) + if err != nil { + return false, prepareError(err) + } + return true, nil + } + + var labels = make(map[string]string) + for i := 0; i < len(params.Data.Labels); i++ { + if params.Data.Labels[i] != nil { + labels[params.Data.Labels[i].Key] = params.Data.Labels[i].Value + } + } + var annotations = make(map[string]string) + for i := 0; i < len(params.Data.Annotations); i++ { + if params.Data.Annotations[i] != nil { + annotations[params.Data.Annotations[i].Key] = params.Data.Annotations[i].Value + } + } + var nodeSelector = make(map[string]string) + for i := 0; i < len(params.Data.NodeSelector); i++ { + if params.Data.NodeSelector[i] != nil { + nodeSelector[params.Data.NodeSelector[i].Key] = params.Data.NodeSelector[i].Value + } + } + + var storageClassName string + if ¶ms.Data.StorageClassName != nil { + storageClassName = params.Data.StorageClassName + } + + minTenant.Spec.Prometheus.Labels = labels + minTenant.Spec.Prometheus.Annotations = annotations + minTenant.Spec.Prometheus.NodeSelector = nodeSelector + minTenant.Spec.Prometheus.Image = params.Data.Image + minTenant.Spec.Prometheus.SideCarImage = params.Data.SidecarImage + minTenant.Spec.Prometheus.InitImage = params.Data.InitImage + minTenant.Spec.Prometheus.StorageClassName = &storageClassName + diskCapacityGB, err := strconv.Atoi(params.Data.DiskCapacityGB) + if err == nil { + *minTenant.Spec.Prometheus.DiskCapacityDB = diskCapacityGB + } + minTenant.Spec.Prometheus.ServiceAccountName = params.Data.ServiceAccountName + _, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{}) + if err != nil { + return false, prepareError(err) + } + + return true, nil + +} + // parseTenantPoolRequest parse pool request and returns the equivalent // miniov2.Pool object func parseTenantPoolRequest(poolParams *models.Pool) (*miniov2.Pool, error) { diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts index 7397c4ed1..a363abe6e 100644 --- a/portal-ui/src/common/SecureComponent/permissions.ts +++ b/portal-ui/src/common/SecureComponent/permissions.ts @@ -191,6 +191,8 @@ export const IAM_PAGES = { "/namespaces/:tenantNamespace/tenants/:tenantName/license", NAMESPACE_TENANT_SECURITY: "/namespaces/:tenantNamespace/tenants/:tenantName/security", + NAMESPACE_TENANT_MONITORING: + "/namespaces/:tenantNamespace/tenants/:tenantName/monitoring", LICENSE: "/license", DOCUMENTATION: "/documentation", }; diff --git a/portal-ui/src/screens/Console/Console.tsx b/portal-ui/src/screens/Console/Console.tsx index 1d62954be..f71b6f69f 100644 --- a/portal-ui/src/screens/Console/Console.tsx +++ b/portal-ui/src/screens/Console/Console.tsx @@ -434,6 +434,19 @@ const Console = ({ path: IAM_PAGES.NAMESPACE_TENANT_SECURITY, forceDisplay: true, }, + { + component: TenantDetails, + path: IAM_PAGES.NAMESPACE_TENANT_MONITORING, + forceDisplay: true, + }, + { + component: TenantDetails, + path: "/namespaces/:tenantNamespace/tenants/:tenantName/monitoring", + }, + { + component: TenantDetails, + path: "/namespaces/:tenantNamespace/tenants/:tenantName/monitoring", + }, { component: License, path: IAM_PAGES.LICENSE, diff --git a/portal-ui/src/screens/Console/Tenants/ListTenants/types.ts b/portal-ui/src/screens/Console/Tenants/ListTenants/types.ts index b822cf79f..39ac7fb06 100644 --- a/portal-ui/src/screens/Console/Tenants/ListTenants/types.ts +++ b/portal-ui/src/screens/Console/Tenants/ListTenants/types.ts @@ -129,3 +129,39 @@ export interface IResourcesSize { cpuRequest: number; cpuLimit: number; } + +export interface ITenantMonitoringStruct { + image: string; + sidecarImage: string; + initImage: string; + storageClassName: string; + labels: IKeyValue[]; + annotations: IKeyValue[]; + nodeSelector: IKeyValue[]; + diskCapacityGB: string; + serviceAccountName: string; + prometheusEnabled: boolean; +} + +export interface IKeyValue { + key: string; + value: string; +} + +export interface ITenantMonitoringStruct { + image: string; + sidecarImage: string; + initImage: string; + storageClassName: string; + labels: IKeyValue[]; + annotations: IKeyValue[]; + nodeSelector: IKeyValue[]; + diskCapacityGB: string; + serviceAccountName: string; + prometheusEnabled: boolean; +} + +export interface IKeyValue { + key: string; + value: string; +} diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/ConfirmationDialog.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/ConfirmationDialog.tsx new file mode 100644 index 000000000..73ad53ea4 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/ConfirmationDialog.tsx @@ -0,0 +1,83 @@ +import React, { useState } from "react"; +import { Theme } from "@mui/material/styles"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { + containerForHeader, + tenantDetailsStyles, +} from "../../Common/FormComponents/common/styleLibrary"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + LinearProgress, +} from "@mui/material"; + +interface IConfirmationDialog { + classes: any; + open: boolean; + cancelLabel: string; + okLabel: string; + onClose: any; + cancelOnClick: any; + okOnClick: any; + title: string; + description: string; +} + +const styles = (theme: Theme) => + createStyles({ + ...tenantDetailsStyles, + ...containerForHeader(theme.spacing(4)), + }); + +const ConfirmationDialog = ({ + classes, + open, + cancelLabel, + okLabel, + onClose, + cancelOnClick, + okOnClick, + title, + description, +}: IConfirmationDialog) => { + const [isSending, setIsSending] = useState(false); + const onClick = () => { + setIsSending(true); + if (okOnClick !== null) { + okOnClick(); + } + setIsSending(false); + }; + if (!open) return null; + return ( + + {title} + + {isSending && } + + {description} + + + + + + + + ); +}; + +export default withStyles(styles)(ConfirmationDialog); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/EditTenantMonitoringModal.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/EditTenantMonitoringModal.tsx new file mode 100644 index 000000000..5f75b1284 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/EditTenantMonitoringModal.tsx @@ -0,0 +1,327 @@ +import React, { useEffect, useState } from "react"; +import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { Theme } from "@mui/material/styles"; +import { modalBasic } from "../../Common/FormComponents/common/styleLibrary"; +import { Button } from "@mui/material"; +import api from "../../../../common/api"; +import { ITenant } from "../ListTenants/types"; +import { ErrorResponseHandler } from "../../../../common/types"; +import { IKeyValue } from "../ListTenants/types"; +import KeyPairEdit from "./KeyPairEdit"; +import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import { + commonFormValidation, + IValidation, +} from "../../../../utils/validationFunctions"; +import { setModalErrorSnackMessage } from "../../../../actions"; + +interface IEditTenantMonitoringProps { + tenant: ITenant; + classes: any; + open: boolean; + onClose: (shouldReload: boolean) => void; + image: string; + sidecarImage: string; + initImage: string; + labels: IKeyValue[]; + annotations: IKeyValue[]; + nodeSelector: IKeyValue[]; + diskCapacityGB: number; + serviceAccountName: string; + tenantName: string; + tenantNamespace: string; + storageClassName: string; +} + +const styles = (theme: Theme) => + createStyles({ + buttonContainer: { + textAlign: "right", + }, + ...modalBasic, + }); + +const EditTenantMonitoringModal = ({ + tenant, + classes, + open, + onClose, + image, + sidecarImage, + initImage, + labels, + annotations, + nodeSelector, + diskCapacityGB, + serviceAccountName, + storageClassName, + tenantName, + tenantNamespace, +}: IEditTenantMonitoringProps) => { + const [validationErrors, setValidationErrors] = useState({}); + const [newLabels, setNewLabels] = useState( + labels.length > 0 ? [...labels] : [{ key: "", value: "" }] + ); + const [newAnnotations, setNewAnnotations] = useState( + annotations.length > 0 ? [...annotations] : [{ key: "", value: "" }] + ); + const [newNodeSelector, setNewNodeSelector] = useState( + nodeSelector.length > 0 ? [...nodeSelector] : [{ key: "", value: "" }] + ); + const [newImage, setNewImage] = useState(image); + const [newSidecarImage, setNewSidecarImage] = useState(sidecarImage); + const [newInitImage, setNewInitImage] = useState(initImage); + const [newDiskCapacityGB, setNewDiskCapacityGB] = useState( + diskCapacityGB.toString() + ); + const [newServiceAccountName, setNewServiceAccountName] = + useState(serviceAccountName); + const [newStorageClassName, setNewStorageClassName] = + useState(storageClassName); + + const [labelsError, setLabelsError] = useState({}); + const [annotationsError, setAnnotationsError] = useState({}); + const [nodeSelectorError, setNodeSelectorError] = useState({}); + + const trim = (x: IKeyValue[]): IKeyValue[] => { + let retval: IKeyValue[] = []; + for (let i = 0; i < x.length; i++) { + if (x[i].key !== "") { + retval.push(x[i]); + } + } + return retval; + }; + + useEffect(() => { + let tenantMonitoringValidation: IValidation[] = []; + + tenantMonitoringValidation.push({ + fieldKey: `image`, + required: false, + value: newImage, + pattern: + /^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/, + customPatternMessage: "Invalid image", + }); + tenantMonitoringValidation.push({ + fieldKey: `sidecarImage`, + required: false, + value: newSidecarImage, + pattern: + /^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/, + customPatternMessage: "Invalid image", + }); + tenantMonitoringValidation.push({ + fieldKey: `initImage`, + required: false, + value: newInitImage, + pattern: + /^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/, + customPatternMessage: "Invalid image", + }); + tenantMonitoringValidation.push({ + fieldKey: `diskCapacityGB`, + required: true, + value: newDiskCapacityGB as any as string, + pattern: /^[0-9]?(10)?$/, + customPatternMessage: "Must be an integer between 0 and 10", + }); + tenantMonitoringValidation.push({ + fieldKey: `serviceAccountName`, + required: false, + value: newServiceAccountName, + pattern: /^[a-zA-Z0-9-.]{1,253}$/, + customPatternMessage: "Invalid service account name", + }); + tenantMonitoringValidation.push({ + fieldKey: `storageClassName`, + required: false, + value: newStorageClassName, + pattern: /^[a-zA-Z0-9-.]{1,253}$/, + customPatternMessage: "Invalid storage class name", + }); + + const commonVal = commonFormValidation(tenantMonitoringValidation); + setValidationErrors(commonVal); + }, [ + newImage, + newSidecarImage, + newInitImage, + newDiskCapacityGB, + newServiceAccountName, + newStorageClassName, + setValidationErrors, + ]); + + const checkValid = (): boolean => { + if ( + Object.keys(validationErrors).length !== 0 || + Object.keys(labelsError).length !== 0 || + Object.keys(annotationsError).length !== 0 || + Object.keys(nodeSelectorError).length !== 0 + ) { + let err: ErrorResponseHandler = { + errorMessage: "Invalid entry", + detailedError: "", + }; + setModalErrorSnackMessage(err); + return false; + } else { + return true; + } + }; + + const submitMonitoringInfo = (event: React.FormEvent) => { + event.preventDefault(); + + api + .invoke( + "PUT", + `/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/monitoring`, + { + labels: trim(newLabels), + annotations: trim(newAnnotations), + nodeSelector: trim(newNodeSelector), + image: newImage, + sidecarImage: newSidecarImage, + initImage: newInitImage, + diskCapacityGB: newDiskCapacityGB, + serviceAccountName: newServiceAccountName, + storageClassName: newStorageClassName, + } + ) + .then(() => { + onClose(true); + }) + .catch((err: ErrorResponseHandler) => {}); + }; + + return ( + onClose(true)} + modalOpen={open} + title="Edit Prometheus Monitoring Configuration" + > +
+

Image

+ ) => { + setNewImage(event.target.value); + }} + key={`image`} + error={validationErrors[`image`] || ""} + /> +

Sidecar Image

+ ) => { + setNewSidecarImage(event.target.value); + }} + key={`sidecarImage`} + error={validationErrors[`sidecarImage`] || ""} + /> +

Init Image

+ ) => { + setNewInitImage(event.target.value); + }} + key={`initImage`} + error={validationErrors[`initImage`] || ""} + /> +

Disk Capacity (GB)

+ ) => { + setNewDiskCapacityGB(event.target.value); + }} + key={`diskCapacityGB`} + error={validationErrors[`diskCapacityGB`] || ""} + /> +

Service Account Name

+ ) => { + setNewServiceAccountName(event.target.value); + }} + key={`serviceAccountName`} + error={validationErrors[`serviceAccountName`] || ""} + /> +

Storage Class Name

+ ) => { + setNewStorageClassName(event.target.value); + }} + key={`storageClassName`} + error={validationErrors[`storageClassName`] || ""} + /> +

Labels

+ +

Annotations

+ +

Node Selector

+ + +
+ +
+ +
+ ); +}; + +export default withStyles(styles)(EditTenantMonitoringModal); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairEdit.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairEdit.tsx new file mode 100644 index 000000000..3c1f0a9f8 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairEdit.tsx @@ -0,0 +1,178 @@ +import React, { Fragment, useEffect } from "react"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { Theme } from "@mui/material/styles"; +import { modalBasic } from "../../Common/FormComponents/common/styleLibrary"; +import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import { IconButton, Tooltip } from "@mui/material"; +import AddIcon from "@mui/icons-material/Add"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { IKeyValue } from "../ListTenants/types"; +import { + commonFormValidation, + IValidation, +} from "../../../../utils/validationFunctions"; +import { clearValidationError } from "../utils"; + +interface IKeyPairEditProps { + classes: any; + paramName: string; + error: any; + setError: (e: any) => void; + newValues: IKeyValue[]; + setNewValues: (vals: IKeyValue[]) => void; +} + +const styles = (theme: Theme) => + createStyles({ + buttonContainer: { + textAlign: "right", + }, + multiContainer: { + display: "flex", + alignItems: "center" as const, + justifyContent: "flex-start" as const, + }, + sizeFactorContainer: { + marginLeft: 8, + }, + bottomContainer: { + display: "flex", + flexGrow: 1, + alignItems: "center", + "& div": { + flexGrow: 1, + width: "100%", + }, + }, + factorElements: { + display: "flex", + justifyContent: "flex-start", + }, + sizeNumber: { + fontSize: 35, + fontWeight: 700, + textAlign: "center", + }, + sizeDescription: { + fontSize: 14, + color: "#777", + textAlign: "center", + }, + shortened: { + gridTemplateColumns: "auto auto 20px 20px", + display: "grid", + gridGap: 20, + }, + ...modalBasic, + }); + +const KeyPairEdit = ({ + classes, + paramName, + newValues, + setNewValues, + error, + setError, +}: IKeyPairEditProps) => { + const cleanValidation = (fieldName: string) => { + setError(clearValidationError(error, fieldName)); + }; + + useEffect(() => { + let keyPairValidation: IValidation[] = []; + + for (var i = 0; i < newValues.length; i++) { + keyPairValidation.push({ + fieldKey: `key-${i.toString()}`, + required: false, + value: newValues[i].key, + customPatternMessage: "Invalid key", + }); + keyPairValidation.push({ + fieldKey: `val-${i.toString()}`, + required: false, + value: newValues[i].value, + customPatternMessage: "Invalid value", + }); + } + + const commonVal = commonFormValidation(keyPairValidation); + setError(commonVal); + }, [newValues, setError]); + + let keyValueInputs = newValues.map((_, index) => { + return ( + +
+ { + let tempLabels = [...newValues]; + tempLabels[index].key = e.target.value; + setNewValues(tempLabels); + cleanValidation(`key-${index.toString()}`); + }} + index={index} + key={`csv-key-${index.toString()}`} + error={error[`key-${index.toString()}`] || ""} + /> + { + let tempLabels = [...newValues]; + tempLabels[index].value = e.target.value; + setNewValues(tempLabels); + cleanValidation(`val-${index.toString()}`); + }} + index={index} + key={`csv-val-${index.toString()}`} + error={error[`val-${index.toString()}`] || ""} + /> + + { + let tempLabels = [...newValues]; + tempLabels.push({ key: "", value: "" }); + setNewValues(tempLabels); + }} + > + + + + + { + if (newValues.length === 1) { + setNewValues([{ key: "", value: "" }]); + } + if (newValues.length > 1) { + let tempLabels = [...newValues]; + tempLabels.splice(index, 1); + setNewValues(tempLabels); + } + }} + > + + + +
+
+ ); + }); + + return {keyValueInputs}; +}; + +export default withStyles(styles)(KeyPairEdit); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx new file mode 100644 index 000000000..e0c7c656b --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx @@ -0,0 +1,101 @@ +// 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 . + +import React, { Fragment } from "react"; +import { connect } from "react-redux"; +import get from "lodash/get"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import { Theme } from "@mui/material/styles"; +import { + containerForHeader, + tenantDetailsStyles, +} from "../../Common/FormComponents/common/styleLibrary"; +import { AppState } from "../../../../store"; +import TableWrapper from "../../Common/TableWrapper/TableWrapper"; +import { IKeyValue } from "../ListTenants/types"; + +interface IKeyPairView { + classes: any; + records: IKeyValue[]; + recordName: string; +} + +const styles = (theme: Theme) => + createStyles({ + ...tenantDetailsStyles, + + centerAlign: { + textAlign: "center", + }, + listHeight: { + height: "50", + }, + ...containerForHeader(theme.spacing(4)), + }); + +const KeyPairView = ({ classes, records, recordName }: IKeyPairView) => { + return ( + + + + ); +}; + +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 + ), + minioTLS: get(state.tenants.tenantDetails.tenantInfo, "minioTLS", false), + consoleTLS: get(state.tenants.tenantDetails.tenantInfo, "consoleTLS", false), + consoleEnabled: get( + state.tenants.tenantDetails.tenantInfo, + "consoleEnabled", + false + ), + adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false), + oidcEnabled: get( + state.tenants.tenantDetails.tenantInfo, + "idpOidcEnabled", + false + ), +}); + +const connector = connect(mapState, null); + +export default withStyles(styles)(connector(KeyPairView)); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx index 64d6b99f1..13ab97df8 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx @@ -67,6 +67,9 @@ const DeleteTenant = withSuspense( React.lazy(() => import("../ListTenants/DeleteTenant")) ); const PodDetails = withSuspense(React.lazy(() => import("./pods/PodDetails"))); +const TenantMonitoring = withSuspense( + React.lazy(() => import("./TenantMonitoring")) +); interface ITenantDetailsProps { classes: any; @@ -442,6 +445,10 @@ const TenantDetails = ({ path="/namespaces/:tenantNamespace/tenants/:tenantName/license" component={TenantLicense} /> + ( @@ -512,6 +519,15 @@ const TenantDetails = ({ to: getRoutePath("license"), }, }} + + {{ + tabConfig: { + label: "Monitoring", + value: "monitoring", + component: Link, + to: getRoutePath("monitoring"), + }, + }} diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantMonitoring.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantMonitoring.tsx new file mode 100644 index 000000000..9115ff604 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantMonitoring.tsx @@ -0,0 +1,324 @@ +// 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 . + +import React, { Fragment, useEffect, useState } from "react"; +import { connect } from "react-redux"; +import { Theme } from "@mui/material/styles"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import Grid from "@mui/material/Grid"; +import { + actionsTray, + containerForHeader, + searchField, + tenantDetailsStyles, +} from "../../Common/FormComponents/common/styleLibrary"; +import { Button, CircularProgress, DialogContentText } from "@mui/material"; +import Paper from "@mui/material/Paper"; +import { ITenant } from "../ListTenants/types"; +import { setErrorSnackMessage } from "../../../../actions"; +import { ErrorResponseHandler } from "../../../../common/types"; +import EditTenantMonitoringModal from "./EditTenantMonitoringModal"; + +import api from "../../../../common/api"; +import { EditIcon } from "../../../../icons"; +import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; +import { ITenantMonitoringStruct } from "../ListTenants/types"; +import KeyPairView from "./KeyPairView"; +import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog"; + +interface ITenantMonitoring { + classes: any; + match: any; + tenant: ITenant | null; + loadingTenant: boolean; +} + +const styles = (theme: Theme) => + createStyles({ + ...tenantDetailsStyles, + headerLabel: { + fontSize: 22, + fontWeight: 600, + color: "#000", + marginTop: 4, + }, + breadcrumLink: { + textDecoration: "none", + color: "black", + }, + tableWrapper: { + height: "calc(100vh - 267px)", + }, + paperContainer: { + padding: "15px 15px 15px 50px", + }, + ...actionsTray, + ...searchField, + ...containerForHeader(theme.spacing(4)), + }); + +const TenantMonitoring = ({ + classes, + match, + tenant, + loadingTenant, +}: ITenantMonitoring) => { + const [prometheusMonitoringEnabled, setPrometheusMonitoringEnabled] = + useState(false); + const [edit, setEdit] = useState(false); + const [monitoringInfo, setMonitoringInfo] = + useState(); + const [confirmOpen, setConfirmOpen] = useState(false); + const [refreshMonitoringInfo, setRefreshMonitoringInfo] = + useState(true); + + const tenantName = match.params["tenantName"]; + const tenantNamespace = match.params["tenantNamespace"]; + + const onCloseEditAndRefresh = () => { + setEdit(false); + setRefreshMonitoringInfo(true); + }; + + useEffect(() => { + if (refreshMonitoringInfo) { + api + .invoke( + "GET", + `/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/monitoring` + ) + .then((res: ITenantMonitoringStruct) => { + setPrometheusMonitoringEnabled(res.prometheusEnabled); + setMonitoringInfo(res); + setRefreshMonitoringInfo(false); + }) + .catch((err: ErrorResponseHandler) => { + setErrorSnackMessage(err); + setRefreshMonitoringInfo(false); + }); + } + }, [refreshMonitoringInfo]); + + const togglePrometheus = () => { + const configInfo = { + prometheusEnabled: prometheusMonitoringEnabled, + toggle: true, + }; + api + .invoke( + "PUT", + `/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/monitoring`, + configInfo + ) + .then(() => { + setPrometheusMonitoringEnabled(!prometheusMonitoringEnabled); + setRefreshMonitoringInfo(true); + setConfirmOpen(false); + setRefreshMonitoringInfo(true); + }) + .catch((err: ErrorResponseHandler) => { + setErrorSnackMessage(err); + }); + }; + + return ( + + {edit && tenant !== null && prometheusMonitoringEnabled && ( + + )} + {confirmOpen && ( + setConfirmOpen(false)} + onConfirm={togglePrometheus} + confirmationContent={ + + {prometheusMonitoringEnabled + ? "Disabling monitoring will erase any custom values you have used to configure Prometheus monitoring" + : "Prometheus monitoring will be enabled with default values"} + + } + /> + )} + +

Monitoring

+
+ { + setConfirmOpen(true); + }} + description="" + /> + {prometheusMonitoringEnabled && ( + + )} +
+ {prometheusMonitoringEnabled && monitoringInfo !== undefined && ( + + + + + + {loadingTenant ? ( + + + + ) : ( + monitoringInfo !== undefined && ( + + {monitoringInfo.image != null && ( + + + + + )} + {monitoringInfo.sidecarImage != null && ( + + + + + )} + {monitoringInfo.initImage != null && ( + + + + + )} + {monitoringInfo.diskCapacityGB != null && ( + + + + + )} + {monitoringInfo.serviceAccountName != null && ( + + + + + )} + {monitoringInfo.storageClassName != null && ( + + + + + )} + {monitoringInfo.labels != null && + monitoringInfo.labels.length > 0 && ( + +

Labels

+ + + )} + {monitoringInfo.annotations != null && + monitoringInfo.annotations.length > 0 && ( + +

Annotations

+ + + )} + {monitoringInfo.nodeSelector != null && + monitoringInfo.nodeSelector.length > 0 && ( + +

Node Seletor

+ + + )} + + ) + )} + +
+ +
Image:{monitoringInfo.image}
Sidecar Image:{monitoringInfo?.sidecarImage}
Init Image:{monitoringInfo?.initImage}
+ Disk Capacity (GB): + {monitoringInfo?.diskCapacityGB}
+ Service Account Name: + {monitoringInfo?.serviceAccountName}
+ Storage Class Name: + {monitoringInfo?.storageClassName}
+ +
+ +
+ +
+
+
+
+ )} +
+ ); +}; + +const mapDispatchToProps = { + setErrorSnackMessage, +}; + +const connector = connect(null, mapDispatchToProps); + +export default withStyles(styles)(connector(TenantMonitoring)); diff --git a/portal-ui/src/utils/validationFunctions.ts b/portal-ui/src/utils/validationFunctions.ts index ebfd7679a..e64cc98e8 100644 --- a/portal-ui/src/utils/validationFunctions.ts +++ b/portal-ui/src/utils/validationFunctions.ts @@ -28,12 +28,20 @@ export const commonFormValidation = (fieldsValidate: IValidation[]) => { let returnErrors: any = {}; fieldsValidate.forEach((field) => { - if (field.required && field.value.trim() === "") { + if ( + field.required && + typeof field.value !== "undefined" && + field.value.trim() === "" + ) { returnErrors[field.fieldKey] = "Field cannot be empty"; return; } // if it's not required and the value is empty, we are done here - if (!field.required && field.value.trim() === "") { + if ( + !field.required && + typeof field.value !== "undefined" && + field.value.trim() === "" + ) { return; } @@ -45,7 +53,11 @@ export const commonFormValidation = (fieldsValidate: IValidation[]) => { if (field.pattern && field.customPatternMessage) { const rgx = new RegExp(field.pattern, "g"); - if (!field.value.match(rgx)) { + if ( + !field.value.match(rgx) && + typeof field.value !== "undefined" && + field.value.trim() !== "" + ) { returnErrors[field.fieldKey] = field.customPatternMessage; } return; diff --git a/swagger-operator.yml b/swagger-operator.yml index e37724871..775cadef2 100644 --- a/swagger-operator.yml +++ b/swagger-operator.yml @@ -636,6 +636,56 @@ paths: $ref: "#/definitions/error" tags: - OperatorAPI + /namespaces/{namespace}/tenants/{tenant}/monitoring: + get: + summary: Get Prometheus Monitoring config info For The Tenant + operationId: GetTenantMonitoring + 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/tenantMonitoringInfo" + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - OperatorAPI + put: + summary: Set Prometheus monitoring fields for tenant + operationId: SetTenantMonitoring + parameters: + - name: namespace + in: path + required: true + type: string + - name: tenant + in: path + required: true + type: string + - name: data + in: body + required: true + schema: + $ref: "#/definitions/tenantMonitoringInfo" + responses: + 201: + description: A successful response. + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - OperatorAPI /namespaces/{namespace}/tenants/{tenant}/certificates: put: @@ -2337,6 +2387,68 @@ definitions: type: string message: type: string + + tenantMonitoringInfo: + type: object + properties: + prometheusEnabled: + type: boolean + toggle: + type: boolean + image: + type: string + sidecarImage: + type: string + initImage: + type: string + labels: + type: array + items: + $ref: "#/definitions/label" + annotations: + type: array + items: + $ref: "#/definitions/annotation" + diskCapacityGB: + type: string + nodeSelector: + type: array + items: + $ref: "#/definitions/nodeSelector" + serviceAccountName: + type: string + storageClassName: + type: string + + label: + type: object + properties: + key: + type: string + value: + type: string + + annotation: + type: object + properties: + key: + type: string + value: + type: string + + nodeSelector: + type: object + properties: + key: + type: string + value: + type: string + + configureTenantRequest: + type: object + properties: + prometheusEnabled: + type: boolean formatConfiguration: type: object