Skip to content

Commit

Permalink
Add appbinding ref in FerretDB api
Browse files Browse the repository at this point in the history
Signed-off-by: sayedppqq <[email protected]>
  • Loading branch information
sayedppqq committed Jun 14, 2024
1 parent 7181477 commit 549d427
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 293 deletions.
3 changes: 1 addition & 2 deletions apis/kubedb/v1alpha2/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -1223,8 +1223,7 @@ const (

FerretDBServerPath = "/etc/certs/server"

FerretDBExternalCAPath = "/etc/certs/ext"
FerretDBExternalClientPath = "/etc/certs/ext/client"
FerretDBExternalClientPath = "/etc/certs/ext"

FerretDBDefaultPort = 27017
FerretDBMetricsPort = 8080
Expand Down
69 changes: 51 additions & 18 deletions apis/kubedb/v1alpha2/ferretdb_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package v1alpha2
import (
"context"
"fmt"
"net/url"

"kubedb.dev/apimachinery/apis"
catalog "kubedb.dev/apimachinery/apis/catalog/v1alpha1"
Expand Down Expand Up @@ -110,7 +111,15 @@ func (f *FerretDB) GetAuthSecretName() string {
if f.Spec.AuthSecret != nil && f.Spec.AuthSecret.Name != "" {
return f.Spec.AuthSecret.Name
}
return meta_util.NameWithSuffix(f.PgBackendName(), "auth")
return meta_util.NameWithSuffix(f.OffshootName(), "auth")
}

func (f *FerretDB) GetPersistentSecrets() []string {
var secrets []string
if f.Spec.AuthSecret != nil {
secrets = append(secrets, f.Spec.AuthSecret.Name)
}
return secrets
}

// AsOwner returns owner reference to resources
Expand Down Expand Up @@ -142,11 +151,13 @@ func (f *FerretDB) GetCertSecretName(alias FerretDBCertificateAlias) string {

return f.CertificateName(alias)
}

func (f *FerretDB) GetExternalBackendClientSecretName() string {
return f.Name + "ext-pg-client-cert"
return f.Name + "-ext-pg-client-cert"
}
func (f *FerretDB) GetExternalBackendCASecretName() string {
return f.Name + "ext-pg-ca-cert"

func (f *FerretDB) GetSecretVolumeName(secretName string) string {
return secretName + "-vol"
}

func (f *FerretDB) SetHealthCheckerDefaults() {
Expand Down Expand Up @@ -216,11 +227,13 @@ func (f *FerretDB) SetDefaults() {
f.Spec.Backend.LinkedDB = "ferretdb"
}
}

if f.Spec.AuthSecret == nil {
f.Spec.AuthSecret = &SecretReference{
ExternallyManaged: f.Spec.Backend.ExternallyManaged,
ExternallyManaged: false,
}
}

f.Spec.Monitor.SetDefaults()
if f.Spec.Monitor != nil && f.Spec.Monitor.Prometheus != nil {
if f.Spec.Monitor.Prometheus.Exporter.SecurityContext.RunAsUser == nil {
Expand All @@ -231,18 +244,12 @@ func (f *FerretDB) SetDefaults() {
}
}

//defaultVersion := "13.13"
//if !f.Spec.Backend.ExternallyManaged {
// if f.Spec.Backend.Postgres == nil {
// f.Spec.Backend.Postgres = &PostgresRef{
// Version: &defaultVersion,
// }
// } else {
// if f.Spec.Backend.Postgres.Version == nil {
// f.Spec.Backend.Postgres.Version = &defaultVersion
// }
// }
//}
defaultVersion := "13.13"
if !f.Spec.Backend.ExternallyManaged {
if f.Spec.Backend.Version == nil {
f.Spec.Backend.Version = &defaultVersion
}
}
f.SetTLSDefaults()
f.SetHealthCheckerDefaults()
}
Expand Down Expand Up @@ -376,6 +383,32 @@ func (f *FerretDB) ReplicasAreReady(lister pslister.PetSetLister) (bool, string,
expectedItems := 1
return checkReplicasOfPetSet(lister.PetSets(f.Namespace), labels.SelectorFromSet(f.OffshootLabels()), expectedItems)
}
func (f *FerretDB) GetSSLModeFromAppBinding(apb *appcat.AppBinding) (PostgresSSLMode, error) {

func (f *FerretDB) GetSSLModeFromAppBinding(apb *appcat.AppBinding) (PostgresSSLMode, error) {
var sslMode string
if apb.Spec.ClientConfig.URL != nil {
parsedURL, err := url.Parse(*apb.Spec.ClientConfig.URL)
if err != nil {
return "", fmt.Errorf("parse error in appbinding %s/%s 'spec.clientConfig.url'. error: %v", apb.Namespace, apb.Name, err)
}
if parsedURL.Scheme != "postgres" && parsedURL.Scheme != "postgresql" {
return "", fmt.Errorf("invalid scheme provided in URL in provided appbinding %s/%s", apb.Namespace, apb.Name)
}
sslMode = parsedURL.Query().Get("sslmode")
}
if apb.Spec.ClientConfig.Service != nil {
values, err := url.ParseQuery(apb.Spec.ClientConfig.Service.Query)
if err != nil {
return "", fmt.Errorf("parse error in appbinding %s/%s 'spec.clientConfig.service.query'. error: %v", apb.Namespace, apb.Name, err)
}
if sslMode != "" && sslMode != values.Get("sslmode") {
return "", fmt.Errorf("sslMode is not same in 'spec.clientConfig.service.query' and 'spec.clientConfig.url' of appbinding %s/%s", apb.Namespace, apb.Name)
}
sslMode = values.Get("sslmode")
}
// If sslMode is not specified anywhere, it will be disabled
if sslMode == "" {
sslMode = "disable"
}
return PostgresSSLMode(sslMode), nil
}
58 changes: 7 additions & 51 deletions apis/kubedb/v1alpha2/ferretdb_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ type FerretDBSpec struct {
Replicas *int32 `json:"replicas,omitempty"`

// Database authentication secret.
// If authSecret is nil, authSecret.externallyManaged will set to backend.externallyManaged
// Use this only when backend is internally managed.
// For externally managed backend, we will get the authSecret from AppBinding
// +optional
AuthSecret *SecretReference `json:"authSecret,omitempty"`

Expand Down Expand Up @@ -122,62 +123,17 @@ type FerretDBStatus struct {

type FerretDBBackend struct {
// PostgresRef refers to the AppBinding of the backend Postgres server
PostgresRef *kmapi.ObjectReference `json:"postgresRef"`
// +optional
PostgresRef *kmapi.ObjectReference `json:"postgresRef,omitempty"`
// Which versions pg will be used as backend of ferretdb. default 13.13 when backend internally managed
// +optional
Version *string `json:"version,omitempty"`
// A DB inside backend specifically made for ferretdb
// +optional
LinkedDB string `json:"linkedDB,omitempty"`
ExternallyManaged bool `json:"externallyManaged"`
}

//type PostgresRef struct {
// // Postgres URL address
// // +optional
// URL *string `json:"url,omitempty"`
// // Service information for Postgres
// // +optional
// Service *PostgresServiceRef `json:"service,omitempty"`
// // Which versions pg will be used as backend of ferretdb
// // +optional
// Version *string `json:"version,omitempty"`
//
// // SSLMode for both standalone and clusters. [disable;allow;prefer;require;verify-ca;verify-full]
// // +optional
// SSLMode PostgresSSLMode `json:"sslMode,omitempty"`
//
// // SSL Configuration of external Postgres
// // +optional
// CertSecrets *PostgresCertSecrets `json:"certSecrets,omitempty"`
//
// // ClientAuthMode for sidecar or sharding. (default will be md5. [md5;scram;cert])
// // +optional
// ClientAuthMode PostgresClientAuthMode `json:"clientAuthMode,omitempty"`
//}
//
//type PostgresCertSecrets struct {
// // SSLSecretName is the name of a kubernetes.io/tls type secret.
// // It must contain tls.crt(client cert), tls.key(client key) and ca.crt(ca cert) of
// // backend external Postgres.
// // +optional
// ClientSecret *kmapi.ObjectReference `json:"clientSecret,omitempty"`
//
// // CASecretName is the name of a secret providing ca.cert.
// // If External Postgres only needs ca.crt to get connect with FerretDB
// // +optional
// CASecret *kmapi.ObjectReference `json:"caSecret,omitempty"`
//}
//
//type PostgresServiceRef struct {
// // +optional
// Name string `json:"name,omitempty"`
// // +optional
// Namespace string `json:"namespace,omitempty"`
// // PgPort is used because the service referred to the
// // pg pod can have any port between 1 and 65535, inclusive
// // but targetPort is fixed to 5432
// // +optional
// PgPort int32 `json:"pgPort,omitempty"`
//}

// +kubebuilder:validation:Enum=server;client
type FerretDBCertificateAlias string

Expand Down
128 changes: 41 additions & 87 deletions apis/kubedb/v1alpha2/ferretdb_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"context"
"errors"
"fmt"
appcat "kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1"
"net/url"

"kubedb.dev/apimachinery/apis/catalog/v1alpha1"

Expand All @@ -32,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
appcat "kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
Expand Down Expand Up @@ -150,7 +149,7 @@ func (f *FerretDB) ValidateCreateOrUpdate() field.ErrorList {
if f.Spec.AuthSecret != nil && f.Spec.AuthSecret.ExternallyManaged && f.Spec.AuthSecret.Name == "" {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("authSecret"),
f.Name,
`'spec.authSecret.name' needs to specify when auth secret is externally managed`))
`'spec.authSecret.name' need to specify when auth secret is externally managed`))
}

// Termination policy related
Expand All @@ -167,6 +166,11 @@ func (f *FerretDB) ValidateCreateOrUpdate() field.ErrorList {
f.Name,
`'backend.postgresRef' is missing when backend is externally managed`))
} else {
if f.Spec.Backend.PostgresRef.Namespace == "" {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
f.Name,
`'backend.postgresRef.namespace' is needed when backend is externally managed`))
}
apb := appcat.AppBinding{}
err := DefaultClient.Get(context.TODO(), types.NamespacedName{
Name: f.Spec.Backend.PostgresRef.Name,
Expand All @@ -178,81 +182,52 @@ func (f *FerretDB) ValidateCreateOrUpdate() field.ErrorList {
err.Error(),
))
}

if apb.Spec.Secret == nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
f.Name,
`spec.secret needed in external pg appbinding`))
}

if apb.Spec.ClientConfig.Service == nil && apb.Spec.ClientConfig.URL == nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("postgresRef"),
f.Name,
`'clientConfig.url' or 'clientConfig.service' needed in the external pg appbinding`,
))
}
var sslMode string
if apb.Spec.ClientConfig.Service == nil {
parsedURL, err := url.Parse(*apb.Spec.ClientConfig.URL)
if err != nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("postgresRef"),
f.Name,
fmt.Sprintf("invalid URL in provided appbinding. error: %v", err),
))
}
if parsedURL.Scheme != "postgres" && parsedURL.Scheme != "postgresql" {
sslMode, err := f.GetSSLModeFromAppBinding(&apb)
if err != nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("postgresRef"),
f.Name,
err.Error(),
))
}

if sslMode == PostgresSSLModeRequire || sslMode == PostgresSSLModeVerifyCA || sslMode == PostgresSSLModeVerifyFull {
if apb.Spec.ClientConfig.CABundle == nil && apb.Spec.TLSSecret == nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("postgresRef"),
f.Name,
fmt.Sprintf("invalid scheme provided in URL in provided appbinding"),
"backend postgres connection is ssl encrypted but 'spec.clientConfig.caBundle' or 'spec.tlsSecret' is not provided in appbinding",
))
}
sslMode = parsedURL.Query().Get("sslMode")
} else {

}
//if f.Spec.Backend.Postgres.URL == nil {
// err := f.validateServiceRef(f.Spec.Backend.Postgres.Service)
// if err != nil {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// err.Error()))
// }
//}
//if f.Spec.Backend.Postgres.SSLMode == "" {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// `'backend.postgres.sslMode' is missing when backend is externally managed`))
//}
//
//if f.Spec.Backend.Postgres.ClientAuthMode == "" {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// `'backend.postgres.clientAuthMode' is missing when backend is externally managed`))
//}
//
//if f.Spec.Backend.Postgres.SSLMode == PostgresSSLModeDisable &&
// f.Spec.Backend.Postgres.ClientAuthMode == ClientAuthModeCert {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// `can't have sslMode 'disable' and clientAuthMode to 'cert'`))
//}
//
//if f.Spec.Backend.Postgres.SSLMode == PostgresSSLModeRequire ||
// f.Spec.Backend.Postgres.SSLMode == PostgresSSLModeVerifyCA ||
// f.Spec.Backend.Postgres.SSLMode == PostgresSSLModeVerifyFull {
//
// err := f.validateExternalSSLSecrets(f.Spec.Backend.Postgres)
// if err != nil {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// err.Error()))
// }
//}
if (apb.Spec.ClientConfig.CABundle != nil || apb.Spec.TLSSecret != nil) && sslMode == PostgresSSLModeDisable {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("postgresRef"),
f.Name,
"no client certificate or ca bundle possible when sslMode set to disable in backend postgres",
))
}
}
} else {
if f.Spec.Backend.Version != nil {
err := f.validatePostgresVersion()
if err != nil {
allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
f.Name,
err.Error()))
}
}
}
//else {
// if f.Spec.Backend.Postgres != nil && f.Spec.Backend.Postgres.Version != nil {
// err := f.validatePostgresVersion()
// if err != nil {
// allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("backend"),
// f.Name,
// err.Error()))
// }
// }
//}

// TLS related
if f.Spec.SSLMode == SSLModeAllowSSL || f.Spec.SSLMode == SSLModePreferSSL {
Expand Down Expand Up @@ -309,30 +284,9 @@ func (f *FerretDB) validateFerretDBVersion() error {

func (f *FerretDB) validatePostgresVersion() error {
pgVersion := v1alpha1.PostgresVersion{}
err := DefaultClient.Get(context.TODO(), types.NamespacedName{Name: *f.Spec.Backend.Postgres.Version}, &pgVersion)
err := DefaultClient.Get(context.TODO(), types.NamespacedName{Name: *f.Spec.Backend.Version}, &pgVersion)
if err != nil {
return errors.New("postgres version not supported in KubeDB")
}
return nil
}

func (f *FerretDB) validateServiceRef(ref *PostgresServiceRef) error {
if ref == nil {
return errors.New(`have to provide 'backend.postgres.url' or 'backend.postgres.service' when backend is externally managed`)
}
// port needs to be 0 < x < 65536
if ref.Namespace == "" || ref.Name == "" || ref.PgPort <= 0 || ref.PgPort >= 65536 {
return errors.New("pg service reference name, namespace and port(0<x<65536) needs to specify properly")
}
return nil
}

func (f *FerretDB) validateExternalSSLSecrets(pgRef *PostgresRef) error {
if pgRef.CertSecrets == nil {
return errors.New(`'backend.postgres.sslSecrets' is needed when `)
}
if pgRef.ClientAuthMode == ClientAuthModeCert && pgRef.CertSecrets.ClientSecret == nil {
return errors.New(`'backend.postgres.sslSecrets.clientSecretName' is needed when clientAuthMode set to 'cert'`)
}
return nil
}
Loading

0 comments on commit 549d427

Please sign in to comment.