From aa9f6f02ca54fff51ac9c71f4470fdafe13973c1 Mon Sep 17 00:00:00 2001 From: Javier Adriel Date: Fri, 13 Jan 2023 13:11:40 -0600 Subject: [PATCH] Refactor and tests for adding tenant (#2577) --- .github/workflows/jobs.yaml | 2 +- operatorapi/tenant_add.go | 376 ++++++++++++++++++---------------- operatorapi/tenants_2_test.go | 195 +++++++++++++++++- 3 files changed, 393 insertions(+), 180 deletions(-) diff --git a/.github/workflows/jobs.yaml b/.github/workflows/jobs.yaml index 28260ecde..d550fb7b0 100644 --- a/.github/workflows/jobs.yaml +++ b/.github/workflows/jobs.yaml @@ -1542,7 +1542,7 @@ jobs: go tool cover -func=all.out | grep total > tmp2 result=`cat tmp2 | awk 'END {print $3}'` result=${result%\%} - threshold=62.0 + threshold=63.2 echo "Result:" echo "$result%" if (( $(echo "$result >= $threshold" |bc -l) )); then diff --git a/operatorapi/tenant_add.go b/operatorapi/tenant_add.go index efac88e78..86152cd36 100644 --- a/operatorapi/tenant_add.go +++ b/operatorapi/tenant_add.go @@ -22,18 +22,17 @@ import ( "fmt" "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" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/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) { tenantReq := params.Body minioImage := getTenantMinIOImage(tenantReq.Image) - imm := true ns := *tenantReq.Namespace @@ -64,6 +62,11 @@ func createTenant(ctx context.Context, params operator_api.CreateTenantParams, c tenantName := *tenantReq.Name 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) if err != nil { 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_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 if tenantReq.ErasureCodingParity > 0 { 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 case 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 + tenantConfigurationENV = setTenantOIDCConfig(tenantReq, tenantConfigurationENV) case 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.createSecret(ctx, ns, &userSecret, metav1.CreateOptions{}) - if err != nil { - return nil, restapi.ErrorWithContext(ctx, err) - } + users, err = setTenantBuiltInUsers(ctx, clientSet, tenantReq, users) + if err != nil { + return nil, restapi.ErrorWithContext(ctx, err) } // attach the users to the tenant 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 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 + minInst, err = setTenantLogSearchConfiguration(ctx, tenantReq, minInst) + if err != nil { + return nil, restapi.ErrorWithContext(ctx, err) } - 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 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) + minInst, err = setTenantPrometheusConfig(ctx, tenantReq, minInst) + if err != nil { + return nil, restapi.ErrorWithContext(ctx, err) } - 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 @@ -636,3 +475,186 @@ func setTenantActiveDirectoryConfig(ctx context.Context, clientSet K8sClientI, t } 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 +} diff --git a/operatorapi/tenants_2_test.go b/operatorapi/tenants_2_test.go index ff330da91..dec667291 100644 --- a/operatorapi/tenants_2_test.go +++ b/operatorapi/tenants_2_test.go @@ -17,7 +17,11 @@ package operatorapi import ( + "context" + "errors" + "fmt" "net/http" + "strings" "testing" "github.com/minio/console/models" @@ -25,17 +29,24 @@ import ( "github.com/minio/console/operatorapi/operations/operator_api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type TenantTestSuite struct { suite.Suite - assert *assert.Assertions - opClient opClientMock + assert *assert.Assertions + opClient opClientMock + k8sclient k8sClientMock } func (suite *TenantTestSuite) SetupSuite() { suite.assert = assert.New(suite.T()) 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() { @@ -129,6 +140,186 @@ func (suite *TenantTestSuite) TestCreateTenantHandlerWithError() { 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) { registerTenantHandlers(&api) params.HTTPRequest = &http.Request{}