Compare commits

...

3 Commits

Author SHA1 Message Date
Minio Trusted
c59387c2b4 update v0.3.19 2020-09-04 11:45:13 -07:00
Lenin Alevski
c5a3eff745 Added endpoint for update tenant certificates (minio/console) (#258) 2020-09-03 10:20:58 -07:00
Cesar N
624891ae1f Replace resources api to return the max allocatable memory (#264) 2020-09-02 17:06:02 -07:00
32 changed files with 2982 additions and 1077 deletions

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.3.18
image: minio/console:v0.3.19
imagePullPolicy: "IfNotPresent"
args:
- server

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.3.18
image: minio/console:v0.3.19
imagePullPolicy: "IfNotPresent"
env:
- name: CONSOLE_OPERATOR_MODE

View File

@@ -1,97 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// ClusterResources cluster resources
//
// swagger:model clusterResources
type ClusterResources struct {
// nodes
Nodes []*NodeInfo `json:"nodes"`
}
// Validate validates this cluster resources
func (m *ClusterResources) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateNodes(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ClusterResources) validateNodes(formats strfmt.Registry) error {
if swag.IsZero(m.Nodes) { // not required
return nil
}
for i := 0; i < len(m.Nodes); i++ {
if swag.IsZero(m.Nodes[i]) { // not required
continue
}
if m.Nodes[i] != nil {
if err := m.Nodes[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("nodes" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *ClusterResources) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ClusterResources) UnmarshalBinary(b []byte) error {
var res ClusterResources
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -27,28 +27,22 @@ import (
"github.com/go-openapi/swag"
)
// NodeTaints node taints
// MaxAllocatableMemResponse max allocatable mem response
//
// swagger:model nodeTaints
type NodeTaints struct {
// swagger:model maxAllocatableMemResponse
type MaxAllocatableMemResponse struct {
// no execute
NoExecute []string `json:"no_execute"`
// no schedule
NoSchedule []string `json:"no_schedule"`
// prefer no schedule
PreferNoSchedule []string `json:"prefer_no_schedule"`
// max memory
MaxMemory int64 `json:"max_memory,omitempty"`
}
// Validate validates this node taints
func (m *NodeTaints) Validate(formats strfmt.Registry) error {
// Validate validates this max allocatable mem response
func (m *MaxAllocatableMemResponse) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *NodeTaints) MarshalBinary() ([]byte, error) {
func (m *MaxAllocatableMemResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
@@ -56,8 +50,8 @@ func (m *NodeTaints) MarshalBinary() ([]byte, error) {
}
// UnmarshalBinary interface implementation
func (m *NodeTaints) UnmarshalBinary(b []byte) error {
var res NodeTaints
func (m *MaxAllocatableMemResponse) UnmarshalBinary(b []byte) error {
var res MaxAllocatableMemResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}

View File

@@ -28,29 +28,27 @@ import (
"github.com/go-openapi/swag"
)
// NodeInfo node info
// UpdateCertificatesRequest update certificates request
//
// swagger:model nodeInfo
type NodeInfo struct {
// swagger:model updateCertificatesRequest
type UpdateCertificatesRequest struct {
// Represents the resources of a node that are available for scheduling.
AllocatableResources map[string]int64 `json:"allocatable_resources,omitempty"`
// console
Console *KeyPairConfiguration `json:"console,omitempty"`
// name
Name string `json:"name,omitempty"`
// taints
Taints *NodeTaints `json:"taints,omitempty"`
// Represents the total resources of a node.
TotalResources map[string]int64 `json:"total_resources,omitempty"`
// minio
Minio *KeyPairConfiguration `json:"minio,omitempty"`
}
// Validate validates this node info
func (m *NodeInfo) Validate(formats strfmt.Registry) error {
// Validate validates this update certificates request
func (m *UpdateCertificatesRequest) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateTaints(formats); err != nil {
if err := m.validateConsole(formats); err != nil {
res = append(res, err)
}
if err := m.validateMinio(formats); err != nil {
res = append(res, err)
}
@@ -60,16 +58,34 @@ func (m *NodeInfo) Validate(formats strfmt.Registry) error {
return nil
}
func (m *NodeInfo) validateTaints(formats strfmt.Registry) error {
func (m *UpdateCertificatesRequest) validateConsole(formats strfmt.Registry) error {
if swag.IsZero(m.Taints) { // not required
if swag.IsZero(m.Console) { // not required
return nil
}
if m.Taints != nil {
if err := m.Taints.Validate(formats); err != nil {
if m.Console != nil {
if err := m.Console.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("taints")
return ve.ValidateName("console")
}
return err
}
}
return nil
}
func (m *UpdateCertificatesRequest) validateMinio(formats strfmt.Registry) error {
if swag.IsZero(m.Minio) { // not required
return nil
}
if m.Minio != nil {
if err := m.Minio.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("minio")
}
return err
}
@@ -79,7 +95,7 @@ func (m *NodeInfo) validateTaints(formats strfmt.Registry) error {
}
// MarshalBinary interface implementation
func (m *NodeInfo) MarshalBinary() ([]byte, error) {
func (m *UpdateCertificatesRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
@@ -87,8 +103,8 @@ func (m *NodeInfo) MarshalBinary() ([]byte, error) {
}
// UnmarshalBinary interface implementation
func (m *NodeInfo) UnmarshalBinary(b []byte) error {
var res NodeInfo
func (m *UpdateCertificatesRequest) UnmarshalBinary(b []byte) error {
var res UpdateCertificatesRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}

View File

@@ -18,12 +18,13 @@ package restapi
import (
"context"
"fmt"
"log"
"strings"
"sort"
"github.com/minio/console/cluster"
"errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/swag"
"github.com/minio/console/models"
@@ -35,73 +36,94 @@ import (
)
func registerNodesHandlers(api *operations.ConsoleAPI) {
api.AdminAPIGetClusterResourcesHandler = admin_api.GetClusterResourcesHandlerFunc(func(params admin_api.GetClusterResourcesParams, session *models.Principal) middleware.Responder {
resp, err := getClusterResourcesResponse(session)
api.AdminAPIGetMaxAllocatableMemHandler = admin_api.GetMaxAllocatableMemHandlerFunc(func(params admin_api.GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
resp, err := getMaxAllocatableMemoryResponse(principal, params.NumNodes)
if err != nil {
return admin_api.NewGetClusterResourcesDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
return admin_api.NewGetMaxAllocatableMemDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return admin_api.NewGetClusterResourcesOK().WithPayload(resp)
return admin_api.NewGetMaxAllocatableMemOK().WithPayload(resp)
})
}
// getClusterResources get cluster nodes and collects taints, available and allocatable resources of the node
func getClusterResources(ctx context.Context, clientset v1.CoreV1Interface) (*models.ClusterResources, error) {
// getMaxAllocatableMemory get max allocatable memory given a desired number of nodes
func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
if numNodes == 0 {
return nil, errors.New("error NumNodes must be greated than 0")
}
// get all nodes from cluster
nodes, err := clientset.Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
// construct ClusterResources response
res := &models.ClusterResources{}
availableMemSizes := []int64{}
OUTER:
for _, n := range nodes.Items {
// Get Total Resources
totalResources := make(map[string]int64)
for resource, quantity := range n.Status.Capacity {
totalResources[string(resource)] = quantity.Value()
}
// Get Allocatable Resources
allocatableResources := make(map[string]int64)
for resource, quantity := range n.Status.Allocatable {
allocatableResources[string(resource)] = quantity.Value()
}
// Get Node taints and split them by effect
taints := &models.NodeTaints{}
// Don't consider node if it has a NoSchedule or NoExecute Taint
for _, t := range n.Spec.Taints {
var taint string
// when value is not defined the taint string is created without `=`
if strings.TrimSpace(t.Value) != "" {
taint = fmt.Sprintf("%s=%s:%s", t.Key, t.Value, t.Effect)
} else {
taint = fmt.Sprintf("%s:%s", t.Key, t.Effect)
}
switch t.Effect {
case corev1.TaintEffectNoSchedule:
taints.NoSchedule = append(taints.NoSchedule, taint)
continue OUTER
case corev1.TaintEffectNoExecute:
taints.NoExecute = append(taints.NoExecute, taint)
case corev1.TaintEffectPreferNoSchedule:
taints.PreferNoSchedule = append(taints.PreferNoSchedule, taint)
continue OUTER
default:
continue
}
}
// create node object an add it to the nodes list
nodeInfo := &models.NodeInfo{
Name: n.Name,
Taints: taints,
AllocatableResources: allocatableResources,
TotalResources: totalResources,
if quantity, ok := n.Status.Allocatable[corev1.ResourceMemory]; ok {
availableMemSizes = append(availableMemSizes, quantity.Value())
}
res.Nodes = append(res.Nodes, nodeInfo)
}
maxAllocatableMemory := getMaxClusterMemory(numNodes, availableMemSizes)
res := &models.MaxAllocatableMemResponse{
MaxMemory: maxAllocatableMemory,
}
return res, nil
}
func getClusterResourcesResponse(session *models.Principal) (*models.ClusterResources, error) {
// getMaxClusterMemory returns the maximum memory size that can be used
// across numNodes (number of nodes)
func getMaxClusterMemory(numNodes int32, nodesMemorySizes []int64) int64 {
if int32(len(nodesMemorySizes)) < numNodes || numNodes == 0 {
return 0
}
// sort nodesMemorySizes int64 array
sort.Slice(nodesMemorySizes, func(i, j int) bool { return nodesMemorySizes[i] < nodesMemorySizes[j] })
maxIndex := 0
maxAllocatableMemory := nodesMemorySizes[maxIndex]
for i, size := range nodesMemorySizes {
// maxAllocatableMemory is the minimum value of nodesMemorySizes array
// only within the size of numNodes, if more nodes are available
// then the maxAllocatableMemory is equal to the next minimum value
// on the sorted nodesMemorySizes array.
// e.g. with numNodes = 4;
// maxAllocatableMemory of [2,4,8,8] => 2
// maxAllocatableMemory of [2,4,8,8,16] => 4
if int32(i) < numNodes {
maxAllocatableMemory = min(maxAllocatableMemory, size)
} else {
maxIndex++
maxAllocatableMemory = nodesMemorySizes[maxIndex]
}
}
return maxAllocatableMemory
}
// min returns the smaller of x or y.
func min(x, y int64) int64 {
if x > y {
return y
}
return x
}
func getMaxAllocatableMemoryResponse(session *models.Principal, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
ctx := context.Background()
client, err := cluster.K8sClient(session.SessionToken)
if err != nil {
@@ -109,7 +131,7 @@ func getClusterResourcesResponse(session *models.Principal) (*models.ClusterReso
return nil, err
}
clusterResources, err := getClusterResources(ctx, client.CoreV1())
clusterResources, err := getMaxAllocatableMemory(ctx, client.CoreV1(), numNodes)
if err != nil {
log.Println("error getting cluster's resources:", err)
return nil, err

View File

@@ -18,7 +18,6 @@ package restapi
import (
"context"
"encoding/json"
"reflect"
"testing"
@@ -31,45 +30,68 @@ import (
"k8s.io/client-go/kubernetes/fake"
)
func Test_GetClusterResources(t *testing.T) {
func Test_MaxAllocatableMemory(t *testing.T) {
type args struct {
ctx context.Context
objs []runtime.Object
ctx context.Context
numNodes int32
objs []runtime.Object
}
tests := []struct {
name string
args args
expected *models.ClusterResources
expected *models.MaxAllocatableMemResponse
wantErr bool
}{
{
name: "Get Nodes Taints and Resources",
name: "Get Max Ram No Taints",
args: args{
ctx: context.Background(),
ctx: context.Background(),
numNodes: 2,
objs: []runtime.Object{
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
corev1.Taint{
Key: "node.kubernetes.io/unreachable",
Effect: corev1.TaintEffectNoSchedule,
},
corev1.Taint{
Key: "own.minio.io/taint",
Effect: corev1.TaintEffectNoExecute,
Value: "val",
},
corev1.Taint{
Key: "node.kubernetes.io/unschedulable",
Effect: corev1.TaintEffectPreferNoSchedule,
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("2Ki"),
corev1.ResourceCPU: resource.MustParse("4Ki"),
},
},
},
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node2",
},
Status: corev1.NodeStatus{
Capacity: corev1.ResourceList{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("512"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
},
},
expected: &models.MaxAllocatableMemResponse{
MaxMemory: int64(512),
},
wantErr: false,
},
{
// Description: if there are more nodes than the amount
// of nodes we want to use, but one has taints of NoSchedule
// node should not be considered for max memory
name: "Get Max Ram on nodes with NoSchedule",
args: args{
ctx: context.Background(),
numNodes: 2,
objs: []runtime.Object{
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("2Ki"),
corev1.ResourceCPU: resource.MustParse("4Ki"),
},
@@ -88,71 +110,256 @@ func Test_GetClusterResources(t *testing.T) {
},
},
Status: corev1.NodeStatus{
Capacity: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("1Ki"),
corev1.ResourceCPU: resource.MustParse("2Ki"),
},
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("512"),
corev1.ResourceMemory: resource.MustParse("6Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node3",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("4Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
},
},
expected: &models.ClusterResources{
Nodes: []*models.NodeInfo{
&models.NodeInfo{
Name: "node1",
Taints: &models.NodeTaints{
NoExecute: []string{
"own.minio.io/taint=val:NoExecute",
},
NoSchedule: []string{
"node.kubernetes.io/unreachable:NoSchedule",
},
PreferNoSchedule: []string{
"node.kubernetes.io/unschedulable:PreferNoSchedule",
expected: &models.MaxAllocatableMemResponse{
MaxMemory: int64(2048),
},
wantErr: false,
},
{
// Description: if there are more nodes than the amount
// of nodes we want to use, but one has taints of NoExecute
// node should not be considered for max memory
// if one node has PreferNoSchedule that should be considered.
name: "Get Max Ram on nodes with NoExecute",
args: args{
ctx: context.Background(),
numNodes: 2,
objs: []runtime.Object{
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("2Ki"),
corev1.ResourceCPU: resource.MustParse("4Ki"),
},
},
TotalResources: map[string]int64{
"memory": int64(2048),
"cpu": int64(4096),
},
AllocatableResources: map[string]int64{},
},
&models.NodeInfo{
Name: "node2",
Taints: &models.NodeTaints{
NoSchedule: []string{
"node.kubernetes.io/unreachable:NoSchedule",
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node2",
},
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
corev1.Taint{
Key: "node.kubernetes.io/unreachable",
Effect: corev1.TaintEffectNoExecute,
},
},
},
TotalResources: map[string]int64{
"memory": int64(1024),
"cpu": int64(2048),
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("6Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
AllocatableResources: map[string]int64{
"memory": int64(512),
"cpu": int64(1024),
},
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node3",
},
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
corev1.Taint{
Key: "node.kubernetes.io/unreachable",
Effect: corev1.TaintEffectPreferNoSchedule,
},
},
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("4Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
},
},
expected: &models.MaxAllocatableMemResponse{
MaxMemory: int64(2048),
},
wantErr: false,
},
{
// Description: if there are more nodes than the amount
// of nodes we want to use, max allocatable memory should
// be the minimum ram on the n nodes requested
name: "Get Max Ram, several nodes available",
args: args{
ctx: context.Background(),
numNodes: 2,
objs: []runtime.Object{
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("2Ki"),
corev1.ResourceCPU: resource.MustParse("4Ki"),
},
},
},
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node2",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("6Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node3",
},
Status: corev1.NodeStatus{
Allocatable: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("4Ki"),
corev1.ResourceCPU: resource.MustParse("1Ki"),
},
},
},
},
},
expected: &models.MaxAllocatableMemResponse{
MaxMemory: int64(4096),
},
wantErr: false,
},
{
// Description: if request has nil as request, expect error
name: "Nil nodes should be greater than 0",
args: args{
ctx: context.Background(),
numNodes: 0,
},
wantErr: true,
},
}
for _, tt := range tests {
kubeClient := fake.NewSimpleClientset(tt.args.objs...)
t.Run(tt.name, func(t *testing.T) {
got, err := getClusterResources(tt.args.ctx, kubeClient.CoreV1())
got, err := getMaxAllocatableMemory(tt.args.ctx, kubeClient.CoreV1(), tt.args.numNodes)
if (err != nil) != tt.wantErr {
t.Errorf("deleteTenantAction() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("getMaxAllocatableMemory() error = %v, wantErr %v", err, tt.wantErr)
}
if !reflect.DeepEqual(got, tt.expected) {
ji, _ := json.Marshal(got)
vi, _ := json.Marshal(tt.expected)
t.Errorf("\ngot: %s \nwant: %s", ji, vi)
t.Errorf("\ngot: %d \nwant: %d", got, tt.expected)
}
})
}
}
func Test_MaxMemoryFunc(t *testing.T) {
type args struct {
numNodes int32
nodesMemorySizes []int64
}
tests := []struct {
name string
args args
expected int64
wantErr bool
}{
{
name: "Get Max memory",
args: args{
numNodes: int32(4),
nodesMemorySizes: []int64{4294967296, 8589934592, 8589934592, 17179869184, 17179869184, 17179869184, 25769803776, 25769803776, 68719476736},
},
expected: int64(17179869184),
wantErr: false,
},
{
// Description, if not enough nodes return 0
name: "Get Max memory Not enough nodes",
args: args{
numNodes: int32(4),
nodesMemorySizes: []int64{4294967296, 8589934592, 68719476736},
},
expected: int64(0),
wantErr: false,
},
{
// Description, if not enough nodes return 0
name: "Get Max memory no nodes",
args: args{
numNodes: int32(4),
nodesMemorySizes: []int64{},
},
expected: int64(0),
wantErr: false,
},
{
// Description, if not enough nodes return 0
name: "Get Max memory no nodes, no request",
args: args{
numNodes: int32(0),
nodesMemorySizes: []int64{},
},
expected: int64(0),
wantErr: false,
},
{
// Description, if there are multiple nodes
// and required nodes is only 1, max should be equal to max memory
name: "Get Max memory one node",
args: args{
numNodes: int32(1),
nodesMemorySizes: []int64{4294967296, 8589934592, 68719476736},
},
expected: int64(68719476736),
wantErr: false,
},
{
// Description: if more nodes max memory should be the minimum
// value across pairs of numNodes
name: "Get Max memory two nodes",
args: args{
numNodes: int32(2),
nodesMemorySizes: []int64{8589934592, 68719476736, 4294967296},
},
expected: int64(8589934592),
wantErr: false,
},
{
name: "Get Max Multiple Memory Sizes",
args: args{
numNodes: int32(4),
nodesMemorySizes: []int64{0, 0, 0, 0, 4294967296, 8589934592, 8589934592, 17179869184, 17179869184, 17179869184, 25769803776, 25769803776, 68719476736, 34359738368, 34359738368, 34359738368, 34359738368},
},
expected: int64(34359738368),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getMaxClusterMemory(tt.args.numNodes, tt.args.nodesMemorySizes)
if !reflect.DeepEqual(got, tt.expected) {
t.Errorf("\ngot: %d \nwant: %d", got, tt.expected)
}
})
}

View File

@@ -18,9 +18,7 @@ package restapi
import (
"context"
"crypto"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -32,11 +30,8 @@ import (
"strings"
"time"
"github.com/minio/console/pkg/kes"
"gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/api/core/v1"
@@ -126,6 +121,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
return admin_api.NewUpdateTenantCreated()
})
// Add Tenant Zones
api.AdminAPITenantAddZoneHandler = admin_api.TenantAddZoneHandlerFunc(func(params admin_api.TenantAddZoneParams, session *models.Principal) middleware.Responder {
err := getTenantAddZoneResponse(session, params)
if err != nil {
@@ -135,6 +131,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
return admin_api.NewTenantAddZoneCreated()
})
// Get Tenant Usage
api.AdminAPIGetTenantUsageHandler = admin_api.GetTenantUsageHandlerFunc(func(params admin_api.GetTenantUsageParams, session *models.Principal) middleware.Responder {
payload, err := getTenantUsageResponse(session, params)
if err != nil {
@@ -144,6 +141,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
return admin_api.NewGetTenantUsageOK().WithPayload(payload)
})
// Update Tenant Zones
api.AdminAPITenantUpdateZonesHandler = admin_api.TenantUpdateZonesHandlerFunc(func(params admin_api.TenantUpdateZonesParams, session *models.Principal) middleware.Responder {
resp, err := getTenantUpdateZoneResponse(session, params)
if err != nil {
@@ -152,6 +150,26 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
}
return admin_api.NewTenantUpdateZonesOK().WithPayload(resp)
})
// Update Tenant Certificates
api.AdminAPITenantUpdateCertificateHandler = admin_api.TenantUpdateCertificateHandlerFunc(func(params admin_api.TenantUpdateCertificateParams, session *models.Principal) middleware.Responder {
err := getTenantUpdateCertificatesResponse(session, params)
if err != nil {
log.Println(err)
return admin_api.NewGetTenantUsageDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to update tenant certificates")})
}
return admin_api.NewTenantUpdateCertificateCreated()
})
// Update Tenant Encryption Configuration
api.AdminAPITenantUpdateEncryptionHandler = admin_api.TenantUpdateEncryptionHandlerFunc(func(params admin_api.TenantUpdateEncryptionParams, session *models.Principal) middleware.Responder {
err := getTenantUpdateEncryptionResponse(session, params)
if err != nil {
log.Println(err)
return admin_api.NewGetTenantUsageDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to update encryption configuration")})
}
return admin_api.NewTenantUpdateCertificateCreated()
})
}
// getDeleteTenantResponse gets the output of deleting a minio instance
@@ -180,7 +198,7 @@ func getDeleteTenantResponse(session *models.Principal, params admin_api.DeleteT
// It also adds the option of deleting the tenant's underlying pvcs if deletePvcs set
func deleteTenantAction(
ctx context.Context,
operatorClient OperatorClient,
operatorClient OperatorClientI,
clientset v1.CoreV1Interface,
namespace, tenantName string,
deletePvcs bool) error {
@@ -216,7 +234,7 @@ func getTenantScheme(mi *operator.Tenant) string {
return scheme
}
func getTenantAdminClient(ctx context.Context, client K8sClient, namespace, tenantName, serviceName, scheme string, insecure bool) (*madmin.AdminClient, error) {
func getTenantAdminClient(ctx context.Context, client K8sClientI, namespace, tenantName, serviceName, scheme string, insecure bool) (*madmin.AdminClient, error) {
// get admin credentials from secret
creds, err := client.getSecret(ctx, namespace, fmt.Sprintf("%s-secret", tenantName), metav1.GetOptions{})
if err != nil {
@@ -239,7 +257,7 @@ func getTenantAdminClient(ctx context.Context, client K8sClient, namespace, tena
return mAdmin, nil
}
func getTenant(ctx context.Context, operatorClient OperatorClient, namespace, tenantName string) (*operator.Tenant, error) {
func getTenant(ctx context.Context, operatorClient OperatorClientI, namespace, tenantName string) (*operator.Tenant, error) {
minInst, err := operatorClient.TenantGet(ctx, namespace, tenantName, metav1.GetOptions{})
if err != nil {
return nil, err
@@ -292,7 +310,7 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
return info, nil
}
func listTenants(ctx context.Context, operatorClient OperatorClient, namespace string, limit *int32) (*models.ListTenantsResponse, error) {
func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace string, limit *int32) (*models.ListTenantsResponse, error) {
listOpts := metav1.ListOptions{
Limit: 10,
}
@@ -389,7 +407,10 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
}
}
// get Kubernetes Client
clientset, err := cluster.K8sClient(session.SessionToken)
clientSet, err := cluster.K8sClient(session.SessionToken)
k8sClient := k8sClient{
client: clientSet,
}
if err != nil {
return nil, err
}
@@ -425,7 +446,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
},
}
_, err = clientset.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
@@ -518,43 +539,15 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Minio != nil {
// User provided TLS certificates for MinIO
isEncryptionAvailable = true
externalTLSCertificateSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
// disable autoCert
minInst.Spec.RequestAutoCert = false
tlsCrt, err := base64.StdEncoding.DecodeString(*tenantReq.TLS.Minio.Crt)
// Certificates used by the MinIO instance
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
if err != nil {
return nil, err
}
tlsKey, err := base64.StdEncoding.DecodeString(*tenantReq.TLS.Minio.Key)
if err != nil {
return nil, err
}
externalTLSCertificateSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: externalTLSCertificateSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeTLS,
Immutable: &imm,
Data: map[string][]byte{
"tls.crt": tlsCrt,
"tls.key": tlsKey,
},
}
_, err = clientset.CoreV1().Secrets(ns).Create(ctx, &externalTLSCertificateSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// Certificates used by the minio instance
minInst.Spec.ExternalCertSecret = &operator.LocalCertificateReference{
Name: externalTLSCertificateSecretName,
Type: "kubernetes.io/tls",
}
minInst.Spec.ExternalCertSecret = externalCertSecret
}
if tenantReq.Encryption != nil && isEncryptionAvailable {
@@ -565,13 +558,14 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
})
// KES client mTLSCertificates used by MinIO instance, only if autoCert is not enabled
if !minInst.Spec.RequestAutoCert {
minInst.Spec.ExternalClientCertSecret, err = getTenantExternalClientCertificates(ctx, clientset, ns, tenantReq.Encryption, secretName, tenantName)
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
minInst.Spec.ExternalClientCertSecret, err = createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.Encryption.Client, tenantExternalClientCertSecretName, tenantName)
if err != nil {
return nil, err
}
}
// KES configuration for Tenant instance
minInst.Spec.KES, err = getKESConfiguration(ctx, clientset, ns, tenantReq.Encryption, secretName, tenantName, minInst.Spec.RequestAutoCert)
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName, minInst.Spec.RequestAutoCert)
if err != nil {
return nil, err
}
@@ -630,12 +624,12 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
}
}
_, err = clientset.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
const consoleVersion = "minio/console:v0.3.18"
const consoleVersion = "minio/console:v0.3.19"
minInst.Spec.Console = &operator.ConsoleConfiguration{
Replicas: 1,
Image: consoleVersion,
@@ -646,42 +640,15 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
},
},
}
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Console != nil {
consoleExternalTLSCertificateSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
tlsCrt, err := base64.StdEncoding.DecodeString(*tenantReq.TLS.Console.Crt)
// Certificates used by the console instance
externalCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Console, externalCertSecretName, tenantName)
if err != nil {
return nil, err
}
tlsKey, err := base64.StdEncoding.DecodeString(*tenantReq.TLS.Console.Key)
if err != nil {
return nil, err
}
consoleExternalTLSCertificateSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: consoleExternalTLSCertificateSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeTLS,
Immutable: &imm,
Data: map[string][]byte{
"tls.crt": tlsCrt,
"tls.key": tlsKey,
},
}
_, err = clientset.CoreV1().Secrets(ns).Create(ctx, &consoleExternalTLSCertificateSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// Certificates used by the minio instance
minInst.Spec.Console.ExternalCertSecret = &operator.LocalCertificateReference{
Name: consoleExternalTLSCertificateSecretName,
Type: "kubernetes.io/tls",
}
minInst.Spec.Console.ExternalCertSecret = externalCertSecret
}
}
// set the service name if provided
@@ -718,7 +685,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
if tenantReq.ImagePullSecret != "" {
imagePullSecret = tenantReq.ImagePullSecret
} else if imagePullSecret, err = setImageRegistry(ctx, tenantName, tenantReq.ImageRegistry, clientset.CoreV1(), ns); err != nil {
} else if imagePullSecret, err = setImageRegistry(ctx, tenantName, tenantReq.ImageRegistry, clientSet.CoreV1(), ns); err != nil {
log.Println("error setting image registry secret:", err)
return nil, err
}
@@ -753,7 +720,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
// Integratrions
if os.Getenv("GKE_INTEGRATION") != "" {
err := gkeIntegration(clientset, tenantName, ns, session.SessionToken)
err := gkeIntegration(clientSet, tenantName, ns, session.SessionToken)
if err != nil {
return nil, err
}
@@ -832,7 +799,7 @@ func setImageRegistry(ctx context.Context, tenantName string, req *models.ImageR
}
// updateTenantAction does an update on the minioTenant by patching the desired changes
func updateTenantAction(ctx context.Context, operatorClient OperatorClient, clientset v1.CoreV1Interface, httpCl cluster.HTTPClientI, namespace string, params admin_api.UpdateTenantParams) error {
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl cluster.HTTPClientI, namespace string, params admin_api.UpdateTenantParams) error {
imageToUpdate := params.Body.Image
imageRegistryReq := params.Body.ImageRegistry
@@ -908,7 +875,7 @@ func getUpdateTenantResponse(session *models.Principal, params admin_api.UpdateT
}
// addTenantZone creates a zone to a defined tenant
func addTenantZone(ctx context.Context, operatorClient OperatorClient, params admin_api.TenantAddZoneParams) error {
func addTenantZone(ctx context.Context, operatorClient OperatorClientI, params admin_api.TenantAddZoneParams) error {
tenant, err := operatorClient.TenantGet(ctx, params.Namespace, params.Tenant, metav1.GetOptions{})
if err != nil {
return err
@@ -1423,344 +1390,6 @@ func parseNodeSelectorTerm(term *corev1.NodeSelectorTerm) *models.NodeSelectorTe
return &t
}
func getTenantExternalClientCertificates(ctx context.Context, clientSet *kubernetes.Clientset, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string) (clientCertificates *operator.LocalCertificateReference, err error) {
instanceExternalClientCertificateSecretName := fmt.Sprintf("%s-instance-external-client-mtls-certificates", secretName)
// If there's an error during this process we delete all KES configuration secrets
defer func() {
if err != nil {
errDelete := clientSet.CoreV1().Secrets(ns).Delete(ctx, instanceExternalClientCertificateSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
return
}
}()
imm := true
// Secret to store KES clients TLS mTLSCertificates (mTLS authentication)
clientTLSCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Client.Crt)
if err != nil {
return nil, err
}
clientTLSKey, err := base64.StdEncoding.DecodeString(*encryptionCfg.Client.Key)
if err != nil {
return nil, err
}
instanceExternalClientCertificateSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: instanceExternalClientCertificateSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeTLS,
Immutable: &imm,
Data: map[string][]byte{
"tls.crt": clientTLSCrt,
"tls.key": clientTLSKey,
},
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceExternalClientCertificateSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// KES client mTLSCertificates used by MinIO instance
clientCertificates = &operator.LocalCertificateReference{
Name: instanceExternalClientCertificateSecretName,
Type: "kubernetes.io/tls",
}
return clientCertificates, nil
}
func getKESConfiguration(ctx context.Context, clientSet *kubernetes.Clientset, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string, autoCert bool) (kesConfiguration *operator.KESConfig, err error) {
// secrets used by the KES configuration
instanceExternalClientCertificateSecretName := fmt.Sprintf("%s-instance-external-client-mtls-certificates", secretName)
kesExternalCertificateSecretName := fmt.Sprintf("%s-kes-external-mtls-certificates", secretName)
kesClientCertSecretName := fmt.Sprintf("%s-kes-mtls-certificates", secretName)
kesConfigurationSecretName := fmt.Sprintf("%s-kes-configuration", secretName)
// If there's an error during this process we delete all KES configuration secrets
defer func() {
if err != nil {
errDelete := clientSet.CoreV1().Secrets(ns).Delete(ctx, instanceExternalClientCertificateSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.CoreV1().Secrets(ns).Delete(ctx, kesExternalCertificateSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.CoreV1().Secrets(ns).Delete(ctx, kesClientCertSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.CoreV1().Secrets(ns).Delete(ctx, kesConfigurationSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
return
}
}()
imm := true
kesConfiguration = &operator.KESConfig{
Image: "minio/kes:v0.11.0",
Replicas: 1,
Metadata: nil,
}
// Using custom image for KES
if encryptionCfg.Image != "" {
kesConfiguration.Image = encryptionCfg.Image
}
// if autoCert is enabled then Operator will generate the client certificates, calculate the client cert identity
// and pass it to KES via the $MINIO_KES_IDENTITY variable
clientCrtIdentity := "$MINIO_KES_IDENTITY"
// Generate server certificates for KES only if autoCert is disabled
if !autoCert {
serverTLSCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Server.Crt)
if err != nil {
return nil, err
}
serverTLSKey, err := base64.StdEncoding.DecodeString(*encryptionCfg.Server.Key)
if err != nil {
return nil, err
}
// Secret to store KES server TLS mTLSCertificates
kesExternalCertificateSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kesExternalCertificateSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeTLS,
Immutable: &imm,
Data: map[string][]byte{
"tls.crt": serverTLSCrt,
"tls.key": serverTLSKey,
},
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &kesExternalCertificateSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// External mTLSCertificates used by KES
kesConfiguration.ExternalCertSecret = &operator.LocalCertificateReference{
Name: kesExternalCertificateSecretName,
Type: "kubernetes.io/tls",
}
// Client certificate for KES used by Minio to mTLS
clientTLSCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Client.Crt)
if err != nil {
return nil, err
}
// Calculate the client cert identity based on the clientTLSCrt
h := crypto.SHA256.New()
certificate, err := kes.ParseCertificate(clientTLSCrt)
if err != nil {
return nil, err
}
h.Write(certificate.RawSubjectPublicKeyInfo)
clientCrtIdentity = hex.EncodeToString(h.Sum(nil))
}
// Default kesConfiguration for KES
kesConfig := kes.ServerConfig{
Addr: "0.0.0.0:7373",
Root: "disabled",
TLS: kes.TLS{
KeyPath: "/tmp/kes/server.key",
CertPath: "/tmp/kes/server.crt",
},
Policies: map[string]kes.Policy{
"default-policy": {
Paths: []string{
"/v1/key/create/my-minio-key",
"/v1/key/generate/my-minio-key",
"/v1/key/decrypt/my-minio-key",
},
Identities: []kes.Identity{
kes.Identity(clientCrtIdentity),
},
},
},
Cache: kes.Cache{
Expiry: &kes.Expiry{
Any: 5 * time.Minute,
Unused: 20 * time.Second,
},
},
Log: kes.Log{
Error: "on",
Audit: "off",
},
Keys: kes.Keys{},
}
// operator will mount the mTLSCertificates in the following paths
// therefore we set these values in the KES yaml kesConfiguration
var mTLSClientCrtPath = "/tmp/kes/client.crt"
var mTLSClientKeyPath = "/tmp/kes/client.key"
var mTLSClientCaPath = "/tmp/kes/ca.crt"
// map to hold mTLSCertificates for KES mTLS against Vault
mTLSCertificates := map[string][]byte{}
// if encryption is enabled and encryption is configured to use Vault
if encryptionCfg.Vault != nil {
// Initialize Vault Config
kesConfig.Keys.Vault = &kes.Vault{
Endpoint: *encryptionCfg.Vault.Endpoint,
EnginePath: encryptionCfg.Vault.Engine,
Namespace: encryptionCfg.Vault.Namespace,
Prefix: encryptionCfg.Vault.Prefix,
Status: &kes.VaultStatus{
Ping: 10 * time.Second,
},
}
// Vault AppRole credentials
if encryptionCfg.Vault.Approle != nil {
kesConfig.Keys.Vault.AppRole = &kes.AppRole{
EnginePath: encryptionCfg.Vault.Approle.Engine,
ID: *encryptionCfg.Vault.Approle.ID,
Secret: *encryptionCfg.Vault.Approle.Secret,
Retry: 15 * time.Second,
}
} else {
return nil, errors.New("approle credentials missing for kes")
}
// Vault mTLS kesConfiguration
if encryptionCfg.Vault.TLS != nil {
vaultTLSConfig := encryptionCfg.Vault.TLS
kesConfig.Keys.Vault.TLS = &kes.VaultTLS{}
if vaultTLSConfig.Crt != "" {
clientCrt, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Crt)
if err != nil {
return nil, err
}
mTLSCertificates["client.crt"] = clientCrt
kesConfig.Keys.Vault.TLS.CertPath = mTLSClientCrtPath
}
if vaultTLSConfig.Key != "" {
clientKey, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Key)
if err != nil {
return nil, err
}
mTLSCertificates["client.key"] = clientKey
kesConfig.Keys.Vault.TLS.KeyPath = mTLSClientKeyPath
}
if vaultTLSConfig.Ca != "" {
caCrt, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Ca)
if err != nil {
return nil, err
}
mTLSCertificates["ca.crt"] = caCrt
kesConfig.Keys.Vault.TLS.CAPath = mTLSClientCaPath
}
}
} else if encryptionCfg.Aws != nil {
// Initialize AWS
kesConfig.Keys.Aws = &kes.Aws{
SecretsManager: &kes.AwsSecretManager{},
}
// AWS basic kesConfiguration
if encryptionCfg.Aws.Secretsmanager != nil {
kesConfig.Keys.Aws.SecretsManager.Endpoint = *encryptionCfg.Aws.Secretsmanager.Endpoint
kesConfig.Keys.Aws.SecretsManager.Region = *encryptionCfg.Aws.Secretsmanager.Region
kesConfig.Keys.Aws.SecretsManager.KmsKey = encryptionCfg.Aws.Secretsmanager.Kmskey
// AWS credentials
if encryptionCfg.Aws.Secretsmanager.Credentials != nil {
kesConfig.Keys.Aws.SecretsManager.Login = &kes.AwsSecretManagerLogin{
AccessKey: *encryptionCfg.Aws.Secretsmanager.Credentials.Accesskey,
SecretKey: *encryptionCfg.Aws.Secretsmanager.Credentials.Secretkey,
SessionToken: encryptionCfg.Aws.Secretsmanager.Credentials.Token,
}
}
}
} else if encryptionCfg.Gemalto != nil {
// Initialize Gemalto
kesConfig.Keys.Gemalto = &kes.Gemalto{
KeySecure: &kes.GemaltoKeySecure{},
}
// Gemalto Configuration
if encryptionCfg.Gemalto.Keysecure != nil {
kesConfig.Keys.Gemalto.KeySecure.Endpoint = *encryptionCfg.Gemalto.Keysecure.Endpoint
// Gemalto TLS kesConfiguration
if encryptionCfg.Gemalto.Keysecure.TLS != nil {
if encryptionCfg.Gemalto.Keysecure.TLS.Ca != nil {
caCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Gemalto.Keysecure.TLS.Ca)
if err != nil {
return nil, err
}
mTLSCertificates["ca.crt"] = caCrt
kesConfig.Keys.Gemalto.KeySecure.TLS = &kes.GemaltoTLS{
CAPath: mTLSClientCaPath,
}
}
}
// Gemalto Login
if encryptionCfg.Gemalto.Keysecure.Credentials != nil {
kesConfig.Keys.Gemalto.KeySecure.Credentials = &kes.GemaltoCredentials{
Token: *encryptionCfg.Gemalto.Keysecure.Credentials.Token,
Domain: *encryptionCfg.Gemalto.Keysecure.Credentials.Domain,
Retry: 15 * time.Second,
}
}
}
}
// if mTLSCertificates contains elements we create the kubernetes secret
if len(mTLSCertificates) > 0 {
// Secret to store KES mTLS kesConfiguration
kesClientCertSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kesClientCertSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Immutable: &imm,
Data: mTLSCertificates,
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &kesClientCertSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// kubernetes generic secret
kesConfiguration.ClientCertSecret = &operator.LocalCertificateReference{
Name: kesClientCertSecretName,
}
}
// Generate Yaml kesConfiguration for KES
serverConfigYaml, err := yaml.Marshal(kesConfig)
if err != nil {
return nil, err
}
// Secret to store KES server kesConfiguration
kesConfigurationSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kesConfigurationSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Immutable: &imm,
Data: map[string][]byte{
"server-config.yaml": serverConfigYaml,
},
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &kesConfigurationSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// Configuration used by KES
kesConfiguration.Configuration = &corev1.LocalObjectReference{
Name: kesConfigurationSecretName,
}
return kesConfiguration, nil
}
func getTenantUpdateZoneResponse(session *models.Principal, params admin_api.TenantUpdateZonesParams) (*models.Tenant, error) {
ctx := context.Background()
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
@@ -1789,7 +1418,7 @@ func getTenantUpdateZoneResponse(session *models.Principal, params admin_api.Ten
// It does the equivalent to a PUT request on Tenant's zones
func updateTenantZones(
ctx context.Context,
operatorClient OperatorClient,
operatorClient OperatorClientI,
namespace string,
tenantName string,
zonesReq []*models.Zone) (*operator.Tenant, error) {

View File

@@ -0,0 +1,516 @@
// This file is part of MinIO Kubernetes Cloud
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package restapi
import (
"context"
"crypto"
"encoding/base64"
"encoding/hex"
"fmt"
"log"
"time"
"errors"
"github.com/minio/console/cluster"
"github.com/minio/console/models"
"github.com/minio/console/pkg/kes"
"github.com/minio/console/restapi/operations/admin_api"
operator "github.com/minio/operator/pkg/apis/minio.min.io/v1"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// tenantUpdateCertificates receives the keyPair certificates (public and private keys) for Minio and Console and will try
// to replace the existing kubernetes secrets with the new values, then will restart the affected pods so the new volumes can be mounted
func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClientI, clientSet K8sClientI, namespace string, params admin_api.TenantUpdateCertificateParams) error {
tenantName := params.Tenant
tenant, err := operatorClient.TenantGet(ctx, namespace, tenantName, metav1.GetOptions{})
if err != nil {
return err
}
secretName := fmt.Sprintf("%s-secret", tenantName)
body := params.Body
// check if MinIO is deployed with external certs and user provided new MinIO keypair
if tenant.ExternalCert() && body.Minio != nil {
minioCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
// update certificates
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Minio, minioCertSecretName, tenantName); err != nil {
return err
}
// restart MinIO pods
err := clientSet.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", operator.TenantLabel, tenantName),
})
if err != nil {
return err
}
}
// check if Console is deployed with external certs and user provided new Console keypair
if tenant.ConsoleExternalCert() && tenant.HasConsoleEnabled() && body.Console != nil {
consoleCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
// update certificates
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Console, consoleCertSecretName, tenantName); err != nil {
return err
}
// restart Console pods
err := clientSet.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", operator.ConsoleTenantLabel, fmt.Sprintf("%s-console", tenantName)),
})
if err != nil {
return err
}
}
return nil
}
// getTenantUpdateCertificatesResponse wrapper of tenantUpdateCertificates
func getTenantUpdateCertificatesResponse(session *models.Principal, params admin_api.TenantUpdateCertificateParams) error {
ctx := context.Background()
// get Kubernetes Client
clientSet, err := cluster.K8sClient(session.SessionToken)
if err != nil {
return err
}
k8sClient := k8sClient{
client: clientSet,
}
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
if err != nil {
return err
}
opClient := operatorClient{
client: opClientClientSet,
}
if err := tenantUpdateCertificates(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
return err
}
return nil
}
// tenantUpdateEncryption allow user to update KES server certificates, KES client certificates (used by MinIO for mTLS) and KES configuration (KMS configuration, credentials, etc)
func tenantUpdateEncryption(ctx context.Context, operatorClient OperatorClientI, clientSet K8sClientI, namespace string, params admin_api.TenantUpdateEncryptionParams) error {
tenantName := params.Tenant
secretName := fmt.Sprintf("%s-secret", tenantName)
tenant, err := operatorClient.TenantGet(ctx, namespace, tenantName, metav1.GetOptions{})
body := params.Body
if err != nil {
return err
}
// Check if encryption is enabled for MinIO via KES
if tenant.HasKESEnabled() {
// check if KES is deployed with external certificates and user provided new server keypair
if tenant.KESExternalCert() && body.Server != nil {
kesExternalCertSecretName := fmt.Sprintf("%s-kes-external-cert", secretName)
// update certificates
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Server, kesExternalCertSecretName, tenantName); err != nil {
return err
}
}
// check if Tenant is deployed with external client certificates and user provided new client keypaiir
if tenant.ExternalClientCert() && body.Client != nil {
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
// Update certificates
if _, err := createOrReplaceExternalCertSecret(ctx, clientSet, namespace, body.Client, tenantExternalClientCertSecretName, tenantName); err != nil {
return err
}
// Restart MinIO pods to mount the new client secrets
err := clientSet.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", operator.TenantLabel, tenantName),
})
if err != nil {
return err
}
}
// update KES identities in kes-configuration.yaml secret
kesConfigurationSecretName := fmt.Sprintf("%s-kes-configuration", secretName)
kesClientCertSecretName := fmt.Sprintf("%s-kes-client-cert", secretName)
_, _, err := createOrReplaceKesConfigurationSecrets(ctx, clientSet, namespace, body, kesConfigurationSecretName, kesClientCertSecretName, tenantName)
if err != nil {
return err
}
// Restart KES pods to mount the new configuration
err = clientSet.deletePodCollection(ctx, namespace, metav1.DeleteOptions{}, metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", operator.KESInstanceLabel, fmt.Sprintf("%s-kes", tenantName)),
})
if err != nil {
return err
}
}
return nil
}
// getTenantUpdateEncryptionResponse is a wrapper for tenantUpdateEncryption
func getTenantUpdateEncryptionResponse(session *models.Principal, params admin_api.TenantUpdateEncryptionParams) error {
ctx := context.Background()
// get Kubernetes Client
clientSet, err := cluster.K8sClient(session.SessionToken)
if err != nil {
return err
}
k8sClient := k8sClient{
client: clientSet,
}
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
if err != nil {
return err
}
opClient := operatorClient{
client: opClientClientSet,
}
if err := tenantUpdateEncryption(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
return err
}
return nil
}
// getKESConfiguration will generate the KES server certificate secrets, the tenant client secrets for mTLS authentication between MinIO and KES and the
// kes-configuration.yaml file used by the KES service (how to connect to the external KMS, eg: Vault, AWS, Gemalto, etc)
func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string, autoCert bool) (kesConfiguration *operator.KESConfig, err error) {
// Secrets used by the MiniO tenant service
//
// tenantExternalClientCertSecretName is the name of the secret that will store the certificates for mTLS between MinIO and the KES, eg: app.key and app.crt
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
// Secrets used by the KES service
//
// kesExternalCertSecretName is the name of the secret that will store the certificates for TLS in the KES server, eg: server.key and server.crt
kesExternalCertSecretName := fmt.Sprintf("%s-kes-external-cert", secretName)
// kesClientCertSecretName is the name of the secret that will store the certificates for mTLS between KES and the KMS, eg: mTLS with Vault or Gemalto KMS
kesClientCertSecretName := fmt.Sprintf("%s-kes-client-cert", secretName)
// kesConfigurationSecretName is the name of the secret that will store the configuration file, eg: kes-configuration.yaml
kesConfigurationSecretName := fmt.Sprintf("%s-kes-configuration", secretName)
// if there's an error during this process we delete all KES configuration secrets
defer func() {
if err != nil {
errDelete := clientSet.deleteSecret(ctx, ns, tenantExternalClientCertSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.deleteSecret(ctx, ns, kesExternalCertSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.deleteSecret(ctx, ns, kesClientCertSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
errDelete = clientSet.deleteSecret(ctx, ns, kesConfigurationSecretName, metav1.DeleteOptions{})
if errDelete != nil {
log.Print(errDelete)
}
return
}
}()
kesConfiguration = &operator.KESConfig{
Image: "minio/kes:v0.11.0",
Replicas: 1,
Metadata: nil,
}
// Using custom image for KES
if encryptionCfg.Image != "" {
kesConfiguration.Image = encryptionCfg.Image
}
// Generate server certificates for KES only if autoCert is disabled
if !autoCert {
kesExternalCertSecret, err := createOrReplaceExternalCertSecret(ctx, clientSet, ns, encryptionCfg.Server, kesExternalCertSecretName, tenantName)
if err != nil {
return nil, err
}
// External TLS certificates used by KES
kesConfiguration.ExternalCertSecret = kesExternalCertSecret
}
// Prepare kesConfiguration for KES
serverConfigSecret, clientCertSecret, err := createOrReplaceKesConfigurationSecrets(ctx, clientSet, ns, encryptionCfg, kesConfigurationSecretName, kesClientCertSecretName, tenantName)
if err != nil {
return nil, err
}
// Configuration used by KES
kesConfiguration.Configuration = serverConfigSecret
kesConfiguration.ClientCertSecret = clientCertSecret
return kesConfiguration, nil
}
// createOrReplaceExternalCertSecret receives a keypair, public and private key, encoded in base64, decode it and generate a new kubernetes secret
// to be used by the operator for TLS encryption
func createOrReplaceExternalCertSecret(ctx context.Context, clientSet K8sClientI, ns string, keyPair *models.KeyPairConfiguration, secretName, tenantName string) (*operator.LocalCertificateReference, error) {
if keyPair == nil || keyPair.Crt == nil || keyPair.Key == nil || *keyPair.Crt == "" || *keyPair.Key == "" {
return nil, errors.New("certificate files must not be empty")
}
// delete secret with same name if exists
err := clientSet.deleteSecret(ctx, ns, secretName, metav1.DeleteOptions{})
if err != nil {
// log the error if any and continue
log.Println(err)
}
imm := true
tlsCrt, err := base64.StdEncoding.DecodeString(*keyPair.Crt)
if err != nil {
return nil, err
}
tlsKey, err := base64.StdEncoding.DecodeString(*keyPair.Key)
if err != nil {
return nil, err
}
externalTLSCertificateSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Type: corev1.SecretTypeTLS,
Immutable: &imm,
Data: map[string][]byte{
"tls.crt": tlsCrt,
"tls.key": tlsKey,
},
}
_, err = clientSet.createSecret(ctx, ns, externalTLSCertificateSecret, metav1.CreateOptions{})
if err != nil {
return nil, err
}
// Certificates used by the minio instance
return &operator.LocalCertificateReference{
Name: secretName,
Type: "kubernetes.io/tls",
}, nil
}
func createOrReplaceKesConfigurationSecrets(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, kesConfigurationSecretName, kesClientCertSecretName, tenantName string) (*corev1.LocalObjectReference, *operator.LocalCertificateReference, error) {
// delete KES configuration secret if exists
if err := clientSet.deleteSecret(ctx, ns, kesConfigurationSecretName, metav1.DeleteOptions{}); err != nil {
// log the error if any and continue
log.Println(err)
}
// delete KES client cert secret if exists
if err := clientSet.deleteSecret(ctx, ns, kesClientCertSecretName, metav1.DeleteOptions{}); err != nil {
// log the error if any and continue
log.Println(err)
}
// if autoCert is enabled then Operator will generate the client certificates, calculate the client cert identity
// and pass it to KES via the $MINIO_KES_IDENTITY variable
clientCrtIdentity := "$MINIO_KES_IDENTITY"
// If a client certificate is provided proceed to calculate the identity
if encryptionCfg.Client != nil {
// Client certificate for KES used by Minio to mTLS
clientTLSCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Client.Crt)
if err != nil {
return nil, nil, err
}
// Calculate the client cert identity based on the clientTLSCrt
h := crypto.SHA256.New()
certificate, err := kes.ParseCertificate(clientTLSCrt)
if err != nil {
return nil, nil, err
}
h.Write(certificate.RawSubjectPublicKeyInfo)
clientCrtIdentity = hex.EncodeToString(h.Sum(nil))
}
// Default kesConfiguration for KES
kesConfig := &kes.ServerConfig{
Addr: "0.0.0.0:7373",
Root: "disabled",
TLS: kes.TLS{
KeyPath: "/tmp/kes/server.key",
CertPath: "/tmp/kes/server.crt",
},
Policies: map[string]kes.Policy{
"default-policy": {
Paths: []string{
"/v1/key/create/my-minio-key",
"/v1/key/generate/my-minio-key",
"/v1/key/decrypt/my-minio-key",
},
Identities: []kes.Identity{
kes.Identity(clientCrtIdentity),
},
},
},
Cache: kes.Cache{
Expiry: &kes.Expiry{
Any: 5 * time.Minute,
Unused: 20 * time.Second,
},
},
Log: kes.Log{
Error: "on",
Audit: "off",
},
Keys: kes.Keys{},
}
// operator will mount the mTLSCertificates in the following paths
// therefore we set these values in the KES yaml kesConfiguration
var mTLSClientCrtPath = "/tmp/kes/client.crt"
var mTLSClientKeyPath = "/tmp/kes/client.key"
var mTLSClientCaPath = "/tmp/kes/ca.crt"
// map to hold mTLSCertificates for KES mTLS against Vault
mTLSCertificates := map[string][]byte{}
// if encryption is enabled and encryption is configured to use Vault
if encryptionCfg.Vault != nil {
// Initialize Vault Config
kesConfig.Keys.Vault = &kes.Vault{
Endpoint: *encryptionCfg.Vault.Endpoint,
EnginePath: encryptionCfg.Vault.Engine,
Namespace: encryptionCfg.Vault.Namespace,
Prefix: encryptionCfg.Vault.Prefix,
Status: &kes.VaultStatus{
Ping: 10 * time.Second,
},
}
// Vault AppRole credentials
if encryptionCfg.Vault.Approle != nil {
kesConfig.Keys.Vault.AppRole = &kes.AppRole{
EnginePath: encryptionCfg.Vault.Approle.Engine,
ID: *encryptionCfg.Vault.Approle.ID,
Secret: *encryptionCfg.Vault.Approle.Secret,
Retry: 15 * time.Second,
}
} else {
return nil, nil, errors.New("approle credentials missing for kes")
}
// Vault mTLS kesConfiguration
if encryptionCfg.Vault.TLS != nil {
vaultTLSConfig := encryptionCfg.Vault.TLS
kesConfig.Keys.Vault.TLS = &kes.VaultTLS{}
if vaultTLSConfig.Crt != "" {
clientCrt, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Crt)
if err != nil {
return nil, nil, err
}
mTLSCertificates["client.crt"] = clientCrt
kesConfig.Keys.Vault.TLS.CertPath = mTLSClientCrtPath
}
if vaultTLSConfig.Key != "" {
clientKey, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Key)
if err != nil {
return nil, nil, err
}
mTLSCertificates["client.key"] = clientKey
kesConfig.Keys.Vault.TLS.KeyPath = mTLSClientKeyPath
}
if vaultTLSConfig.Ca != "" {
caCrt, err := base64.StdEncoding.DecodeString(vaultTLSConfig.Ca)
if err != nil {
return nil, nil, err
}
mTLSCertificates["ca.crt"] = caCrt
kesConfig.Keys.Vault.TLS.CAPath = mTLSClientCaPath
}
}
} else if encryptionCfg.Aws != nil {
// Initialize AWS
kesConfig.Keys.Aws = &kes.Aws{
SecretsManager: &kes.AwsSecretManager{},
}
// AWS basic kesConfiguration
if encryptionCfg.Aws.Secretsmanager != nil {
kesConfig.Keys.Aws.SecretsManager.Endpoint = *encryptionCfg.Aws.Secretsmanager.Endpoint
kesConfig.Keys.Aws.SecretsManager.Region = *encryptionCfg.Aws.Secretsmanager.Region
kesConfig.Keys.Aws.SecretsManager.KmsKey = encryptionCfg.Aws.Secretsmanager.Kmskey
// AWS credentials
if encryptionCfg.Aws.Secretsmanager.Credentials != nil {
kesConfig.Keys.Aws.SecretsManager.Login = &kes.AwsSecretManagerLogin{
AccessKey: *encryptionCfg.Aws.Secretsmanager.Credentials.Accesskey,
SecretKey: *encryptionCfg.Aws.Secretsmanager.Credentials.Secretkey,
SessionToken: encryptionCfg.Aws.Secretsmanager.Credentials.Token,
}
}
}
} else if encryptionCfg.Gemalto != nil {
// Initialize Gemalto
kesConfig.Keys.Gemalto = &kes.Gemalto{
KeySecure: &kes.GemaltoKeySecure{},
}
// Gemalto Configuration
if encryptionCfg.Gemalto.Keysecure != nil {
kesConfig.Keys.Gemalto.KeySecure.Endpoint = *encryptionCfg.Gemalto.Keysecure.Endpoint
// Gemalto TLS kesConfiguration
if encryptionCfg.Gemalto.Keysecure.TLS != nil {
if encryptionCfg.Gemalto.Keysecure.TLS.Ca != nil {
caCrt, err := base64.StdEncoding.DecodeString(*encryptionCfg.Gemalto.Keysecure.TLS.Ca)
if err != nil {
return nil, nil, err
}
mTLSCertificates["ca.crt"] = caCrt
kesConfig.Keys.Gemalto.KeySecure.TLS = &kes.GemaltoTLS{
CAPath: mTLSClientCaPath,
}
}
}
// Gemalto Login
if encryptionCfg.Gemalto.Keysecure.Credentials != nil {
kesConfig.Keys.Gemalto.KeySecure.Credentials = &kes.GemaltoCredentials{
Token: *encryptionCfg.Gemalto.Keysecure.Credentials.Token,
Domain: *encryptionCfg.Gemalto.Keysecure.Credentials.Domain,
Retry: 15 * time.Second,
}
}
}
}
imm := true
// if mTLSCertificates contains elements we create the kubernetes secret
var clientCertSecretReference *operator.LocalCertificateReference
if len(mTLSCertificates) > 0 {
// Secret to store KES mTLS kesConfiguration to authenticate against a KMS
kesClientCertSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kesClientCertSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Immutable: &imm,
Data: mTLSCertificates,
}
_, err := clientSet.createSecret(ctx, ns, &kesClientCertSecret, metav1.CreateOptions{})
if err != nil {
return nil, nil, err
}
// kubernetes generic secret
clientCertSecretReference = &operator.LocalCertificateReference{
Name: kesClientCertSecretName,
}
}
// Generate Yaml kesConfiguration for KES
serverConfigYaml, err := yaml.Marshal(kesConfig)
if err != nil {
return nil, nil, err
}
// Secret to store KES server kesConfiguration
kesConfigurationSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: kesConfigurationSecretName,
Labels: map[string]string{
operator.TenantLabel: tenantName,
},
},
Immutable: &imm,
Data: map[string][]byte{
"server-config.yaml": serverConfigYaml,
},
}
_, err = clientSet.createSecret(ctx, ns, &kesConfigurationSecret, metav1.CreateOptions{})
if err != nil {
return nil, nil, err
}
return &corev1.LocalObjectReference{
Name: kesConfigurationSecretName,
}, clientCertSecretReference, nil
}

View File

@@ -0,0 +1,456 @@
package restapi
import (
"context"
"errors"
"reflect"
"testing"
"github.com/minio/console/models"
"github.com/minio/console/restapi/operations/admin_api"
operator "github.com/minio/operator/pkg/apis/minio.min.io/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var DeletePodCollectionMock func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
var DeleteSecretMock func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error
var CreateSecretMock func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error)
func (c k8sClientMock) deletePodCollection(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
return DeletePodCollectionMock(ctx, namespace, opts, listOpts)
}
func (c k8sClientMock) deleteSecret(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return DeleteSecretMock(ctx, namespace, name, opts)
}
func (c k8sClientMock) createSecret(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return CreateSecretMock(ctx, namespace, secret, opts)
}
func Test_tenantUpdateCertificates(t *testing.T) {
k8sClient := k8sClientMock{}
opClient := opClientMock{}
crt := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUzRENDQTBTZ0F3SUJBZ0lRS0pDalNyK0xaMnJrYVo5ODAvZnV5akFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKdFRFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNVVV3UXdZRFZRUUxERHhoYkdWMgpjMnRBVEdWdWFXNXpMVTFoWTBKdmIyc3RVSEp2TG14dlkyRnNJQ2hNWlc1cGJpQkJiR1YyYzJ0cElFaDFaWEowCllTQkJjbWxoY3lreFREQktCZ05WQkFNTVEyMXJZMlZ5ZENCaGJHVjJjMnRBVEdWdWFXNXpMVTFoWTBKdmIyc3QKVUhKdkxteHZZMkZzSUNoTVpXNXBiaUJCYkdWMmMydHBJRWgxWlhKMFlTQkJjbWxoY3lrd0hoY05NVGt3TmpBeApNREF3TURBd1doY05NekF3T0RBeU1ERTFNekEzV2pCaU1TY3dKUVlEVlFRS0V4NXRhMk5sY25RZ1pHVjJaV3h2CmNHMWxiblFnWTJWeWRHbG1hV05oZEdVeE56QTFCZ05WQkFzTUxtRnNaWFp6YTBCMGFXWmhMbXh2WTJGc0lDaE0KWlc1cGJpQkJiR1YyYzJ0cElFaDFaWEowWVNCQmNtbGhjeWt3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQgpEd0F3Z2dFS0FvSUJBUUM2dlhLVyswWmhJQUNycmpxTU9QZ3VSdGpSemk1L0VxK2JvZTJkQ1BpT3djdXo0WFlhCm1rVlcxSkhBS3VTc0I1UHE0QnFSRXFueUhYT0ZROWQ1bEFjRGNDYmhlTFRVb2h2ZkhOWW9SdWRrYkZ3RzAyOFQKdVMxTFNtcHU5VjhFU2Q0Q3BiOGRvUkcvUW8vRTF4RGk5STFhNVhoeUdHTTNsUFlYQU1HU1ZkWUNNNzh0M2ZLTwp0NFVlcEt6d2dFQ2NKRVg4MVFoem1ZT25ELysyazNxSVppZU9nOGFkbDNFUWV2eFBISXlXQ1JkaDJZTkZsRi9rCmEwTzQyRVl2NVNUT1N0dzI2TzMwTVRrcEg0dzRRZm9VV3BnZU93MDZyYTluRHdzV3VhMUZIaHlKZmxOanR1USsKU0NlaDdqTVlVUThYV1JkbXVHbzFRT0g5cjdDKy82L1JhMlZIQWdNQkFBR2pnYmt3Z2JZd0RnWURWUjBQQVFILwpCQVFEQWdXZ01CTUdBMVVkSlFRTU1Bb0dDQ3NHQVFVRkJ3TUJNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqCkJCZ3dGb0FVNHg4NUIyeGVlQzg0UEdvM1dZVWwxRGVvZlFNd1lBWURWUjBSQkZrd1Y0SWpLaTV0YVc1cGJ5MWgKYkdWMmMyc3Ribk11YzNaakxtTnNkWE4wWlhJdWJHOWpZV3lDTUNvdWFHOXVaWGwzWld4c0xXaHNMbTFwYm1sdgpMV0ZzWlhaemF5MXVjeTV6ZG1NdVkyeDFjM1JsY2k1c2IyTmhiREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBWUVBCnZnMFlIVGtzd3Z4NEE5ZXl0b1V3eTBOSFVjQXpuNy9BVCt3WEt6d3Fqa0RiS3hVYlFTMFNQVVI4SkVDMkpuUUgKU3pTNGFCNXRURVRYZUcrbHJZVU02b0RNcXlIalpUN1NJaW83OUNxOFloWWxORU9qMkhvaXdxalczZm10UU9kVApoVG1tS01lRnZleHo2cnRxbHp0cVdKa3kvOGd1MkMrMWw5UDFFUmhFNDZZY0puVmJ5REFTSGNvV2tiZVhFUGErCjNpNFd4bU1yMDlNZXFTNkFUb2NKanFBeEtYcURYWmlFZjRUVEp1ZTRBQ2s4WmhqaEtUUEV6RnQ3WllVaHY3dVoKdlZCOXhla2FqRzdMRU5kSHZncXVMRUxUV3czWkpBaXpSTU5KUUpzZU11LzdQN1NIeXRKTVJYdlVwd2R2dWhDOApKNm54aGRmUS91QVlQY1lHdlU5NUZPZ1NjWVZqNW1WQktXM0ZHbkl2YzZuamQ1OFhBSTE3dlk0K0ZZTnY4M2UxCm9mOGlxRFdWNTFyT1FlbG9FZFdmdHkvZTI2bXVWUUQzQlVjY2Z5SWpGYy9SeGNHdm5maUEwUm1uRDNkWFcyZ0oKUHFTd01ZZ3hxQndqMm1Qck5jdkFWY3BOeWRjTWJKOTFIOHRWY0ttRHMrY1NiV0tnblpmKzUvZm5yaTdmU3FLUQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
key := "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQzZ2WEtXKzBaaElBQ3IKcmpxTU9QZ3VSdGpSemk1L0VxK2JvZTJkQ1BpT3djdXo0WFlhbWtWVzFKSEFLdVNzQjVQcTRCcVJFcW55SFhPRgpROWQ1bEFjRGNDYmhlTFRVb2h2ZkhOWW9SdWRrYkZ3RzAyOFR1UzFMU21wdTlWOEVTZDRDcGI4ZG9SRy9Rby9FCjF4RGk5STFhNVhoeUdHTTNsUFlYQU1HU1ZkWUNNNzh0M2ZLT3Q0VWVwS3p3Z0VDY0pFWDgxUWh6bVlPbkQvKzIKazNxSVppZU9nOGFkbDNFUWV2eFBISXlXQ1JkaDJZTkZsRi9rYTBPNDJFWXY1U1RPU3R3MjZPMzBNVGtwSDR3NApRZm9VV3BnZU93MDZyYTluRHdzV3VhMUZIaHlKZmxOanR1UStTQ2VoN2pNWVVROFhXUmRtdUdvMVFPSDlyN0MrCi82L1JhMlZIQWdNQkFBRUNnZ0VCQUlyTlVFUnJSM2ZmK3IraGhJRS93ekZhbGNUMUZWaDh3aXpUWXJQcnZCMFkKYlZvcVJzZ2xUVTdxTjkvM3dmc2dzdEROZk5IQ1pySEJOR0drK0orMDZMV2tnakhycjdXeFBUaE16ZDRvUGN4RwpRdTBMOGE5ZVlBMXJwY3NOOVc5Um5JU3BRSEk4aTkxM0V6Z0RoOWk2WCt0bFQyNjNNK0JYaDhlM1Z5cDNSTmhpCjZZUTQwcWJsNlQ0TUlyLzRSMGJmcFExZWVMNVNnbHB6Z1d6ZGs4WGtmc0E5YnRiU1RoMjZKRlBPUU1tMm5adkcKbjBlZm85TDZtaktwRW9rRWpTY1hWYTRZNHJaREZFYTVIbkpUSDBHblByQzU1cDhBb3BFN1c1M2RDT3lxd29CcQo4SWtZb2grcm1qTUZrOGc5VlRVYlcwVE9YVTFSY1E1Z0gxWS9jam5uVTRrQ2dZRUE4amw5ZEQyN2JaQ2ZaTW9jCjJYRThiYkJTaFVXTjRvaklKc1lHY2xyTjJDUEtUNmExaVhvS3BrTXdGNUdsODEzQURGR1pTbWtUSUFVQXRXQU8KNzZCcEpGUlVCZ1hmUXhSU0gyS1RaRldxbE5yekZPQjNsT3h2bFJ1amw5eE9ueStyWUhJWC9BOWNqQlp3a2orSAo3MDZRTExvS1ZFL2lMYy9DMlI5VzRLRVI3R3NDZ1lFQXhWd3FyM1JnUXhHUmpueG1zbDZtUW5DQ3k2MXFoUzUxCkJicDJzWldraFNTV0w0YzFDY0NDMnZ2ektPSmJkZ2NZQU43L05sTHgzWjlCZUFjTVZMUEVoc2NPalB6YUlneEMKczJ2UkdwQUFtYnRjWi9MVlpKaERWTjIrSHowRHhnRUtCa1JzQU5XTG51cGRoUWJBdlZuTkVsY29YVlo1cC9qcwp3U1BCelFoSklaVUNnWUJqTUlHY0dTOW9SWUhRRHlmVEx4aVV2bEI4ZktnR2JRYXhRZ1FmemVsZktnRE5yekhGCnN6RXJOblk2SUkxNVpCbWhzY1I1QVNBd3kzdW55a2N6ZjFldTVjMW1qZjhJQkFsQkN1ZmFmVzRWK0xiMEJKdFQKWTZLcHg2Q3RMaTBQNk1CZ0JUaW5JazgrbW0zTXBiRnZvSmRQaVh0elhTYjhwWWhmeXdLVGg4SEVNd0tCZ1FDUwpvR0VPTFpYKy9pUjRDYkI2d0pzaExWbmZYSjJSQ096a0xwNVVYV3IzaURFVWFvMWJDMjJzcUJjRnZ2WllmL2l6ClhQbWJNSkNGS1BhSTZDT2ZJbGZXRWptYlFaZ0dSN21lZDNISkhFZDE3NTg5azBvN0RHeXB0bnl6MUs3akFvNmkKRFY5NFZ5NytCLzBuQWRkY1ZrVm5aTjJXU3RMam1xcTY2NGZtZmt0bTZRS0JnQzBsMk5OVlFWK2N0OFFRRFpINQpBRFIrSGM3Qk0wNDhURGhNTmhoR3JHc2tWNngwMCtMZTdISGdpT3h2NXJudkNlTlY2M001YUZHdFVWbllTN1VoCkE1NndaNVlZeFFnQ0xzNi9PRmZhK3NiTngrSjdnSjRjNXdMZVlJMXVPMTlzZHBHa2VHZ25vK3dXVmxDSzFCbW0KRGM0TXA2STRiUTVtdy93YVpLQnpjRTJLCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
badCrt := "ASD"
badKey := "ASD"
type args struct {
ctx context.Context
opClient OperatorClientI
clientSet K8sClientI
namespace string
params admin_api.TenantUpdateCertificateParams
mockTenantGet func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error)
mockDeleteSecret func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error
mockCreateSecret func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error)
mockDeletePodCollection func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "error getting tenant information",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return nil, errors.New("invalid tenant")
},
},
wantErr: true,
},
{
name: "error replacing external certs for tenant because of missing keypair",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Minio: &models.KeyPairConfiguration{},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
}, nil
},
},
wantErr: true,
},
{
name: "error replacing external certs for tenant because of malformed encoded certs",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Minio: &models.KeyPairConfiguration{
Crt: &badCrt,
Key: &badKey,
},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
}, nil
},
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
},
wantErr: true,
},
{
name: "error replacing external certs for tenant because of error during secret creation",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Minio: &models.KeyPairConfiguration{
Crt: &crt,
Key: &key,
},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
}, nil
},
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
mockCreateSecret: func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return nil, errors.New("error creating secret")
},
},
wantErr: true,
},
{
name: "certificates replaced but error during deleting existing tenant pods",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Minio: &models.KeyPairConfiguration{
Crt: &crt,
Key: &key,
},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
}, nil
},
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
mockCreateSecret: func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return &v1.Secret{}, nil
},
mockDeletePodCollection: func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
return errors.New("error deleting minio pods")
},
},
wantErr: true,
},
{
name: "error replacing external certs for console because of missing keypair",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Console: &models.KeyPairConfiguration{},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
Console: &operator.ConsoleConfiguration{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
},
}, nil
},
},
wantErr: true,
},
{
name: "certificates replaced but error during deleting existing tenant pods",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateCertificateParams{
Body: &models.TLSConfiguration{
Console: &models.KeyPairConfiguration{
Crt: &crt,
Key: &key,
},
},
},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return &operator.Tenant{
Spec: operator.TenantSpec{
Console: &operator.ConsoleConfiguration{
ExternalCertSecret: &operator.LocalCertificateReference{
Name: "secret",
},
},
},
}, nil
},
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
mockCreateSecret: func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return &v1.Secret{}, nil
},
mockDeletePodCollection: func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
return errors.New("error deleting console pods")
},
},
wantErr: true,
},
}
for _, tt := range tests {
opClientTenantGetMock = tt.args.mockTenantGet
DeleteSecretMock = tt.args.mockDeleteSecret
CreateSecretMock = tt.args.mockCreateSecret
DeletePodCollectionMock = tt.args.mockDeletePodCollection
t.Run(tt.name, func(t *testing.T) {
if err := tenantUpdateCertificates(tt.args.ctx, tt.args.opClient, tt.args.clientSet, tt.args.namespace, tt.args.params); (err != nil) != tt.wantErr {
t.Errorf("tenantUpdateCertificates() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_tenantUpdateEncryption(t *testing.T) {
k8sClient := k8sClientMock{}
opClient := opClientMock{}
type args struct {
ctx context.Context
opClient OperatorClientI
clientSet K8sClientI
namespace string
params admin_api.TenantUpdateEncryptionParams
mockTenantGet func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error)
mockDeleteSecret func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error
mockCreateSecret func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error)
mockDeletePodCollection func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "error updating encryption configuration because of error getting tenant",
args: args{
ctx: context.Background(),
opClient: opClient,
clientSet: k8sClient,
namespace: "",
params: admin_api.TenantUpdateEncryptionParams{},
mockTenantGet: func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*operator.Tenant, error) {
return nil, errors.New("invalid tenant")
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opClientTenantGetMock = tt.args.mockTenantGet
DeleteSecretMock = tt.args.mockDeleteSecret
CreateSecretMock = tt.args.mockCreateSecret
DeletePodCollectionMock = tt.args.mockDeletePodCollection
if err := tenantUpdateEncryption(tt.args.ctx, tt.args.opClient, tt.args.clientSet, tt.args.namespace, tt.args.params); (err != nil) != tt.wantErr {
t.Errorf("tenantUpdateEncryption() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_createOrReplaceKesConfigurationSecrets(t *testing.T) {
k8sClient := k8sClientMock{}
crt := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUzRENDQTBTZ0F3SUJBZ0lRS0pDalNyK0xaMnJrYVo5ODAvZnV5akFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKdFRFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNVVV3UXdZRFZRUUxERHhoYkdWMgpjMnRBVEdWdWFXNXpMVTFoWTBKdmIyc3RVSEp2TG14dlkyRnNJQ2hNWlc1cGJpQkJiR1YyYzJ0cElFaDFaWEowCllTQkJjbWxoY3lreFREQktCZ05WQkFNTVEyMXJZMlZ5ZENCaGJHVjJjMnRBVEdWdWFXNXpMVTFoWTBKdmIyc3QKVUhKdkxteHZZMkZzSUNoTVpXNXBiaUJCYkdWMmMydHBJRWgxWlhKMFlTQkJjbWxoY3lrd0hoY05NVGt3TmpBeApNREF3TURBd1doY05NekF3T0RBeU1ERTFNekEzV2pCaU1TY3dKUVlEVlFRS0V4NXRhMk5sY25RZ1pHVjJaV3h2CmNHMWxiblFnWTJWeWRHbG1hV05oZEdVeE56QTFCZ05WQkFzTUxtRnNaWFp6YTBCMGFXWmhMbXh2WTJGc0lDaE0KWlc1cGJpQkJiR1YyYzJ0cElFaDFaWEowWVNCQmNtbGhjeWt3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQgpEd0F3Z2dFS0FvSUJBUUM2dlhLVyswWmhJQUNycmpxTU9QZ3VSdGpSemk1L0VxK2JvZTJkQ1BpT3djdXo0WFlhCm1rVlcxSkhBS3VTc0I1UHE0QnFSRXFueUhYT0ZROWQ1bEFjRGNDYmhlTFRVb2h2ZkhOWW9SdWRrYkZ3RzAyOFQKdVMxTFNtcHU5VjhFU2Q0Q3BiOGRvUkcvUW8vRTF4RGk5STFhNVhoeUdHTTNsUFlYQU1HU1ZkWUNNNzh0M2ZLTwp0NFVlcEt6d2dFQ2NKRVg4MVFoem1ZT25ELysyazNxSVppZU9nOGFkbDNFUWV2eFBISXlXQ1JkaDJZTkZsRi9rCmEwTzQyRVl2NVNUT1N0dzI2TzMwTVRrcEg0dzRRZm9VV3BnZU93MDZyYTluRHdzV3VhMUZIaHlKZmxOanR1USsKU0NlaDdqTVlVUThYV1JkbXVHbzFRT0g5cjdDKy82L1JhMlZIQWdNQkFBR2pnYmt3Z2JZd0RnWURWUjBQQVFILwpCQVFEQWdXZ01CTUdBMVVkSlFRTU1Bb0dDQ3NHQVFVRkJ3TUJNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqCkJCZ3dGb0FVNHg4NUIyeGVlQzg0UEdvM1dZVWwxRGVvZlFNd1lBWURWUjBSQkZrd1Y0SWpLaTV0YVc1cGJ5MWgKYkdWMmMyc3Ribk11YzNaakxtTnNkWE4wWlhJdWJHOWpZV3lDTUNvdWFHOXVaWGwzWld4c0xXaHNMbTFwYm1sdgpMV0ZzWlhaemF5MXVjeTV6ZG1NdVkyeDFjM1JsY2k1c2IyTmhiREFOQmdrcWhraUc5dzBCQVFzRkFBT0NBWUVBCnZnMFlIVGtzd3Z4NEE5ZXl0b1V3eTBOSFVjQXpuNy9BVCt3WEt6d3Fqa0RiS3hVYlFTMFNQVVI4SkVDMkpuUUgKU3pTNGFCNXRURVRYZUcrbHJZVU02b0RNcXlIalpUN1NJaW83OUNxOFloWWxORU9qMkhvaXdxalczZm10UU9kVApoVG1tS01lRnZleHo2cnRxbHp0cVdKa3kvOGd1MkMrMWw5UDFFUmhFNDZZY0puVmJ5REFTSGNvV2tiZVhFUGErCjNpNFd4bU1yMDlNZXFTNkFUb2NKanFBeEtYcURYWmlFZjRUVEp1ZTRBQ2s4WmhqaEtUUEV6RnQ3WllVaHY3dVoKdlZCOXhla2FqRzdMRU5kSHZncXVMRUxUV3czWkpBaXpSTU5KUUpzZU11LzdQN1NIeXRKTVJYdlVwd2R2dWhDOApKNm54aGRmUS91QVlQY1lHdlU5NUZPZ1NjWVZqNW1WQktXM0ZHbkl2YzZuamQ1OFhBSTE3dlk0K0ZZTnY4M2UxCm9mOGlxRFdWNTFyT1FlbG9FZFdmdHkvZTI2bXVWUUQzQlVjY2Z5SWpGYy9SeGNHdm5maUEwUm1uRDNkWFcyZ0oKUHFTd01ZZ3hxQndqMm1Qck5jdkFWY3BOeWRjTWJKOTFIOHRWY0ttRHMrY1NiV0tnblpmKzUvZm5yaTdmU3FLUQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
key := "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQzZ2WEtXKzBaaElBQ3IKcmpxTU9QZ3VSdGpSemk1L0VxK2JvZTJkQ1BpT3djdXo0WFlhbWtWVzFKSEFLdVNzQjVQcTRCcVJFcW55SFhPRgpROWQ1bEFjRGNDYmhlTFRVb2h2ZkhOWW9SdWRrYkZ3RzAyOFR1UzFMU21wdTlWOEVTZDRDcGI4ZG9SRy9Rby9FCjF4RGk5STFhNVhoeUdHTTNsUFlYQU1HU1ZkWUNNNzh0M2ZLT3Q0VWVwS3p3Z0VDY0pFWDgxUWh6bVlPbkQvKzIKazNxSVppZU9nOGFkbDNFUWV2eFBISXlXQ1JkaDJZTkZsRi9rYTBPNDJFWXY1U1RPU3R3MjZPMzBNVGtwSDR3NApRZm9VV3BnZU93MDZyYTluRHdzV3VhMUZIaHlKZmxOanR1UStTQ2VoN2pNWVVROFhXUmRtdUdvMVFPSDlyN0MrCi82L1JhMlZIQWdNQkFBRUNnZ0VCQUlyTlVFUnJSM2ZmK3IraGhJRS93ekZhbGNUMUZWaDh3aXpUWXJQcnZCMFkKYlZvcVJzZ2xUVTdxTjkvM3dmc2dzdEROZk5IQ1pySEJOR0drK0orMDZMV2tnakhycjdXeFBUaE16ZDRvUGN4RwpRdTBMOGE5ZVlBMXJwY3NOOVc5Um5JU3BRSEk4aTkxM0V6Z0RoOWk2WCt0bFQyNjNNK0JYaDhlM1Z5cDNSTmhpCjZZUTQwcWJsNlQ0TUlyLzRSMGJmcFExZWVMNVNnbHB6Z1d6ZGs4WGtmc0E5YnRiU1RoMjZKRlBPUU1tMm5adkcKbjBlZm85TDZtaktwRW9rRWpTY1hWYTRZNHJaREZFYTVIbkpUSDBHblByQzU1cDhBb3BFN1c1M2RDT3lxd29CcQo4SWtZb2grcm1qTUZrOGc5VlRVYlcwVE9YVTFSY1E1Z0gxWS9jam5uVTRrQ2dZRUE4amw5ZEQyN2JaQ2ZaTW9jCjJYRThiYkJTaFVXTjRvaklKc1lHY2xyTjJDUEtUNmExaVhvS3BrTXdGNUdsODEzQURGR1pTbWtUSUFVQXRXQU8KNzZCcEpGUlVCZ1hmUXhSU0gyS1RaRldxbE5yekZPQjNsT3h2bFJ1amw5eE9ueStyWUhJWC9BOWNqQlp3a2orSAo3MDZRTExvS1ZFL2lMYy9DMlI5VzRLRVI3R3NDZ1lFQXhWd3FyM1JnUXhHUmpueG1zbDZtUW5DQ3k2MXFoUzUxCkJicDJzWldraFNTV0w0YzFDY0NDMnZ2ektPSmJkZ2NZQU43L05sTHgzWjlCZUFjTVZMUEVoc2NPalB6YUlneEMKczJ2UkdwQUFtYnRjWi9MVlpKaERWTjIrSHowRHhnRUtCa1JzQU5XTG51cGRoUWJBdlZuTkVsY29YVlo1cC9qcwp3U1BCelFoSklaVUNnWUJqTUlHY0dTOW9SWUhRRHlmVEx4aVV2bEI4ZktnR2JRYXhRZ1FmemVsZktnRE5yekhGCnN6RXJOblk2SUkxNVpCbWhzY1I1QVNBd3kzdW55a2N6ZjFldTVjMW1qZjhJQkFsQkN1ZmFmVzRWK0xiMEJKdFQKWTZLcHg2Q3RMaTBQNk1CZ0JUaW5JazgrbW0zTXBiRnZvSmRQaVh0elhTYjhwWWhmeXdLVGg4SEVNd0tCZ1FDUwpvR0VPTFpYKy9pUjRDYkI2d0pzaExWbmZYSjJSQ096a0xwNVVYV3IzaURFVWFvMWJDMjJzcUJjRnZ2WllmL2l6ClhQbWJNSkNGS1BhSTZDT2ZJbGZXRWptYlFaZ0dSN21lZDNISkhFZDE3NTg5azBvN0RHeXB0bnl6MUs3akFvNmkKRFY5NFZ5NytCLzBuQWRkY1ZrVm5aTjJXU3RMam1xcTY2NGZtZmt0bTZRS0JnQzBsMk5OVlFWK2N0OFFRRFpINQpBRFIrSGM3Qk0wNDhURGhNTmhoR3JHc2tWNngwMCtMZTdISGdpT3h2NXJudkNlTlY2M001YUZHdFVWbllTN1VoCkE1NndaNVlZeFFnQ0xzNi9PRmZhK3NiTngrSjdnSjRjNXdMZVlJMXVPMTlzZHBHa2VHZ25vK3dXVmxDSzFCbW0KRGM0TXA2STRiUTVtdy93YVpLQnpjRTJLCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
badCrt := "ASD"
badKey := "ASD"
appRole := "ASD"
appSecret := "ASD"
endpoint := "ASD"
type args struct {
ctx context.Context
clientSet K8sClientI
ns string
encryptionCfg *models.EncryptionConfiguration
kesConfigurationSecretName string
kesClientCertSecretName string
tenantName string
mockDeleteSecret func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error
mockCreateSecret func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error)
}
tests := []struct {
name string
args args
want *v1.LocalObjectReference
want1 *operator.LocalCertificateReference
wantErr bool
}{
{
name: "error decoding the client certificate",
args: args{
ctx: context.Background(),
clientSet: k8sClient,
encryptionCfg: &models.EncryptionConfiguration{
Client: &models.KeyPairConfiguration{
Crt: &badCrt,
Key: &badKey,
},
},
ns: "default",
kesConfigurationSecretName: "test-secret",
kesClientCertSecretName: "test-client-secret",
tenantName: "test",
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
},
want: nil,
want1: nil,
wantErr: true,
},
{
name: "error because of malformed decoded certificate",
args: args{
ctx: context.Background(),
clientSet: k8sClient,
encryptionCfg: &models.EncryptionConfiguration{
Client: &models.KeyPairConfiguration{
Crt: &key, // will cause an error because we are passing a private key as the public key
Key: &key,
},
},
ns: "default",
kesConfigurationSecretName: "test-secret",
kesClientCertSecretName: "test-client-secret",
tenantName: "test",
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
},
want: nil,
want1: nil,
wantErr: true,
},
{
name: "error because of malformed decoded certificate",
args: args{
ctx: context.Background(),
clientSet: k8sClient,
encryptionCfg: &models.EncryptionConfiguration{
Client: &models.KeyPairConfiguration{
Crt: &crt,
Key: &key,
},
Vault: &models.VaultConfiguration{
Approle: &models.VaultConfigurationApprole{
Engine: "",
ID: &appRole,
Retry: 0,
Secret: &appSecret,
},
Endpoint: &endpoint,
Engine: "",
Namespace: "",
Prefix: "",
Status: nil,
TLS: &models.VaultConfigurationTLS{
Ca: crt,
Crt: crt,
Key: key,
},
},
},
ns: "default",
kesConfigurationSecretName: "test-secret",
kesClientCertSecretName: "test-client-secret",
tenantName: "test",
mockDeleteSecret: func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return nil
},
mockCreateSecret: func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return &v1.Secret{}, nil
},
},
want: &v1.LocalObjectReference{
Name: "test-secret",
},
want1: &operator.LocalCertificateReference{
Name: "test-client-secret",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
DeleteSecretMock = tt.args.mockDeleteSecret
CreateSecretMock = tt.args.mockCreateSecret
got, got1, err := createOrReplaceKesConfigurationSecrets(tt.args.ctx, tt.args.clientSet, tt.args.ns, tt.args.encryptionCfg, tt.args.kesConfigurationSecretName, tt.args.kesClientCertSecretName, tt.args.tenantName)
if (err != nil) != tt.wantErr {
t.Errorf("createOrReplaceKesConfigurationSecrets() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("createOrReplaceKesConfigurationSecrets() got = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got1, tt.want1) {
t.Errorf("createOrReplaceKesConfigurationSecrets() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

View File

@@ -88,7 +88,7 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
kClient := k8sClientMock{}
type args struct {
ctx context.Context
client K8sClient
client K8sClientI
namespace string
tenantName string
serviceName string
@@ -339,7 +339,7 @@ func Test_deleteTenantAction(t *testing.T) {
opClient := opClientMock{}
type args struct {
ctx context.Context
operatorClient OperatorClient
operatorClient OperatorClientI
nameSpace string
tenantName string
deletePvcs bool
@@ -532,7 +532,7 @@ func Test_TenantAddZone(t *testing.T) {
type args struct {
ctx context.Context
operatorClient OperatorClient
operatorClient OperatorClientI
nameSpace string
mockTenantPatch func(ctx context.Context, namespace string, tenantName string, pt types.PatchType, data []byte, options metav1.PatchOptions) (*v1.Tenant, error)
mockTenantGet func(ctx context.Context, namespace string, tenantName string, options metav1.GetOptions) (*v1.Tenant, error)
@@ -706,7 +706,7 @@ func Test_UpdateTenantAction(t *testing.T) {
type args struct {
ctx context.Context
operatorClient OperatorClient
operatorClient OperatorClientI
httpCl cluster.HTTPClientI
nameSpace string
tenantName string
@@ -871,7 +871,7 @@ func Test_UpdateTenantAction(t *testing.T) {
},
params: admin_api.UpdateTenantParams{
Body: &models.UpdateTenantRequest{
ConsoleImage: "minio/console:v0.3.18",
ConsoleImage: "minio/console:v0.3.19",
},
},
},

View File

@@ -437,18 +437,28 @@ func init() {
}
}
},
"/cluster/resources": {
"/cluster/max-allocatable-memory": {
"get": {
"tags": [
"AdminAPI"
],
"summary": "Get Cluster Resources",
"operationId": "GetClusterResources",
"summary": "Get maximum allocatable memory for given number of nodes",
"operationId": "GetMaxAllocatableMem",
"parameters": [
{
"minimum": 1,
"type": "integer",
"format": "int32",
"name": "num_nodes",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/clusterResources"
"$ref": "#/definitions/maxAllocatableMemResponse"
}
},
"default": {
@@ -1066,6 +1076,90 @@ func init() {
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/certificates": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Tenant Update Certificates",
"operationId": "TenantUpdateCertificate",
"parameters": [
{
"type": "string",
"name": "namespace",
"in": "path",
"required": true
},
{
"type": "string",
"name": "tenant",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/tlsConfiguration"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/encryption": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Tenant Update Encryption",
"operationId": "TenantUpdateEncryption",
"parameters": [
{
"type": "string",
"name": "namespace",
"in": "path",
"required": true
},
{
"type": "string",
"name": "tenant",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/encryptionConfiguration"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/usage": {
"get": {
"tags": [
@@ -2044,17 +2138,6 @@ func init() {
}
}
},
"clusterResources": {
"type": "object",
"properties": {
"nodes": {
"type": "array",
"items": {
"$ref": "#/definitions/nodeInfo"
}
}
}
},
"configDescription": {
"type": "object",
"properties": {
@@ -2594,30 +2677,12 @@ func init() {
}
}
},
"nodeInfo": {
"maxAllocatableMemResponse": {
"type": "object",
"properties": {
"allocatable_resources": {
"description": "Represents the resources of a node that are available for scheduling.",
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int64"
}
},
"name": {
"type": "string"
},
"taints": {
"$ref": "#/definitions/nodeTaints"
},
"total_resources": {
"description": "Represents the total resources of a node.",
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int64"
}
"max_memory": {
"type": "integer",
"format": "int64"
}
}
},
@@ -2685,29 +2750,6 @@ func init() {
}
}
},
"nodeTaints": {
"type": "object",
"properties": {
"no_execute": {
"type": "array",
"items": {
"type": "string"
}
},
"no_schedule": {
"type": "array",
"items": {
"type": "string"
}
},
"prefer_no_schedule": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"nofiticationService": {
"type": "string",
"enum": [
@@ -4019,18 +4061,28 @@ func init() {
}
}
},
"/cluster/resources": {
"/cluster/max-allocatable-memory": {
"get": {
"tags": [
"AdminAPI"
],
"summary": "Get Cluster Resources",
"operationId": "GetClusterResources",
"summary": "Get maximum allocatable memory for given number of nodes",
"operationId": "GetMaxAllocatableMem",
"parameters": [
{
"minimum": 1,
"type": "integer",
"format": "int32",
"name": "num_nodes",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/clusterResources"
"$ref": "#/definitions/maxAllocatableMemResponse"
}
},
"default": {
@@ -4648,6 +4700,90 @@ func init() {
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/certificates": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Tenant Update Certificates",
"operationId": "TenantUpdateCertificate",
"parameters": [
{
"type": "string",
"name": "namespace",
"in": "path",
"required": true
},
{
"type": "string",
"name": "tenant",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/tlsConfiguration"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/encryption": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Tenant Update Encryption",
"operationId": "TenantUpdateEncryption",
"parameters": [
{
"type": "string",
"name": "namespace",
"in": "path",
"required": true
},
{
"type": "string",
"name": "tenant",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/encryptionConfiguration"
}
}
],
"responses": {
"201": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/namespaces/{namespace}/tenants/{tenant}/usage": {
"get": {
"tags": [
@@ -6143,17 +6279,6 @@ func init() {
}
}
},
"clusterResources": {
"type": "object",
"properties": {
"nodes": {
"type": "array",
"items": {
"$ref": "#/definitions/nodeInfo"
}
}
}
},
"configDescription": {
"type": "object",
"properties": {
@@ -6693,30 +6818,12 @@ func init() {
}
}
},
"nodeInfo": {
"maxAllocatableMemResponse": {
"type": "object",
"properties": {
"allocatable_resources": {
"description": "Represents the resources of a node that are available for scheduling.",
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int64"
}
},
"name": {
"type": "string"
},
"taints": {
"$ref": "#/definitions/nodeTaints"
},
"total_resources": {
"description": "Represents the total resources of a node.",
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int64"
}
"max_memory": {
"type": "integer",
"format": "int64"
}
}
},
@@ -6740,29 +6847,6 @@ func init() {
}
}
},
"nodeTaints": {
"type": "object",
"properties": {
"no_execute": {
"type": "array",
"items": {
"type": "string"
}
},
"no_schedule": {
"type": "array",
"items": {
"type": "string"
}
},
"prefer_no_schedule": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"nofiticationService": {
"type": "string",
"enum": [

View File

@@ -24,13 +24,16 @@ import (
"k8s.io/client-go/kubernetes"
)
// K8sClient interface with all functions to be implemented
// by mock when testing, it should include all K8sClient respective api calls
// K8sClientI interface with all functions to be implemented
// by mock when testing, it should include all K8sClientI respective api calls
// that are used within this project.
type K8sClient interface {
type K8sClientI interface {
getResourceQuota(ctx context.Context, namespace, resource string, opts metav1.GetOptions) (*v1.ResourceQuota, error)
getSecret(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*v1.Secret, error)
getService(ctx context.Context, namespace, serviceName string, opts metav1.GetOptions) (*v1.Service, error)
deletePodCollection(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
deleteSecret(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error
createSecret(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error)
}
// Interface implementation
@@ -51,3 +54,15 @@ func (c *k8sClient) getSecret(ctx context.Context, namespace, secretName string,
func (c *k8sClient) getService(ctx context.Context, namespace, serviceName string, opts metav1.GetOptions) (*v1.Service, error) {
return c.client.CoreV1().Services(namespace).Get(ctx, serviceName, opts)
}
func (c *k8sClient) deletePodCollection(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
return c.client.CoreV1().Pods(namespace).DeleteCollection(ctx, opts, listOpts)
}
func (c *k8sClient) deleteSecret(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
return c.client.CoreV1().Secrets(namespace).Delete(ctx, name, opts)
}
func (c *k8sClient) createSecret(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
return c.client.CoreV1().Secrets(namespace).Create(ctx, secret, opts)
}

View File

@@ -1,62 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewGetClusterResourcesParams creates a new GetClusterResourcesParams object
// no default values defined in spec.
func NewGetClusterResourcesParams() GetClusterResourcesParams {
return GetClusterResourcesParams{}
}
// GetClusterResourcesParams contains all the bound params for the get cluster resources operation
// typically these are obtained from a http.Request
//
// swagger:parameters GetClusterResources
type GetClusterResourcesParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewGetClusterResourcesParams() beforehand.
func (o *GetClusterResourcesParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -1,133 +0,0 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// GetClusterResourcesOKCode is the HTTP code returned for type GetClusterResourcesOK
const GetClusterResourcesOKCode int = 200
/*GetClusterResourcesOK A successful response.
swagger:response getClusterResourcesOK
*/
type GetClusterResourcesOK struct {
/*
In: Body
*/
Payload *models.ClusterResources `json:"body,omitempty"`
}
// NewGetClusterResourcesOK creates GetClusterResourcesOK with default headers values
func NewGetClusterResourcesOK() *GetClusterResourcesOK {
return &GetClusterResourcesOK{}
}
// WithPayload adds the payload to the get cluster resources o k response
func (o *GetClusterResourcesOK) WithPayload(payload *models.ClusterResources) *GetClusterResourcesOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get cluster resources o k response
func (o *GetClusterResourcesOK) SetPayload(payload *models.ClusterResources) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetClusterResourcesOK) 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
}
}
}
/*GetClusterResourcesDefault Generic error response.
swagger:response getClusterResourcesDefault
*/
type GetClusterResourcesDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewGetClusterResourcesDefault creates GetClusterResourcesDefault with default headers values
func NewGetClusterResourcesDefault(code int) *GetClusterResourcesDefault {
if code <= 0 {
code = 500
}
return &GetClusterResourcesDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the get cluster resources default response
func (o *GetClusterResourcesDefault) WithStatusCode(code int) *GetClusterResourcesDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the get cluster resources default response
func (o *GetClusterResourcesDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the get cluster resources default response
func (o *GetClusterResourcesDefault) WithPayload(payload *models.Error) *GetClusterResourcesDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get cluster resources default response
func (o *GetClusterResourcesDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetClusterResourcesDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -30,40 +30,40 @@ import (
"github.com/minio/console/models"
)
// GetClusterResourcesHandlerFunc turns a function with the right signature into a get cluster resources handler
type GetClusterResourcesHandlerFunc func(GetClusterResourcesParams, *models.Principal) middleware.Responder
// GetMaxAllocatableMemHandlerFunc turns a function with the right signature into a get max allocatable mem handler
type GetMaxAllocatableMemHandlerFunc func(GetMaxAllocatableMemParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn GetClusterResourcesHandlerFunc) Handle(params GetClusterResourcesParams, principal *models.Principal) middleware.Responder {
func (fn GetMaxAllocatableMemHandlerFunc) Handle(params GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// GetClusterResourcesHandler interface for that can handle valid get cluster resources params
type GetClusterResourcesHandler interface {
Handle(GetClusterResourcesParams, *models.Principal) middleware.Responder
// GetMaxAllocatableMemHandler interface for that can handle valid get max allocatable mem params
type GetMaxAllocatableMemHandler interface {
Handle(GetMaxAllocatableMemParams, *models.Principal) middleware.Responder
}
// NewGetClusterResources creates a new http.Handler for the get cluster resources operation
func NewGetClusterResources(ctx *middleware.Context, handler GetClusterResourcesHandler) *GetClusterResources {
return &GetClusterResources{Context: ctx, Handler: handler}
// NewGetMaxAllocatableMem creates a new http.Handler for the get max allocatable mem operation
func NewGetMaxAllocatableMem(ctx *middleware.Context, handler GetMaxAllocatableMemHandler) *GetMaxAllocatableMem {
return &GetMaxAllocatableMem{Context: ctx, Handler: handler}
}
/*GetClusterResources swagger:route GET /cluster/resources AdminAPI getClusterResources
/*GetMaxAllocatableMem swagger:route GET /cluster/max-allocatable-memory AdminAPI getMaxAllocatableMem
Get Cluster Resources
Get maximum allocatable memory for given number of nodes
*/
type GetClusterResources struct {
type GetMaxAllocatableMem struct {
Context *middleware.Context
Handler GetClusterResourcesHandler
Handler GetMaxAllocatableMemHandler
}
func (o *GetClusterResources) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
func (o *GetMaxAllocatableMem) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewGetClusterResourcesParams()
var Params = NewGetMaxAllocatableMemParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {

View File

@@ -0,0 +1,119 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// NewGetMaxAllocatableMemParams creates a new GetMaxAllocatableMemParams object
// no default values defined in spec.
func NewGetMaxAllocatableMemParams() GetMaxAllocatableMemParams {
return GetMaxAllocatableMemParams{}
}
// GetMaxAllocatableMemParams contains all the bound params for the get max allocatable mem operation
// typically these are obtained from a http.Request
//
// swagger:parameters GetMaxAllocatableMem
type GetMaxAllocatableMemParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
Minimum: 1
In: query
*/
NumNodes int32
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewGetMaxAllocatableMemParams() beforehand.
func (o *GetMaxAllocatableMemParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
qs := runtime.Values(r.URL.Query())
qNumNodes, qhkNumNodes, _ := qs.GetOK("num_nodes")
if err := o.bindNumNodes(qNumNodes, qhkNumNodes, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindNumNodes binds and validates parameter NumNodes from query.
func (o *GetMaxAllocatableMemParams) bindNumNodes(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("num_nodes", "query", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// AllowEmptyValue: false
if err := validate.RequiredString("num_nodes", "query", raw); err != nil {
return err
}
value, err := swag.ConvertInt32(raw)
if err != nil {
return errors.InvalidType("num_nodes", "query", "int32", raw)
}
o.NumNodes = value
if err := o.validateNumNodes(formats); err != nil {
return err
}
return nil
}
// validateNumNodes carries on validations for parameter NumNodes
func (o *GetMaxAllocatableMemParams) validateNumNodes(formats strfmt.Registry) error {
if err := validate.MinimumInt("num_nodes", "query", int64(o.NumNodes), 1, false); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,133 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// GetMaxAllocatableMemOKCode is the HTTP code returned for type GetMaxAllocatableMemOK
const GetMaxAllocatableMemOKCode int = 200
/*GetMaxAllocatableMemOK A successful response.
swagger:response getMaxAllocatableMemOK
*/
type GetMaxAllocatableMemOK struct {
/*
In: Body
*/
Payload *models.MaxAllocatableMemResponse `json:"body,omitempty"`
}
// NewGetMaxAllocatableMemOK creates GetMaxAllocatableMemOK with default headers values
func NewGetMaxAllocatableMemOK() *GetMaxAllocatableMemOK {
return &GetMaxAllocatableMemOK{}
}
// WithPayload adds the payload to the get max allocatable mem o k response
func (o *GetMaxAllocatableMemOK) WithPayload(payload *models.MaxAllocatableMemResponse) *GetMaxAllocatableMemOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get max allocatable mem o k response
func (o *GetMaxAllocatableMemOK) SetPayload(payload *models.MaxAllocatableMemResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetMaxAllocatableMemOK) 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
}
}
}
/*GetMaxAllocatableMemDefault Generic error response.
swagger:response getMaxAllocatableMemDefault
*/
type GetMaxAllocatableMemDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewGetMaxAllocatableMemDefault creates GetMaxAllocatableMemDefault with default headers values
func NewGetMaxAllocatableMemDefault(code int) *GetMaxAllocatableMemDefault {
if code <= 0 {
code = 500
}
return &GetMaxAllocatableMemDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the get max allocatable mem default response
func (o *GetMaxAllocatableMemDefault) WithStatusCode(code int) *GetMaxAllocatableMemDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the get max allocatable mem default response
func (o *GetMaxAllocatableMemDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the get max allocatable mem default response
func (o *GetMaxAllocatableMemDefault) WithPayload(payload *models.Error) *GetMaxAllocatableMemDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get max allocatable mem default response
func (o *GetMaxAllocatableMemDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetMaxAllocatableMemDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -26,17 +26,23 @@ import (
"errors"
"net/url"
golangswaggerpaths "path"
"github.com/go-openapi/swag"
)
// GetClusterResourcesURL generates an URL for the get cluster resources operation
type GetClusterResourcesURL struct {
// GetMaxAllocatableMemURL generates an URL for the get max allocatable mem operation
type GetMaxAllocatableMemURL struct {
NumNodes int32
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *GetClusterResourcesURL) WithBasePath(bp string) *GetClusterResourcesURL {
func (o *GetMaxAllocatableMemURL) WithBasePath(bp string) *GetMaxAllocatableMemURL {
o.SetBasePath(bp)
return o
}
@@ -44,15 +50,15 @@ func (o *GetClusterResourcesURL) WithBasePath(bp string) *GetClusterResourcesURL
// 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 *GetClusterResourcesURL) SetBasePath(bp string) {
func (o *GetMaxAllocatableMemURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *GetClusterResourcesURL) Build() (*url.URL, error) {
func (o *GetMaxAllocatableMemURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/cluster/resources"
var _path = "/cluster/max-allocatable-memory"
_basePath := o._basePath
if _basePath == "" {
@@ -60,11 +66,20 @@ func (o *GetClusterResourcesURL) Build() (*url.URL, error) {
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
qs := make(url.Values)
numNodesQ := swag.FormatInt32(o.NumNodes)
if numNodesQ != "" {
qs.Set("num_nodes", numNodesQ)
}
_result.RawQuery = qs.Encode()
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *GetClusterResourcesURL) Must(u *url.URL, err error) *url.URL {
func (o *GetMaxAllocatableMemURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
@@ -75,17 +90,17 @@ func (o *GetClusterResourcesURL) Must(u *url.URL, err error) *url.URL {
}
// String returns the string representation of the path with query string
func (o *GetClusterResourcesURL) String() string {
func (o *GetMaxAllocatableMemURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *GetClusterResourcesURL) BuildFull(scheme, host string) (*url.URL, error) {
func (o *GetMaxAllocatableMemURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on GetClusterResourcesURL")
return nil, errors.New("scheme is required for a full url on GetMaxAllocatableMemURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on GetClusterResourcesURL")
return nil, errors.New("host is required for a full url on GetMaxAllocatableMemURL")
}
base, err := o.Build()
@@ -99,6 +114,6 @@ func (o *GetClusterResourcesURL) BuildFull(scheme, host string) (*url.URL, error
}
// StringFull returns the string representation of a complete url
func (o *GetClusterResourcesURL) StringFull(scheme, host string) string {
func (o *GetMaxAllocatableMemURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,90 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
)
// TenantUpdateCertificateHandlerFunc turns a function with the right signature into a tenant update certificate handler
type TenantUpdateCertificateHandlerFunc func(TenantUpdateCertificateParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn TenantUpdateCertificateHandlerFunc) Handle(params TenantUpdateCertificateParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// TenantUpdateCertificateHandler interface for that can handle valid tenant update certificate params
type TenantUpdateCertificateHandler interface {
Handle(TenantUpdateCertificateParams, *models.Principal) middleware.Responder
}
// NewTenantUpdateCertificate creates a new http.Handler for the tenant update certificate operation
func NewTenantUpdateCertificate(ctx *middleware.Context, handler TenantUpdateCertificateHandler) *TenantUpdateCertificate {
return &TenantUpdateCertificate{Context: ctx, Handler: handler}
}
/*TenantUpdateCertificate swagger:route PUT /namespaces/{namespace}/tenants/{tenant}/certificates AdminAPI tenantUpdateCertificate
Tenant Update Certificates
*/
type TenantUpdateCertificate struct {
Context *middleware.Context
Handler TenantUpdateCertificateHandler
}
func (o *TenantUpdateCertificate) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewTenantUpdateCertificateParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,145 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"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/minio/console/models"
)
// NewTenantUpdateCertificateParams creates a new TenantUpdateCertificateParams object
// no default values defined in spec.
func NewTenantUpdateCertificateParams() TenantUpdateCertificateParams {
return TenantUpdateCertificateParams{}
}
// TenantUpdateCertificateParams contains all the bound params for the tenant update certificate operation
// typically these are obtained from a http.Request
//
// swagger:parameters TenantUpdateCertificate
type TenantUpdateCertificateParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.TLSConfiguration
/*
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 NewTenantUpdateCertificateParams() beforehand.
func (o *TenantUpdateCertificateParams) 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.TLSConfiguration
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body", ""))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "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 *TenantUpdateCertificateParams) 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 *TenantUpdateCertificateParams) 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
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// TenantUpdateCertificateCreatedCode is the HTTP code returned for type TenantUpdateCertificateCreated
const TenantUpdateCertificateCreatedCode int = 201
/*TenantUpdateCertificateCreated A successful response.
swagger:response tenantUpdateCertificateCreated
*/
type TenantUpdateCertificateCreated struct {
}
// NewTenantUpdateCertificateCreated creates TenantUpdateCertificateCreated with default headers values
func NewTenantUpdateCertificateCreated() *TenantUpdateCertificateCreated {
return &TenantUpdateCertificateCreated{}
}
// WriteResponse to the client
func (o *TenantUpdateCertificateCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(201)
}
/*TenantUpdateCertificateDefault Generic error response.
swagger:response tenantUpdateCertificateDefault
*/
type TenantUpdateCertificateDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewTenantUpdateCertificateDefault creates TenantUpdateCertificateDefault with default headers values
func NewTenantUpdateCertificateDefault(code int) *TenantUpdateCertificateDefault {
if code <= 0 {
code = 500
}
return &TenantUpdateCertificateDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the tenant update certificate default response
func (o *TenantUpdateCertificateDefault) WithStatusCode(code int) *TenantUpdateCertificateDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the tenant update certificate default response
func (o *TenantUpdateCertificateDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the tenant update certificate default response
func (o *TenantUpdateCertificateDefault) WithPayload(payload *models.Error) *TenantUpdateCertificateDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the tenant update certificate default response
func (o *TenantUpdateCertificateDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *TenantUpdateCertificateDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,124 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
)
// TenantUpdateCertificateURL generates an URL for the tenant update certificate operation
type TenantUpdateCertificateURL 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 *TenantUpdateCertificateURL) WithBasePath(bp string) *TenantUpdateCertificateURL {
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 *TenantUpdateCertificateURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *TenantUpdateCertificateURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/namespaces/{namespace}/tenants/{tenant}/certificates"
namespace := o.Namespace
if namespace != "" {
_path = strings.Replace(_path, "{namespace}", namespace, -1)
} else {
return nil, errors.New("namespace is required on TenantUpdateCertificateURL")
}
tenant := o.Tenant
if tenant != "" {
_path = strings.Replace(_path, "{tenant}", tenant, -1)
} else {
return nil, errors.New("tenant is required on TenantUpdateCertificateURL")
}
_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 *TenantUpdateCertificateURL) 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 *TenantUpdateCertificateURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *TenantUpdateCertificateURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on TenantUpdateCertificateURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on TenantUpdateCertificateURL")
}
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 *TenantUpdateCertificateURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,90 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
)
// TenantUpdateEncryptionHandlerFunc turns a function with the right signature into a tenant update encryption handler
type TenantUpdateEncryptionHandlerFunc func(TenantUpdateEncryptionParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn TenantUpdateEncryptionHandlerFunc) Handle(params TenantUpdateEncryptionParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// TenantUpdateEncryptionHandler interface for that can handle valid tenant update encryption params
type TenantUpdateEncryptionHandler interface {
Handle(TenantUpdateEncryptionParams, *models.Principal) middleware.Responder
}
// NewTenantUpdateEncryption creates a new http.Handler for the tenant update encryption operation
func NewTenantUpdateEncryption(ctx *middleware.Context, handler TenantUpdateEncryptionHandler) *TenantUpdateEncryption {
return &TenantUpdateEncryption{Context: ctx, Handler: handler}
}
/*TenantUpdateEncryption swagger:route PUT /namespaces/{namespace}/tenants/{tenant}/encryption AdminAPI tenantUpdateEncryption
Tenant Update Encryption
*/
type TenantUpdateEncryption struct {
Context *middleware.Context
Handler TenantUpdateEncryptionHandler
}
func (o *TenantUpdateEncryption) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewTenantUpdateEncryptionParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,145 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"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/minio/console/models"
)
// NewTenantUpdateEncryptionParams creates a new TenantUpdateEncryptionParams object
// no default values defined in spec.
func NewTenantUpdateEncryptionParams() TenantUpdateEncryptionParams {
return TenantUpdateEncryptionParams{}
}
// TenantUpdateEncryptionParams contains all the bound params for the tenant update encryption operation
// typically these are obtained from a http.Request
//
// swagger:parameters TenantUpdateEncryption
type TenantUpdateEncryptionParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.EncryptionConfiguration
/*
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 NewTenantUpdateEncryptionParams() beforehand.
func (o *TenantUpdateEncryptionParams) 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.EncryptionConfiguration
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body", ""))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "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 *TenantUpdateEncryptionParams) 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 *TenantUpdateEncryptionParams) 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
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/console/models"
)
// TenantUpdateEncryptionCreatedCode is the HTTP code returned for type TenantUpdateEncryptionCreated
const TenantUpdateEncryptionCreatedCode int = 201
/*TenantUpdateEncryptionCreated A successful response.
swagger:response tenantUpdateEncryptionCreated
*/
type TenantUpdateEncryptionCreated struct {
}
// NewTenantUpdateEncryptionCreated creates TenantUpdateEncryptionCreated with default headers values
func NewTenantUpdateEncryptionCreated() *TenantUpdateEncryptionCreated {
return &TenantUpdateEncryptionCreated{}
}
// WriteResponse to the client
func (o *TenantUpdateEncryptionCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(201)
}
/*TenantUpdateEncryptionDefault Generic error response.
swagger:response tenantUpdateEncryptionDefault
*/
type TenantUpdateEncryptionDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewTenantUpdateEncryptionDefault creates TenantUpdateEncryptionDefault with default headers values
func NewTenantUpdateEncryptionDefault(code int) *TenantUpdateEncryptionDefault {
if code <= 0 {
code = 500
}
return &TenantUpdateEncryptionDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the tenant update encryption default response
func (o *TenantUpdateEncryptionDefault) WithStatusCode(code int) *TenantUpdateEncryptionDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the tenant update encryption default response
func (o *TenantUpdateEncryptionDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the tenant update encryption default response
func (o *TenantUpdateEncryptionDefault) WithPayload(payload *models.Error) *TenantUpdateEncryptionDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the tenant update encryption default response
func (o *TenantUpdateEncryptionDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *TenantUpdateEncryptionDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,124 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
"strings"
)
// TenantUpdateEncryptionURL generates an URL for the tenant update encryption operation
type TenantUpdateEncryptionURL 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 *TenantUpdateEncryptionURL) WithBasePath(bp string) *TenantUpdateEncryptionURL {
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 *TenantUpdateEncryptionURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *TenantUpdateEncryptionURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/namespaces/{namespace}/tenants/{tenant}/encryption"
namespace := o.Namespace
if namespace != "" {
_path = strings.Replace(_path, "{namespace}", namespace, -1)
} else {
return nil, errors.New("namespace is required on TenantUpdateEncryptionURL")
}
tenant := o.Tenant
if tenant != "" {
_path = strings.Replace(_path, "{tenant}", tenant, -1)
} else {
return nil, errors.New("tenant is required on TenantUpdateEncryptionURL")
}
_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 *TenantUpdateEncryptionURL) 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 *TenantUpdateEncryptionURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *TenantUpdateEncryptionURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on TenantUpdateEncryptionURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on TenantUpdateEncryptionURL")
}
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 *TenantUpdateEncryptionURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -114,8 +114,8 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
AdminAPIDeleteTenantHandler: admin_api.DeleteTenantHandlerFunc(func(params admin_api.DeleteTenantParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.DeleteTenant has not yet been implemented")
}),
AdminAPIGetClusterResourcesHandler: admin_api.GetClusterResourcesHandlerFunc(func(params admin_api.GetClusterResourcesParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.GetClusterResources has not yet been implemented")
AdminAPIGetMaxAllocatableMemHandler: admin_api.GetMaxAllocatableMemHandlerFunc(func(params admin_api.GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.GetMaxAllocatableMem has not yet been implemented")
}),
AdminAPIGetResourceQuotaHandler: admin_api.GetResourceQuotaHandlerFunc(func(params admin_api.GetResourceQuotaParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.GetResourceQuota has not yet been implemented")
@@ -213,6 +213,12 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
AdminAPITenantInfoHandler: admin_api.TenantInfoHandlerFunc(func(params admin_api.TenantInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantInfo has not yet been implemented")
}),
AdminAPITenantUpdateCertificateHandler: admin_api.TenantUpdateCertificateHandlerFunc(func(params admin_api.TenantUpdateCertificateParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantUpdateCertificate has not yet been implemented")
}),
AdminAPITenantUpdateEncryptionHandler: admin_api.TenantUpdateEncryptionHandlerFunc(func(params admin_api.TenantUpdateEncryptionParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantUpdateEncryption has not yet been implemented")
}),
AdminAPITenantUpdateZonesHandler: admin_api.TenantUpdateZonesHandlerFunc(func(params admin_api.TenantUpdateZonesParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.TenantUpdateZones has not yet been implemented")
}),
@@ -311,8 +317,8 @@ type ConsoleAPI struct {
UserAPIDeleteServiceAccountHandler user_api.DeleteServiceAccountHandler
// AdminAPIDeleteTenantHandler sets the operation handler for the delete tenant operation
AdminAPIDeleteTenantHandler admin_api.DeleteTenantHandler
// AdminAPIGetClusterResourcesHandler sets the operation handler for the get cluster resources operation
AdminAPIGetClusterResourcesHandler admin_api.GetClusterResourcesHandler
// AdminAPIGetMaxAllocatableMemHandler sets the operation handler for the get max allocatable mem operation
AdminAPIGetMaxAllocatableMemHandler admin_api.GetMaxAllocatableMemHandler
// AdminAPIGetResourceQuotaHandler sets the operation handler for the get resource quota operation
AdminAPIGetResourceQuotaHandler admin_api.GetResourceQuotaHandler
// AdminAPIGetTenantUsageHandler sets the operation handler for the get tenant usage operation
@@ -377,6 +383,10 @@ type ConsoleAPI struct {
AdminAPITenantAddZoneHandler admin_api.TenantAddZoneHandler
// AdminAPITenantInfoHandler sets the operation handler for the tenant info operation
AdminAPITenantInfoHandler admin_api.TenantInfoHandler
// AdminAPITenantUpdateCertificateHandler sets the operation handler for the tenant update certificate operation
AdminAPITenantUpdateCertificateHandler admin_api.TenantUpdateCertificateHandler
// AdminAPITenantUpdateEncryptionHandler sets the operation handler for the tenant update encryption operation
AdminAPITenantUpdateEncryptionHandler admin_api.TenantUpdateEncryptionHandler
// AdminAPITenantUpdateZonesHandler sets the operation handler for the tenant update zones operation
AdminAPITenantUpdateZonesHandler admin_api.TenantUpdateZonesHandler
// AdminAPIUpdateGroupHandler sets the operation handler for the update group operation
@@ -511,8 +521,8 @@ func (o *ConsoleAPI) Validate() error {
if o.AdminAPIDeleteTenantHandler == nil {
unregistered = append(unregistered, "admin_api.DeleteTenantHandler")
}
if o.AdminAPIGetClusterResourcesHandler == nil {
unregistered = append(unregistered, "admin_api.GetClusterResourcesHandler")
if o.AdminAPIGetMaxAllocatableMemHandler == nil {
unregistered = append(unregistered, "admin_api.GetMaxAllocatableMemHandler")
}
if o.AdminAPIGetResourceQuotaHandler == nil {
unregistered = append(unregistered, "admin_api.GetResourceQuotaHandler")
@@ -610,6 +620,12 @@ func (o *ConsoleAPI) Validate() error {
if o.AdminAPITenantInfoHandler == nil {
unregistered = append(unregistered, "admin_api.TenantInfoHandler")
}
if o.AdminAPITenantUpdateCertificateHandler == nil {
unregistered = append(unregistered, "admin_api.TenantUpdateCertificateHandler")
}
if o.AdminAPITenantUpdateEncryptionHandler == nil {
unregistered = append(unregistered, "admin_api.TenantUpdateEncryptionHandler")
}
if o.AdminAPITenantUpdateZonesHandler == nil {
unregistered = append(unregistered, "admin_api.TenantUpdateZonesHandler")
}
@@ -796,7 +812,7 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/cluster/resources"] = admin_api.NewGetClusterResources(o.context, o.AdminAPIGetClusterResourcesHandler)
o.handlers["GET"]["/cluster/max-allocatable-memory"] = admin_api.NewGetMaxAllocatableMem(o.context, o.AdminAPIGetMaxAllocatableMemHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
@@ -928,6 +944,14 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/namespaces/{namespace}/tenants/{tenant}/certificates"] = admin_api.NewTenantUpdateCertificate(o.context, o.AdminAPITenantUpdateCertificateHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/namespaces/{namespace}/tenants/{tenant}/encryption"] = admin_api.NewTenantUpdateEncryption(o.context, o.AdminAPITenantUpdateEncryptionHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/namespaces/{namespace}/tenants/{tenant}/zones"] = admin_api.NewTenantUpdateZones(o.context, o.AdminAPITenantUpdateZonesHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)

View File

@@ -25,10 +25,10 @@ import (
types "k8s.io/apimachinery/pkg/types"
)
// OperatorClient interface with all functions to be implemented
// by mock when testing, it should include all OperatorClient respective api calls
// OperatorClientI interface with all functions to be implemented
// by mock when testing, it should include all OperatorClientI respective api calls
// that are used within this project.
type OperatorClient interface {
type OperatorClientI interface {
TenantDelete(ctx context.Context, namespace string, instanceName string, options metav1.DeleteOptions) error
TenantGet(ctx context.Context, namespace string, instanceName string, options metav1.GetOptions) (*v1.Tenant, error)
TenantPatch(ctx context.Context, namespace string, instanceName string, pt types.PatchType, data []byte, options metav1.PatchOptions) (*v1.Tenant, error)

View File

@@ -42,7 +42,7 @@ func registerResourceQuotaHandlers(api *operations.ConsoleAPI) {
})
}
func getResourceQuota(ctx context.Context, client K8sClient, namespace, resourcequota string) (*models.ResourceQuota, error) {
func getResourceQuota(ctx context.Context, client K8sClientI, namespace, resourcequota string) (*models.ResourceQuota, error) {
resourceQuota, err := client.getResourceQuota(ctx, namespace, resourcequota, metav1.GetOptions{})
if err != nil {
return nil, err

View File

@@ -48,7 +48,7 @@ func Test_ResourceQuota(t *testing.T) {
kClient := k8sClientMock{}
type args struct {
ctx context.Context
client K8sClient
client K8sClientI
}
tests := []struct {
name string

View File

@@ -1202,6 +1202,62 @@ paths:
tags:
- AdminAPI
/namespaces/{namespace}/tenants/{tenant}/certificates:
put:
summary: Tenant Update Certificates
operationId: TenantUpdateCertificate
parameters:
- name: namespace
in: path
required: true
type: string
- name: tenant
in: path
required: true
type: string
- name: body
in: body
required: true
schema:
$ref: "#/definitions/tlsConfiguration"
responses:
201:
description: A successful response.
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- AdminAPI
/namespaces/{namespace}/tenants/{tenant}/encryption:
put:
summary: Tenant Update Encryption
operationId: TenantUpdateEncryption
parameters:
- name: namespace
in: path
required: true
type: string
- name: tenant
in: path
required: true
type: string
- name: body
in: body
required: true
schema:
$ref: "#/definitions/encryptionConfiguration"
responses:
201:
description: A successful response.
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- AdminAPI
/namespaces/{namespace}/resourcequotas/{resource-quota-name}:
get:
summary: Get Resource Quota
@@ -1227,15 +1283,22 @@ paths:
tags:
- AdminAPI
/cluster/resources:
/cluster/max-allocatable-memory:
get:
summary: Get Cluster Resources
operationId: GetClusterResources
summary: Get maximum allocatable memory for given number of nodes
operationId: GetMaxAllocatableMem
parameters:
- name: num_nodes
in: query
required: true
type: integer
format: int32
minimum: 1
responses:
200:
description: A successful response.
schema:
$ref: "#/definitions/clusterResources"
$ref: "#/definitions/maxAllocatableMemResponse"
default:
description: Generic error response.
schema:
@@ -2589,50 +2652,10 @@ definitions:
type: array
items:
$ref: "#/definitions/zone"
clusterResources:
type: object
properties:
nodes:
type: array
items:
$ref: "#/definitions/nodeInfo"
nodeInfo:
maxAllocatableMemResponse:
type: object
properties:
name:
type: string
taints:
$ref: "#/definitions/nodeTaints"
allocatable_resources:
type: object
additionalProperties:
type: integer
format: int64
description: Represents the resources of a node that are available for scheduling.
total_resources:
type: object
additionalProperties:
type: integer
format: int64
description: Represents the total resources of a node.
nodeTaints:
type: object
properties:
no_schedule:
type: array
items:
type: string
no_execute:
type: array
items:
type: string
prefer_no_schedule:
type: array
items:
type: string
max_memory:
type: integer
format: int64