Refactor and tests for adding tenant (#2577)
This commit is contained in:
2
.github/workflows/jobs.yaml
vendored
2
.github/workflows/jobs.yaml
vendored
@@ -1542,7 +1542,7 @@ jobs:
|
|||||||
go tool cover -func=all.out | grep total > tmp2
|
go tool cover -func=all.out | grep total > tmp2
|
||||||
result=`cat tmp2 | awk 'END {print $3}'`
|
result=`cat tmp2 | awk 'END {print $3}'`
|
||||||
result=${result%\%}
|
result=${result%\%}
|
||||||
threshold=62.0
|
threshold=63.2
|
||||||
echo "Result:"
|
echo "Result:"
|
||||||
echo "$result%"
|
echo "$result%"
|
||||||
if (( $(echo "$result >= $threshold" |bc -l) )); then
|
if (( $(echo "$result >= $threshold" |bc -l) )); then
|
||||||
|
|||||||
@@ -22,18 +22,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
|
||||||
"github.com/minio/console/restapi"
|
"github.com/minio/console/restapi"
|
||||||
|
|
||||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
|
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
"github.com/minio/console/cluster"
|
"github.com/minio/console/cluster"
|
||||||
"github.com/minio/console/models"
|
"github.com/minio/console/models"
|
||||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
)
|
)
|
||||||
@@ -56,7 +55,6 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
|||||||
func createTenant(ctx context.Context, params operator_api.CreateTenantParams, clientSet K8sClientI, cv1 v1.CoreV1Interface, session *models.Principal) (response *models.CreateTenantResponse, mError *models.Error) {
|
func createTenant(ctx context.Context, params operator_api.CreateTenantParams, clientSet K8sClientI, cv1 v1.CoreV1Interface, session *models.Principal) (response *models.CreateTenantResponse, mError *models.Error) {
|
||||||
tenantReq := params.Body
|
tenantReq := params.Body
|
||||||
minioImage := getTenantMinIOImage(tenantReq.Image)
|
minioImage := getTenantMinIOImage(tenantReq.Image)
|
||||||
imm := true
|
|
||||||
|
|
||||||
ns := *tenantReq.Namespace
|
ns := *tenantReq.Namespace
|
||||||
|
|
||||||
@@ -64,6 +62,11 @@ func createTenant(ctx context.Context, params operator_api.CreateTenantParams, c
|
|||||||
tenantName := *tenantReq.Name
|
tenantName := *tenantReq.Name
|
||||||
var users []*corev1.LocalObjectReference
|
var users []*corev1.LocalObjectReference
|
||||||
|
|
||||||
|
// delete secrets created if an errors occurred during tenant creation,
|
||||||
|
defer func() {
|
||||||
|
deleteSecretsIfTenantCreationFails(ctx, mError, tenantName, ns, clientSet)
|
||||||
|
}()
|
||||||
|
|
||||||
err := createTenantCredentialsSecret(ctx, ns, tenantName, clientSet)
|
err := createTenantCredentialsSecret(ctx, ns, tenantName, clientSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
@@ -74,11 +77,6 @@ func createTenant(ctx context.Context, params operator_api.CreateTenantParams, c
|
|||||||
tenantConfigurationENV["MINIO_ROOT_USER"] = accessKey
|
tenantConfigurationENV["MINIO_ROOT_USER"] = accessKey
|
||||||
tenantConfigurationENV["MINIO_ROOT_PASSWORD"] = secretKey
|
tenantConfigurationENV["MINIO_ROOT_PASSWORD"] = secretKey
|
||||||
|
|
||||||
// delete secrets created if an errors occurred during tenant creation,
|
|
||||||
defer func() {
|
|
||||||
deleteSecretsIfTenantCreationFails(ctx, mError, tenantName, ns, clientSet)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
||||||
if tenantReq.ErasureCodingParity > 0 {
|
if tenantReq.ErasureCodingParity > 0 {
|
||||||
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
||||||
@@ -115,44 +113,11 @@ func createTenant(ctx context.Context, params operator_api.CreateTenantParams, c
|
|||||||
minInst.Spec.Users = users
|
minInst.Spec.Users = users
|
||||||
case tenantReq.Idp.Oidc != nil:
|
case tenantReq.Idp.Oidc != nil:
|
||||||
tenantExternalIDPConfigured = true
|
tenantExternalIDPConfigured = true
|
||||||
// Enable IDP (OIDC) for MinIO
|
tenantConfigurationENV = setTenantOIDCConfig(tenantReq, tenantConfigurationENV)
|
||||||
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
|
|
||||||
case len(tenantReq.Idp.Keys) > 0:
|
case len(tenantReq.Idp.Keys) > 0:
|
||||||
// Create the secret any built-in user passed if no external IDP was configured
|
users, err = setTenantBuiltInUsers(ctx, clientSet, tenantReq, users)
|
||||||
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
|
if err != nil {
|
||||||
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
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.createSecret(ctx, ns, &userSecret, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// attach the users to the tenant
|
// attach the users to the tenant
|
||||||
minInst.Spec.Users = users
|
minInst.Spec.Users = users
|
||||||
@@ -294,144 +259,18 @@ func createTenant(ctx context.Context, params operator_api.CreateTenantParams, c
|
|||||||
|
|
||||||
// Is Log Search enabled? (present in the parameters) if so configure
|
// Is Log Search enabled? (present in the parameters) if so configure
|
||||||
if tenantReq.LogSearchConfiguration != nil {
|
if tenantReq.LogSearchConfiguration != nil {
|
||||||
|
minInst, err = setTenantLogSearchConfiguration(ctx, tenantReq, minInst)
|
||||||
// Default class name for Log search
|
if err != nil {
|
||||||
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
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, restapi.ErrorWithContext(ctx, 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, restapi.ErrorWithContext(ctx, 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
|
// Is Prometheus/Monitoring enabled? (config present in the parameters) if so configure
|
||||||
if tenantReq.PrometheusConfiguration != nil {
|
if tenantReq.PrometheusConfiguration != nil {
|
||||||
prometheusDiskSpace := 5 // Default is 5 by API
|
minInst, err = setTenantPrometheusConfig(ctx, tenantReq, minInst)
|
||||||
prometheusImage := "" // Default is ""
|
if err != nil {
|
||||||
prometheusSidecardImage := "" // Default is ""
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
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, restapi.ErrorWithContext(ctx, err)
|
|
||||||
}
|
|
||||||
minInst.Spec.Prometheus.SecurityContext = sc
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// expose services
|
// expose services
|
||||||
@@ -636,3 +475,186 @@ func setTenantActiveDirectoryConfig(ctx context.Context, clientSet K8sClientI, t
|
|||||||
}
|
}
|
||||||
return tenantConfigurationENV, users, nil
|
return tenantConfigurationENV, users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setTenantOIDCConfig(tenantReq *models.CreateTenantRequest, tenantConfigurationENV map[string]string) map[string]string {
|
||||||
|
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
|
||||||
|
return tenantConfigurationENV
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTenantBuiltInUsers(ctx context.Context, clientSet K8sClientI, tenantReq *models.CreateTenantRequest, users []*corev1.LocalObjectReference) ([]*corev1.LocalObjectReference, error) {
|
||||||
|
imm := true
|
||||||
|
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
|
||||||
|
userSecretName := fmt.Sprintf("%s-user-%d", *tenantReq.Name, i)
|
||||||
|
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
|
||||||
|
userSecret := corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: userSecretName,
|
||||||
|
Labels: map[string]string{
|
||||||
|
miniov2.TenantLabel: *tenantReq.Name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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.createSecret(ctx, *tenantReq.Namespace, &userSecret, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTenantLogSearchConfiguration(ctx context.Context, tenantReq *models.CreateTenantRequest, minInst miniov2.Tenant) (miniov2.Tenant, error) {
|
||||||
|
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 minInst, 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 minInst, 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: *tenantReq.Name + "-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
|
||||||
|
}
|
||||||
|
return minInst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTenantPrometheusConfig(ctx context.Context, tenantReq *models.CreateTenantRequest, minInst miniov2.Tenant) (miniov2.Tenant, error) {
|
||||||
|
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 minInst, err
|
||||||
|
}
|
||||||
|
minInst.Spec.Prometheus.SecurityContext = sc
|
||||||
|
}
|
||||||
|
return minInst, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
package operatorapi
|
package operatorapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/minio/console/models"
|
"github.com/minio/console/models"
|
||||||
@@ -25,17 +29,24 @@ import (
|
|||||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TenantTestSuite struct {
|
type TenantTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
assert *assert.Assertions
|
assert *assert.Assertions
|
||||||
opClient opClientMock
|
opClient opClientMock
|
||||||
|
k8sclient k8sClientMock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *TenantTestSuite) SetupSuite() {
|
func (suite *TenantTestSuite) SetupSuite() {
|
||||||
suite.assert = assert.New(suite.T())
|
suite.assert = assert.New(suite.T())
|
||||||
suite.opClient = opClientMock{}
|
suite.opClient = opClientMock{}
|
||||||
|
suite.k8sclient = k8sClientMock{}
|
||||||
|
k8sClientDeleteSecretsCollectionMock = func(ctx context.Context, namespace string, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *TenantTestSuite) SetupTest() {
|
func (suite *TenantTestSuite) SetupTest() {
|
||||||
@@ -129,6 +140,186 @@ func (suite *TenantTestSuite) TestCreateTenantHandlerWithError() {
|
|||||||
suite.assert.True(ok)
|
suite.assert.True(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongECP() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
params.Body.ErasureCodingParity = 1
|
||||||
|
k8sClientCreateSecretMock = func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongActiveDirectoryConfig() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
params.Body.ErasureCodingParity = 2
|
||||||
|
url := "mock-url"
|
||||||
|
lookup := "mock-lookup"
|
||||||
|
params.Body.Idp = &models.IdpConfiguration{
|
||||||
|
ActiveDirectory: &models.IdpConfigurationActiveDirectory{
|
||||||
|
SkipTLSVerification: true,
|
||||||
|
ServerInsecure: true,
|
||||||
|
ServerStartTLS: true,
|
||||||
|
UserDNS: []string{"mock-user"},
|
||||||
|
URL: &url,
|
||||||
|
LookupBindDn: &lookup,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k8sClientCreateSecretMock = func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
|
||||||
|
if strings.HasPrefix(secret.Name, fmt.Sprintf("%s-user-", *params.Body.Name)) {
|
||||||
|
return nil, errors.New("mock-error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongBuiltInUsers() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
accessKey := "mock-access-key"
|
||||||
|
secretKey := "mock-secret-key"
|
||||||
|
params.Body.Idp = &models.IdpConfiguration{
|
||||||
|
Keys: []*models.IdpConfigurationKeysItems0{
|
||||||
|
{
|
||||||
|
AccessKey: &accessKey,
|
||||||
|
SecretKey: &secretKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k8sClientCreateSecretMock = func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
|
||||||
|
if strings.HasPrefix(secret.Name, fmt.Sprintf("%s-user-", *params.Body.Name)) {
|
||||||
|
return nil, errors.New("mock-error")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithOIDCAndWrongServerCertificates() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
url := "mock-url"
|
||||||
|
clientID := "mock-client-id"
|
||||||
|
clientSecret := "mock-client-secret"
|
||||||
|
claimName := "mock-claim-name"
|
||||||
|
crt := "mock-crt"
|
||||||
|
key := "mock-key"
|
||||||
|
params.Body.Idp = &models.IdpConfiguration{
|
||||||
|
Oidc: &models.IdpConfigurationOidc{
|
||||||
|
ClientID: &clientID,
|
||||||
|
SecretID: &clientSecret,
|
||||||
|
ClaimName: &claimName,
|
||||||
|
ConfigurationURL: &url,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
params.Body.TLS = &models.TLSConfiguration{
|
||||||
|
MinioServerCertificates: []*models.KeyPairConfiguration{
|
||||||
|
{
|
||||||
|
Crt: &crt,
|
||||||
|
Key: &key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongClientCertificates() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
crt := "mock-crt"
|
||||||
|
key := "mock-key"
|
||||||
|
params.Body.TLS = &models.TLSConfiguration{
|
||||||
|
MinioClientCertificates: []*models.KeyPairConfiguration{
|
||||||
|
{
|
||||||
|
Crt: &crt,
|
||||||
|
Key: &key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongCAsCertificates() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
params.Body.TLS = &models.TLSConfiguration{
|
||||||
|
MinioCAsCertificates: []string{"bW9jay1jcnQ="},
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
k8sClientCreateSecretMock = func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
|
||||||
|
if strings.HasPrefix(secret.Name, fmt.Sprintf("%s-ca-certificate-", *params.Body.Name)) {
|
||||||
|
return nil, errors.New("mock-error")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongMtlsCertificates() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
crt := "mock-crt"
|
||||||
|
key := "mock-key"
|
||||||
|
enableTLS := true
|
||||||
|
params.Body.EnableTLS = &enableTLS
|
||||||
|
params.Body.Encryption = &models.EncryptionConfiguration{
|
||||||
|
MinioMtls: &models.KeyPairConfiguration{
|
||||||
|
Crt: &crt,
|
||||||
|
Key: &key,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongKESConfig() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
crt := "mock-crt"
|
||||||
|
key := "mock-key"
|
||||||
|
enableTLS := true
|
||||||
|
params.Body.EnableTLS = &enableTLS
|
||||||
|
params.Body.Encryption = &models.EncryptionConfiguration{
|
||||||
|
ServerTLS: &models.KeyPairConfiguration{
|
||||||
|
Crt: &crt,
|
||||||
|
Key: &key,
|
||||||
|
},
|
||||||
|
Image: "mock-image",
|
||||||
|
Replicas: "1",
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *TenantTestSuite) TestCreateTenantWithWrongPool() {
|
||||||
|
params, _ := suite.initCreateTenantRequest()
|
||||||
|
params.Body.Annotations = map[string]string{"mock": "mock"}
|
||||||
|
params.Body.Pools = []*models.Pool{{}}
|
||||||
|
k8sClientCreateSecretMock = func(ctx context.Context, namespace string, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
k8sClientDeleteSecretMock = func(ctx context.Context, namespace, name string, opts metav1.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := createTenant(context.Background(), params, suite.k8sclient, nil, &models.Principal{})
|
||||||
|
suite.assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *TenantTestSuite) initCreateTenantRequest() (params operator_api.CreateTenantParams, api operations.OperatorAPI) {
|
func (suite *TenantTestSuite) initCreateTenantRequest() (params operator_api.CreateTenantParams, api operations.OperatorAPI) {
|
||||||
registerTenantHandlers(&api)
|
registerTenantHandlers(&api)
|
||||||
params.HTTPRequest = &http.Request{}
|
params.HTTPRequest = &http.Request{}
|
||||||
|
|||||||
Reference in New Issue
Block a user