123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- package dbstore
- import (
- "encoding/json"
- "fmt"
- "git.linuxforward.com/byop/byop-engine/dbmanager"
- "git.linuxforward.com/byop/byop-engine/models"
- "gorm.io/gorm"
- )
- // DeploymentStore handles database operations for deployments
- type DeploymentStore struct {
- db *gorm.DB
- }
- // NewDeploymentStore creates a new DeploymentStore
- func NewDeploymentStore(dbManager dbmanager.DbManager) *DeploymentStore {
- return &DeploymentStore{
- db: dbManager.GetDB(),
- }
- }
- // Create creates a new deployment
- func (ds *DeploymentStore) Create(deployment *models.Deployment) error {
- // Ensure logs, metrics, and alerts config are properly JSON serialized
- if err := ds.serializeConfigFields(deployment); err != nil {
- return fmt.Errorf("failed to serialize config fields: %w", err)
- }
- // Create deployment in a transaction to handle deployed apps
- return ds.db.Transaction(func(tx *gorm.DB) error {
- // Create the deployment
- if err := tx.Create(deployment).Error; err != nil {
- return err
- }
- // Create any deployed apps in the same transaction
- for i := range deployment.DeployedApps {
- app := &deployment.DeployedApps[i]
- // Ensure ID is 0 so GORM can auto-generate it
- app.ID = 0
- // GORM will auto-generate the ID, just set the deployment ID relationship
- app.DeploymentID = deployment.ID
- // Create the deployed app
- if err := tx.Create(app).Error; err != nil {
- return err
- }
- // Handle resources if provided
- if app.Resources != (models.ResourceAllocation{}) {
- resource := models.DeployedAppResource{
- ID: 0, // Ensure ID is 0 for auto-increment
- DeployedAppID: app.ID,
- CPU: app.Resources.CPU,
- CPUUsage: app.Resources.CPUUsage,
- Memory: app.Resources.Memory,
- MemoryUsage: app.Resources.MemoryUsage,
- Storage: app.Resources.Storage,
- StorageUsage: app.Resources.StorageUsage,
- }
- if err := tx.Create(&resource).Error; err != nil {
- return err
- }
- }
- }
- return nil
- })
- }
- // GetByID retrieves a deployment by ID
- func (ds *DeploymentStore) GetByID(id int64) (*models.Deployment, error) {
- var deployment models.Deployment
- // Get deployment with all related deployed apps
- err := ds.db.
- Preload("DeployedApps").
- Where("rowid = ?", id). // Use SQLite's rowid for ID
- First(&deployment).Error
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- return nil, nil // No deployment found
- }
- return nil, fmt.Errorf("failed to get deployment: %w", err)
- }
- // Load resources for each deployed app
- for i, app := range deployment.DeployedApps {
- var resource models.DeployedAppResource
- if err := ds.db.Where("deployed_app_id = ?", app.ID).First(&resource).Error; err != nil {
- if err != gorm.ErrRecordNotFound {
- return nil, fmt.Errorf("failed to get resources for deployed app: %w", err)
- }
- } else {
- deployment.DeployedApps[i].Resources = models.ResourceAllocation{
- CPU: resource.CPU,
- CPUUsage: resource.CPUUsage,
- Memory: resource.Memory,
- MemoryUsage: resource.MemoryUsage,
- Storage: resource.Storage,
- StorageUsage: resource.StorageUsage,
- }
- }
- }
- // Deserialize config fields
- if err := ds.deserializeConfigFields(&deployment); err != nil {
- return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
- }
- return &deployment, nil
- }
- // Update updates an existing deployment
- func (ds *DeploymentStore) Update(deployment *models.Deployment) error {
- // Ensure logs, metrics, and alerts config are properly JSON serialized
- if err := ds.serializeConfigFields(deployment); err != nil {
- return fmt.Errorf("failed to serialize config fields: %w", err)
- }
- // Use transaction to handle deployment and deployed apps
- return ds.db.Transaction(func(tx *gorm.DB) error {
- // Update the deployment
- if err := tx.Save(deployment).Error; err != nil {
- return err
- }
- // Handle deployed apps - this is trickier as we need to compare with existing apps
- var existingApps []models.DeployedApp
- if err := tx.Where("deployment_id = ?", deployment.ID).Find(&existingApps).Error; err != nil {
- return err
- }
- // Create a map of existing app IDs for quick lookup
- existingAppMap := make(map[int64]bool)
- for _, app := range existingApps {
- existingAppMap[app.ID] = true
- }
- // Process each app in the updated deployment
- for i := range deployment.DeployedApps {
- app := &deployment.DeployedApps[i]
- // If app has ID and exists, update it
- if app.ID != 0 && existingAppMap[app.ID] {
- if err := tx.Save(app).Error; err != nil {
- return err
- }
- delete(existingAppMap, app.ID)
- } else {
- // New app, create it (GORM will auto-generate ID)
- app.ID = 0 // Ensure ID is 0 for auto-increment
- app.DeploymentID = deployment.ID
- if err := tx.Create(app).Error; err != nil {
- return err
- }
- }
- // Handle resources
- if app.Resources != (models.ResourceAllocation{}) {
- var resource models.DeployedAppResource
- result := tx.Where("deployed_app_id = ?", app.ID).First(&resource)
- if result.Error != nil && result.Error != gorm.ErrRecordNotFound {
- return result.Error
- }
- if result.Error == gorm.ErrRecordNotFound {
- // Create new resource (GORM will auto-generate ID)
- resource = models.DeployedAppResource{
- ID: 0, // Ensure ID is 0 for auto-increment
- DeployedAppID: app.ID,
- CPU: app.Resources.CPU,
- CPUUsage: app.Resources.CPUUsage,
- Memory: app.Resources.Memory,
- MemoryUsage: app.Resources.MemoryUsage,
- Storage: app.Resources.Storage,
- StorageUsage: app.Resources.StorageUsage,
- }
- if err := tx.Create(&resource).Error; err != nil {
- return err
- }
- } else {
- // Update existing resource
- resource.CPU = app.Resources.CPU
- resource.CPUUsage = app.Resources.CPUUsage
- resource.Memory = app.Resources.Memory
- resource.MemoryUsage = app.Resources.MemoryUsage
- resource.Storage = app.Resources.Storage
- resource.StorageUsage = app.Resources.StorageUsage
- if err := tx.Save(&resource).Error; err != nil {
- return err
- }
- }
- }
- }
- // Delete any apps that are no longer part of the deployment
- for appID := range existingAppMap {
- if err := tx.Delete(&models.DeployedApp{}, "id = ?", appID).Error; err != nil {
- return err
- }
- // Delete associated resources
- if err := tx.Delete(&models.DeployedAppResource{}, "deployed_app_id = ?", appID).Error; err != nil && err != gorm.ErrRecordNotFound {
- return err
- }
- }
- return nil
- })
- }
- // Delete deletes a deployment by ID
- func (ds *DeploymentStore) Delete(id int64) error {
- return ds.db.Transaction(func(tx *gorm.DB) error {
- // Delete associated DeployedAppResources
- var deployedApps []models.DeployedApp
- if err := tx.Where("deployment_id = ?", id).Find(&deployedApps).Error; err != nil {
- return err
- }
- for _, app := range deployedApps {
- if err := tx.Delete(&models.DeployedAppResource{}, "deployed_app_id = ?", app.ID).Error; err != nil && err != gorm.ErrRecordNotFound {
- return err
- }
- }
- // Delete deployed apps
- if err := tx.Delete(&models.DeployedApp{}, "deployment_id = ?", id).Error; err != nil && err != gorm.ErrRecordNotFound {
- return err
- }
- // Delete the deployment itself
- return tx.Delete(&models.Deployment{}, "id = ?", id).Error
- })
- }
- // List retrieves all deployments with optional filtering
- func (ds *DeploymentStore) List(filter map[string]interface{}) ([]*models.Deployment, error) {
- var deployments []*models.Deployment
- // Build query from filters
- query := ds.db.Preload("DeployedApps")
- if filter != nil {
- for key, value := range filter {
- query = query.Where(key+" = ?", value)
- }
- }
- // Execute query
- if err := query.Find(&deployments).Error; err != nil {
- return nil, fmt.Errorf("failed to list deployments: %w", err)
- }
- // Load resources and deserialize config for each deployment
- for i, deployment := range deployments {
- // Load resources for each deployed app
- for j, app := range deployment.DeployedApps {
- var resource models.DeployedAppResource
- if err := ds.db.Where("deployed_app_id = ?", app.ID).First(&resource).Error; err != nil {
- if err != gorm.ErrRecordNotFound {
- return nil, fmt.Errorf("failed to get resources for deployed app: %w", err)
- }
- } else {
- deployments[i].DeployedApps[j].Resources = models.ResourceAllocation{
- CPU: resource.CPU,
- CPUUsage: resource.CPUUsage,
- Memory: resource.Memory,
- MemoryUsage: resource.MemoryUsage,
- Storage: resource.Storage,
- StorageUsage: resource.StorageUsage,
- }
- }
- }
- // Deserialize config fields
- if err := ds.deserializeConfigFields(deployments[i]); err != nil {
- return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
- }
- }
- return deployments, nil
- }
- // GetByClientID retrieves deployments for a specific client
- func (ds *DeploymentStore) GetByClientID(clientID int64) ([]*models.Deployment, error) {
- return ds.List(map[string]interface{}{"client_id": clientID})
- }
- // GetByUserID retrieves deployments created by a specific user
- func (ds *DeploymentStore) GetByUserID(userID string) ([]*models.Deployment, error) {
- return ds.List(map[string]interface{}{"created_by": userID})
- }
- // GetByAppID retrieves deployments based on a specific app (was template)
- func (ds *DeploymentStore) GetByAppID(appID int64) ([]*models.Deployment, error) {
- return ds.List(map[string]interface{}{"app_id": appID})
- }
- // GetByTemplateID is deprecated, use GetByAppID instead
- func (ds *DeploymentStore) GetByTemplateID(templateID int64) ([]*models.Deployment, error) {
- return ds.GetByAppID(templateID)
- }
- // serializeConfigFields serializes JSON config fields to strings
- func (ds *DeploymentStore) serializeConfigFields(deployment *models.Deployment) error {
- // Serialize logs config if provided
- if deployment.LogsConfig == "" {
- logsConfig := models.LogConfiguration{
- Enabled: true,
- RetentionDays: 7,
- }
- logsConfigBytes, err := json.Marshal(logsConfig)
- if err != nil {
- return err
- }
- deployment.LogsConfig = string(logsConfigBytes)
- }
- // Serialize metrics config if provided
- if deployment.MetricsConfig == "" {
- metricsConfig := models.MetricsConfiguration{
- Enabled: true,
- RetentionDays: 30,
- }
- metricsConfigBytes, err := json.Marshal(metricsConfig)
- if err != nil {
- return err
- }
- deployment.MetricsConfig = string(metricsConfigBytes)
- }
- // Serialize alerts config if provided
- if deployment.AlertsConfig == "" {
- alertsConfig := []models.AlertConfiguration{}
- alertsConfigBytes, err := json.Marshal(alertsConfig)
- if err != nil {
- return err
- }
- deployment.AlertsConfig = string(alertsConfigBytes)
- }
- return nil
- }
- // deserializeConfigFields deserializes JSON config fields from strings
- func (ds *DeploymentStore) deserializeConfigFields(deployment *models.Deployment) error {
- // No need to deserialize in the store, as these fields are stored as strings
- // in the database and are deserialized as needed by the service layer
- return nil
- }
|