mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 11:02:54 +00:00
- Add DeploymentService struct with database dependency and NewDeploymentService constructor - Add DeploymentFilter struct with Service/Environment/Status/Limit/Offset fields for query filtering - Add List method with context support, filter application (service/environment/status), pagination (default limit: 20), and DESC ordering by created_at - Add Get method with gorm.ErrRecordNotFound handling and formatted
158 lines
4.4 KiB
Go
158 lines
4.4 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/nutcas3/telecom-platform/apps/api-server/internal/database"
|
|
"github.com/nutcas3/telecom-platform/apps/api-server/internal/models"
|
|
)
|
|
|
|
// DeploymentService persists deployment records. Actual orchestration
|
|
// (Helm/ArgoCD/kubectl) is handled by an external controller or operator;
|
|
// this service is the system of record for deployment history and status.
|
|
type DeploymentService struct {
|
|
db *database.Database
|
|
}
|
|
|
|
func NewDeploymentService(db *database.Database) *DeploymentService {
|
|
return &DeploymentService{db: db}
|
|
}
|
|
|
|
type DeploymentFilter struct {
|
|
Service string
|
|
Environment string
|
|
Status string
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
func (s *DeploymentService) List(ctx context.Context, f DeploymentFilter) ([]models.DeploymentRecord, int64, error) {
|
|
q := s.db.DB.WithContext(ctx).Model(&models.DeploymentRecord{})
|
|
if f.Service != "" {
|
|
q = q.Where("service = ?", f.Service)
|
|
}
|
|
if f.Environment != "" {
|
|
q = q.Where("environment = ?", f.Environment)
|
|
}
|
|
if f.Status != "" {
|
|
q = q.Where("status = ?", f.Status)
|
|
}
|
|
var total int64
|
|
if err := q.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if f.Limit <= 0 {
|
|
f.Limit = 20
|
|
}
|
|
var out []models.DeploymentRecord
|
|
if err := q.Order("created_at DESC").Limit(f.Limit).Offset(f.Offset).Find(&out).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return out, total, nil
|
|
}
|
|
|
|
func (s *DeploymentService) Get(ctx context.Context, id uint) (*models.DeploymentRecord, error) {
|
|
var d models.DeploymentRecord
|
|
if err := s.db.DB.WithContext(ctx).First(&d, id).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, fmt.Errorf("deployment %d not found", id)
|
|
}
|
|
return nil, err
|
|
}
|
|
return &d, nil
|
|
}
|
|
|
|
type StartDeploymentInput struct {
|
|
Service string `json:"service" binding:"required"`
|
|
Version string `json:"version" binding:"required"`
|
|
Environment string `json:"environment" binding:"required"`
|
|
Strategy string `json:"strategy"`
|
|
Replicas int `json:"replicas"`
|
|
TriggeredBy string `json:"triggered_by"`
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
func (s *DeploymentService) Start(ctx context.Context, in StartDeploymentInput) (*models.DeploymentRecord, error) {
|
|
now := time.Now()
|
|
d := models.DeploymentRecord{
|
|
Service: in.Service,
|
|
Version: in.Version,
|
|
Environment: in.Environment,
|
|
Strategy: defaultStr(in.Strategy, "rolling"),
|
|
Replicas: in.Replicas,
|
|
TriggeredBy: defaultStr(in.TriggeredBy, "api"),
|
|
Reason: in.Reason,
|
|
Status: "in_progress",
|
|
StartedAt: &now,
|
|
}
|
|
if err := s.db.DB.WithContext(ctx).Create(&d).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &d, nil
|
|
}
|
|
|
|
type RollbackInput struct {
|
|
Service string `json:"service" binding:"required"`
|
|
ToVersion string `json:"to_version" binding:"required"`
|
|
Reason string `json:"reason"`
|
|
TriggeredBy string `json:"triggered_by"`
|
|
}
|
|
|
|
func (s *DeploymentService) Rollback(ctx context.Context, in RollbackInput) (*models.DeploymentRecord, error) {
|
|
// Ensure the target version exists in history for that service.
|
|
var prior models.DeploymentRecord
|
|
err := s.db.DB.WithContext(ctx).
|
|
Where("service = ? AND version = ?", in.Service, in.ToVersion).
|
|
Order("created_at DESC").
|
|
First(&prior).Error
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, fmt.Errorf("no prior deployment of %s at version %s", in.Service, in.ToVersion)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
now := time.Now()
|
|
d := models.DeploymentRecord{
|
|
Service: in.Service,
|
|
Version: in.ToVersion,
|
|
Environment: prior.Environment,
|
|
Strategy: "rolling",
|
|
Replicas: prior.Replicas,
|
|
TriggeredBy: defaultStr(in.TriggeredBy, "api"),
|
|
Reason: in.Reason,
|
|
Status: "rolled_back",
|
|
StartedAt: &now,
|
|
RollbackTo: in.ToVersion,
|
|
}
|
|
if err := s.db.DB.WithContext(ctx).Create(&d).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &d, nil
|
|
}
|
|
|
|
// MarkCompleted updates deployment status; intended to be called by the
|
|
// orchestration layer (controller) once the real rollout finishes.
|
|
func (s *DeploymentService) MarkCompleted(ctx context.Context, id uint, success bool) (*models.DeploymentRecord, error) {
|
|
d, err := s.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
now := time.Now()
|
|
d.CompletedAt = &now
|
|
if success {
|
|
d.Status = "completed"
|
|
} else {
|
|
d.Status = "failed"
|
|
}
|
|
if err := s.db.DB.WithContext(ctx).Save(d).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return d, nil
|
|
}
|