diff --git a/apis/kubedb/v1alpha2/constants.go b/apis/kubedb/v1alpha2/constants.go index 261f55c6e1..553894659c 100644 --- a/apis/kubedb/v1alpha2/constants.go +++ b/apis/kubedb/v1alpha2/constants.go @@ -677,6 +677,8 @@ const ( DatabaseReadAccess = "DatabaseReadAccess" // user for databases that have write access DatabaseWriteAccess = "DatabaseWriteAccess" + // check dependencies are ready + DatabaseDependencyReady = "DatabaseDependencyReady" // Condition reasons DataRestoreStartedByExternalInitializer = "DataRestoreStartedByExternalInitializer" @@ -698,6 +700,7 @@ const ( DatabaseWriteAccessCheckFailed = "DatabaseWriteAccessCheckFailed" InternalUsersCredentialSyncFailed = "InternalUsersCredentialsSyncFailed" InternalUsersCredentialsSyncedSuccessfully = "InternalUsersCredentialsSyncedSuccessfully" + FailedToEnsureDependency = "FailedToEnsureDependency" ) const ( @@ -966,7 +969,7 @@ const ( DruidExporterPort = 9104 // Common Runtime Configurations Properties - // ZooKeeperSpec + // ZooKeeper DruidZKServiceHost = "druid.zk.service.host" DruidZKPathsBase = "druid.zk.paths.base" DruidZKServiceCompress = "druid.zk.service.compress" @@ -984,6 +987,25 @@ const ( DruidMetadataStorageConnectorPasswordEnvConfig = "{\"type\": \"environment\", \"variable\": \"DRUID_METADATA_STORAGE_PASSWORD\"}" DruidMetadataStorageCreateTables = "druid.metadata.storage.connector.createTables" + // MySQL TLS + DruidMetadataMySQLUseSSL = "druid.metadata.mysql.ssl.useSSL" + DruidMetadataMySQLClientCertKeyStoreURL = "druid.metadata.mysql.ssl.clientCertificateKeyStoreUrl" + DruidMetadataMySQLClientCertKeyStorePath = "/opt/druid/conf/tls/metadatakeystore.jks" + DruidMetadataMySQLClientCertKeyStoreType = "druid.metadata.mysql.ssl.clientCertificateKeyStoreType" + DruidMetadataMySQLClientCertKeyStoreTypeJKS = "JKS" + DruidMetadataMySQLClientCertKeyStorePassword = "druid.metadata.mysql.ssl.clientCertificateKeyStorePassword" + DruidMetadataMySQLClientCertKeyStorePasswordValue = "password" + + // Postgres TLS + DruidMetadataPostgresUseSSL = "druid.metadata.postgres.ssl.useSSL" + DruidMetadataPGUseSSLMode = "druid.metadata.postgres.ssl.sslMode" + DruidMetadataPGSSLCert = "druid.metadata.postgres.ssl.sslCert" + DruidMetadataPGSSLCertPath = "/opt/druid/conf/tls/tls.crt" + DruidMetadataPGSSLKey = "druid.metadata.postgres.ssl.sslKey" + DruidMetadataPGSSLKeyPath = "/opt/druid/conf/tls/tls.key" + DruidMetadataPGSSLRootCert = "druid.metadata.postgres.ssl.sslRootCert" + DruidMetadataPGSSLRootCertPath = "/opt/druid/conf/tls/ca.cert" + // Deep Storage DruidDeepStorageTypeKey = "druid.storage.type" DruidDeepStorageTypeS3 = "s3" diff --git a/apis/kubedb/v1alpha2/druid_helpers.go b/apis/kubedb/v1alpha2/druid_helpers.go index 9d63a5f1a9..f54e8bf547 100644 --- a/apis/kubedb/v1alpha2/druid_helpers.go +++ b/apis/kubedb/v1alpha2/druid_helpers.go @@ -37,6 +37,8 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" + "k8s.io/utils/ptr" + kmapi "kmodules.xyz/client-go/api/v1" "kmodules.xyz/client-go/apiextensions" coreutil "kmodules.xyz/client-go/core/v1" meta_util "kmodules.xyz/client-go/meta" @@ -45,6 +47,7 @@ import ( mona "kmodules.xyz/monitoring-agent-api/api/v1" ofst "kmodules.xyz/offshoot-api/api/v2" pslister "kubeops.dev/petset/client/listers/apps/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) func (d *Druid) CustomResourceDefinition() *apiextensions.CustomResourceDefinition { @@ -313,8 +316,10 @@ func (d *Druid) AddDruidExtensionLoadList(druidExtensionLoadList string, extensi func (d *Druid) GetMetadataStorageType(metadataStorage string) DruidMetadataStorageType { if metadataStorage == string(DruidMetadataStorageMySQL) || metadataStorage == strings.ToLower(string(DruidMetadataStorageMySQL)) { return DruidMetadataStorageMySQL - } else { + } else if metadataStorage == string(DruidMetadataStoragePostgreSQL) || metadataStorage == strings.ToLower(string(DruidMetadataStoragePostgreSQL)) { return DruidMetadataStoragePostgreSQL + } else { + panic(fmt.Sprintf("Unknown metadata storage type: %s", metadataStorage)) } } @@ -441,7 +446,7 @@ func (d *Druid) SetDefaults() { d.Spec.Topology.MiddleManagers.StorageType = StorageTypeDurable } if d.Spec.Topology.MiddleManagers.Storage == nil && d.Spec.Topology.MiddleManagers.StorageType == StorageTypeDurable { - d.Spec.Topology.MiddleManagers.Storage = d.getDefaultPVC() + d.Spec.Topology.MiddleManagers.Storage = d.GetDefaultPVC() } if version.Major() > 25 { if d.Spec.Topology.MiddleManagers.PodTemplate.Spec.SecurityContext == nil { @@ -463,7 +468,7 @@ func (d *Druid) SetDefaults() { d.Spec.Topology.Historicals.StorageType = StorageTypeDurable } if d.Spec.Topology.Historicals.Storage == nil && d.Spec.Topology.Historicals.StorageType == StorageTypeDurable { - d.Spec.Topology.Historicals.Storage = d.getDefaultPVC() + d.Spec.Topology.Historicals.Storage = d.GetDefaultPVC() } if version.Major() > 25 { if d.Spec.Topology.Historicals.PodTemplate.Spec.SecurityContext == nil { @@ -504,11 +509,65 @@ func (d *Druid) SetDefaults() { } } } - if d.Spec.MetadataStorage != nil { - if d.Spec.MetadataStorage.Name != "" && d.Spec.MetadataStorage.Namespace == "" { - d.Spec.MetadataStorage.Namespace = d.Namespace + + if d.Spec.MetadataStorage == nil { + d.Spec.MetadataStorage = &MetadataStorage{} + } + if d.Spec.MetadataStorage.Namespace == "" { + d.Spec.MetadataStorage.Namespace = d.Namespace + } + if d.Spec.MetadataStorage.LinkedDB == "" { + d.Spec.MetadataStorage.LinkedDB = "druid" + } + if d.Spec.MetadataStorage.CreateTables == nil { + d.Spec.MetadataStorage.CreateTables = ptr.To(true) + } + + if d.Spec.MetadataStorage.Type == "" { + if d.Spec.MetadataStorage.ExternallyManaged { + appBinding, err := d.GetAppBinding(d.Spec.MetadataStorage.Name, d.Spec.MetadataStorage.Namespace) + if err != nil { + return + } + d.Spec.MetadataStorage.Type = d.GetMetadataStorageType(appBinding.Spec.AppRef.Kind) + } else { + d.Spec.MetadataStorage.Type = DruidMetadataStorageMySQL + } + } + if !d.Spec.MetadataStorage.ExternallyManaged { + if d.Spec.MetadataStorage.ObjectReference == nil { + d.Spec.MetadataStorage.ObjectReference = &kmapi.ObjectReference{} } + d.Spec.MetadataStorage.Name = d.GetMetadataStorageName() + } + + if d.Spec.MetadataStorage.Version == nil { + var defaultVersion string + if d.Spec.MetadataStorage.Type == DruidMetadataStorageMySQL { + defaultVersion = "8.0.35" + } else { + defaultVersion = "13.13" + } + d.Spec.MetadataStorage.Version = &defaultVersion + } + + if d.Spec.ZookeeperRef == nil { + d.Spec.ZookeeperRef = &ZookeeperRef{} } + if !d.Spec.ZookeeperRef.ExternallyManaged { + if d.Spec.ZookeeperRef.ObjectReference == nil { + d.Spec.ZookeeperRef.ObjectReference = &kmapi.ObjectReference{} + } + if d.Spec.ZookeeperRef.Name == "" { + d.Spec.ZookeeperRef.Name = d.GetZooKeeperName() + } + if d.Spec.ZookeeperRef.Namespace == "" { + d.Spec.ZookeeperRef.Namespace = d.Namespace + } + defaultVersion := "3.7.2" + d.Spec.ZookeeperRef.Version = &defaultVersion + } + if d.Spec.Monitor != nil { if d.Spec.Monitor.Prometheus == nil { d.Spec.Monitor.Prometheus = &mona.PrometheusSpec{} @@ -520,8 +579,11 @@ func (d *Druid) SetDefaults() { } } -func (d *Druid) getDefaultPVC() *core.PersistentVolumeClaimSpec { +func (d *Druid) GetDefaultPVC() *core.PersistentVolumeClaimSpec { return &core.PersistentVolumeClaimSpec{ + AccessModes: []core.PersistentVolumeAccessMode{ + core.ReadWriteOnce, + }, Resources: core.VolumeResourceRequirements{ Requests: core.ResourceList{ core.ResourceStorage: resource.MustParse("1Gi"), @@ -618,3 +680,30 @@ func (d *Druid) ReplicasAreReady(lister pslister.PetSetLister) (bool, string, er } return checkReplicasOfPetSet(lister.PetSets(d.Namespace), labels.SelectorFromSet(d.OffshootLabels()), expectedItems) } + +func (d *Druid) GetAppBinding(name string, namespace string) (*appcat.AppBinding, error) { + appbinding := &appcat.AppBinding{} + appbinding.Namespace = namespace + appbinding.Name = name + + if err := DefaultClient.Get(context.TODO(), client.ObjectKeyFromObject(appbinding), appbinding); err != nil { + klog.Error(err, fmt.Sprintf("failed to get appbinding for metadata storage %s/%s", name, namespace)) + return nil, err + } + return appbinding, nil +} + +func (d *Druid) GetMetadataStorageName() string { + if d.Spec.MetadataStorage.Type == DruidMetadataStoragePostgreSQL { + return d.OffShootName() + "-pg-metadata" + } + return d.OffShootName() + "-mysql-metadata" +} + +func (d *Druid) GetZooKeeperName() string { + return d.OffShootName() + "-zk" +} + +func (d *Druid) GetInitConfigMapName() string { + return d.OffShootName() + "-init-script" +} diff --git a/apis/kubedb/v1alpha2/druid_types.go b/apis/kubedb/v1alpha2/druid_types.go index 496144a737..1a7ba61ba6 100644 --- a/apis/kubedb/v1alpha2/druid_types.go +++ b/apis/kubedb/v1alpha2/druid_types.go @@ -80,7 +80,8 @@ type DruidSpec struct { //TLS *kmapi.TLSConfig `json:"tls,omitempty"` // MetadataStorage contains information for Druid to connect to external dependency metadata storage - MetadataStorage *MetadataStorage `json:"metadataStorage"` + // +optional + MetadataStorage *MetadataStorage `json:"metadataStorage,omitempty"` // DeepStorage contains specification for druid to connect to the deep storage DeepStorage *DeepStorageSpec `json:"deepStorage"` @@ -156,7 +157,7 @@ type DruidDataNode struct { } type MetadataStorage struct { - // Name of the appbinding of zookeeper + // Name and namespace of the appbinding of metadata storage // +optional *kmapi.ObjectReference `json:",omitempty"` @@ -167,6 +168,16 @@ type MetadataStorage struct { // If Druid has the permission to create new tables // +optional CreateTables *bool `json:"createTables,omitempty"` + + // +optional + LinkedDB string `json:"linkedDB,omitempty"` + + // +optional + ExternallyManaged bool `json:"externallyManaged,omitempty"` + + // Version of the MySQL/PG used + // +optional + Version *string `json:"version,omitempty"` } type DeepStorageSpec struct { @@ -181,13 +192,20 @@ type DeepStorageSpec struct { } type ZookeeperRef struct { - // Name of the appbinding of zookeeper + // Name and namespace of appbinding of zookeeper // +optional *kmapi.ObjectReference `json:",omitempty"` // Base ZooKeeperSpec path // +optional PathsBase string `json:"pathsBase,omitempty"` + + // +optional + ExternallyManaged bool `json:"externallyManaged,omitempty"` + + // Version of the ZK used + // +optional + Version *string `json:"version,omitempty"` } // DruidStatus defines the observed state of Druid diff --git a/apis/kubedb/v1alpha2/druid_webhook.go b/apis/kubedb/v1alpha2/druid_webhook.go index 5c6858e722..cd4b4344dc 100644 --- a/apis/kubedb/v1alpha2/druid_webhook.go +++ b/apis/kubedb/v1alpha2/druid_webhook.go @@ -130,10 +130,10 @@ func (d *Druid) validateCreateOrUpdate() field.ErrorList { d.Name, "spec.metadataStorage is missing")) } else { - if d.Spec.MetadataStorage.Name == "" && d.Spec.MetadataStorage.Type == "" { + if d.Spec.MetadataStorage.ExternallyManaged && d.Spec.MetadataStorage.Name == "" { allErr = append(allErr, field.Invalid(field.NewPath("spec").Child("metadataStorage").Child("name"), d.Name, - "spec.metadataStorage.type and spec.metadataStorage.name both can not be empty simultaneously")) + "spec.metadataStorage.name can not be empty when d.Spec.MetadataStorage.ExternallyManaged is true")) } } diff --git a/apis/kubedb/v1alpha2/openapi_generated.go b/apis/kubedb/v1alpha2/openapi_generated.go index 6649284e66..7773e2bc06 100644 --- a/apis/kubedb/v1alpha2/openapi_generated.go +++ b/apis/kubedb/v1alpha2/openapi_generated.go @@ -24904,7 +24904,7 @@ func schema_apimachinery_apis_kubedb_v1alpha2_DruidSpec(ref common.ReferenceCall }, }, }, - Required: []string{"version", "metadataStorage", "deepStorage"}, + Required: []string{"version", "deepStorage"}, }, }, Dependencies: []string{ @@ -27987,6 +27987,25 @@ func schema_apimachinery_apis_kubedb_v1alpha2_MetadataStorage(ref common.Referen Format: "", }, }, + "linkedDB": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "externallyManaged": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version of the MySQL/PG used", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, @@ -33301,6 +33320,19 @@ func schema_apimachinery_apis_kubedb_v1alpha2_ZookeeperRef(ref common.ReferenceC Format: "", }, }, + "externallyManaged": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version of the ZK used", + Type: []string{"string"}, + Format: "", + }, + }, }, }, }, diff --git a/apis/kubedb/v1alpha2/zz_generated.deepcopy.go b/apis/kubedb/v1alpha2/zz_generated.deepcopy.go index 3c271bcf71..6b5c9e2ae7 100644 --- a/apis/kubedb/v1alpha2/zz_generated.deepcopy.go +++ b/apis/kubedb/v1alpha2/zz_generated.deepcopy.go @@ -2638,6 +2638,11 @@ func (in *MetadataStorage) DeepCopyInto(out *MetadataStorage) { *out = new(bool) **out = **in } + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } return } @@ -5724,6 +5729,11 @@ func (in *ZookeeperRef) DeepCopyInto(out *ZookeeperRef) { *out = new(apiv1.ObjectReference) **out = **in } + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } return } diff --git a/crds/kubedb.com_druids.yaml b/crds/kubedb.com_druids.yaml index 914612c55f..d6703678af 100644 --- a/crds/kubedb.com_druids.yaml +++ b/crds/kubedb.com_druids.yaml @@ -110,6 +110,10 @@ spec: properties: createTables: type: boolean + externallyManaged: + type: boolean + linkedDB: + type: string name: type: string namespace: @@ -119,6 +123,8 @@ spec: - MySQL - PostgreSQL type: string + version: + type: string required: - name type: object @@ -19046,18 +19052,21 @@ spec: type: string zookeeperRef: properties: + externallyManaged: + type: boolean name: type: string namespace: type: string pathsBase: type: string + version: + type: string required: - name type: object required: - deepStorage - - metadataStorage - version type: object status: