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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user