Skip to content

Commit

Permalink
feat: Initial database support (#246)
Browse files Browse the repository at this point in the history
* Initial database support

- Add status checking
- Add better storage flags
- Add spec.storage.format validation
- Add DDL
-Add HIBERNATE format to DB (test)
- Update service image
- Revert identifier to DATABASE
- Update CR options (remove mandatory data)

* Remove default DDL generation env var

* Update service image to latest tag

* Add migration awareness

* Add updating pods for migration

* Change JDBC url from mysql to mariadb

* Fix TLS mount

* Revert images

* Remove redundant logic

* Fix comments
  • Loading branch information
ruivieira authored Jul 12, 2024
1 parent 45027a6 commit 4c8550e
Show file tree
Hide file tree
Showing 16 changed files with 1,270 additions and 406 deletions.
39 changes: 33 additions & 6 deletions api/v1alpha1/trustyaiservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ type TrustyAIService struct {
}

type StorageSpec struct {
Format string `json:"format"`
Folder string `json:"folder"`
Size string `json:"size"`
// Format only supports "PVC" or "DATABASE" values
// +kubebuilder:validation:Enum=PVC;DATABASE
Format string `json:"format"`
Folder string `json:"folder,omitempty"`
Size string `json:"size,omitempty"`
DatabaseConfigurations string `json:"databaseConfigurations,omitempty"`
}

type DataSpec struct {
Filename string `json:"filename"`
Format string `json:"format"`
Filename string `json:"filename,omitempty"`
Format string `json:"format,omitempty"`
}

type MetricsSpec struct {
Expand All @@ -55,7 +58,7 @@ type TrustyAIServiceSpec struct {
// +optional
Replicas *int32 `json:"replicas"`
Storage StorageSpec `json:"storage"`
Data DataSpec `json:"data"`
Data DataSpec `json:"data,omitempty"`
Metrics MetricsSpec `json:"metrics"`
}

Expand Down Expand Up @@ -90,6 +93,30 @@ func init() {
SchemeBuilder.Register(&TrustyAIService{}, &TrustyAIServiceList{})
}

// IsDatabaseConfigurationsSet returns true if the DatabaseConfigurations field is set.
func (s *StorageSpec) IsDatabaseConfigurationsSet() bool {
return s.DatabaseConfigurations != ""
}

// IsStoragePVC returns true if the storage is set to PVC.
func (s *StorageSpec) IsStoragePVC() bool {
return s.Format == "PVC"
}

// IsStorageDatabase returns true if the storage is set to database.
func (s *StorageSpec) IsStorageDatabase() bool {
return s.Format == "DATABASE"
}

// IsMigration returns true if the migration fields are set.
func (t *TrustyAIService) IsMigration() bool {
if t.Spec.Storage.Format == "DATABASE" && t.Spec.Storage.Folder != "" && t.Spec.Data.Filename != "" {
return true
} else {
return false
}
}

// SetStatus sets the status of the TrustyAIService
func (t *TrustyAIService) SetStatus(condType, reason, message string, status corev1.ConditionStatus) {
now := metav1.Now()
Expand Down
12 changes: 6 additions & 6 deletions config/crd/bases/trustyai.opendatahub.io_trustyaiservices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ spec:
type: string
format:
type: string
required:
- filename
- format
type: object
metrics:
properties:
Expand All @@ -60,19 +57,22 @@ spec:
type: integer
storage:
properties:
databaseConfigurations:
type: string
folder:
type: string
format:
description: Format only supports "PVC" or "DATABASE" values
enum:
- PVC
- DATABASE
type: string
size:
type: string
required:
- folder
- format
- size
type: object
required:
- data
- metrics
- storage
type: object
Expand Down
11 changes: 10 additions & 1 deletion controllers/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ const (
modelMeshLabelKey = "modelmesh-service"
modelMeshLabelValue = "modelmesh-serving"
volumeMountName = "volume"
defaultRequeueDelay = 10 * time.Second
defaultRequeueDelay = 30 * time.Second
dbCredentialsSuffix = "-db-credentials"
)

// Allowed storage formats
const (
STORAGE_PVC = "PVC"
STORAGE_DATABASE = "DATABASE"
)

// Configuration constants
Expand Down Expand Up @@ -56,3 +63,5 @@ const (
EventReasonInferenceServiceConfigured = "InferenceServiceConfigured"
EventReasonServiceMonitorCreated = "ServiceMonitorCreated"
)

const migrationAnnotationKey = "trustyai.opendatahub.io/db-migration"
100 changes: 72 additions & 28 deletions controllers/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ type DeploymentConfig struct {
PVCClaimName string
CustomCertificatesBundle CustomCertificatesBundle
Version string
BatchSize int
}

// createDeploymentObject returns a Deployment for the TrustyAI Service instance
func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService, serviceImage string, caBunble CustomCertificatesBundle) (*appsv1.Deployment, error) {

var batchSize int
// If not batch size is provided, assume the default one
// If no batch size is provided, assume the default one
if instance.Spec.Metrics.BatchSize == nil {
batchSize = defaultBatchSize
} else {
Expand All @@ -66,6 +67,7 @@ func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context,
PVCClaimName: pvcName,
CustomCertificatesBundle: caBunble,
Version: Version,
BatchSize: batchSize,
}

var deployment *appsv1.Deployment
Expand All @@ -81,42 +83,78 @@ func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context,
// reconcileDeployment returns a Deployment object with the same name/namespace as the cr
func (r *TrustyAIServiceReconciler) createDeployment(ctx context.Context, cr *trustyaiopendatahubiov1alpha1.TrustyAIService, imageName string, caBundle CustomCertificatesBundle) error {

pvcName := generatePVCName(cr)
if !cr.Spec.Storage.IsDatabaseConfigurationsSet() {

pvc := &corev1.PersistentVolumeClaim{}
pvcerr := r.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: cr.Namespace}, pvc)
if pvcerr != nil {
log.FromContext(ctx).Error(pvcerr, "PVC not found")
return pvcerr
}
if pvcerr == nil {
// The PVC is ready. We can now create the Deployment.
deployment, err := r.createDeploymentObject(ctx, cr, imageName, caBundle)
if err != nil {
// Error creating the deployment resource object
return err
}
pvcName := generatePVCName(cr)

if err := ctrl.SetControllerReference(cr, deployment, r.Scheme); err != nil {
log.FromContext(ctx).Error(err, "Error setting TrustyAIService as owner of Deployment.")
return err
pvc := &corev1.PersistentVolumeClaim{}
pvcerr := r.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: cr.Namespace}, pvc)
if pvcerr != nil {
log.FromContext(ctx).Error(pvcerr, "PVC not found")
return pvcerr
}
log.FromContext(ctx).Info("Creating Deployment.")
err = r.Create(ctx, deployment)
if err != nil {
log.FromContext(ctx).Error(err, "Error creating Deployment.")
return err
}

// We can now create the Deployment.
deployment, err := r.createDeploymentObject(ctx, cr, imageName, caBundle)
if err != nil {
// Error creating the deployment resource object
return err
}

if err := ctrl.SetControllerReference(cr, deployment, r.Scheme); err != nil {
log.FromContext(ctx).Error(err, "Error setting TrustyAIService as owner of Deployment.")
return err
}
log.FromContext(ctx).Info("Creating Deployment.")
err = r.Create(ctx, deployment)
if err != nil {
log.FromContext(ctx).Error(err, "Error creating Deployment.")
return err
}
// Created successfully
return nil

}

// updateDeployment returns a Deployment object with the same name/namespace as the cr
func (r *TrustyAIServiceReconciler) updateDeployment(ctx context.Context, cr *trustyaiopendatahubiov1alpha1.TrustyAIService, imageName string, caBundle CustomCertificatesBundle) error {

if !cr.Spec.Storage.IsDatabaseConfigurationsSet() {

pvcName := generatePVCName(cr)

pvc := &corev1.PersistentVolumeClaim{}
pvcerr := r.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: cr.Namespace}, pvc)
if pvcerr != nil {
log.FromContext(ctx).Error(pvcerr, "PVC not found")
return pvcerr
}
// Created successfully
return nil
}

} else {
return ErrPVCNotReady
// We can now create the Deployment object.
deployment, err := r.createDeploymentObject(ctx, cr, imageName, caBundle)
if err != nil {
// Error creating the deployment resource object
return err
}

if err := ctrl.SetControllerReference(cr, deployment, r.Scheme); err != nil {
log.FromContext(ctx).Error(err, "Error setting TrustyAIService as owner of Deployment.")
return err
}
log.FromContext(ctx).Info("Updating Deployment.")
err = r.Update(ctx, deployment)
if err != nil {
log.FromContext(ctx).Error(err, "Error updating Deployment.")
return err
}
// Created successfully
return nil

}

func (r *TrustyAIServiceReconciler) ensureDeployment(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService, caBundle CustomCertificatesBundle) error {
func (r *TrustyAIServiceReconciler) ensureDeployment(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService, caBundle CustomCertificatesBundle, migration bool) error {

// Get image and tag from ConfigMap
// If there's a ConfigMap with custom images, it is only applied when the operator is first deployed
Expand All @@ -138,6 +176,12 @@ func (r *TrustyAIServiceReconciler) ensureDeployment(ctx context.Context, instan
// Some other error occurred when trying to get the Deployment
return err
}
// Deployment exists, but we are migrating
if migration {
log.FromContext(ctx).Info("Found migration annotation. Migrating.")
return r.updateDeployment(ctx, instance, image, caBundle)
}

// Deployment is ready and using the PVC
return nil
}
Expand Down
Loading

0 comments on commit 4c8550e

Please sign in to comment.