589 lines
21 KiB
Go
589 lines
21 KiB
Go
// This file is part of MinIO Console Server
|
|
// Copyright (c) 2022 MinIO, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package operatorapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
|
|
"github.com/minio/console/restapi"
|
|
|
|
"github.com/minio/console/operatorapi/operations/operator_api"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
|
|
"github.com/go-openapi/swag"
|
|
"github.com/minio/console/cluster"
|
|
"github.com/minio/console/models"
|
|
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
func getTenantCreatedResponse(session *models.Principal, params operator_api.CreateTenantParams) (response *models.CreateTenantResponse, mError *models.Error) {
|
|
tenantReq := params.Body
|
|
minioImage := tenantReq.Image
|
|
ctx := context.Background()
|
|
if minioImage == "" {
|
|
minImg, err := cluster.GetMinioImage()
|
|
// we can live without figuring out the latest version of MinIO, Operator will use a hardcoded value
|
|
if err == nil {
|
|
minioImage = *minImg
|
|
}
|
|
}
|
|
// get Kubernetes Client
|
|
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
|
k8sClient := k8sClient{
|
|
client: clientSet,
|
|
}
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
|
|
ns := *tenantReq.Namespace
|
|
// if access/secret are provided, use them, else create a random pair
|
|
|
|
accessKey := restapi.RandomCharString(16)
|
|
secretKey := restapi.RandomCharString(32)
|
|
|
|
if tenantReq.AccessKey != "" {
|
|
accessKey = tenantReq.AccessKey
|
|
}
|
|
if tenantReq.SecretKey != "" {
|
|
secretKey = tenantReq.SecretKey
|
|
}
|
|
|
|
tenantName := *tenantReq.Name
|
|
|
|
imm := true
|
|
var instanceSecret corev1.Secret
|
|
var users []*corev1.LocalObjectReference
|
|
|
|
tenantConfigurationENV := map[string]string{}
|
|
|
|
// Create the secret for the root credentials (deprecated)
|
|
secretName := fmt.Sprintf("%s-secret", tenantName)
|
|
instanceSecret = corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: secretName,
|
|
Labels: map[string]string{
|
|
miniov2.TenantLabel: tenantName,
|
|
},
|
|
},
|
|
Immutable: &imm,
|
|
Data: map[string][]byte{
|
|
"accesskey": []byte(""),
|
|
"secretkey": []byte(""),
|
|
},
|
|
}
|
|
|
|
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
|
|
// 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
|
|
|
|
// delete secrets created if an error occurred during tenant creation,
|
|
defer func() {
|
|
if mError != nil {
|
|
restapi.LogError("deleting secrets created for failed tenant: %s if any: %v", tenantName, mError)
|
|
opts := metav1.ListOptions{
|
|
LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, tenantName),
|
|
}
|
|
err = clientSet.CoreV1().Secrets(ns).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
|
|
if err != nil {
|
|
restapi.LogError("error deleting tenant's secrets: %v", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
|
if tenantReq.ErasureCodingParity > 0 {
|
|
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
|
return nil, prepareError(errorInvalidErasureCodingValue)
|
|
}
|
|
tenantConfigurationENV["MINIO_STORAGE_CLASS_STANDARD"] = fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity)
|
|
}
|
|
|
|
//Construct a MinIO Instance with everything we are getting from parameters
|
|
minInst := miniov2.Tenant{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: tenantName,
|
|
Labels: tenantReq.Labels,
|
|
},
|
|
Spec: miniov2.TenantSpec{
|
|
Image: minioImage,
|
|
Mountpath: "/export",
|
|
CredsSecret: &corev1.LocalObjectReference{
|
|
Name: secretName,
|
|
},
|
|
},
|
|
}
|
|
var tenantExternalIDPConfigured bool
|
|
if tenantReq.Idp != nil {
|
|
// Enable IDP (Active Directory) for MinIO
|
|
if tenantReq.Idp.ActiveDirectory != nil {
|
|
tenantExternalIDPConfigured = true
|
|
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
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_ADDR"] = serverAddress
|
|
if tlsSkipVerify {
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY"] = "on"
|
|
}
|
|
if serverInsecure {
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_INSECURE"] = "on"
|
|
}
|
|
if serverStartTLS {
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_SERVER_STARTTLS"] = "on"
|
|
}
|
|
|
|
// LDAP Username
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USERNAME_FORMAT"] = userNameFormat
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER"] = userNameSearchFilter
|
|
|
|
// LDAP Lookup
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN"] = lookupBindDN
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD"] = lookupBindPassword
|
|
|
|
// LDAP User DN
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN"] = userDNSearchBaseDN
|
|
tenantConfigurationENV["MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER"] = userDNSearchFilter
|
|
|
|
// LDAP Group
|
|
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
|
|
|
|
// 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,
|
|
},
|
|
},
|
|
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)
|
|
}
|
|
}
|
|
// attach the users to the tenant
|
|
minInst.Spec.Users = users
|
|
|
|
} else if tenantReq.Idp.Oidc != nil {
|
|
tenantExternalIDPConfigured = true
|
|
// Enable IDP (OIDC) for MinIO
|
|
configurationURL := *tenantReq.Idp.Oidc.ConfigurationURL
|
|
clientID := *tenantReq.Idp.Oidc.ClientID
|
|
secretID := *tenantReq.Idp.Oidc.SecretID
|
|
claimName := *tenantReq.Idp.Oidc.ClaimName
|
|
scopes := tenantReq.Idp.Oidc.Scopes
|
|
callbackURL := tenantReq.Idp.Oidc.CallbackURL
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_CONFIG_URL"] = configurationURL
|
|
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
|
|
if scopes == "" {
|
|
scopes = "openid,profile,email"
|
|
}
|
|
tenantConfigurationENV["MINIO_IDENTITY_OPENID_SCOPES"] = scopes
|
|
} 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
|
|
}
|
|
}
|
|
|
|
isEncryptionEnabled := false
|
|
|
|
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
|
|
minInst.Spec.RequestAutoCert = tenantReq.EnableTLS
|
|
if *tenantReq.EnableTLS {
|
|
// requestAutoCert is enabled, MinIO will be deployed with TLS enabled and encryption can be enabled
|
|
isEncryptionEnabled = true
|
|
}
|
|
}
|
|
// External TLS certificates for MinIO
|
|
if tenantReq.TLS != nil && len(tenantReq.TLS.Minio) > 0 {
|
|
isEncryptionEnabled = true
|
|
// Certificates used by the MinIO instance
|
|
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
|
|
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
minInst.Spec.ExternalCertSecret = externalCertSecret
|
|
}
|
|
// If encryption configuration is present and TLS will be enabled (using AutoCert or External certificates)
|
|
if tenantReq.Encryption != nil && isEncryptionEnabled {
|
|
// KES client mTLSCertificates used by MinIO instance
|
|
if tenantReq.Encryption.Client != nil {
|
|
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
|
|
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
|
|
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
|
|
if err != nil {
|
|
return nil, prepareError(restapi.ErrorGeneric)
|
|
}
|
|
if len(certificateSecrets) > 0 {
|
|
minInst.Spec.ExternalClientCertSecret = certificateSecrets[0]
|
|
}
|
|
}
|
|
|
|
// KES configuration for Tenant instance
|
|
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName)
|
|
if err != nil {
|
|
return nil, prepareError(restapi.ErrorGeneric)
|
|
}
|
|
// Set Labels, Annotations and Node Selector for KES
|
|
minInst.Spec.KES.Labels = tenantReq.Encryption.Labels
|
|
minInst.Spec.KES.Annotations = tenantReq.Encryption.Annotations
|
|
minInst.Spec.KES.NodeSelector = tenantReq.Encryption.NodeSelector
|
|
|
|
if tenantReq.Encryption.SecurityContext != nil {
|
|
sc, err := convertModelSCToK8sSC(tenantReq.Encryption.SecurityContext)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
minInst.Spec.KES.SecurityContext = sc
|
|
}
|
|
}
|
|
// 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 {
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
|
}
|
|
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 {
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
|
}
|
|
minInst.Spec.ExternalCaCertSecret = certificateSecrets
|
|
}
|
|
}
|
|
|
|
// add annotations
|
|
var annotations map[string]string
|
|
|
|
if len(tenantReq.Annotations) > 0 {
|
|
annotations = tenantReq.Annotations
|
|
minInst.Annotations = annotations
|
|
}
|
|
// set the pools if they are provided
|
|
for _, pool := range tenantReq.Pools {
|
|
pool, err := parseTenantPoolRequest(pool)
|
|
if err != nil {
|
|
restapi.LogError("parseTenantPoolRequest failed: %v", err)
|
|
return nil, prepareError(err)
|
|
}
|
|
minInst.Spec.Pools = append(minInst.Spec.Pools, *pool)
|
|
}
|
|
|
|
// Set Mount Path if provided
|
|
if tenantReq.MounthPath != "" {
|
|
minInst.Spec.Mountpath = tenantReq.MounthPath
|
|
}
|
|
|
|
// 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
|
|
} else if imagePullSecret, err = setImageRegistry(ctx, tenantReq.ImageRegistry, clientSet.CoreV1(), ns, tenantName); err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
// pass the image pull secret to the Tenant
|
|
if imagePullSecret != "" {
|
|
minInst.Spec.ImagePullSecret = corev1.LocalObjectReference{
|
|
Name: imagePullSecret,
|
|
}
|
|
}
|
|
|
|
// prometheus annotations support
|
|
if tenantReq.EnablePrometheus != nil && *tenantReq.EnablePrometheus && minInst.Annotations != nil {
|
|
minInst.Annotations[prometheusPath] = "/minio/prometheus/metrics"
|
|
minInst.Annotations[prometheusPort] = fmt.Sprint(miniov2.MinIOPort)
|
|
minInst.Annotations[prometheusScrape] = "true"
|
|
}
|
|
|
|
// Is Log Search enabled? (present in the parameters) if so configure
|
|
if tenantReq.LogSearchConfiguration != nil {
|
|
|
|
//Default class name for Log search
|
|
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
|
|
logSearchImage := ""
|
|
logSearchPgImage := ""
|
|
logSearchPgInitImage := ""
|
|
var logSearchStorageClass *string // Nil means use default storage class
|
|
var logSearchSecurityContext *corev1.PodSecurityContext
|
|
var logSearchPgSecurityContext *corev1.PodSecurityContext
|
|
|
|
if tenantReq.LogSearchConfiguration.StorageSize != nil {
|
|
diskSpaceFromAPI = int64(*tenantReq.LogSearchConfiguration.StorageSize) * humanize.GiByte
|
|
}
|
|
if tenantReq.LogSearchConfiguration.StorageClass != "" {
|
|
logSearchStorageClass = stringPtr(tenantReq.LogSearchConfiguration.StorageClass)
|
|
}
|
|
if tenantReq.LogSearchConfiguration.Image != "" {
|
|
logSearchImage = tenantReq.LogSearchConfiguration.Image
|
|
}
|
|
if tenantReq.LogSearchConfiguration.PostgresImage != "" {
|
|
logSearchPgImage = tenantReq.LogSearchConfiguration.PostgresImage
|
|
}
|
|
if tenantReq.LogSearchConfiguration.PostgresInitImage != "" {
|
|
logSearchPgInitImage = tenantReq.LogSearchConfiguration.PostgresInitImage
|
|
}
|
|
// if security context for logSearch is present, configure it.
|
|
if tenantReq.LogSearchConfiguration.SecurityContext != nil {
|
|
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.SecurityContext)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
logSearchSecurityContext = sc
|
|
}
|
|
// if security context for logSearch is present, configure it.
|
|
if tenantReq.LogSearchConfiguration.PostgresSecurityContext != nil {
|
|
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.PostgresSecurityContext)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
logSearchPgSecurityContext = sc
|
|
}
|
|
|
|
logSearchDiskSpace := resource.NewQuantity(diskSpaceFromAPI, resource.DecimalExponent)
|
|
|
|
// 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)
|
|
}
|
|
// default activate lgo search and prometheus
|
|
minInst.Spec.Log = &miniov2.LogConfig{
|
|
Audit: &miniov2.AuditConfig{DiskCapacityGB: swag.Int(auditMaxCap)},
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
// set log search images if any
|
|
if logSearchImage != "" {
|
|
minInst.Spec.Log.Image = logSearchImage
|
|
}
|
|
if logSearchPgImage != "" {
|
|
minInst.Spec.Log.Db.Image = logSearchPgImage
|
|
}
|
|
if logSearchPgInitImage != "" {
|
|
minInst.Spec.Log.Db.InitImage = logSearchPgInitImage
|
|
}
|
|
if logSearchSecurityContext != nil {
|
|
minInst.Spec.Log.SecurityContext = logSearchSecurityContext
|
|
}
|
|
if logSearchPgSecurityContext != nil {
|
|
minInst.Spec.Log.Db.SecurityContext = logSearchPgSecurityContext
|
|
}
|
|
|
|
}
|
|
|
|
// Is Prometheus/Monitoring enabled? (config present in the parameters) if so configure
|
|
if tenantReq.PrometheusConfiguration != nil {
|
|
prometheusDiskSpace := 5 // Default is 5 by API
|
|
prometheusImage := "" // Default is ""
|
|
prometheusSidecardImage := "" // Default is ""
|
|
prometheusInitImage := "" // Default is ""
|
|
|
|
var prometheusStorageClass *string // Nil means default storage class
|
|
|
|
if tenantReq.PrometheusConfiguration.StorageSize != nil {
|
|
prometheusDiskSpace = int(*tenantReq.PrometheusConfiguration.StorageSize)
|
|
}
|
|
if tenantReq.PrometheusConfiguration.StorageClass != "" {
|
|
prometheusStorageClass = stringPtr(tenantReq.PrometheusConfiguration.StorageClass)
|
|
}
|
|
if tenantReq.PrometheusConfiguration.Image != "" {
|
|
prometheusImage = tenantReq.PrometheusConfiguration.Image
|
|
}
|
|
if tenantReq.PrometheusConfiguration.SidecarImage != "" {
|
|
prometheusSidecardImage = tenantReq.PrometheusConfiguration.SidecarImage
|
|
}
|
|
if tenantReq.PrometheusConfiguration.InitImage != "" {
|
|
prometheusInitImage = tenantReq.PrometheusConfiguration.InitImage
|
|
}
|
|
|
|
minInst.Spec.Prometheus = &miniov2.PrometheusConfig{
|
|
DiskCapacityDB: swag.Int(prometheusDiskSpace),
|
|
StorageClassName: prometheusStorageClass,
|
|
}
|
|
if prometheusImage != "" {
|
|
minInst.Spec.Prometheus.Image = prometheusImage
|
|
}
|
|
if prometheusSidecardImage != "" {
|
|
minInst.Spec.Prometheus.SideCarImage = prometheusSidecardImage
|
|
}
|
|
if prometheusInitImage != "" {
|
|
minInst.Spec.Prometheus.InitImage = prometheusInitImage
|
|
}
|
|
// if security context for prometheus is present, configure it.
|
|
if tenantReq.PrometheusConfiguration != nil && tenantReq.PrometheusConfiguration.SecurityContext != nil {
|
|
sc, err := convertModelSCToK8sSC(tenantReq.PrometheusConfiguration.SecurityContext)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
minInst.Spec.Prometheus.SecurityContext = sc
|
|
}
|
|
|
|
}
|
|
|
|
// expose services
|
|
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{
|
|
"config.env": []byte(GenerateTenantConfigurationFile(tenantConfigurationENV)),
|
|
},
|
|
},
|
|
}, tenantName)
|
|
if err != nil {
|
|
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
|
}
|
|
minInst.Spec.Configuration = &corev1.LocalObjectReference{Name: tenantConfigurationName}
|
|
|
|
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
|
|
_, err = opClient.MinioV2().Tenants(ns).Create(context.Background(), &minInst, metav1.CreateOptions{})
|
|
if err != nil {
|
|
restapi.LogError("Creating new tenant failed with: %v", err)
|
|
return nil, prepareError(err)
|
|
}
|
|
|
|
// Integrations
|
|
if os.Getenv("GKE_INTEGRATION") != "" {
|
|
err := gkeIntegration(clientSet, tenantName, ns, session.STSSessionToken)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
}
|
|
response = &models.CreateTenantResponse{
|
|
ExternalIDP: tenantExternalIDPConfigured,
|
|
}
|
|
thisClient := &operatorClient{
|
|
client: opClient,
|
|
}
|
|
|
|
minTenant, err := getTenant(ctx, thisClient, ns, tenantName)
|
|
|
|
if tenantReq.Idp != nil && !tenantExternalIDPConfigured {
|
|
for _, credential := range tenantReq.Idp.Keys {
|
|
response.Console = append(response.Console, &models.TenantResponseItem{
|
|
AccessKey: *credential.AccessKey,
|
|
SecretKey: *credential.SecretKey,
|
|
URL: GetTenantServiceURL(minTenant),
|
|
})
|
|
}
|
|
}
|
|
return response, nil
|
|
}
|