Cleanup, bug fixes, and improvements

This commit changes how projects are handled internally so that projects
can be renamed. This commit also fixes some bugs, and removes redundant
code.
This commit is contained in:
Zoe
2025-04-13 05:37:39 -05:00
parent 79322c4c5e
commit f4bf2ff5a1
17 changed files with 401 additions and 206 deletions

View File

@@ -2,31 +2,60 @@ package server
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"sync"
"github.com/google/uuid"
"github.com/juls0730/flux/pkg"
"go.uber.org/zap"
)
type App struct {
ID int64 `json:"id,omitempty"`
Deployment *Deployment `json:"-"`
Id uuid.UUID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Deployment *Deployment `json:"-"`
DeploymentID int64 `json:"deployment_id,omitempty"`
flux *FluxServer
}
func (flux *FluxServer) GetAppByNameHandler(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("name")
app := flux.appManager.GetAppByName(name)
if app == nil {
http.Error(w, "App not found", http.StatusNotFound)
return
}
var extApp pkg.App
deploymentStatus, err := app.Deployment.Status(r.Context())
if err != nil {
logger.Errorw("Failed to get deployment status", zap.Error(err))
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
extApp.Id = app.Id
extApp.Name = app.Name
extApp.DeploymentID = app.DeploymentID
extApp.DeploymentStatus = deploymentStatus
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(extApp)
}
// Create the initial app row in the database and create and start the deployment. The app is the overarching data
// structure that contains all of the data for a project
func CreateApp(ctx context.Context, imageName string, projectPath string, projectConfig *pkg.ProjectConfig) (*App, error) {
func (flux *FluxServer) CreateApp(ctx context.Context, imageName string, projectPath string, projectConfig *pkg.ProjectConfig, id uuid.UUID) (*App, error) {
app := &App{
Name: projectConfig.Name,
Id: id,
flux: flux,
}
logger.Debugw("Creating deployment", zap.String("name", app.Name))
logger.Debugw("Creating deployment", zap.String("id", app.Id.String()))
deployment, err := CreateDeployment(projectConfig.Port, projectConfig.Url, Flux.db)
deployment, err := flux.CreateDeployment(projectConfig.Port, projectConfig.Url)
app.Deployment = deployment
if err != nil {
logger.Errorw("Failed to create deployment", zap.Error(err))
@@ -34,7 +63,7 @@ func CreateApp(ctx context.Context, imageName string, projectPath string, projec
}
for _, container := range projectConfig.Containers {
c, err := CreateContainer(ctx, &container, projectConfig.Name, false, deployment)
c, err := flux.CreateContainer(ctx, &container, false, deployment, container.Name)
if err != nil {
return nil, fmt.Errorf("failed to create container: %v", err)
}
@@ -42,37 +71,41 @@ func CreateApp(ctx context.Context, imageName string, projectPath string, projec
c.Start(ctx, true)
}
headContainer := &pkg.Container{
Name: projectConfig.Name,
headContainer := pkg.Container{
ImageName: imageName,
Volumes: projectConfig.Volumes,
Environment: projectConfig.Environment,
}
// this call does a lot for us, see it's documentation for more info
_, err = CreateContainer(ctx, headContainer, projectConfig.Name, true, deployment)
_, err = flux.CreateContainer(ctx, &headContainer, true, deployment, projectConfig.Name)
if err != nil {
return nil, fmt.Errorf("failed to create container: %v", err)
}
// create app in the database
err = appInsertStmt.QueryRow(projectConfig.Name, deployment.ID).Scan(&app.ID, &app.Name, &app.DeploymentID)
var appIdBlob []byte
err = appInsertStmt.QueryRow(id[:], projectConfig.Name, deployment.ID).Scan(&appIdBlob, &app.Name, &app.DeploymentID)
if err != nil {
return nil, fmt.Errorf("failed to insert app: %v", err)
}
app.Id, err = uuid.FromBytes(appIdBlob)
if err != nil {
return nil, fmt.Errorf("failed to parse app id: %v", err)
}
err = deployment.Start(ctx)
if err != nil {
return nil, fmt.Errorf("failed to start deployment: %v", err)
}
Flux.appManager.AddApp(app.Name, app)
flux.appManager.AddApp(app.Id, app)
return app, nil
}
func (app *App) Upgrade(ctx context.Context, imageName string, projectPath string, projectConfig *pkg.ProjectConfig) error {
logger.Debugw("Upgrading deployment", zap.String("name", app.Name))
logger.Debugw("Upgrading deployment", zap.String("id", app.Id.String()))
// if deploy is not started, start it
deploymentStatus, err := app.Deployment.Status(ctx)
@@ -87,6 +120,8 @@ func (app *App) Upgrade(ctx context.Context, imageName string, projectPath strin
}
}
app.flux.db.Exec("UPDATE apps SET name = ? WHERE id = ?", projectConfig.Name, app.Id[:])
err = app.Deployment.Upgrade(ctx, projectConfig, imageName, projectPath)
if err != nil {
return fmt.Errorf("failed to upgrade deployment: %v", err)
@@ -97,7 +132,7 @@ func (app *App) Upgrade(ctx context.Context, imageName string, projectPath strin
// delete an app and deployment from the database, and its project files from disk.
func (app *App) Remove(ctx context.Context) error {
Flux.appManager.RemoveApp(app.Name)
app.flux.appManager.RemoveApp(app.Id)
err := app.Deployment.Remove(ctx)
if err != nil {
@@ -105,13 +140,13 @@ func (app *App) Remove(ctx context.Context) error {
return err
}
_, err = Flux.db.Exec("DELETE FROM apps WHERE id = ?", app.ID)
_, err = app.flux.db.Exec("DELETE FROM apps WHERE id = ?", app.Id[:])
if err != nil {
logger.Errorw("Failed to delete app", zap.Error(err))
return err
}
projectPath := filepath.Join(Flux.rootDir, "apps", app.Name)
projectPath := filepath.Join(app.flux.rootDir, "apps", app.Id.String())
err = os.RemoveAll(projectPath)
if err != nil {
return fmt.Errorf("failed to remove project directory: %v", err)
@@ -121,57 +156,71 @@ func (app *App) Remove(ctx context.Context) error {
}
type AppManager struct {
sync.Map
pkg.TypedMap[uuid.UUID, *App]
nameIndex pkg.TypedMap[string, uuid.UUID]
}
func (am *AppManager) GetApp(name string) *App {
app, exists := am.Load(name)
func (am *AppManager) GetAppByName(name string) *App {
id, ok := am.nameIndex.Load(name)
if !ok {
return nil
}
return am.GetApp(id)
}
func (am *AppManager) GetApp(id uuid.UUID) *App {
app, exists := am.Load(id)
if !exists {
return nil
}
return app.(*App)
return app
}
func (am *AppManager) GetAllApps() []*App {
var apps []*App
am.Range(func(key, value interface{}) bool {
if app, ok := value.(*App); ok {
apps = append(apps, app)
}
am.Range(func(key uuid.UUID, app *App) bool {
apps = append(apps, app)
return true
})
return apps
}
// removes an app from the app manager
func (am *AppManager) RemoveApp(name string) {
am.Delete(name)
func (am *AppManager) RemoveApp(id uuid.UUID) {
app, ok := am.Load(id)
if !ok {
return
}
am.nameIndex.Delete(app.Name)
am.Delete(id)
}
// add a given app to the app manager
func (am *AppManager) AddApp(name string, app *App) {
if app.Deployment.Containers == nil || app.Deployment.Head == nil || len(app.Deployment.Containers) == 0 {
panic("nil containers")
func (am *AppManager) AddApp(id uuid.UUID, app *App) {
if app.Deployment.Containers == nil || app.Deployment.Head == nil || len(app.Deployment.Containers) == 0 || app.Name == "" {
panic("invalid app")
}
am.Store(name, app)
am.nameIndex.Store(app.Name, id)
am.Store(id, app)
}
// nukes an app completely
func (am *AppManager) DeleteApp(name string) error {
app := am.GetApp(name)
func (am *AppManager) DeleteApp(id uuid.UUID) error {
app := am.GetApp(id)
if app == nil {
return fmt.Errorf("app not found")
}
// calls RemoveApp
err := app.Remove(context.Background())
if err != nil {
return err
}
am.Delete(name)
return nil
}
@@ -193,10 +242,13 @@ func (am *AppManager) Init() {
var apps []App
for rows.Next() {
var app App
if err := rows.Scan(&app.ID, &app.Name, &app.DeploymentID); err != nil {
var appIdBlob []byte
if err := rows.Scan(&appIdBlob, &app.Name, &app.DeploymentID); err != nil {
logger.Warnw("Failed to scan app", zap.Error(err))
return
}
app.Id = uuid.Must(uuid.FromBytes(appIdBlob))
app.flux = Flux
apps = append(apps, app)
}
@@ -250,7 +302,7 @@ func (am *AppManager) Init() {
deployment.Head = headContainer
app.Deployment = deployment
am.AddApp(app.Name, &app)
am.AddApp(app.Id, &app)
status, err := deployment.Status(context.Background())
if err != nil {