This commit is contained in:
Kar
2026-02-01 20:22:29 +05:30
commit 52265ed4cc
30 changed files with 2058 additions and 0 deletions

106
internal/worker/deploy.go Normal file
View File

@@ -0,0 +1,106 @@
package worker
import (
"context"
"database/sql"
"deployment-manager/internal/db"
"deployment-manager/internal/events"
"deployment-manager/internal/k8s"
"deployment-manager/internal/model"
)
type DeploymentManager struct {
repoStore *db.RepoStore
kubectl *k8s.KubectlClient
eventChan chan<- *events.Event
}
func NewDeploymentManager(database *sql.DB, eventChan chan<- *events.Event) *DeploymentManager {
return &DeploymentManager{
repoStore: db.NewRepoStore(database),
kubectl: k8s.NewKubectlClient("default"),
eventChan: eventChan,
}
}
func (dm *DeploymentManager) StopDeployment(ctx context.Context, repoID int64) error {
repo, err := dm.repoStore.Get(repoID)
if err != nil {
return err
}
appName := k8s.GetAppName(repo)
// Scale deployment to 0
if err := dm.kubectl.ScaleDeployment(ctx, dm.eventChan, repoID, appName, 0); err != nil {
return err
}
// Update status
repo.Status = model.StatusStopped
if err := dm.repoStore.Update(repo); err != nil {
return err
}
// Send event
dm.eventChan <- events.NewRepoEvent(repo, events.EventTypeRepoUpdated)
return nil
}
func (dm *DeploymentManager) RestartDeployment(ctx context.Context, repoID int64) error {
repo, err := dm.repoStore.Get(repoID)
if err != nil {
return err
}
appName := k8s.GetAppName(repo)
// Update status to restarting
repo.Status = model.StatusRestarting
if err := dm.repoStore.Update(repo); err != nil {
return err
}
// Scale deployment to 1
if err := dm.kubectl.ScaleDeployment(ctx, dm.eventChan, repoID, appName, 1); err != nil {
return err
}
// Update status to deployed
repo.Status = model.StatusDeployed
if err := dm.repoStore.Update(repo); err != nil {
return err
}
// Send event
dm.eventChan <- events.NewRepoEvent(repo, events.EventTypeRepoUpdated)
return nil
}
func (dm *DeploymentManager) DeleteDeployment(ctx context.Context, repoID int64) error {
repo, err := dm.repoStore.Get(repoID)
if err != nil {
return err
}
appName := k8s.GetAppName(repo)
// Delete Kubernetes resources
if err := dm.kubectl.DeleteResources(ctx, dm.eventChan, repoID, appName); err != nil {
return err
}
// Update status to deleted
repo.Status = model.StatusDeleted
if err := dm.repoStore.Update(repo); err != nil {
return err
}
// Send event
dm.eventChan <- events.NewRepoEvent(repo, events.EventTypeRepoDeleted)
return nil
}

151
internal/worker/pool.go Normal file
View File

@@ -0,0 +1,151 @@
package worker
import (
"context"
"log"
"database/sql"
"deployment-manager/internal/db"
"deployment-manager/internal/events"
"deployment-manager/internal/executor"
"deployment-manager/internal/k8s"
"deployment-manager/internal/model"
)
type Worker struct {
id int
db *sql.DB
repoStore *db.RepoStore
eventChan chan<- *events.Event
jobChan <-chan int64
}
func NewWorker(id int, database *sql.DB, eventChan chan<- *events.Event, jobChan <-chan int64) *Worker {
return &Worker{
id: id,
db: database,
repoStore: db.NewRepoStore(database),
eventChan: eventChan,
jobChan: jobChan,
}
}
func (w *Worker) Run(ctx context.Context) {
log.Printf("Worker %d started", w.id)
for {
select {
case <-ctx.Done():
log.Printf("Worker %d shutting down", w.id)
return
case repoID := <-w.jobChan:
if err := w.processJob(ctx, repoID); err != nil {
log.Printf("Worker %d failed to process job %d: %v", w.id, repoID, err)
}
}
}
}
func (w *Worker) processJob(ctx context.Context, repoID int64) error {
log.Printf("Worker %d processing job for repo %d", w.id, repoID)
// Get repo from database
repo, err := w.repoStore.Get(repoID)
if err != nil {
return err
}
// Update status to deploying
repo.Status = model.StatusDeploying
if err := w.repoStore.Update(repo); err != nil {
return err
}
// Send deployment started event
w.eventChan <- events.NewRepoEvent(repo, events.EventTypeDeployStarted)
// Perform deployment
if err := w.deploy(ctx, repo); err != nil {
// Update status to error
repo.Status = model.StatusError
repo.LastError = &[]string{err.Error()}[0]
if updateErr := w.repoStore.Update(repo); updateErr != nil {
log.Printf("Failed to update repo error status: %v", updateErr)
}
// Send error event
w.eventChan <- events.NewRepoEvent(repo, events.EventTypeDeployError)
return err
}
// Update status to deployed
repo.Status = model.StatusDeployed
repo.LastError = nil
if err := w.repoStore.Update(repo); err != nil {
return err
}
// Send success event
w.eventChan <- events.NewRepoEvent(repo, events.EventTypeDeploySuccess)
log.Printf("Worker %d completed deployment for repo %d", w.id, repoID)
return nil
}
func (w *Worker) deploy(ctx context.Context, repo *model.Repo) error {
// Clone repository
workDir, err := w.cloneRepo(ctx, repo)
if err != nil {
return err
}
// Create Dockerfile
if err := executor.CreateDockerfile(workDir, repo.Type); err != nil {
return err
}
// Build and push Docker image
imageName, err := executor.BuildAndPushImage(ctx, w.eventChan, repo, workDir)
if err != nil {
return err
}
// Update repo with image tag
repo.ImageTag = &imageName
if err := w.repoStore.Update(repo); err != nil {
return err
}
// Deploy to Kubernetes
if err := w.deployToK8s(ctx, repo, imageName); err != nil {
return err
}
return nil
}
func (w *Worker) cloneRepo(ctx context.Context, repo *model.Repo) (string, error) {
workDir := "/tmp/repo-" + string(rune(repo.ID))
// Clone the repository
if err := executor.RunCmd(ctx, w.eventChan, repo.ID, "git", "clone", repo.RepoURL, workDir); err != nil {
return "", err
}
return workDir, nil
}
func (w *Worker) deployToK8s(ctx context.Context, repo *model.Repo, imageName string) error {
kubectl := k8s.NewKubectlClient("default")
// Generate manifest
manifest := k8s.GenerateFullManifest(repo, imageName)
// Apply manifest
if err := kubectl.ApplyManifest(ctx, w.eventChan, repo.ID, manifest); err != nil {
return err
}
return nil
}