2020-07-01 18:03:22 -07:00
|
|
|
// This file is part of MinIO Kubernetes Cloud
|
2021-01-19 17:04:13 -06:00
|
|
|
// Copyright (c) 2021 MinIO, Inc.
|
2020-07-01 18:03:22 -07:00
|
|
|
//
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
package operatorapi
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
import (
|
2021-05-10 15:27:52 -07:00
|
|
|
"bytes"
|
2020-07-01 18:03:22 -07:00
|
|
|
"context"
|
2021-06-17 17:30:20 -07:00
|
|
|
"crypto/x509"
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
"encoding/base64"
|
2020-07-01 18:03:22 -07:00
|
|
|
"encoding/json"
|
2021-06-17 17:30:20 -07:00
|
|
|
"encoding/pem"
|
2020-07-27 14:06:12 -07:00
|
|
|
"errors"
|
2020-07-01 18:03:22 -07:00
|
|
|
"fmt"
|
2020-07-09 12:24:01 -07:00
|
|
|
"net"
|
2020-07-01 18:03:22 -07:00
|
|
|
"net/http"
|
|
|
|
|
"os"
|
2021-06-17 16:24:22 -07:00
|
|
|
"sort"
|
2020-07-09 12:24:01 -07:00
|
|
|
"strconv"
|
2020-07-01 18:03:22 -07:00
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2021-07-21 14:05:40 -07:00
|
|
|
"github.com/dustin/go-humanize"
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
"github.com/minio/console/restapi"
|
|
|
|
|
|
|
|
|
|
"github.com/minio/console/operatorapi/operations/operator_api"
|
|
|
|
|
|
2021-06-17 17:30:20 -07:00
|
|
|
"github.com/minio/console/pkg/auth/utils"
|
|
|
|
|
|
2021-05-10 15:27:52 -07:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
"k8s.io/apimachinery/pkg/types"
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
|
|
2020-07-26 00:34:17 -07:00
|
|
|
"github.com/minio/console/cluster"
|
2021-06-01 11:34:55 -07:00
|
|
|
"github.com/minio/madmin-go"
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
"github.com/go-openapi/runtime/middleware"
|
|
|
|
|
"github.com/go-openapi/swag"
|
2020-07-26 00:34:17 -07:00
|
|
|
"github.com/minio/console/models"
|
2021-07-19 11:48:50 -07:00
|
|
|
"github.com/minio/console/operatorapi/operations"
|
2021-02-01 12:13:51 -08:00
|
|
|
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
2020-08-04 20:54:59 -07:00
|
|
|
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
2020-07-01 18:03:22 -07:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2021-05-10 15:27:52 -07:00
|
|
|
k8sJson "k8s.io/apimachinery/pkg/runtime/serializer/json"
|
2020-08-04 20:54:59 -07:00
|
|
|
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
2020-07-01 18:03:22 -07:00
|
|
|
)
|
|
|
|
|
|
2020-08-04 20:54:59 -07:00
|
|
|
type imageRegistry struct {
|
|
|
|
|
Auths map[string]imageRegistryCredentials `json:"auths"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type imageRegistryCredentials struct {
|
|
|
|
|
Username string `json:"username"`
|
|
|
|
|
Password string `json:"password"`
|
|
|
|
|
Auth string `json:"auth"`
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func registerTenantHandlers(api *operations.OperatorAPI) {
|
2020-07-01 18:03:22 -07:00
|
|
|
// Add Tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPICreateTenantHandler = operator_api.CreateTenantHandlerFunc(func(params operator_api.CreateTenantParams, session *models.Principal) middleware.Responder {
|
2020-07-10 19:14:28 -07:00
|
|
|
resp, err := getTenantCreatedResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewCreateTenantDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewCreateTenantOK().WithPayload(resp)
|
2020-07-01 18:03:22 -07:00
|
|
|
})
|
|
|
|
|
// List All Tenants of all namespaces
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIListAllTenantsHandler = operator_api.ListAllTenantsHandlerFunc(func(params operator_api.ListAllTenantsParams, session *models.Principal) middleware.Responder {
|
2020-07-10 19:14:28 -07:00
|
|
|
resp, err := getListAllTenantsResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewListTenantsDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewListTenantsOK().WithPayload(resp)
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
// List Tenants by namespace
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIListTenantsHandler = operator_api.ListTenantsHandlerFunc(func(params operator_api.ListTenantsParams, session *models.Principal) middleware.Responder {
|
2020-07-10 19:14:28 -07:00
|
|
|
resp, err := getListTenantsResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewListTenantsDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewListTenantsOK().WithPayload(resp)
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
// Detail Tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantDetailsHandler = operator_api.TenantDetailsHandlerFunc(func(params operator_api.TenantDetailsParams, session *models.Principal) middleware.Responder {
|
2021-06-16 14:50:04 -07:00
|
|
|
resp, err := getTenantDetailsResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantDetailsDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantDetailsOK().WithPayload(resp)
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
2021-06-17 17:30:20 -07:00
|
|
|
// Tenant Security details
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantSecurityHandler = operator_api.TenantSecurityHandlerFunc(func(params operator_api.TenantSecurityParams, session *models.Principal) middleware.Responder {
|
2021-06-17 17:30:20 -07:00
|
|
|
resp, err := getTenantSecurityResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantSecurityDefault(int(err.Code)).WithPayload(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantSecurityOK().WithPayload(resp)
|
2021-06-17 17:30:20 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Update Tenant Security configuration
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIUpdateTenantSecurityHandler = operator_api.UpdateTenantSecurityHandlerFunc(func(params operator_api.UpdateTenantSecurityParams, session *models.Principal) middleware.Responder {
|
2021-06-17 17:30:20 -07:00
|
|
|
err := getUpdateTenantSecurityResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewUpdateTenantSecurityDefault(int(err.Code)).WithPayload(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewUpdateTenantSecurityNoContent()
|
2021-06-17 17:30:20 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
// Delete Tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIDeleteTenantHandler = operator_api.DeleteTenantHandlerFunc(func(params operator_api.DeleteTenantParams, session *models.Principal) middleware.Responder {
|
2020-07-10 19:14:28 -07:00
|
|
|
err := getDeleteTenantResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewDeleteTenantDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewDeleteTenantNoContent()
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
2021-07-09 09:56:29 -07:00
|
|
|
// Delete Pod
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIDeletePodHandler = operator_api.DeletePodHandlerFunc(func(params operator_api.DeletePodParams, session *models.Principal) middleware.Responder {
|
2021-07-09 09:56:29 -07:00
|
|
|
err := getDeletePodResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewDeletePodDefault(int(err.Code)).WithPayload(err)
|
2021-07-09 09:56:29 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewDeletePodNoContent()
|
2021-07-09 09:56:29 -07:00
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
// Update Tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIUpdateTenantHandler = operator_api.UpdateTenantHandlerFunc(func(params operator_api.UpdateTenantParams, session *models.Principal) middleware.Responder {
|
2020-07-10 19:14:28 -07:00
|
|
|
err := getUpdateTenantResponse(session, params)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewUpdateTenantDefault(int(err.Code)).WithPayload(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewUpdateTenantCreated()
|
2020-07-01 18:03:22 -07:00
|
|
|
})
|
2020-07-13 20:36:27 -07:00
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// Add Tenant Pools
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantAddPoolHandler = operator_api.TenantAddPoolHandlerFunc(func(params operator_api.TenantAddPoolParams, session *models.Principal) middleware.Responder {
|
2020-12-07 09:49:51 -06:00
|
|
|
err := getTenantAddPoolResponse(session, params)
|
2020-07-13 20:36:27 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantAddPoolDefault(int(err.Code)).WithPayload(err)
|
2020-07-13 20:36:27 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantAddPoolCreated()
|
2020-07-13 20:36:27 -07:00
|
|
|
})
|
2020-07-27 14:19:40 -07:00
|
|
|
|
2020-09-03 10:20:58 -07:00
|
|
|
// Get Tenant Usage
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIGetTenantUsageHandler = operator_api.GetTenantUsageHandlerFunc(func(params operator_api.GetTenantUsageParams, session *models.Principal) middleware.Responder {
|
2020-07-27 14:19:40 -07:00
|
|
|
payload, err := getTenantUsageResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantUsageDefault(int(err.Code)).WithPayload(err)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantUsageOK().WithPayload(payload)
|
2020-07-27 14:19:40 -07:00
|
|
|
})
|
2020-08-26 17:12:59 -07:00
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIGetTenantPodsHandler = operator_api.GetTenantPodsHandlerFunc(func(params operator_api.GetTenantPodsParams, session *models.Principal) middleware.Responder {
|
2021-05-06 18:57:14 -07:00
|
|
|
payload, err := getTenantPodsResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantPodsDefault(int(err.Code)).WithPayload(err)
|
2021-05-06 18:57:14 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantPodsOK().WithPayload(payload)
|
2021-05-06 18:57:14 -07:00
|
|
|
})
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIGetPodLogsHandler = operator_api.GetPodLogsHandlerFunc(func(params operator_api.GetPodLogsParams, session *models.Principal) middleware.Responder {
|
2021-06-07 18:56:05 -07:00
|
|
|
payload, err := getPodLogsResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetPodLogsDefault(int(err.Code)).WithPayload(err)
|
2021-06-07 18:56:05 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetPodLogsOK().WithPayload(payload)
|
2021-06-07 18:56:05 -07:00
|
|
|
})
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIGetPodEventsHandler = operator_api.GetPodEventsHandlerFunc(func(params operator_api.GetPodEventsParams, session *models.Principal) middleware.Responder {
|
2021-06-17 16:24:22 -07:00
|
|
|
payload, err := getPodEventsResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetPodEventsDefault(int(err.Code)).WithPayload(err)
|
2021-06-17 16:24:22 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetPodEventsOK().WithPayload(payload)
|
2021-06-17 16:24:22 -07:00
|
|
|
})
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// Update Tenant Pools
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantUpdatePoolsHandler = operator_api.TenantUpdatePoolsHandlerFunc(func(params operator_api.TenantUpdatePoolsParams, session *models.Principal) middleware.Responder {
|
2020-12-07 09:49:51 -06:00
|
|
|
resp, err := getTenantUpdatePoolResponse(session, params)
|
2020-08-26 17:12:59 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdatePoolsDefault(int(err.Code)).WithPayload(err)
|
2020-08-26 17:12:59 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdatePoolsOK().WithPayload(resp)
|
2020-08-26 17:12:59 -07:00
|
|
|
})
|
2020-09-03 10:20:58 -07:00
|
|
|
|
|
|
|
|
// Update Tenant Certificates
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantUpdateCertificateHandler = operator_api.TenantUpdateCertificateHandlerFunc(func(params operator_api.TenantUpdateCertificateParams, session *models.Principal) middleware.Responder {
|
2020-09-03 10:20:58 -07:00
|
|
|
err := getTenantUpdateCertificatesResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdateCertificateDefault(int(err.Code)).WithPayload(err)
|
2020-09-03 10:20:58 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdateCertificateCreated()
|
2020-09-03 10:20:58 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Update Tenant Encryption Configuration
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPITenantUpdateEncryptionHandler = operator_api.TenantUpdateEncryptionHandlerFunc(func(params operator_api.TenantUpdateEncryptionParams, session *models.Principal) middleware.Responder {
|
2020-09-03 10:20:58 -07:00
|
|
|
err := getTenantUpdateEncryptionResponse(session, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdateEncryptionDefault(int(err.Code)).WithPayload(err)
|
2020-09-03 10:20:58 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewTenantUpdateEncryptionCreated()
|
2020-09-03 10:20:58 -07:00
|
|
|
})
|
2021-05-10 15:27:52 -07:00
|
|
|
|
|
|
|
|
// Get Tenant YAML
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIGetTenantYAMLHandler = operator_api.GetTenantYAMLHandlerFunc(func(params operator_api.GetTenantYAMLParams, principal *models.Principal) middleware.Responder {
|
2021-05-10 15:27:52 -07:00
|
|
|
payload, err := getTenantYAML(principal, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantYAMLDefault(int(err.Code)).WithPayload(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewGetTenantYAMLOK().WithPayload(payload)
|
2021-05-10 15:27:52 -07:00
|
|
|
})
|
|
|
|
|
// Update Tenant YAML
|
2021-07-19 11:48:50 -07:00
|
|
|
api.OperatorAPIPutTenantYAMLHandler = operator_api.PutTenantYAMLHandlerFunc(func(params operator_api.PutTenantYAMLParams, principal *models.Principal) middleware.Responder {
|
2021-05-10 15:27:52 -07:00
|
|
|
err := getUpdateTenantYAML(principal, params)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewPutTenantYAMLDefault(int(err.Code)).WithPayload(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
2021-07-19 11:48:50 -07:00
|
|
|
return operator_api.NewPutTenantYAMLCreated()
|
2021-05-10 15:27:52 -07:00
|
|
|
})
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getDeleteTenantResponse gets the output of deleting a minio instance
|
2021-07-19 11:48:50 -07:00
|
|
|
func getDeleteTenantResponse(session *models.Principal, params operator_api.DeleteTenantParams) *models.Error {
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-08-19 20:34:43 -07:00
|
|
|
// get Kubernetes Client
|
2020-12-07 17:11:08 -06:00
|
|
|
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
2020-08-19 20:34:43 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-08-19 20:34:43 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
2020-08-19 20:34:43 -07:00
|
|
|
deleteTenantPVCs := false
|
|
|
|
|
if params.Body != nil {
|
|
|
|
|
deleteTenantPVCs = params.Body.DeletePvcs
|
|
|
|
|
}
|
2021-08-24 16:01:25 -07:00
|
|
|
|
|
|
|
|
tenant, err := opClient.TenantGet(params.HTTPRequest.Context(), params.Namespace, params.Tenant, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return prepareError(err)
|
|
|
|
|
}
|
|
|
|
|
tenant.EnsureDefaults()
|
|
|
|
|
|
|
|
|
|
if err = deleteTenantAction(params.HTTPRequest.Context(), opClient, clientset.CoreV1(), tenant, deleteTenantPVCs); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-09-04 20:32:57 -07:00
|
|
|
}
|
|
|
|
|
return nil
|
2020-08-19 20:34:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// deleteTenantAction performs the actions of deleting a tenant
|
|
|
|
|
//
|
|
|
|
|
// It also adds the option of deleting the tenant's underlying pvcs if deletePvcs set
|
|
|
|
|
func deleteTenantAction(
|
|
|
|
|
ctx context.Context,
|
2020-09-03 10:20:58 -07:00
|
|
|
operatorClient OperatorClientI,
|
2020-08-19 20:34:43 -07:00
|
|
|
clientset v1.CoreV1Interface,
|
2021-08-24 16:01:25 -07:00
|
|
|
tenant *miniov2.Tenant,
|
2020-08-19 20:34:43 -07:00
|
|
|
deletePvcs bool) error {
|
|
|
|
|
|
2021-08-24 16:01:25 -07:00
|
|
|
err := operatorClient.TenantDelete(ctx, tenant.Namespace, tenant.Name, metav1.DeleteOptions{})
|
2020-08-19 20:34:43 -07:00
|
|
|
if err != nil {
|
|
|
|
|
// try to delete pvc even if the tenant doesn't exist anymore but only if deletePvcs is set to true,
|
|
|
|
|
// else, we return the error
|
|
|
|
|
if (deletePvcs && !k8sErrors.IsNotFound(err)) || !deletePvcs {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if deletePvcs {
|
2021-08-24 16:01:25 -07:00
|
|
|
|
2021-08-23 14:09:21 -07:00
|
|
|
// delete MinIO PVCs
|
2020-08-19 20:34:43 -07:00
|
|
|
opts := metav1.ListOptions{
|
2021-08-24 16:01:25 -07:00
|
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, tenant.Name),
|
2020-08-19 20:34:43 -07:00
|
|
|
}
|
2021-08-23 14:09:21 -07:00
|
|
|
err = clientset.PersistentVolumeClaims(tenant.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
|
2020-08-20 18:57:34 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-08-23 14:09:21 -07:00
|
|
|
// delete postgres PVCs
|
2021-08-24 16:01:25 -07:00
|
|
|
|
|
|
|
|
logOpts := metav1.ListOptions{
|
|
|
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.LogDBInstanceLabel, tenant.LogStatefulsetName()),
|
2021-08-23 14:09:21 -07:00
|
|
|
}
|
2021-08-24 16:01:25 -07:00
|
|
|
err := clientset.PersistentVolumeClaims(tenant.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, logOpts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-23 14:09:21 -07:00
|
|
|
// delete prometheus PVCs
|
2021-08-24 16:01:25 -07:00
|
|
|
|
|
|
|
|
promOpts := metav1.ListOptions{
|
|
|
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.PrometheusInstanceLabel, tenant.PrometheusStatefulsetName()),
|
2021-08-23 14:09:21 -07:00
|
|
|
}
|
2021-08-24 16:01:25 -07:00
|
|
|
|
|
|
|
|
if err := clientset.PersistentVolumeClaims(tenant.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, promOpts); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-20 18:57:34 -07:00
|
|
|
// delete all tenant's secrets only if deletePvcs = true
|
2021-08-23 14:09:21 -07:00
|
|
|
return clientset.Secrets(tenant.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
|
2020-08-19 20:34:43 -07:00
|
|
|
}
|
|
|
|
|
return nil
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-09 09:56:29 -07:00
|
|
|
// getDeleteTenantResponse gets the output of deleting a minio instance
|
2021-07-19 11:48:50 -07:00
|
|
|
func getDeletePodResponse(session *models.Principal, params operator_api.DeletePodParams) *models.Error {
|
2021-07-09 09:56:29 -07:00
|
|
|
ctx := context.Background()
|
|
|
|
|
// get Kubernetes Client
|
|
|
|
|
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-07-09 09:56:29 -07:00
|
|
|
}
|
|
|
|
|
listOpts := metav1.ListOptions{
|
|
|
|
|
LabelSelector: fmt.Sprintf("v1.min.io/tenant=%s", params.Tenant),
|
|
|
|
|
FieldSelector: fmt.Sprintf("metadata.name=%s%s", params.Tenant, params.PodName[len(params.Tenant):]),
|
|
|
|
|
}
|
|
|
|
|
if err = clientset.CoreV1().Pods(params.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-07-09 09:56:29 -07:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 21:31:30 -07:00
|
|
|
// GetTenantServiceURL gets tenant's service url with the proper scheme and port
|
2021-02-01 12:13:51 -08:00
|
|
|
func GetTenantServiceURL(mi *miniov2.Tenant) (svcURL string) {
|
2020-07-09 12:24:01 -07:00
|
|
|
scheme := "http"
|
2021-02-01 12:13:51 -08:00
|
|
|
port := miniov2.MinIOPortLoadBalancerSVC
|
2020-07-23 11:13:05 -07:00
|
|
|
if mi.AutoCert() || mi.ExternalCert() {
|
2020-07-09 12:24:01 -07:00
|
|
|
scheme = "https"
|
2021-02-01 12:13:51 -08:00
|
|
|
port = miniov2.MinIOTLSPortLoadBalancerSVC
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2021-06-04 11:35:55 -07:00
|
|
|
return fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(mi.MinIOFQDNServiceName(), strconv.Itoa(port)))
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
func getTenantAdminClient(ctx context.Context, client K8sClientI, tenant *miniov2.Tenant, svcURL string) (*madmin.AdminClient, error) {
|
2020-11-30 12:41:58 -08:00
|
|
|
tenantCreds, err := getTenantCreds(ctx, client, tenant)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
sessionToken := ""
|
2021-08-18 13:23:03 -07:00
|
|
|
mAdmin, pErr := restapi.NewAdminClientWithInsecure(svcURL, tenantCreds.accessKey, tenantCreds.secretKey, sessionToken, true)
|
2020-11-30 12:41:58 -08:00
|
|
|
if pErr != nil {
|
|
|
|
|
return nil, pErr.Cause
|
|
|
|
|
}
|
|
|
|
|
return mAdmin, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type tenantKeys struct {
|
|
|
|
|
accessKey string
|
|
|
|
|
secretKey string
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
func getTenantCreds(ctx context.Context, client K8sClientI, tenant *miniov2.Tenant) (*tenantKeys, error) {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfiguration, err := GetTenantConfiguration(ctx, client, tenant)
|
2020-07-09 12:24:01 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantAccessKey, ok := tenantConfiguration["accesskey"]
|
2020-07-09 12:24:01 -07:00
|
|
|
if !ok {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("tenant's secret doesn't contain accesskey")
|
|
|
|
|
return nil, restapi.ErrorGeneric
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantSecretKey, ok := tenantConfiguration["secretkey"]
|
2020-07-09 12:24:01 -07:00
|
|
|
if !ok {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("tenant's secret doesn't contain secretkey")
|
|
|
|
|
return nil, restapi.ErrorGeneric
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
return &tenantKeys{accessKey: tenantAccessKey, secretKey: tenantSecretKey}, nil
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
func getTenant(ctx context.Context, operatorClient OperatorClientI, namespace, tenantName string) (*miniov2.Tenant, error) {
|
2021-08-23 21:06:32 -07:00
|
|
|
tenant, err := operatorClient.TenantGet(ctx, namespace, tenantName, metav1.GetOptions{})
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-08-23 21:06:32 -07:00
|
|
|
return tenant, nil
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
|
2020-09-05 23:48:51 -07:00
|
|
|
func isPrometheusEnabled(annotations map[string]string) bool {
|
|
|
|
|
if annotations == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// if one of the following prometheus annotations are not present
|
|
|
|
|
// we consider the tenant as not integrated with prometheus
|
|
|
|
|
if _, ok := annotations[prometheusPath]; !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if _, ok := annotations[prometheusPort]; !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if _, ok := annotations[prometheusScrape]; !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
func getTenantInfo(tenant *miniov2.Tenant) *models.Tenant {
|
2020-12-07 09:49:51 -06:00
|
|
|
var pools []*models.Pool
|
2020-07-25 14:38:16 -07:00
|
|
|
var totalSize int64
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, p := range tenant.Spec.Pools {
|
|
|
|
|
pools = append(pools, parseTenantPool(&p))
|
|
|
|
|
poolSize := int64(p.Servers) * int64(p.VolumesPerServer) * p.VolumeClaimTemplate.Spec.Resources.Requests.Storage().Value()
|
|
|
|
|
totalSize = totalSize + poolSize
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-09-05 23:37:01 -07:00
|
|
|
var deletion string
|
|
|
|
|
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
2021-01-13 14:08:32 -06:00
|
|
|
deletion = tenant.ObjectMeta.DeletionTimestamp.Format(time.RFC3339)
|
2020-09-05 23:37:01 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
|
|
|
|
|
return &models.Tenant{
|
2021-01-13 14:08:32 -06:00
|
|
|
CreationDate: tenant.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
|
2020-09-05 23:48:51 -07:00
|
|
|
DeletionDate: deletion,
|
|
|
|
|
Name: tenant.Name,
|
|
|
|
|
TotalSize: totalSize,
|
|
|
|
|
CurrentState: tenant.Status.CurrentState,
|
2020-12-07 09:49:51 -06:00
|
|
|
Pools: pools,
|
2020-09-05 23:48:51 -07:00
|
|
|
Namespace: tenant.ObjectMeta.Namespace,
|
|
|
|
|
Image: tenant.Spec.Image,
|
2020-09-17 06:44:16 -07:00
|
|
|
EnablePrometheus: isPrometheusEnabled(tenant.Annotations),
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
|
2020-07-25 14:38:16 -07:00
|
|
|
// 5 seconds timeout
|
2021-08-18 13:23:03 -07:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
defer cancel()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-09 12:24:01 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-25 14:38:16 -07:00
|
|
|
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
2020-07-09 12:24:01 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-09 12:24:01 -07:00
|
|
|
}
|
2020-07-25 14:38:16 -07:00
|
|
|
|
2020-07-27 14:19:40 -07:00
|
|
|
info := getTenantInfo(minTenant)
|
2021-01-12 15:55:07 -06:00
|
|
|
|
2021-05-14 15:04:29 -07:00
|
|
|
// get Kubernetes Client
|
2021-01-13 14:08:32 -06:00
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
k8sClient := k8sClient{
|
|
|
|
|
client: clientSet,
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfiguration, err := GetTenantConfiguration(ctx, &k8sClient, minTenant)
|
|
|
|
|
if err != nil {
|
|
|
|
|
restapi.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
2021-06-16 14:50:04 -07:00
|
|
|
}
|
2021-05-14 15:04:29 -07:00
|
|
|
|
2021-08-03 17:36:26 -07:00
|
|
|
// detect if AD/LDAP is enabled
|
|
|
|
|
ldapEnabled := false
|
|
|
|
|
if string(tenantConfiguration["MINIO_IDENTITY_LDAP_SERVER_ADDR"]) != "" {
|
|
|
|
|
ldapEnabled = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// detect if OpenID is enabled
|
|
|
|
|
oidcEnabled := false
|
|
|
|
|
if string(tenantConfiguration["MINIO_IDENTITY_OPENID_CONFIG_URL"]) != "" {
|
|
|
|
|
oidcEnabled = true
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
// detect if encryption is enabled
|
|
|
|
|
if minTenant.HasKESEnabled() || string(tenantConfiguration["MINIO_KMS_SECRET_KEY"]) != "" {
|
|
|
|
|
info.EncryptionEnabled = true
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 15:04:29 -07:00
|
|
|
info.LogEnabled = minTenant.HasLogEnabled()
|
|
|
|
|
info.MonitoringEnabled = minTenant.HasPrometheusEnabled()
|
2021-08-03 17:36:26 -07:00
|
|
|
info.IdpAdEnabled = ldapEnabled
|
|
|
|
|
info.IdpOidcEnabled = oidcEnabled
|
2021-06-17 17:30:20 -07:00
|
|
|
info.MinioTLS = minTenant.TLS()
|
2021-08-18 13:23:03 -07:00
|
|
|
|
|
|
|
|
// obtain current subnet license for tenant (if exists)
|
|
|
|
|
if license, ok := tenantConfiguration[MinIOSubnetLicense]; ok {
|
|
|
|
|
client := &cluster.HTTPClient{
|
|
|
|
|
Client: restapi.GetConsoleHTTPClient(),
|
|
|
|
|
}
|
|
|
|
|
licenseInfo, _, _ := subscriptionValidate(client, string(license), "", "")
|
|
|
|
|
// if licenseInfo is present attach it to the tenantInfo response
|
|
|
|
|
if licenseInfo != nil {
|
|
|
|
|
info.SubnetLicense = licenseInfo
|
2021-01-12 15:55:07 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 16:26:08 -07:00
|
|
|
// attach status information
|
|
|
|
|
info.Status = &models.TenantStatus{
|
|
|
|
|
HealthStatus: string(minTenant.Status.HealthStatus),
|
|
|
|
|
DrivesHealing: minTenant.Status.DrivesHealing,
|
|
|
|
|
DrivesOffline: minTenant.Status.DrivesOffline,
|
|
|
|
|
DrivesOnline: minTenant.Status.DrivesOnline,
|
|
|
|
|
WriteQuorum: minTenant.Status.WriteQuorum,
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-13 14:08:32 -06:00
|
|
|
// get tenant service
|
|
|
|
|
minTenant.EnsureDefaults()
|
|
|
|
|
//minio service
|
|
|
|
|
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
2021-02-05 14:27:29 -08:00
|
|
|
// we can tolerate this error
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("Unable to get MinIO service name: %v, continuing", err)
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
|
|
|
|
//console service
|
|
|
|
|
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
2021-02-05 14:27:29 -08:00
|
|
|
// we can tolerate this error
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
schema := "http"
|
2021-01-13 13:24:30 -08:00
|
|
|
consoleSchema := "http"
|
2021-08-18 13:23:03 -07:00
|
|
|
consolePort := fmt.Sprintf(":%d", miniov2.ConsolePort)
|
2021-01-13 14:08:32 -06:00
|
|
|
if minTenant.TLS() {
|
|
|
|
|
schema = "https"
|
2021-01-13 13:24:30 -08:00
|
|
|
consoleSchema = "https"
|
2021-08-18 13:23:03 -07:00
|
|
|
consolePort = fmt.Sprintf(":%d", miniov2.ConsoleTLSPort)
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
|
|
|
|
var minioEndpoint string
|
|
|
|
|
var consoleEndpoint string
|
2021-02-05 14:27:29 -08:00
|
|
|
if minSvc != nil && len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
2021-02-23 12:49:46 -08:00
|
|
|
if minSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
|
|
|
|
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].IP)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if minSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
|
|
|
|
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].Hostname)
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
2021-02-05 14:27:29 -08:00
|
|
|
if conSvc != nil && len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
2021-02-23 12:49:46 -08:00
|
|
|
if conSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
|
|
|
|
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].IP, consolePort)
|
|
|
|
|
}
|
|
|
|
|
if conSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
|
|
|
|
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].Hostname, consolePort)
|
|
|
|
|
}
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
|
|
|
|
if minioEndpoint != "" || consoleEndpoint != "" {
|
|
|
|
|
info.Endpoints = &models.TenantEndpoints{
|
|
|
|
|
Console: consoleEndpoint,
|
|
|
|
|
Minio: minioEndpoint,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-09 12:24:01 -07:00
|
|
|
return info, nil
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-17 17:30:20 -07:00
|
|
|
// parseTenantCertificates convert public key pem certificates stored in k8s secrets for a given Tenant into x509 certificates
|
|
|
|
|
func parseTenantCertificates(ctx context.Context, clientSet K8sClientI, namespace string, secrets []*miniov2.LocalCertificateReference) ([]*models.CertificateInfo, error) {
|
|
|
|
|
var certificates []*models.CertificateInfo
|
|
|
|
|
publicKey := "public.crt"
|
|
|
|
|
// Iterate over TLS secrets and build array of CertificateInfo structure
|
|
|
|
|
// that will be used to display information about certs in the UI
|
|
|
|
|
for _, secret := range secrets {
|
|
|
|
|
keyPair, err := clientSet.getSecret(ctx, namespace, secret.Name, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if secret.Type == "kubernetes.io/tls" || secret.Type == "cert-manager.io/v1alpha2" {
|
|
|
|
|
publicKey = "tls.crt"
|
|
|
|
|
}
|
|
|
|
|
// Extract public key from certificate TLS secret
|
|
|
|
|
if rawCert, ok := keyPair.Data[publicKey]; ok {
|
|
|
|
|
block, _ := pem.Decode(rawCert)
|
|
|
|
|
if block == nil {
|
|
|
|
|
// If certificate failed to decode skip
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-08-03 17:36:26 -07:00
|
|
|
domains := []string{}
|
|
|
|
|
// append certificate domain names
|
|
|
|
|
if len(cert.DNSNames) > 0 {
|
|
|
|
|
domains = append(domains, cert.DNSNames...)
|
|
|
|
|
}
|
|
|
|
|
// append certificate IPs
|
|
|
|
|
if len(cert.IPAddresses) > 0 {
|
|
|
|
|
for _, ip := range cert.IPAddresses {
|
|
|
|
|
domains = append(domains, ip.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-17 17:30:20 -07:00
|
|
|
certificates = append(certificates, &models.CertificateInfo{
|
|
|
|
|
SerialNumber: cert.SerialNumber.String(),
|
|
|
|
|
Name: secret.Name,
|
2021-08-03 17:36:26 -07:00
|
|
|
Domains: domains,
|
2021-06-17 17:30:20 -07:00
|
|
|
Expiry: cert.NotAfter.String(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return certificates, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getTenantSecurity(ctx context.Context, clientSet K8sClientI, tenant *miniov2.Tenant) (response *models.TenantSecurityResponse, err error) {
|
|
|
|
|
var minioExternalCertificates []*models.CertificateInfo
|
|
|
|
|
var minioExternalCaCertificates []*models.CertificateInfo
|
|
|
|
|
// Certificates used by MinIO server
|
|
|
|
|
if minioExternalCertificates, err = parseTenantCertificates(ctx, clientSet, tenant.Namespace, tenant.Spec.ExternalCertSecret); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// CA Certificates used by MinIO server
|
|
|
|
|
if minioExternalCaCertificates, err = parseTenantCertificates(ctx, clientSet, tenant.Namespace, tenant.Spec.ExternalCaCertSecret); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &models.TenantSecurityResponse{
|
|
|
|
|
AutoCert: tenant.AutoCert(),
|
|
|
|
|
CustomCertificates: &models.TenantSecurityResponseCustomCertificates{
|
2021-08-18 13:23:03 -07:00
|
|
|
Minio: minioExternalCertificates,
|
|
|
|
|
MinioCAs: minioExternalCaCertificates,
|
2021-06-17 17:30:20 -07:00
|
|
|
},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantSecurityResponse(session *models.Principal, params operator_api.TenantSecurityParams) (*models.TenantSecurityResponse, *models.Error) {
|
2021-06-17 17:30:20 -07:00
|
|
|
// 5 seconds timeout
|
2021-08-03 17:36:26 -07:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
defer cancel()
|
2021-06-17 17:30:20 -07:00
|
|
|
//ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
//defer cancel()
|
|
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
// get Kubernetes Client
|
|
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
k8sClient := k8sClient{
|
|
|
|
|
client: clientSet,
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
info, err := getTenantSecurity(ctx, &k8sClient, minTenant)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
return info, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getUpdateTenantSecurityResponse(session *models.Principal, params operator_api.UpdateTenantSecurityParams) *models.Error {
|
2021-06-17 17:30:20 -07:00
|
|
|
// 5 seconds timeout
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
defer cancel()
|
|
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
// get Kubernetes Client
|
|
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
k8sClient := k8sClient{
|
|
|
|
|
client: clientSet,
|
|
|
|
|
}
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
if err := updateTenantSecurity(ctx, opClient, &k8sClient, params.Namespace, params); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err, errors.New("unable to update tenant"))
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// updateTenantSecurity
|
2021-07-19 11:48:50 -07:00
|
|
|
func updateTenantSecurity(ctx context.Context, operatorClient OperatorClientI, client K8sClientI, namespace string, params operator_api.UpdateTenantSecurityParams) error {
|
2021-06-17 17:30:20 -07:00
|
|
|
minInst, err := operatorClient.TenantGet(ctx, namespace, params.Tenant, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// Update AutoCert
|
|
|
|
|
minInst.Spec.RequestAutoCert = ¶ms.Body.AutoCert
|
|
|
|
|
var newMinIOExternalCertSecret []*miniov2.LocalCertificateReference
|
|
|
|
|
var newMinIOExternalCaCertSecret []*miniov2.LocalCertificateReference
|
|
|
|
|
// Remove Certificate Secrets from MinIO (Tenant.Spec.ExternalCertSecret)
|
|
|
|
|
for _, certificate := range minInst.Spec.ExternalCertSecret {
|
|
|
|
|
skip := false
|
|
|
|
|
for _, certificateToBeDeleted := range params.Body.CustomCertificates.SecretsToBeDeleted {
|
|
|
|
|
if certificate.Name == certificateToBeDeleted {
|
|
|
|
|
skip = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if skip {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
newMinIOExternalCertSecret = append(newMinIOExternalCertSecret, certificate)
|
|
|
|
|
}
|
|
|
|
|
// Remove Certificate Secrets from MinIO CAs (Tenant.Spec.ExternalCaCertSecret)
|
|
|
|
|
for _, certificate := range minInst.Spec.ExternalCaCertSecret {
|
|
|
|
|
skip := false
|
|
|
|
|
for _, certificateToBeDeleted := range params.Body.CustomCertificates.SecretsToBeDeleted {
|
|
|
|
|
if certificate.Name == certificateToBeDeleted {
|
|
|
|
|
skip = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if skip {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
newMinIOExternalCaCertSecret = append(newMinIOExternalCaCertSecret, certificate)
|
|
|
|
|
}
|
|
|
|
|
//Create new Certificate Secrets for MinIO
|
|
|
|
|
secretName := fmt.Sprintf("%s-%s", minInst.Name, strings.ToLower(utils.RandomCharString(5)))
|
|
|
|
|
externalCertSecretName := fmt.Sprintf("%s-external-certificates", secretName)
|
|
|
|
|
externalCertSecrets, err := createOrReplaceExternalCertSecrets(ctx, client, minInst.Namespace, params.Body.CustomCertificates.Minio, externalCertSecretName, minInst.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
newMinIOExternalCertSecret = append(newMinIOExternalCertSecret, externalCertSecrets...)
|
|
|
|
|
// Create new CAs Certificate Secrets for MinIO
|
|
|
|
|
var caCertificates []tenantSecret
|
|
|
|
|
for i, caCertificate := range params.Body.CustomCertificates.MinioCAs {
|
|
|
|
|
certificateContent, err := base64.StdEncoding.DecodeString(caCertificate)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
caCertificates = append(caCertificates, tenantSecret{
|
|
|
|
|
Name: fmt.Sprintf("%s-ca-certificate-%d", secretName, i),
|
|
|
|
|
Content: map[string][]byte{
|
|
|
|
|
"public.crt": certificateContent,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
if len(caCertificates) > 0 {
|
|
|
|
|
certificateSecrets, err := createOrReplaceSecrets(ctx, client, minInst.Namespace, caCertificates, minInst.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
newMinIOExternalCaCertSecret = append(newMinIOExternalCaCertSecret, certificateSecrets...)
|
|
|
|
|
}
|
|
|
|
|
// Update External Certificates
|
|
|
|
|
minInst.Spec.ExternalCertSecret = newMinIOExternalCertSecret
|
|
|
|
|
minInst.Spec.ExternalCaCertSecret = newMinIOExternalCaCertSecret
|
|
|
|
|
_, err = operatorClient.TenantUpdate(ctx, minInst, metav1.UpdateOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// Remove Certificate Secrets from Tenant namespace
|
|
|
|
|
for _, secretName := range params.Body.CustomCertificates.SecretsToBeDeleted {
|
|
|
|
|
err = client.deleteSecret(ctx, minInst.Namespace, secretName, metav1.DeleteOptions{})
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("error deleting secret: %v", err)
|
2021-06-17 17:30:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 10:20:58 -07:00
|
|
|
func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace string, limit *int32) (*models.ListTenantsResponse, error) {
|
2020-07-01 18:03:22 -07:00
|
|
|
listOpts := metav1.ListOptions{
|
|
|
|
|
Limit: 10,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if limit != nil {
|
|
|
|
|
listOpts.Limit = int64(*limit)
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-25 14:38:16 -07:00
|
|
|
minTenants, err := operatorClient.TenantList(ctx, namespace, listOpts)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tenants []*models.TenantList
|
2020-07-27 18:03:47 -07:00
|
|
|
|
2020-07-30 13:55:11 -07:00
|
|
|
for _, tenant := range minTenants.Items {
|
|
|
|
|
var totalSize int64
|
2020-07-01 18:03:22 -07:00
|
|
|
var instanceCount int64
|
|
|
|
|
var volumeCount int64
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, pool := range tenant.Spec.Pools {
|
|
|
|
|
instanceCount = instanceCount + int64(pool.Servers)
|
|
|
|
|
volumeCount = volumeCount + int64(pool.Servers*pool.VolumesPerServer)
|
|
|
|
|
if pool.VolumeClaimTemplate != nil {
|
|
|
|
|
poolSize := int64(pool.VolumesPerServer) * int64(pool.Servers) * pool.VolumeClaimTemplate.Spec.Resources.Requests.Storage().Value()
|
|
|
|
|
totalSize = totalSize + poolSize
|
2020-07-27 18:03:47 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2020-09-05 23:37:01 -07:00
|
|
|
var deletion string
|
|
|
|
|
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
2021-01-13 14:08:32 -06:00
|
|
|
deletion = tenant.ObjectMeta.DeletionTimestamp.Format(time.RFC3339)
|
2020-09-05 23:37:01 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
tenants = append(tenants, &models.TenantList{
|
2021-01-13 14:08:32 -06:00
|
|
|
CreationDate: tenant.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
|
2020-09-05 23:37:01 -07:00
|
|
|
DeletionDate: deletion,
|
2020-07-30 13:55:11 -07:00
|
|
|
Name: tenant.ObjectMeta.Name,
|
2020-12-07 09:49:51 -06:00
|
|
|
PoolCount: int64(len(tenant.Spec.Pools)),
|
2020-07-01 18:03:22 -07:00
|
|
|
InstanceCount: instanceCount,
|
|
|
|
|
VolumeCount: volumeCount,
|
2020-07-30 13:55:11 -07:00
|
|
|
CurrentState: tenant.Status.CurrentState,
|
|
|
|
|
Namespace: tenant.ObjectMeta.Namespace,
|
2020-07-27 18:03:47 -07:00
|
|
|
TotalSize: totalSize,
|
2021-06-02 16:26:08 -07:00
|
|
|
HealthStatus: string(tenant.Status.HealthStatus),
|
2020-07-01 18:03:22 -07:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &models.ListTenantsResponse{
|
|
|
|
|
Tenants: tenants,
|
2020-07-30 13:55:11 -07:00
|
|
|
Total: int64(len(tenants)),
|
2020-07-01 18:03:22 -07:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getListAllTenantsResponse(session *models.Principal, params operator_api.ListAllTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
2020-07-01 18:03:22 -07:00
|
|
|
ctx := context.Background()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
listT, err := listTenants(ctx, opClient, "", params.Limit)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
return listT, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getListTenantsResponse list tenants by namespace
|
2021-07-19 11:48:50 -07:00
|
|
|
func getListTenantsResponse(session *models.Principal, params operator_api.ListTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
2020-07-01 18:03:22 -07:00
|
|
|
ctx := context.Background()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
listT, err := listTenants(ctx, opClient, params.Namespace, params.Limit)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
return listT, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantCreatedResponse(session *models.Principal, params operator_api.CreateTenantParams) (response *models.CreateTenantResponse, mError *models.Error) {
|
2020-08-04 16:04:04 -07:00
|
|
|
tenantReq := params.Body
|
|
|
|
|
minioImage := tenantReq.Image
|
2020-08-04 20:54:59 -07:00
|
|
|
ctx := context.Background()
|
2020-07-01 18:03:22 -07:00
|
|
|
if minioImage == "" {
|
|
|
|
|
minImg, err := cluster.GetMinioImage()
|
2020-08-11 22:29:33 -07:00
|
|
|
// we can live without figuring out the latest version of MinIO, Operator will use a hardcoded value
|
|
|
|
|
if err == nil {
|
|
|
|
|
minioImage = *minImg
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 13:55:11 -07:00
|
|
|
// get Kubernetes Client
|
2020-12-07 17:11:08 -06:00
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
2020-09-03 10:20:58 -07:00
|
|
|
k8sClient := k8sClient{
|
|
|
|
|
client: clientSet,
|
|
|
|
|
}
|
2020-07-30 13:55:11 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-30 13:55:11 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-04 16:04:04 -07:00
|
|
|
ns := *tenantReq.Namespace
|
2020-07-01 18:03:22 -07:00
|
|
|
// if access/secret are provided, use them, else create a random pair
|
2021-04-22 14:18:14 -07:00
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
accessKey := restapi.RandomCharString(16)
|
|
|
|
|
secretKey := restapi.RandomCharString(32)
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
|
2020-08-04 16:04:04 -07:00
|
|
|
if tenantReq.AccessKey != "" {
|
|
|
|
|
accessKey = tenantReq.AccessKey
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-08-04 16:04:04 -07:00
|
|
|
if tenantReq.SecretKey != "" {
|
|
|
|
|
secretKey = tenantReq.SecretKey
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
|
2020-08-20 19:09:13 -07:00
|
|
|
tenantName := *tenantReq.Name
|
2021-05-10 10:56:10 -07:00
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
imm := true
|
2021-04-22 14:18:14 -07:00
|
|
|
var instanceSecret corev1.Secret
|
|
|
|
|
var users []*corev1.LocalObjectReference
|
2021-05-10 10:56:10 -07:00
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV := map[string]string{}
|
2021-08-03 17:36:26 -07:00
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
// Create the secret for the root credentials (deprecated)
|
2021-05-10 10:56:10 -07:00
|
|
|
secretName := fmt.Sprintf("%s-secret", tenantName)
|
|
|
|
|
instanceSecret = corev1.Secret{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: secretName,
|
|
|
|
|
Labels: map[string]string{
|
|
|
|
|
miniov2.TenantLabel: tenantName,
|
2021-04-22 14:18:14 -07:00
|
|
|
},
|
2021-05-10 10:56:10 -07:00
|
|
|
},
|
|
|
|
|
Immutable: &imm,
|
|
|
|
|
Data: map[string][]byte{
|
2021-08-03 17:36:26 -07:00
|
|
|
"accesskey": []byte(""),
|
|
|
|
|
"secretkey": []byte(""),
|
2021-05-10 10:56:10 -07:00
|
|
|
},
|
|
|
|
|
}
|
2021-08-03 17:36:26 -07:00
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
// Enable/Disable console object browser for MinIO tenant (default is on)
|
|
|
|
|
enabledConsole := "on"
|
|
|
|
|
if tenantReq.EnableConsole != nil && !*tenantReq.EnableConsole {
|
|
|
|
|
enabledConsole = "off"
|
|
|
|
|
}
|
|
|
|
|
tenantConfigurationENV["MINIO_BROWSER"] = enabledConsole
|
|
|
|
|
tenantConfigurationENV["MINIO_ROOT_USER"] = accessKey
|
|
|
|
|
tenantConfigurationENV["MINIO_ROOT_PASSWORD"] = secretKey
|
2021-08-03 17:36:26 -07:00
|
|
|
|
2021-05-10 10:56:10 -07:00
|
|
|
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-10 10:56:10 -07:00
|
|
|
}
|
2021-05-10 11:31:56 -07:00
|
|
|
|
2020-09-09 17:08:34 -07:00
|
|
|
// delete secrets created if an error occurred during tenant creation,
|
|
|
|
|
defer func() {
|
|
|
|
|
if mError != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("deleting secrets created for failed tenant: %s if any: %v", tenantName, mError)
|
2020-09-09 17:08:34 -07:00
|
|
|
opts := metav1.ListOptions{
|
2021-02-01 12:13:51 -08:00
|
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, tenantName),
|
2020-09-09 17:08:34 -07:00
|
|
|
}
|
|
|
|
|
err = clientSet.CoreV1().Secrets(ns).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
|
|
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("error deleting tenant's secrets: %v", err)
|
2020-09-09 17:08:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
2020-07-01 18:03:22 -07:00
|
|
|
|
2020-08-04 22:32:41 -07:00
|
|
|
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
|
|
|
|
if tenantReq.ErasureCodingParity > 0 {
|
2020-09-09 17:08:34 -07:00
|
|
|
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(errorInvalidErasureCodingValue)
|
2020-08-04 22:32:41 -07:00
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_STORAGE_CLASS_STANDARD"] = fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity)
|
2020-08-04 22:32:41 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
//Construct a MinIO Instance with everything we are getting from parameters
|
2021-02-01 12:13:51 -08:00
|
|
|
minInst := miniov2.Tenant{
|
2020-07-01 18:03:22 -07:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2020-09-17 06:44:16 -07:00
|
|
|
Name: tenantName,
|
|
|
|
|
Labels: tenantReq.Labels,
|
2020-07-01 18:03:22 -07:00
|
|
|
},
|
2021-02-01 12:13:51 -08:00
|
|
|
Spec: miniov2.TenantSpec{
|
2020-07-01 18:03:22 -07:00
|
|
|
Image: minioImage,
|
|
|
|
|
Mountpath: "/export",
|
|
|
|
|
CredsSecret: &corev1.LocalObjectReference{
|
|
|
|
|
Name: secretName,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
var tenantExternalIDPConfigured bool
|
2021-08-03 17:36:26 -07:00
|
|
|
if tenantReq.Idp != nil {
|
|
|
|
|
// Enable IDP (Active Directory) for MinIO
|
|
|
|
|
if tenantReq.Idp.ActiveDirectory != nil {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantExternalIDPConfigured = true
|
2021-08-03 17:36:26 -07:00
|
|
|
serverAddress := *tenantReq.Idp.ActiveDirectory.URL
|
|
|
|
|
userNameFormat := tenantReq.Idp.ActiveDirectory.UsernameFormat
|
|
|
|
|
userNameSearchFilter := tenantReq.Idp.ActiveDirectory.UsernameSearchFilter
|
|
|
|
|
groupNameAttribute := tenantReq.Idp.ActiveDirectory.GroupNameAttribute
|
|
|
|
|
tlsSkipVerify := tenantReq.Idp.ActiveDirectory.SkipTLSVerification
|
|
|
|
|
serverInsecure := tenantReq.Idp.ActiveDirectory.ServerInsecure
|
|
|
|
|
lookupBindDN := tenantReq.Idp.ActiveDirectory.LookupBindDn
|
|
|
|
|
lookupBindPassword := tenantReq.Idp.ActiveDirectory.LookupBindPassword
|
|
|
|
|
userDNSearchBaseDN := tenantReq.Idp.ActiveDirectory.UserDnSearchBaseDn
|
|
|
|
|
userDNSearchFilter := tenantReq.Idp.ActiveDirectory.UserDnSearchFilter
|
|
|
|
|
groupSearchBaseDN := tenantReq.Idp.ActiveDirectory.GroupSearchBaseDn
|
|
|
|
|
groupSearchFilter := tenantReq.Idp.ActiveDirectory.GroupSearchFilter
|
|
|
|
|
serverStartTLS := tenantReq.Idp.ActiveDirectory.ServerStartTLS
|
|
|
|
|
|
|
|
|
|
// LDAP Server
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_ADDR"] = serverAddress
|
2020-08-02 23:45:54 -07:00
|
|
|
if tlsSkipVerify {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY"] = "on"
|
2020-08-02 23:45:54 -07:00
|
|
|
}
|
|
|
|
|
if serverInsecure {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_INSECURE"] = "on"
|
2021-08-03 17:36:26 -07:00
|
|
|
}
|
|
|
|
|
if serverStartTLS {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_STARTTLS"] = "on"
|
2020-08-02 23:45:54 -07:00
|
|
|
}
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
|
2021-08-03 17:36:26 -07:00
|
|
|
// LDAP Username
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USERNAME_FORMAT"] = userNameFormat
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER"] = userNameSearchFilter
|
2021-08-03 17:36:26 -07:00
|
|
|
|
|
|
|
|
// LDAP Lookup
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN"] = lookupBindDN
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD"] = lookupBindPassword
|
2021-08-03 17:36:26 -07:00
|
|
|
|
|
|
|
|
// LDAP User DN
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN"] = userDNSearchBaseDN
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER"] = userDNSearchFilter
|
2021-08-03 17:36:26 -07:00
|
|
|
|
|
|
|
|
// LDAP Group
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE"] = groupNameAttribute
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN"] = groupSearchBaseDN
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER"] = groupSearchFilter
|
2021-08-03 17:36:26 -07:00
|
|
|
|
|
|
|
|
// Attach the list of LDAP user DNs that will be administrator for the Tenant
|
|
|
|
|
for i, userDN := range tenantReq.Idp.ActiveDirectory.UserDNS {
|
|
|
|
|
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
|
|
|
|
|
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
|
|
|
|
|
userSecret := corev1.Secret{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: userSecretName,
|
|
|
|
|
Labels: map[string]string{
|
|
|
|
|
miniov2.TenantLabel: tenantName,
|
|
|
|
|
},
|
2021-05-10 11:31:56 -07:00
|
|
|
},
|
2021-08-03 17:36:26 -07:00
|
|
|
Immutable: &imm,
|
|
|
|
|
Data: map[string][]byte{
|
|
|
|
|
"CONSOLE_ACCESS_KEY": []byte(userDN),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, prepareError(err)
|
|
|
|
|
}
|
2021-05-10 11:31:56 -07:00
|
|
|
}
|
2021-08-03 17:36:26 -07:00
|
|
|
// attach the users to the tenant
|
|
|
|
|
minInst.Spec.Users = users
|
|
|
|
|
|
|
|
|
|
} else if tenantReq.Idp.Oidc != nil {
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantExternalIDPConfigured = true
|
2021-08-03 17:36:26 -07:00
|
|
|
// Enable IDP (OIDC) for MinIO
|
2021-09-20 16:13:16 -07:00
|
|
|
configurationURL := *tenantReq.Idp.Oidc.ConfigurationURL
|
2021-08-03 17:36:26 -07:00
|
|
|
clientID := *tenantReq.Idp.Oidc.ClientID
|
|
|
|
|
secretID := *tenantReq.Idp.Oidc.SecretID
|
|
|
|
|
claimName := *tenantReq.Idp.Oidc.ClaimName
|
|
|
|
|
scopes := tenantReq.Idp.Oidc.Scopes
|
2021-08-18 13:23:03 -07:00
|
|
|
callbackURL := tenantReq.Idp.Oidc.CallbackURL
|
2021-09-20 16:13:16 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_CONFIG_URL"] = configurationURL
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_CLIENT_ID"] = clientID
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_CLIENT_SECRET"] = secretID
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_CLAIM_NAME"] = claimName
|
|
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_REDIRECT_URI"] = callbackURL
|
2021-08-03 17:36:26 -07:00
|
|
|
if scopes == "" {
|
|
|
|
|
scopes = "openid,profile,email"
|
2021-05-10 11:31:56 -07:00
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_SCOPES"] = scopes
|
2021-08-03 17:36:26 -07:00
|
|
|
} else if len(tenantReq.Idp.Keys) > 0 {
|
|
|
|
|
// Create the secret any built-in user passed if no external IDP was configured
|
|
|
|
|
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
|
|
|
|
|
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
|
|
|
|
|
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
|
|
|
|
|
userSecret := corev1.Secret{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: userSecretName,
|
|
|
|
|
Labels: map[string]string{
|
|
|
|
|
miniov2.TenantLabel: tenantName,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Immutable: &imm,
|
|
|
|
|
Data: map[string][]byte{
|
|
|
|
|
"CONSOLE_ACCESS_KEY": []byte(*tenantReq.Idp.Keys[i].AccessKey),
|
|
|
|
|
"CONSOLE_SECRET_KEY": []byte(*tenantReq.Idp.Keys[i].SecretKey),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, prepareError(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// attach the users to the tenant
|
|
|
|
|
minInst.Spec.Users = users
|
2021-05-10 11:31:56 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-22 15:50:37 -07:00
|
|
|
isEncryptionEnabled := false
|
2020-11-30 12:08:13 -08:00
|
|
|
|
|
|
|
|
if tenantReq.EnableTLS != nil {
|
|
|
|
|
// if enableTLS is defined in the create tenant request we assign the value
|
|
|
|
|
// to the RequestAutoCert attribute in the tenant spec
|
2020-11-04 21:45:48 -08:00
|
|
|
minInst.Spec.RequestAutoCert = tenantReq.EnableTLS
|
2020-11-30 12:08:13 -08:00
|
|
|
if *tenantReq.EnableTLS {
|
|
|
|
|
// requestAutoCert is enabled, MinIO will be deployed with TLS enabled and encryption can be enabled
|
|
|
|
|
isEncryptionEnabled = true
|
|
|
|
|
}
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
}
|
2020-11-30 12:08:13 -08:00
|
|
|
// External TLS certificates for MinIO
|
|
|
|
|
if tenantReq.TLS != nil && len(tenantReq.TLS.Minio) > 0 {
|
2020-09-22 15:50:37 -07:00
|
|
|
isEncryptionEnabled = true
|
2020-09-03 10:20:58 -07:00
|
|
|
// Certificates used by the MinIO instance
|
|
|
|
|
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
|
2020-10-05 12:09:34 -07:00
|
|
|
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
}
|
2020-09-03 10:20:58 -07:00
|
|
|
minInst.Spec.ExternalCertSecret = externalCertSecret
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
}
|
2020-11-30 12:08:13 -08:00
|
|
|
// If encryption configuration is present and TLS will be enabled (using AutoCert or External certificates)
|
2020-09-22 15:50:37 -07:00
|
|
|
if tenantReq.Encryption != nil && isEncryptionEnabled {
|
2020-11-30 12:08:13 -08:00
|
|
|
// KES client mTLSCertificates used by MinIO instance
|
|
|
|
|
if tenantReq.Encryption.Client != nil {
|
2020-09-03 10:20:58 -07:00
|
|
|
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
|
2020-10-05 12:09:34 -07:00
|
|
|
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
|
|
|
|
|
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
|
2020-08-09 14:47:06 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(restapi.ErrorGeneric)
|
2020-08-09 14:47:06 -07:00
|
|
|
}
|
2020-10-05 12:09:34 -07:00
|
|
|
if len(certificateSecrets) > 0 {
|
|
|
|
|
minInst.Spec.ExternalClientCertSecret = certificateSecrets[0]
|
|
|
|
|
}
|
2020-08-07 20:23:03 -07:00
|
|
|
}
|
2020-11-30 12:08:13 -08:00
|
|
|
|
2020-08-07 20:23:03 -07:00
|
|
|
// KES configuration for Tenant instance
|
2020-11-30 12:08:13 -08:00
|
|
|
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName)
|
2020-08-07 20:23:03 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(restapi.ErrorGeneric)
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
}
|
2020-09-22 20:49:25 -07:00
|
|
|
// Set Labels, Annotations and Node Selector for KES
|
2020-09-22 15:50:37 -07:00
|
|
|
minInst.Spec.KES.Labels = tenantReq.Encryption.Labels
|
|
|
|
|
minInst.Spec.KES.Annotations = tenantReq.Encryption.Annotations
|
|
|
|
|
minInst.Spec.KES.NodeSelector = tenantReq.Encryption.NodeSelector
|
|
|
|
|
}
|
2021-02-02 18:49:40 -06:00
|
|
|
// External TLS CA certificates for MinIO
|
|
|
|
|
if tenantReq.TLS != nil && len(tenantReq.TLS.CaCertificates) > 0 {
|
|
|
|
|
var caCertificates []tenantSecret
|
|
|
|
|
for i, caCertificate := range tenantReq.TLS.CaCertificates {
|
|
|
|
|
certificateContent, err := base64.StdEncoding.DecodeString(caCertificate)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
2021-02-02 18:49:40 -06:00
|
|
|
}
|
|
|
|
|
caCertificates = append(caCertificates, tenantSecret{
|
|
|
|
|
Name: fmt.Sprintf("ca-certificate-%d", i),
|
|
|
|
|
Content: map[string][]byte{
|
|
|
|
|
"public.crt": certificateContent,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
if len(caCertificates) > 0 {
|
|
|
|
|
certificateSecrets, err := createOrReplaceSecrets(ctx, &k8sClient, ns, caCertificates, tenantName)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
2021-02-02 18:49:40 -06:00
|
|
|
}
|
|
|
|
|
minInst.Spec.ExternalCaCertSecret = certificateSecrets
|
|
|
|
|
}
|
|
|
|
|
}
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
|
2021-08-18 13:23:03 -07:00
|
|
|
// If Subnet License is present in k8s secrets, copy that to the MINIO_SUBNET_LICENSE env variable
|
|
|
|
|
// of the console tenant
|
|
|
|
|
license, _ := getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
|
|
|
|
if license != "" {
|
|
|
|
|
tenantConfigurationENV[MinIOSubnetLicense] = license
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 12:35:41 -07:00
|
|
|
// add annotations
|
|
|
|
|
var annotations map[string]string
|
2020-09-17 06:44:16 -07:00
|
|
|
|
2020-08-24 15:07:36 -07:00
|
|
|
if len(tenantReq.Annotations) > 0 {
|
2020-08-05 12:35:41 -07:00
|
|
|
annotations = tenantReq.Annotations
|
2020-09-17 06:44:16 -07:00
|
|
|
minInst.Annotations = annotations
|
2020-08-05 12:35:41 -07:00
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
// set the pools if they are provided
|
|
|
|
|
for _, pool := range tenantReq.Pools {
|
|
|
|
|
pool, err := parseTenantPoolRequest(pool)
|
2020-07-29 01:01:17 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("parseTenantPoolRequest failed: %v", err)
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
minInst.Spec.Pools = append(minInst.Spec.Pools, *pool)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set Mount Path if provided
|
2020-08-04 16:04:04 -07:00
|
|
|
if tenantReq.MounthPath != "" {
|
|
|
|
|
minInst.Spec.Mountpath = tenantReq.MounthPath
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-08-04 16:04:04 -07:00
|
|
|
|
2020-08-11 18:20:43 -07:00
|
|
|
// We accept either `image_pull_secret` or the individual details of the `image_registry` but not both
|
|
|
|
|
var imagePullSecret string
|
|
|
|
|
|
|
|
|
|
if tenantReq.ImagePullSecret != "" {
|
|
|
|
|
imagePullSecret = tenantReq.ImagePullSecret
|
2020-09-09 17:08:34 -07:00
|
|
|
} else if imagePullSecret, err = setImageRegistry(ctx, tenantReq.ImageRegistry, clientSet.CoreV1(), ns, tenantName); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-08-11 18:20:43 -07:00
|
|
|
// pass the image pull secret to the Tenant
|
|
|
|
|
if imagePullSecret != "" {
|
|
|
|
|
minInst.Spec.ImagePullSecret = corev1.LocalObjectReference{
|
|
|
|
|
Name: imagePullSecret,
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
|
2020-08-24 15:07:36 -07:00
|
|
|
// prometheus annotations support
|
2020-09-17 06:44:16 -07:00
|
|
|
if tenantReq.EnablePrometheus != nil && *tenantReq.EnablePrometheus && minInst.Annotations != nil {
|
|
|
|
|
minInst.Annotations[prometheusPath] = "/minio/prometheus/metrics"
|
2021-02-01 12:13:51 -08:00
|
|
|
minInst.Annotations[prometheusPort] = fmt.Sprint(miniov2.MinIOPort)
|
2020-09-17 06:44:16 -07:00
|
|
|
minInst.Annotations[prometheusScrape] = "true"
|
2020-08-24 15:07:36 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-21 01:39:14 -05:00
|
|
|
//Default class name for Log search
|
2021-07-21 14:05:40 -07:00
|
|
|
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
|
|
|
|
|
logSearchStorageClass := "" // Default is ""
|
2021-05-04 14:46:10 -07:00
|
|
|
logSearchImage := ""
|
|
|
|
|
logSearchPgImage := ""
|
2021-09-01 20:23:56 -07:00
|
|
|
logSearchPgInitImage := ""
|
2021-04-22 22:54:17 -05:00
|
|
|
|
|
|
|
|
if tenantReq.LogSearchConfiguration != nil {
|
2021-05-04 14:46:10 -07:00
|
|
|
if tenantReq.LogSearchConfiguration.StorageSize != nil {
|
2021-07-21 14:05:40 -07:00
|
|
|
diskSpaceFromAPI = int64(*tenantReq.LogSearchConfiguration.StorageSize) * humanize.GiByte
|
2021-05-04 14:46:10 -07:00
|
|
|
}
|
|
|
|
|
if tenantReq.LogSearchConfiguration.StorageClass != "" {
|
|
|
|
|
logSearchStorageClass = tenantReq.LogSearchConfiguration.StorageClass
|
|
|
|
|
}
|
2021-04-22 22:54:17 -05:00
|
|
|
if tenantReq.LogSearchConfiguration.StorageClass == "" && len(tenantReq.Pools) > 0 {
|
|
|
|
|
logSearchStorageClass = tenantReq.Pools[0].VolumeConfiguration.StorageClassName
|
|
|
|
|
}
|
2021-05-04 14:46:10 -07:00
|
|
|
if tenantReq.LogSearchConfiguration.Image != "" {
|
|
|
|
|
logSearchImage = tenantReq.LogSearchConfiguration.Image
|
|
|
|
|
}
|
|
|
|
|
if tenantReq.LogSearchConfiguration.PostgresImage != "" {
|
|
|
|
|
logSearchPgImage = tenantReq.LogSearchConfiguration.PostgresImage
|
|
|
|
|
}
|
2021-09-01 20:23:56 -07:00
|
|
|
if tenantReq.LogSearchConfiguration.PostgresInitImage != "" {
|
|
|
|
|
logSearchPgInitImage = tenantReq.LogSearchConfiguration.PostgresInitImage
|
|
|
|
|
}
|
2021-04-21 01:39:14 -05:00
|
|
|
}
|
2021-04-22 22:54:17 -05:00
|
|
|
|
|
|
|
|
logSearchDiskSpace := resource.NewQuantity(diskSpaceFromAPI, resource.DecimalExponent)
|
|
|
|
|
|
2021-07-21 14:05:40 -07:00
|
|
|
// the audit max cap cannot be larger than disk size on the DB, else it won't trim the data
|
|
|
|
|
auditMaxCap := 10
|
|
|
|
|
if (diskSpaceFromAPI / humanize.GiByte) < int64(auditMaxCap) {
|
|
|
|
|
auditMaxCap = int(diskSpaceFromAPI / humanize.GiByte)
|
|
|
|
|
}
|
2021-01-13 14:08:32 -06:00
|
|
|
// default activate lgo search and prometheus
|
2021-02-01 12:13:51 -08:00
|
|
|
minInst.Spec.Log = &miniov2.LogConfig{
|
2021-07-21 14:05:40 -07:00
|
|
|
Audit: &miniov2.AuditConfig{DiskCapacityGB: swag.Int(auditMaxCap)},
|
2021-04-21 01:39:14 -05:00
|
|
|
Db: &miniov2.LogDbConfig{
|
|
|
|
|
VolumeClaimTemplate: &corev1.PersistentVolumeClaim{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: tenantName + "-log",
|
|
|
|
|
},
|
|
|
|
|
Spec: corev1.PersistentVolumeClaimSpec{
|
|
|
|
|
AccessModes: []corev1.PersistentVolumeAccessMode{
|
|
|
|
|
corev1.ReadWriteOnce,
|
|
|
|
|
},
|
|
|
|
|
Resources: corev1.ResourceRequirements{
|
|
|
|
|
Requests: corev1.ResourceList{
|
|
|
|
|
corev1.ResourceStorage: *logSearchDiskSpace,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
StorageClassName: &logSearchStorageClass,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
2021-09-01 20:23:56 -07:00
|
|
|
// set log search images if any
|
2021-05-04 14:46:10 -07:00
|
|
|
if logSearchImage != "" {
|
|
|
|
|
minInst.Spec.Log.Image = logSearchImage
|
|
|
|
|
}
|
|
|
|
|
if logSearchPgImage != "" {
|
|
|
|
|
minInst.Spec.Log.Db.Image = logSearchPgImage
|
|
|
|
|
}
|
2021-09-01 20:23:56 -07:00
|
|
|
if logSearchPgInitImage != "" {
|
|
|
|
|
minInst.Spec.Log.Db.InitImage = logSearchPgInitImage
|
|
|
|
|
}
|
2021-04-21 01:39:14 -05:00
|
|
|
|
2021-09-01 20:23:56 -07:00
|
|
|
prometheusDiskSpace := 5 // Default is 5 by API
|
|
|
|
|
prometheusStorageClass := "" // Default is ""
|
|
|
|
|
prometheusImage := "" // Default is ""
|
|
|
|
|
prometheusSidecardImage := "" // Default is ""
|
|
|
|
|
prometheusInitImage := "" // Default is ""
|
2021-04-21 01:39:14 -05:00
|
|
|
|
2021-04-22 22:54:17 -05:00
|
|
|
if tenantReq.PrometheusConfiguration != nil {
|
2021-05-04 14:46:10 -07:00
|
|
|
if tenantReq.PrometheusConfiguration.StorageSize != nil {
|
|
|
|
|
prometheusDiskSpace = int(*tenantReq.PrometheusConfiguration.StorageSize)
|
|
|
|
|
}
|
|
|
|
|
if tenantReq.PrometheusConfiguration.StorageClass != "" {
|
|
|
|
|
prometheusStorageClass = tenantReq.PrometheusConfiguration.StorageClass
|
|
|
|
|
}
|
2021-04-22 22:54:17 -05:00
|
|
|
|
|
|
|
|
// Default class name for prometheus
|
|
|
|
|
if tenantReq.PrometheusConfiguration.StorageClass == "" && len(tenantReq.Pools) > 0 {
|
|
|
|
|
prometheusStorageClass = tenantReq.Pools[0].VolumeConfiguration.StorageClassName
|
|
|
|
|
}
|
2021-05-04 14:46:10 -07:00
|
|
|
|
|
|
|
|
if tenantReq.PrometheusConfiguration.Image != "" {
|
|
|
|
|
prometheusImage = tenantReq.PrometheusConfiguration.Image
|
|
|
|
|
}
|
2021-09-01 20:23:56 -07:00
|
|
|
if tenantReq.PrometheusConfiguration.SidecarImage != "" {
|
|
|
|
|
prometheusSidecardImage = tenantReq.PrometheusConfiguration.SidecarImage
|
|
|
|
|
}
|
|
|
|
|
if tenantReq.PrometheusConfiguration.InitImage != "" {
|
|
|
|
|
prometheusInitImage = tenantReq.PrometheusConfiguration.InitImage
|
|
|
|
|
}
|
2021-04-21 01:39:14 -05:00
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
minInst.Spec.Prometheus = &miniov2.PrometheusConfig{
|
2021-04-21 01:39:14 -05:00
|
|
|
DiskCapacityDB: swag.Int(prometheusDiskSpace),
|
|
|
|
|
StorageClassName: &prometheusStorageClass,
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
2021-05-04 14:46:10 -07:00
|
|
|
if prometheusImage != "" {
|
|
|
|
|
minInst.Spec.Prometheus.Image = prometheusImage
|
|
|
|
|
}
|
2021-09-01 20:23:56 -07:00
|
|
|
if prometheusSidecardImage != "" {
|
|
|
|
|
minInst.Spec.Prometheus.SideCarImage = prometheusSidecardImage
|
|
|
|
|
}
|
|
|
|
|
if prometheusInitImage != "" {
|
|
|
|
|
minInst.Spec.Prometheus.InitImage = prometheusInitImage
|
|
|
|
|
}
|
2021-08-23 21:06:32 -07:00
|
|
|
// if security context for prometheus is present, configure it.
|
|
|
|
|
if tenantReq.PrometheusConfiguration != nil && tenantReq.PrometheusConfiguration.SecurityContext != nil {
|
|
|
|
|
sc := tenantReq.PrometheusConfiguration.SecurityContext
|
|
|
|
|
minInst.Spec.Prometheus.SecurityContext = &corev1.PodSecurityContext{
|
|
|
|
|
RunAsUser: sc.RunAsUser,
|
|
|
|
|
RunAsGroup: sc.RunAsGroup,
|
|
|
|
|
RunAsNonRoot: sc.RunAsNonRoot,
|
|
|
|
|
FSGroup: sc.FsGroup,
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-13 14:08:32 -06:00
|
|
|
|
|
|
|
|
// expose services
|
2021-08-03 17:36:26 -07:00
|
|
|
minInst.Spec.ExposeServices = &miniov2.ExposeServices{
|
|
|
|
|
MinIO: tenantReq.ExposeMinio,
|
|
|
|
|
Console: tenantReq.ExposeConsole,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// write tenant configuration to secret that contains config.env
|
|
|
|
|
tenantConfigurationName := fmt.Sprintf("%s-env-configuration", tenantName)
|
|
|
|
|
_, err = createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{
|
|
|
|
|
{
|
|
|
|
|
Name: tenantConfigurationName,
|
|
|
|
|
Content: map[string][]byte{
|
2021-08-18 13:23:03 -07:00
|
|
|
"config.env": []byte(GenerateTenantConfigurationFile(tenantConfigurationENV)),
|
2021-08-03 17:36:26 -07:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, tenantName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
2021-01-13 14:08:32 -06:00
|
|
|
}
|
2021-08-03 17:36:26 -07:00
|
|
|
minInst.Spec.Configuration = &corev1.LocalObjectReference{Name: tenantConfigurationName}
|
2021-01-13 14:08:32 -06:00
|
|
|
|
2020-12-07 17:11:08 -06:00
|
|
|
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
_, err = opClient.MinioV2().Tenants(ns).Create(context.Background(), &minInst, metav1.CreateOptions{})
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("Creating new tenant failed with: %v", err)
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 14:18:14 -07:00
|
|
|
// Integrations
|
2020-07-01 18:03:22 -07:00
|
|
|
if os.Getenv("GKE_INTEGRATION") != "" {
|
2020-12-07 17:11:08 -06:00
|
|
|
err := gkeIntegration(clientSet, tenantName, ns, session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-08-18 13:23:03 -07:00
|
|
|
response = &models.CreateTenantResponse{
|
|
|
|
|
ExternalIDP: tenantExternalIDPConfigured,
|
|
|
|
|
}
|
|
|
|
|
if tenantReq.Idp != nil && !tenantExternalIDPConfigured {
|
|
|
|
|
for _, credential := range tenantReq.Idp.Keys {
|
|
|
|
|
response.Console = append(response.Console, &models.TenantResponseItem{
|
|
|
|
|
AccessKey: *credential.AccessKey,
|
|
|
|
|
SecretKey: *credential.SecretKey,
|
|
|
|
|
})
|
2020-08-04 16:04:04 -07:00
|
|
|
}
|
2020-07-30 13:55:11 -07:00
|
|
|
}
|
|
|
|
|
return response, nil
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 18:20:43 -07:00
|
|
|
// setImageRegistry creates a secret to store the private registry credentials, if one exist it updates the existing one
|
|
|
|
|
// returns the name of the secret created/updated
|
2020-09-09 17:08:34 -07:00
|
|
|
func setImageRegistry(ctx context.Context, req *models.ImageRegistry, clientset v1.CoreV1Interface, namespace, tenantName string) (string, error) {
|
2020-08-04 20:54:59 -07:00
|
|
|
if req == nil || req.Registry == nil || req.Username == nil || req.Password == nil {
|
2020-08-11 18:20:43 -07:00
|
|
|
return "", nil
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
credentials := make(map[string]imageRegistryCredentials)
|
|
|
|
|
// username:password encoded
|
|
|
|
|
authData := []byte(fmt.Sprintf("%s:%s", *req.Username, *req.Password))
|
|
|
|
|
authStr := base64.StdEncoding.EncodeToString(authData)
|
|
|
|
|
|
|
|
|
|
credentials[*req.Registry] = imageRegistryCredentials{
|
|
|
|
|
Username: *req.Username,
|
|
|
|
|
Password: *req.Password,
|
|
|
|
|
Auth: authStr,
|
|
|
|
|
}
|
|
|
|
|
imRegistry := imageRegistry{
|
|
|
|
|
Auths: credentials,
|
|
|
|
|
}
|
|
|
|
|
imRegistryJSON, err := json.Marshal(imRegistry)
|
|
|
|
|
if err != nil {
|
2020-08-11 18:20:43 -07:00
|
|
|
return "", err
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 18:20:43 -07:00
|
|
|
pullSecretName := fmt.Sprintf("%s-regcred", tenantName)
|
2020-09-06 23:20:27 -07:00
|
|
|
secretCredentials := map[string][]byte{
|
|
|
|
|
corev1.DockerConfigJsonKey: []byte(string(imRegistryJSON)),
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
|
|
|
|
// Get or Create secret if it doesn't exist
|
2020-09-06 23:20:27 -07:00
|
|
|
currentSecret, err := clientset.Secrets(namespace).Get(ctx, pullSecretName, metav1.GetOptions{})
|
2020-08-04 20:54:59 -07:00
|
|
|
if err != nil {
|
|
|
|
|
if k8sErrors.IsNotFound(err) {
|
2020-09-06 23:20:27 -07:00
|
|
|
instanceSecret := corev1.Secret{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: pullSecretName,
|
|
|
|
|
Labels: map[string]string{
|
2021-02-01 12:13:51 -08:00
|
|
|
miniov2.TenantLabel: tenantName,
|
2020-09-06 23:20:27 -07:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Data: secretCredentials,
|
|
|
|
|
Type: corev1.SecretTypeDockerConfigJson,
|
|
|
|
|
}
|
2020-08-04 20:54:59 -07:00
|
|
|
_, err = clientset.Secrets(namespace).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
|
|
|
|
if err != nil {
|
2020-08-11 18:20:43 -07:00
|
|
|
return "", err
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
2020-09-04 17:09:17 -07:00
|
|
|
return pullSecretName, nil
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
2020-08-11 18:20:43 -07:00
|
|
|
return "", err
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
2020-09-06 23:20:27 -07:00
|
|
|
currentSecret.Data = secretCredentials
|
|
|
|
|
_, err = clientset.Secrets(namespace).Update(ctx, currentSecret, metav1.UpdateOptions{})
|
2020-08-04 20:54:59 -07:00
|
|
|
if err != nil {
|
2020-08-11 18:20:43 -07:00
|
|
|
return "", err
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
2020-08-11 18:20:43 -07:00
|
|
|
return pullSecretName, nil
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-25 14:38:16 -07:00
|
|
|
// updateTenantAction does an update on the minioTenant by patching the desired changes
|
2021-07-19 11:48:50 -07:00
|
|
|
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl cluster.HTTPClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
2020-07-01 18:03:22 -07:00
|
|
|
imageToUpdate := params.Body.Image
|
2020-08-04 20:54:59 -07:00
|
|
|
imageRegistryReq := params.Body.ImageRegistry
|
|
|
|
|
|
|
|
|
|
minInst, err := operatorClient.TenantGet(ctx, namespace, params.Tenant, metav1.GetOptions{})
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-08-11 18:20:43 -07:00
|
|
|
// we can take either the `image_pull_secret` of the `image_registry` but not both
|
|
|
|
|
if params.Body.ImagePullSecret != "" {
|
|
|
|
|
minInst.Spec.ImagePullSecret.Name = params.Body.ImagePullSecret
|
|
|
|
|
} else {
|
|
|
|
|
// update the image pull secret content
|
2020-09-09 17:08:34 -07:00
|
|
|
if _, err := setImageRegistry(ctx, imageRegistryReq, clientset, namespace, params.Tenant); err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("error setting image registry secret: %v", err)
|
2020-08-11 18:20:43 -07:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
// if image to update is empty we'll use the latest image by default
|
|
|
|
|
if strings.TrimSpace(imageToUpdate) != "" {
|
2020-08-04 20:54:59 -07:00
|
|
|
minInst.Spec.Image = imageToUpdate
|
2020-07-01 18:03:22 -07:00
|
|
|
} else {
|
|
|
|
|
im, err := cluster.GetLatestMinioImage(httpCl)
|
2020-08-11 18:20:43 -07:00
|
|
|
// if we can't get the MinIO image, we won' auto-update it unless it's explicit by name
|
|
|
|
|
if err == nil {
|
|
|
|
|
minInst.Spec.Image = *im
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 23:48:51 -07:00
|
|
|
// Prometheus Annotations
|
2020-09-17 06:44:16 -07:00
|
|
|
currentAnnotations := minInst.Annotations
|
2020-09-05 23:48:51 -07:00
|
|
|
prometheusAnnotations := map[string]string{
|
|
|
|
|
prometheusPath: "/minio/prometheus/metrics",
|
2021-02-01 12:13:51 -08:00
|
|
|
prometheusPort: fmt.Sprint(miniov2.MinIOPort),
|
2020-09-05 23:48:51 -07:00
|
|
|
prometheusScrape: "true",
|
|
|
|
|
}
|
2020-09-21 21:31:30 -07:00
|
|
|
if params.Body.EnablePrometheus && currentAnnotations != nil {
|
2020-09-05 23:48:51 -07:00
|
|
|
// add prometheus annotations to the tenant
|
2020-09-17 06:44:16 -07:00
|
|
|
minInst.Annotations = addAnnotations(currentAnnotations, prometheusAnnotations)
|
2020-12-07 09:49:51 -06:00
|
|
|
// add prometheus annotations to the each pool
|
|
|
|
|
if minInst.Spec.Pools != nil {
|
|
|
|
|
for _, pool := range minInst.Spec.Pools {
|
|
|
|
|
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
|
|
|
|
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(addAnnotations(poolAnnotations, prometheusAnnotations))
|
2020-09-05 23:48:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// remove prometheus annotations to the tenant
|
2020-09-17 06:44:16 -07:00
|
|
|
minInst.Annotations = removeAnnotations(currentAnnotations, prometheusAnnotations)
|
2020-12-07 09:49:51 -06:00
|
|
|
// add prometheus annotations from each pool
|
|
|
|
|
if minInst.Spec.Pools != nil {
|
|
|
|
|
for _, pool := range minInst.Spec.Pools {
|
|
|
|
|
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
|
|
|
|
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(removeAnnotations(poolAnnotations, prometheusAnnotations))
|
2020-09-05 23:48:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-01 18:03:22 -07:00
|
|
|
payloadBytes, err := json.Marshal(minInst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-08-04 20:54:59 -07:00
|
|
|
_, err = operatorClient.TenantPatch(ctx, namespace, minInst.Name, types.MergePatchType, payloadBytes, metav1.PatchOptions{})
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 23:48:51 -07:00
|
|
|
// addAnnotations will merge two annotation maps
|
|
|
|
|
func addAnnotations(annotationsOne, annotationsTwo map[string]string) map[string]string {
|
|
|
|
|
if annotationsOne == nil {
|
|
|
|
|
annotationsOne = map[string]string{}
|
|
|
|
|
}
|
|
|
|
|
for key, value := range annotationsTwo {
|
|
|
|
|
annotationsOne[key] = value
|
|
|
|
|
}
|
|
|
|
|
return annotationsOne
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// removeAnnotations will remove keys from the first annotations map based on the second one
|
|
|
|
|
func removeAnnotations(annotationsOne, annotationsTwo map[string]string) map[string]string {
|
|
|
|
|
if annotationsOne == nil {
|
|
|
|
|
annotationsOne = map[string]string{}
|
|
|
|
|
}
|
|
|
|
|
for key := range annotationsTwo {
|
|
|
|
|
delete(annotationsOne, key)
|
|
|
|
|
}
|
|
|
|
|
return annotationsOne
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getUpdateTenantResponse(session *models.Principal, params operator_api.UpdateTenantParams) *models.Error {
|
2020-07-01 18:03:22 -07:00
|
|
|
ctx := context.Background()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-01 18:03:22 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-08-04 20:54:59 -07:00
|
|
|
// get Kubernetes Client
|
2020-12-07 17:11:08 -06:00
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
2020-08-04 20:54:59 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-08-04 20:54:59 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
httpC := &cluster.HTTPClient{
|
|
|
|
|
Client: &http.Client{
|
|
|
|
|
Timeout: 4 * time.Second,
|
|
|
|
|
},
|
|
|
|
|
}
|
2020-09-04 20:32:57 -07:00
|
|
|
if err := updateTenantAction(ctx, opClient, clientSet.CoreV1(), httpC, params.Namespace, params); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err, errors.New("unable to update tenant"))
|
2020-07-01 18:03:22 -07:00
|
|
|
}
|
2020-07-13 20:36:27 -07:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// addTenantPool creates a pool to a defined tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
func addTenantPool(ctx context.Context, operatorClient OperatorClientI, params operator_api.TenantAddPoolParams) error {
|
2020-07-29 01:01:17 -07:00
|
|
|
tenant, err := operatorClient.TenantGet(ctx, params.Namespace, params.Tenant, metav1.GetOptions{})
|
2020-07-13 20:36:27 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
poolParams := params.Body
|
|
|
|
|
pool, err := parseTenantPoolRequest(poolParams)
|
2020-07-27 14:06:12 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
tenant.Spec.Pools = append(tenant.Spec.Pools, *pool)
|
2020-07-29 01:01:17 -07:00
|
|
|
payloadBytes, err := json.Marshal(tenant)
|
2020-07-13 20:36:27 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
|
2020-07-29 01:01:17 -07:00
|
|
|
_, err = operatorClient.TenantPatch(ctx, params.Namespace, tenant.Name, types.MergePatchType, payloadBytes, metav1.PatchOptions{})
|
2020-07-13 20:36:27 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantAddPoolResponse(session *models.Principal, params operator_api.TenantAddPoolParams) *models.Error {
|
2020-07-13 20:36:27 -07:00
|
|
|
ctx := context.Background()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-13 20:36:27 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2020-07-13 20:36:27 -07:00
|
|
|
}
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
if err := addTenantPool(ctx, opClient, params); err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err, errors.New("unable to add pool"))
|
2020-07-13 20:36:27 -07:00
|
|
|
}
|
2020-07-01 18:03:22 -07:00
|
|
|
return nil
|
|
|
|
|
}
|
2020-07-27 14:19:40 -07:00
|
|
|
|
|
|
|
|
// getTenantUsageResponse returns the usage of a tenant
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantUsageResponse(session *models.Principal, params operator_api.GetTenantUsageParams) (*models.TenantUsage, *models.Error) {
|
2021-08-18 13:23:03 -07:00
|
|
|
// 30 seconds timeout
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
2020-07-27 14:19:40 -07:00
|
|
|
defer cancel()
|
|
|
|
|
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-07-27 14:19:40 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
2020-12-07 17:11:08 -06:00
|
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
2020-07-27 14:19:40 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
k8sClient := &k8sClient{
|
2020-09-04 20:32:57 -07:00
|
|
|
client: clientSet,
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
2020-08-09 14:36:55 -07:00
|
|
|
minTenant.EnsureDefaults()
|
2020-07-27 14:19:40 -07:00
|
|
|
|
2020-09-21 21:31:30 -07:00
|
|
|
svcURL := GetTenantServiceURL(minTenant)
|
2020-12-07 13:49:00 -06:00
|
|
|
// getTenantAdminClient will use all certificates under ~/.console/certs/CAs to trust the TLS connections with MinIO tenants
|
2020-07-27 14:19:40 -07:00
|
|
|
mAdmin, err := getTenantAdminClient(
|
|
|
|
|
ctx,
|
|
|
|
|
k8sClient,
|
2020-10-09 11:51:02 -07:00
|
|
|
minTenant,
|
2020-09-21 21:31:30 -07:00
|
|
|
svcURL,
|
2020-11-30 12:41:58 -08:00
|
|
|
)
|
2020-07-27 14:19:40 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
|
|
|
|
// create a minioClient interface implementation
|
|
|
|
|
// defining the client to be used
|
2021-07-19 11:48:50 -07:00
|
|
|
adminClient := restapi.AdminClient{Client: mAdmin}
|
2020-07-27 14:19:40 -07:00
|
|
|
// serialize output
|
2021-07-19 11:48:50 -07:00
|
|
|
adminInfo, err := restapi.GetAdminInfo(ctx, adminClient)
|
2020-07-27 14:19:40 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
2020-07-27 14:19:40 -07:00
|
|
|
}
|
2020-08-03 12:11:48 -07:00
|
|
|
info := &models.TenantUsage{Used: adminInfo.Usage, DiskUsed: adminInfo.DisksUsage}
|
2020-07-27 14:19:40 -07:00
|
|
|
return info, nil
|
|
|
|
|
}
|
2020-07-29 01:01:17 -07:00
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantPodsResponse(session *models.Principal, params operator_api.GetTenantPodsParams) ([]*models.TenantPod, *models.Error) {
|
2021-05-06 18:57:14 -07:00
|
|
|
ctx := context.Background()
|
|
|
|
|
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-06 18:57:14 -07:00
|
|
|
}
|
|
|
|
|
listOpts := metav1.ListOptions{
|
|
|
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, params.Tenant),
|
|
|
|
|
}
|
|
|
|
|
pods, err := clientset.CoreV1().Pods(params.Namespace).List(ctx, listOpts)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-06 18:57:14 -07:00
|
|
|
}
|
|
|
|
|
retval := []*models.TenantPod{}
|
2021-05-21 07:55:53 -07:00
|
|
|
for _, pod := range pods.Items {
|
2021-06-07 20:53:03 -07:00
|
|
|
var restarts int64
|
2021-05-21 07:55:53 -07:00
|
|
|
if len(pod.Status.ContainerStatuses) > 0 {
|
|
|
|
|
restarts = int64(pod.Status.ContainerStatuses[0].RestartCount)
|
|
|
|
|
}
|
2021-06-07 18:56:05 -07:00
|
|
|
retval = append(retval, &models.TenantPod{
|
|
|
|
|
Name: swag.String(pod.Name),
|
2021-05-21 07:55:53 -07:00
|
|
|
Status: string(pod.Status.Phase),
|
|
|
|
|
TimeCreated: pod.CreationTimestamp.Unix(),
|
|
|
|
|
PodIP: pod.Status.PodIP,
|
2021-05-06 18:57:14 -07:00
|
|
|
Restarts: restarts,
|
2021-05-21 07:55:53 -07:00
|
|
|
Node: pod.Spec.NodeName})
|
2021-05-06 18:57:14 -07:00
|
|
|
}
|
|
|
|
|
return retval, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getPodLogsResponse(session *models.Principal, params operator_api.GetPodLogsParams) (string, *models.Error) {
|
2021-06-07 18:56:05 -07:00
|
|
|
ctx := context.Background()
|
|
|
|
|
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return "", prepareError(err)
|
2021-06-07 18:56:05 -07:00
|
|
|
}
|
|
|
|
|
listOpts := &corev1.PodLogOptions{}
|
|
|
|
|
logs := clientset.CoreV1().Pods(params.Namespace).GetLogs(params.PodName, listOpts)
|
|
|
|
|
buff, err := logs.DoRaw(ctx)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return "", prepareError(err)
|
2021-06-07 18:56:05 -07:00
|
|
|
}
|
|
|
|
|
return string(buff), nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getPodEventsResponse(session *models.Principal, params operator_api.GetPodEventsParams) (models.EventListWrapper, *models.Error) {
|
2021-06-17 16:24:22 -07:00
|
|
|
ctx := context.Background()
|
|
|
|
|
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 16:24:22 -07:00
|
|
|
}
|
|
|
|
|
pod, err := clientset.CoreV1().Pods(params.Namespace).Get(ctx, params.PodName, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 16:24:22 -07:00
|
|
|
}
|
|
|
|
|
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", pod.UID)})
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-06-17 16:24:22 -07:00
|
|
|
}
|
|
|
|
|
retval := models.EventListWrapper{}
|
|
|
|
|
for i := 0; i < len(events.Items); i++ {
|
|
|
|
|
retval = append(retval, &models.EventListElement{
|
|
|
|
|
Namespace: events.Items[i].Namespace,
|
|
|
|
|
LastSeen: events.Items[i].LastTimestamp.Unix(),
|
|
|
|
|
Message: events.Items[i].Message,
|
|
|
|
|
EventType: events.Items[i].Type,
|
|
|
|
|
Reason: events.Items[i].Reason,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
sort.SliceStable(retval, func(i int, j int) bool {
|
|
|
|
|
return retval[i].LastSeen < retval[j].LastSeen
|
|
|
|
|
})
|
|
|
|
|
return retval, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// parseTenantPoolRequest parse pool request and returns the equivalent
|
2021-02-01 12:13:51 -08:00
|
|
|
// miniov2.Pool object
|
|
|
|
|
func parseTenantPoolRequest(poolParams *models.Pool) (*miniov2.Pool, error) {
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.VolumeConfiguration == nil {
|
2020-07-29 01:01:17 -07:00
|
|
|
return nil, errors.New("a volume configuration must be specified")
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.VolumeConfiguration.Size == nil || *poolParams.VolumeConfiguration.Size <= int64(0) {
|
2020-07-29 01:01:17 -07:00
|
|
|
return nil, errors.New("volume size must be greater than 0")
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.Servers == nil || *poolParams.Servers <= 0 {
|
2020-07-29 01:01:17 -07:00
|
|
|
return nil, errors.New("number of servers must be greater than 0")
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.VolumesPerServer == nil || *poolParams.VolumesPerServer <= 0 {
|
2020-07-29 01:01:17 -07:00
|
|
|
return nil, errors.New("number of volumes per server must be greater than 0")
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
volumeSize := resource.NewQuantity(*poolParams.VolumeConfiguration.Size, resource.DecimalExponent)
|
2020-07-29 01:01:17 -07:00
|
|
|
volTemp := corev1.PersistentVolumeClaimSpec{
|
|
|
|
|
AccessModes: []corev1.PersistentVolumeAccessMode{
|
|
|
|
|
corev1.ReadWriteOnce,
|
|
|
|
|
},
|
|
|
|
|
Resources: corev1.ResourceRequirements{
|
|
|
|
|
Requests: corev1.ResourceList{
|
|
|
|
|
corev1.ResourceStorage: *volumeSize,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.VolumeConfiguration.StorageClassName != "" {
|
|
|
|
|
volTemp.StorageClassName = &poolParams.VolumeConfiguration.StorageClassName
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse resources' requests
|
TLS with user provided certificates and KES support for MinIO (#213)
This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
2020-07-30 17:49:56 -07:00
|
|
|
resourcesRequests := make(corev1.ResourceList)
|
|
|
|
|
resourcesLimits := make(corev1.ResourceList)
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.Resources != nil {
|
|
|
|
|
for key, val := range poolParams.Resources.Requests {
|
2020-07-29 01:01:17 -07:00
|
|
|
resourcesRequests[corev1.ResourceName(key)] = *resource.NewQuantity(val, resource.BinarySI)
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for key, val := range poolParams.Resources.Limits {
|
2020-07-29 01:01:17 -07:00
|
|
|
resourcesLimits[corev1.ResourceName(key)] = *resource.NewQuantity(val, resource.BinarySI)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Node Affinity
|
|
|
|
|
nodeSelectorTerms := []corev1.NodeSelectorTerm{}
|
|
|
|
|
preferredSchedulingTerm := []corev1.PreferredSchedulingTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.Affinity != nil && poolParams.Affinity.NodeAffinity != nil {
|
|
|
|
|
if poolParams.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
|
|
|
|
for _, elem := range poolParams.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
|
2020-07-29 01:01:17 -07:00
|
|
|
term := parseModelsNodeSelectorTerm(elem)
|
|
|
|
|
nodeSelectorTerms = append(nodeSelectorTerms, term)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range poolParams.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
pst := corev1.PreferredSchedulingTerm{
|
|
|
|
|
Weight: *elem.Weight,
|
|
|
|
|
Preference: parseModelsNodeSelectorTerm(elem.Preference),
|
|
|
|
|
}
|
|
|
|
|
preferredSchedulingTerm = append(preferredSchedulingTerm, pst)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var nodeAffinity *corev1.NodeAffinity
|
|
|
|
|
if len(nodeSelectorTerms) > 0 || len(preferredSchedulingTerm) > 0 {
|
|
|
|
|
nodeAffinity = &corev1.NodeAffinity{
|
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
|
|
|
|
|
NodeSelectorTerms: nodeSelectorTerms,
|
|
|
|
|
},
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: preferredSchedulingTerm,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Pod Affinity
|
|
|
|
|
podAffinityTerms := []corev1.PodAffinityTerm{}
|
|
|
|
|
weightedPodAffinityTerms := []corev1.WeightedPodAffinityTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.Affinity != nil && poolParams.Affinity.PodAffinity != nil {
|
|
|
|
|
for _, elem := range poolParams.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
podAffinityTerms = append(podAffinityTerms, parseModelPodAffinityTerm(elem))
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range poolParams.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
wAffinityTerm := corev1.WeightedPodAffinityTerm{
|
|
|
|
|
Weight: *elem.Weight,
|
|
|
|
|
PodAffinityTerm: parseModelPodAffinityTerm(elem.PodAffinityTerm),
|
|
|
|
|
}
|
|
|
|
|
weightedPodAffinityTerms = append(weightedPodAffinityTerms, wAffinityTerm)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var podAffinity *corev1.PodAffinity
|
|
|
|
|
if len(podAffinityTerms) > 0 || len(weightedPodAffinityTerms) > 0 {
|
|
|
|
|
podAffinity = &corev1.PodAffinity{
|
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: podAffinityTerms,
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: weightedPodAffinityTerms,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Pod Anti Affinity
|
|
|
|
|
podAntiAffinityTerms := []corev1.PodAffinityTerm{}
|
|
|
|
|
weightedPodAntiAffinityTerms := []corev1.WeightedPodAffinityTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
if poolParams.Affinity != nil && poolParams.Affinity.PodAntiAffinity != nil {
|
|
|
|
|
for _, elem := range poolParams.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
podAntiAffinityTerms = append(podAntiAffinityTerms, parseModelPodAffinityTerm(elem))
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range poolParams.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
wAffinityTerm := corev1.WeightedPodAffinityTerm{
|
|
|
|
|
Weight: *elem.Weight,
|
|
|
|
|
PodAffinityTerm: parseModelPodAffinityTerm(elem.PodAffinityTerm),
|
|
|
|
|
}
|
|
|
|
|
weightedPodAntiAffinityTerms = append(weightedPodAntiAffinityTerms, wAffinityTerm)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var podAntiAffinity *corev1.PodAntiAffinity
|
|
|
|
|
if len(podAntiAffinityTerms) > 0 || len(weightedPodAntiAffinityTerms) > 0 {
|
|
|
|
|
podAntiAffinity = &corev1.PodAntiAffinity{
|
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: podAntiAffinityTerms,
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: weightedPodAntiAffinityTerms,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var affinity *corev1.Affinity
|
|
|
|
|
if nodeAffinity != nil || podAffinity != nil || podAntiAffinity != nil {
|
|
|
|
|
affinity = &corev1.Affinity{
|
|
|
|
|
NodeAffinity: nodeAffinity,
|
|
|
|
|
PodAffinity: podAffinity,
|
|
|
|
|
PodAntiAffinity: podAntiAffinity,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse tolerations
|
|
|
|
|
tolerations := []corev1.Toleration{}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range poolParams.Tolerations {
|
2020-08-07 20:00:16 -07:00
|
|
|
var tolerationSeconds *int64
|
|
|
|
|
if elem.TolerationSeconds != nil {
|
|
|
|
|
// elem.TolerationSeconds.Seconds is allowed to be nil
|
|
|
|
|
tolerationSeconds = elem.TolerationSeconds.Seconds
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 01:01:17 -07:00
|
|
|
toleration := corev1.Toleration{
|
|
|
|
|
Key: elem.Key,
|
|
|
|
|
Operator: corev1.TolerationOperator(elem.Operator),
|
|
|
|
|
Value: elem.Value,
|
|
|
|
|
Effect: corev1.TaintEffect(elem.Effect),
|
2020-08-07 20:00:16 -07:00
|
|
|
TolerationSeconds: tolerationSeconds,
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
tolerations = append(tolerations, toleration)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-05 12:35:41 -07:00
|
|
|
// Pass annotations to the volume
|
|
|
|
|
vct := &corev1.PersistentVolumeClaim{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2020-09-17 06:44:16 -07:00
|
|
|
Name: "data",
|
2020-12-07 09:49:51 -06:00
|
|
|
Labels: poolParams.VolumeConfiguration.Labels,
|
|
|
|
|
Annotations: poolParams.VolumeConfiguration.Annotations,
|
2020-07-29 01:01:17 -07:00
|
|
|
},
|
2020-08-05 12:35:41 -07:00
|
|
|
Spec: volTemp,
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
pool := &miniov2.Pool{
|
2020-12-07 09:49:51 -06:00
|
|
|
Name: poolParams.Name,
|
|
|
|
|
Servers: int32(*poolParams.Servers),
|
|
|
|
|
VolumesPerServer: *poolParams.VolumesPerServer,
|
2020-08-05 12:35:41 -07:00
|
|
|
VolumeClaimTemplate: vct,
|
2020-07-29 01:01:17 -07:00
|
|
|
Resources: corev1.ResourceRequirements{
|
|
|
|
|
Requests: resourcesRequests,
|
|
|
|
|
Limits: resourcesLimits,
|
|
|
|
|
},
|
2020-12-07 09:49:51 -06:00
|
|
|
NodeSelector: poolParams.NodeSelector,
|
2020-07-29 01:01:17 -07:00
|
|
|
Affinity: affinity,
|
|
|
|
|
Tolerations: tolerations,
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
return pool, nil
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseModelPodAffinityTerm(term *models.PodAffinityTerm) corev1.PodAffinityTerm {
|
|
|
|
|
labelMatchExpressions := []metav1.LabelSelectorRequirement{}
|
|
|
|
|
for _, exp := range term.LabelSelector.MatchExpressions {
|
|
|
|
|
labelSelectorReq := metav1.LabelSelectorRequirement{
|
|
|
|
|
Key: *exp.Key,
|
|
|
|
|
Operator: metav1.LabelSelectorOperator(*exp.Operator),
|
|
|
|
|
Values: exp.Values,
|
|
|
|
|
}
|
|
|
|
|
labelMatchExpressions = append(labelMatchExpressions, labelSelectorReq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
podAffinityTerm := corev1.PodAffinityTerm{
|
|
|
|
|
LabelSelector: &metav1.LabelSelector{
|
|
|
|
|
MatchExpressions: labelMatchExpressions,
|
|
|
|
|
MatchLabels: term.LabelSelector.MatchLabels,
|
|
|
|
|
},
|
|
|
|
|
Namespaces: term.Namespaces,
|
|
|
|
|
TopologyKey: *term.TopologyKey,
|
|
|
|
|
}
|
|
|
|
|
return podAffinityTerm
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseModelsNodeSelectorTerm(elem *models.NodeSelectorTerm) corev1.NodeSelectorTerm {
|
|
|
|
|
var term corev1.NodeSelectorTerm
|
|
|
|
|
for _, matchExpression := range elem.MatchExpressions {
|
|
|
|
|
matchExp := corev1.NodeSelectorRequirement{
|
|
|
|
|
Key: *matchExpression.Key,
|
|
|
|
|
Operator: corev1.NodeSelectorOperator(*matchExpression.Operator),
|
|
|
|
|
Values: matchExpression.Values,
|
|
|
|
|
}
|
|
|
|
|
term.MatchExpressions = append(term.MatchExpressions, matchExp)
|
|
|
|
|
}
|
|
|
|
|
for _, matchField := range elem.MatchFields {
|
|
|
|
|
matchF := corev1.NodeSelectorRequirement{
|
|
|
|
|
Key: *matchField.Key,
|
|
|
|
|
Operator: corev1.NodeSelectorOperator(*matchField.Operator),
|
|
|
|
|
Values: matchField.Values,
|
|
|
|
|
}
|
|
|
|
|
term.MatchFields = append(term.MatchFields, matchF)
|
|
|
|
|
}
|
|
|
|
|
return term
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-01 12:13:51 -08:00
|
|
|
// parseTenantPool miniov2 pool object and returns the equivalent
|
2020-12-07 09:49:51 -06:00
|
|
|
// models.Pool object
|
2021-02-01 12:13:51 -08:00
|
|
|
func parseTenantPool(pool *miniov2.Pool) *models.Pool {
|
2020-07-29 01:01:17 -07:00
|
|
|
var size *int64
|
|
|
|
|
var storageClassName string
|
2020-12-07 09:49:51 -06:00
|
|
|
if pool.VolumeClaimTemplate != nil {
|
|
|
|
|
size = swag.Int64(pool.VolumeClaimTemplate.Spec.Resources.Requests.Storage().Value())
|
|
|
|
|
if pool.VolumeClaimTemplate.Spec.StorageClassName != nil {
|
|
|
|
|
storageClassName = *pool.VolumeClaimTemplate.Spec.StorageClassName
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse resources' requests
|
2020-12-07 09:49:51 -06:00
|
|
|
var resources *models.PoolResources
|
2020-07-29 12:11:48 -07:00
|
|
|
resourcesRequests := make(map[string]int64)
|
|
|
|
|
resourcesLimits := make(map[string]int64)
|
2020-12-07 09:49:51 -06:00
|
|
|
for key, val := range pool.Resources.Requests {
|
2020-07-29 01:01:17 -07:00
|
|
|
resourcesRequests[key.String()] = val.Value()
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for key, val := range pool.Resources.Limits {
|
2020-07-29 01:01:17 -07:00
|
|
|
resourcesLimits[key.String()] = val.Value()
|
|
|
|
|
}
|
|
|
|
|
if len(resourcesRequests) > 0 || len(resourcesLimits) > 0 {
|
2020-12-07 09:49:51 -06:00
|
|
|
resources = &models.PoolResources{
|
2020-07-29 01:01:17 -07:00
|
|
|
Limits: resourcesLimits,
|
|
|
|
|
Requests: resourcesRequests,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Node Affinity
|
|
|
|
|
nodeSelectorTerms := []*models.NodeSelectorTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
preferredSchedulingTerm := []*models.PoolAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{}
|
2020-07-29 01:01:17 -07:00
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if pool.Affinity != nil && pool.Affinity.NodeAffinity != nil {
|
|
|
|
|
if pool.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
|
|
|
|
for _, elem := range pool.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
|
2020-07-29 01:01:17 -07:00
|
|
|
term := parseNodeSelectorTerm(&elem)
|
|
|
|
|
nodeSelectorTerms = append(nodeSelectorTerms, term)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range pool.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
|
|
|
|
pst := &models.PoolAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{
|
2020-07-29 01:01:17 -07:00
|
|
|
Weight: swag.Int32(elem.Weight),
|
|
|
|
|
Preference: parseNodeSelectorTerm(&elem.Preference),
|
|
|
|
|
}
|
|
|
|
|
preferredSchedulingTerm = append(preferredSchedulingTerm, pst)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
var nodeAffinity *models.PoolAffinityNodeAffinity
|
2020-07-29 01:01:17 -07:00
|
|
|
if len(nodeSelectorTerms) > 0 || len(preferredSchedulingTerm) > 0 {
|
2020-12-07 09:49:51 -06:00
|
|
|
nodeAffinity = &models.PoolAffinityNodeAffinity{
|
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &models.PoolAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecution{
|
2020-07-29 01:01:17 -07:00
|
|
|
NodeSelectorTerms: nodeSelectorTerms,
|
|
|
|
|
},
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: preferredSchedulingTerm,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Pod Affinity
|
|
|
|
|
podAffinityTerms := []*models.PodAffinityTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
weightedPodAffinityTerms := []*models.PoolAffinityPodAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{}
|
2020-07-29 01:01:17 -07:00
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if pool.Affinity != nil && pool.Affinity.PodAffinity != nil {
|
|
|
|
|
for _, elem := range pool.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
podAffinityTerms = append(podAffinityTerms, parsePodAffinityTerm(&elem))
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range pool.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
|
|
|
|
wAffinityTerm := &models.PoolAffinityPodAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{
|
2020-07-29 01:01:17 -07:00
|
|
|
Weight: swag.Int32(elem.Weight),
|
|
|
|
|
PodAffinityTerm: parsePodAffinityTerm(&elem.PodAffinityTerm),
|
|
|
|
|
}
|
|
|
|
|
weightedPodAffinityTerms = append(weightedPodAffinityTerms, wAffinityTerm)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
var podAffinity *models.PoolAffinityPodAffinity
|
2020-07-29 01:01:17 -07:00
|
|
|
if len(podAffinityTerms) > 0 || len(weightedPodAffinityTerms) > 0 {
|
2020-12-07 09:49:51 -06:00
|
|
|
podAffinity = &models.PoolAffinityPodAffinity{
|
2020-07-29 01:01:17 -07:00
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: podAffinityTerms,
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: weightedPodAffinityTerms,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse Pod Anti Affinity
|
|
|
|
|
podAntiAffinityTerms := []*models.PodAffinityTerm{}
|
2020-12-07 09:49:51 -06:00
|
|
|
weightedPodAntiAffinityTerms := []*models.PoolAffinityPodAntiAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{}
|
2020-07-29 01:01:17 -07:00
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
if pool.Affinity != nil && pool.Affinity.PodAntiAffinity != nil {
|
|
|
|
|
for _, elem := range pool.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
|
2020-07-29 01:01:17 -07:00
|
|
|
podAntiAffinityTerms = append(podAntiAffinityTerms, parsePodAffinityTerm(&elem))
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, elem := range pool.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
|
|
|
|
wAffinityTerm := &models.PoolAffinityPodAntiAffinityPreferredDuringSchedulingIgnoredDuringExecutionItems0{
|
2020-07-29 01:01:17 -07:00
|
|
|
Weight: swag.Int32(elem.Weight),
|
|
|
|
|
PodAffinityTerm: parsePodAffinityTerm(&elem.PodAffinityTerm),
|
|
|
|
|
}
|
|
|
|
|
weightedPodAntiAffinityTerms = append(weightedPodAntiAffinityTerms, wAffinityTerm)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
var podAntiAffinity *models.PoolAffinityPodAntiAffinity
|
2020-07-29 01:01:17 -07:00
|
|
|
if len(podAntiAffinityTerms) > 0 || len(weightedPodAntiAffinityTerms) > 0 {
|
2020-12-07 09:49:51 -06:00
|
|
|
podAntiAffinity = &models.PoolAffinityPodAntiAffinity{
|
2020-07-29 01:01:17 -07:00
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: podAntiAffinityTerms,
|
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: weightedPodAntiAffinityTerms,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// build affinity object
|
2020-12-07 09:49:51 -06:00
|
|
|
var affinity *models.PoolAffinity
|
2020-07-29 01:01:17 -07:00
|
|
|
if nodeAffinity != nil || podAffinity != nil || podAntiAffinity != nil {
|
2020-12-07 09:49:51 -06:00
|
|
|
affinity = &models.PoolAffinity{
|
2020-07-29 01:01:17 -07:00
|
|
|
NodeAffinity: nodeAffinity,
|
|
|
|
|
PodAffinity: podAffinity,
|
|
|
|
|
PodAntiAffinity: podAntiAffinity,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse tolerations
|
2020-12-07 09:49:51 -06:00
|
|
|
var tolerations models.PoolTolerations
|
|
|
|
|
for _, elem := range pool.Tolerations {
|
|
|
|
|
var tolerationSecs *models.PoolTolerationSeconds
|
2020-08-07 20:00:16 -07:00
|
|
|
if elem.TolerationSeconds != nil {
|
2020-12-07 09:49:51 -06:00
|
|
|
tolerationSecs = &models.PoolTolerationSeconds{
|
2020-08-07 20:00:16 -07:00
|
|
|
Seconds: elem.TolerationSeconds,
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
toleration := &models.PoolTolerationsItems0{
|
2020-07-29 01:01:17 -07:00
|
|
|
Key: elem.Key,
|
|
|
|
|
Operator: string(elem.Operator),
|
|
|
|
|
Value: elem.Value,
|
|
|
|
|
Effect: string(elem.Effect),
|
2020-08-07 20:00:16 -07:00
|
|
|
TolerationSeconds: tolerationSecs,
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
tolerations = append(tolerations, toleration)
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
poolModel := &models.Pool{
|
|
|
|
|
Name: pool.Name,
|
|
|
|
|
Servers: swag.Int64(int64(pool.Servers)),
|
|
|
|
|
VolumesPerServer: swag.Int32(pool.VolumesPerServer),
|
|
|
|
|
VolumeConfiguration: &models.PoolVolumeConfiguration{
|
2020-07-29 01:01:17 -07:00
|
|
|
Size: size,
|
|
|
|
|
StorageClassName: storageClassName,
|
|
|
|
|
},
|
2020-12-07 09:49:51 -06:00
|
|
|
NodeSelector: pool.NodeSelector,
|
2020-07-29 01:01:17 -07:00
|
|
|
Resources: resources,
|
|
|
|
|
Affinity: affinity,
|
|
|
|
|
Tolerations: tolerations,
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
return poolModel
|
2020-07-29 01:01:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parsePodAffinityTerm(term *corev1.PodAffinityTerm) *models.PodAffinityTerm {
|
|
|
|
|
labelMatchExpressions := []*models.PodAffinityTermLabelSelectorMatchExpressionsItems0{}
|
|
|
|
|
for _, exp := range term.LabelSelector.MatchExpressions {
|
|
|
|
|
labelSelectorReq := &models.PodAffinityTermLabelSelectorMatchExpressionsItems0{
|
|
|
|
|
Key: swag.String(exp.Key),
|
|
|
|
|
Operator: swag.String(string(exp.Operator)),
|
|
|
|
|
Values: exp.Values,
|
|
|
|
|
}
|
|
|
|
|
labelMatchExpressions = append(labelMatchExpressions, labelSelectorReq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
podAffinityTerm := &models.PodAffinityTerm{
|
|
|
|
|
LabelSelector: &models.PodAffinityTermLabelSelector{
|
|
|
|
|
MatchExpressions: labelMatchExpressions,
|
|
|
|
|
MatchLabels: term.LabelSelector.MatchLabels,
|
|
|
|
|
},
|
|
|
|
|
Namespaces: term.Namespaces,
|
|
|
|
|
TopologyKey: swag.String(term.TopologyKey),
|
|
|
|
|
}
|
|
|
|
|
return podAffinityTerm
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseNodeSelectorTerm(term *corev1.NodeSelectorTerm) *models.NodeSelectorTerm {
|
|
|
|
|
var t models.NodeSelectorTerm
|
|
|
|
|
for _, matchExpression := range term.MatchExpressions {
|
|
|
|
|
matchExp := &models.NodeSelectorTermMatchExpressionsItems0{
|
|
|
|
|
Key: swag.String(matchExpression.Key),
|
|
|
|
|
Operator: swag.String(string(matchExpression.Operator)),
|
|
|
|
|
Values: matchExpression.Values,
|
|
|
|
|
}
|
|
|
|
|
t.MatchExpressions = append(t.MatchExpressions, matchExp)
|
|
|
|
|
}
|
|
|
|
|
for _, matchField := range term.MatchFields {
|
|
|
|
|
matchF := &models.NodeSelectorTermMatchFieldsItems0{
|
|
|
|
|
Key: swag.String(matchField.Key),
|
|
|
|
|
Operator: swag.String(string(matchField.Operator)),
|
|
|
|
|
Values: matchField.Values,
|
|
|
|
|
}
|
|
|
|
|
t.MatchFields = append(t.MatchFields, matchF)
|
|
|
|
|
}
|
|
|
|
|
return &t
|
|
|
|
|
}
|
2020-08-07 20:23:03 -07:00
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantUpdatePoolResponse(session *models.Principal, params operator_api.TenantUpdatePoolsParams) (*models.Tenant, *models.Error) {
|
2020-08-26 17:12:59 -07:00
|
|
|
ctx := context.Background()
|
2020-12-07 17:11:08 -06:00
|
|
|
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
2020-08-26 17:12:59 -07:00
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-08-26 17:12:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opClient := &operatorClient{
|
|
|
|
|
client: opClientClientSet,
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
t, err := updateTenantPools(ctx, opClient, params.Namespace, params.Tenant, params.Body.Pools)
|
2020-08-26 17:12:59 -07:00
|
|
|
if err != nil {
|
2021-07-19 11:48:50 -07:00
|
|
|
restapi.LogError("error updating Tenant's pools: %v", err)
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2020-08-26 17:12:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse it to models.Tenant
|
|
|
|
|
tenant := getTenantInfo(t)
|
|
|
|
|
return tenant, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// updateTenantPools Sets the Tenant's pools to the ones provided by the request
|
2020-08-26 17:12:59 -07:00
|
|
|
//
|
2020-12-07 09:49:51 -06:00
|
|
|
// It does the equivalent to a PUT request on Tenant's pools
|
|
|
|
|
func updateTenantPools(
|
2020-08-26 17:12:59 -07:00
|
|
|
ctx context.Context,
|
2020-09-03 10:20:58 -07:00
|
|
|
operatorClient OperatorClientI,
|
2020-08-26 17:12:59 -07:00
|
|
|
namespace string,
|
|
|
|
|
tenantName string,
|
2021-02-01 12:13:51 -08:00
|
|
|
poolsReq []*models.Pool) (*miniov2.Tenant, error) {
|
2020-08-26 17:12:59 -07:00
|
|
|
|
|
|
|
|
minInst, err := operatorClient.TenantGet(ctx, namespace, tenantName, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// set the pools if they are provided
|
2021-02-01 12:13:51 -08:00
|
|
|
var newPoolArray []miniov2.Pool
|
2020-12-07 09:49:51 -06:00
|
|
|
for _, pool := range poolsReq {
|
|
|
|
|
pool, err := parseTenantPoolRequest(pool)
|
2020-08-26 17:12:59 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-12-07 09:49:51 -06:00
|
|
|
newPoolArray = append(newPoolArray, *pool)
|
2020-08-26 17:12:59 -07:00
|
|
|
}
|
|
|
|
|
|
2020-12-07 09:49:51 -06:00
|
|
|
// replace pools array
|
|
|
|
|
minInst.Spec.Pools = newPoolArray
|
2020-08-26 17:12:59 -07:00
|
|
|
|
2020-09-16 23:01:28 -07:00
|
|
|
minInst = minInst.DeepCopy()
|
|
|
|
|
minInst.EnsureDefaults()
|
|
|
|
|
|
2020-08-26 17:12:59 -07:00
|
|
|
payloadBytes, err := json.Marshal(minInst)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
tenantUpdated, err := operatorClient.TenantPatch(ctx, namespace, minInst.Name, types.MergePatchType, payloadBytes, metav1.PatchOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return tenantUpdated, nil
|
|
|
|
|
}
|
2021-05-10 15:27:52 -07:00
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getTenantYAML(session *models.Principal, params operator_api.GetTenantYAMLParams) (*models.TenantYAML, *models.Error) {
|
2021-05-10 15:27:52 -07:00
|
|
|
// get Kubernetes Client
|
|
|
|
|
|
|
|
|
|
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tenant, err := opClient.MinioV2().Tenants(params.Namespace).Get(params.HTTPRequest.Context(), params.Tenant, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
// remove managed fields
|
|
|
|
|
tenant.ManagedFields = []metav1.ManagedFieldsEntry{}
|
|
|
|
|
|
|
|
|
|
//yb, err := yaml.Marshal(tenant)
|
|
|
|
|
serializer := k8sJson.NewSerializerWithOptions(
|
|
|
|
|
k8sJson.DefaultMetaFactory, nil, nil,
|
|
|
|
|
k8sJson.SerializerOptions{
|
|
|
|
|
Yaml: true,
|
|
|
|
|
Pretty: true,
|
|
|
|
|
Strict: true,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
|
|
|
|
|
|
err = serializer.Encode(tenant, buf)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return nil, prepareError(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yb := buf.String()
|
|
|
|
|
|
|
|
|
|
return &models.TenantYAML{Yaml: yb}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 11:48:50 -07:00
|
|
|
func getUpdateTenantYAML(session *models.Principal, params operator_api.PutTenantYAMLParams) *models.Error {
|
2021-05-10 15:27:52 -07:00
|
|
|
// https://godoc.org/k8s.io/apimachinery/pkg/runtime#Scheme
|
|
|
|
|
scheme := runtime.NewScheme()
|
|
|
|
|
|
|
|
|
|
// https://godoc.org/k8s.io/apimachinery/pkg/runtime/serializer#CodecFactory
|
|
|
|
|
codecFactory := serializer.NewCodecFactory(scheme)
|
|
|
|
|
|
|
|
|
|
// https://godoc.org/k8s.io/apimachinery/pkg/runtime#Decoder
|
|
|
|
|
deserializer := codecFactory.UniversalDeserializer()
|
|
|
|
|
|
|
|
|
|
tenantObject, _, err := deserializer.Decode([]byte(params.Body.Yaml), nil, &miniov2.Tenant{})
|
|
|
|
|
if err != nil {
|
2021-05-10 19:08:22 -07:00
|
|
|
return &models.Error{Code: 400, Message: swag.String(err.Error())}
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
inTenant := tenantObject.(*miniov2.Tenant)
|
|
|
|
|
// get Kubernetes Client
|
|
|
|
|
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tenant, err := opClient.MinioV2().Tenants(params.Namespace).Get(params.HTTPRequest.Context(), params.Tenant, metav1.GetOptions{})
|
|
|
|
|
if err != nil {
|
2021-07-19 17:17:56 -07:00
|
|
|
return prepareError(err)
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
upTenant := tenant.DeepCopy()
|
2021-05-10 19:08:22 -07:00
|
|
|
// only update safe fields: spec, metadata.finalizers, metadata.labels and metadata.annotations
|
|
|
|
|
upTenant.Labels = inTenant.Labels
|
|
|
|
|
upTenant.Annotations = inTenant.Annotations
|
|
|
|
|
upTenant.Finalizers = inTenant.Finalizers
|
2021-05-10 15:27:52 -07:00
|
|
|
upTenant.Spec = inTenant.Spec
|
|
|
|
|
|
|
|
|
|
_, err = opClient.MinioV2().Tenants(params.Namespace).Update(params.HTTPRequest.Context(), upTenant, metav1.UpdateOptions{})
|
|
|
|
|
if err != nil {
|
2021-05-10 19:08:22 -07:00
|
|
|
return &models.Error{Code: 400, Message: swag.String(err.Error())}
|
2021-05-10 15:27:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|