updated memory calculation and logo
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -28,18 +29,20 @@ type DeployRequest struct {
|
||||
Values map[string]string `json:"values"` // env_var -> user-provided value
|
||||
}
|
||||
|
||||
// DeployStack handles first-time deployment of an app:
|
||||
// 1. Load metadata (.felhom.yml) to know what fields exist
|
||||
// 2. Auto-generate secrets for secret fields (hidden from user)
|
||||
// 3. Auto-fill domain from controller config
|
||||
// 4. Validate all user-provided values (password, path, required fields)
|
||||
// 5. Save app.yaml
|
||||
// 6. Run docker compose up -d with env vars
|
||||
// 7. Update in-memory stack state
|
||||
func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
// DeployStack handles first-time deployment of an app.
|
||||
// Returns a warning message (empty if none) and an error if deployment is blocked.
|
||||
// 1. Check available memory against app requirements
|
||||
// 2. Load metadata (.felhom.yml) to know what fields exist
|
||||
// 3. Auto-generate secrets for secret fields (hidden from user)
|
||||
// 4. Auto-fill domain from controller config
|
||||
// 5. Validate all user-provided values (password, path, required fields)
|
||||
// 6. Save app.yaml
|
||||
// 7. Run docker compose up -d with env vars
|
||||
// 8. Update in-memory stack state
|
||||
func (m *Manager) DeployStack(req DeployRequest) (string, error) {
|
||||
stack, ok := m.GetStack(req.StackName)
|
||||
if !ok {
|
||||
return fmt.Errorf("stack %q not found", req.StackName)
|
||||
return "", fmt.Errorf("stack %q not found", req.StackName)
|
||||
}
|
||||
|
||||
stackDir := filepath.Dir(stack.ComposePath)
|
||||
@@ -48,7 +51,43 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
// Check if already deployed
|
||||
existing := LoadAppConfig(stackDir)
|
||||
if existing != nil && existing.Deployed {
|
||||
return fmt.Errorf("stack %q is already deployed; use update instead", req.StackName)
|
||||
return "", fmt.Errorf("stack %q is already deployed; use update instead", req.StackName)
|
||||
}
|
||||
|
||||
// --- Memory validation ---
|
||||
var deployWarning string
|
||||
reservedMB := m.cfg.System.ReservedMemoryMB
|
||||
totalMB, memErr := system.GetTotalMemoryMB()
|
||||
if memErr != nil {
|
||||
m.logger.Printf("[WARN] Cannot read system memory: %v — skipping memory check", memErr)
|
||||
} else {
|
||||
usableMB := totalMB - reservedMB
|
||||
currentReqMB, currentLimitMB := m.CommittedMemory()
|
||||
newReqMB := ParseMemoryMB(meta.Resources.MemRequest)
|
||||
newLimitMB := ParseMemoryMB(meta.Resources.MemLimit)
|
||||
|
||||
m.logger.Printf("[INFO] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, committed_req=%dMB, new_req=%dMB, remaining=%dMB",
|
||||
totalMB, reservedMB, usableMB, currentReqMB, newReqMB, usableMB-currentReqMB-newReqMB)
|
||||
|
||||
// Hard block: requests exceed usable memory
|
||||
if newReqMB > 0 && currentReqMB+newReqMB > usableMB {
|
||||
return "", fmt.Errorf(
|
||||
"Nincs elég memória az alkalmazás telepítéséhez. "+
|
||||
"Szükséges: %d MB, Elérhető: %d MB "+
|
||||
"(összesen: %d MB, ebből %d MB már foglalt, %d MB rendszer számára fenntartva)",
|
||||
newReqMB,
|
||||
usableMB-currentReqMB,
|
||||
totalMB,
|
||||
currentReqMB,
|
||||
reservedMB,
|
||||
)
|
||||
}
|
||||
|
||||
// Soft warning: limits exceed total (overcommit)
|
||||
if newLimitMB > 0 && currentLimitMB+newLimitMB > totalMB {
|
||||
deployWarning = "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. " +
|
||||
"Normál használat mellett ez nem okoz problémát."
|
||||
}
|
||||
}
|
||||
|
||||
// Debug: log received values (redact passwords/secrets)
|
||||
@@ -77,7 +116,7 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
// Always auto-generate, user never sees these
|
||||
generated, err := generateValue(field.Generate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating %s: %w", field.EnvVar, err)
|
||||
return "", fmt.Errorf("generating %s: %w", field.EnvVar, err)
|
||||
}
|
||||
value = generated
|
||||
|
||||
@@ -87,7 +126,7 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
if userVal, ok := req.Values[field.EnvVar]; ok && userVal != "" {
|
||||
value = userVal
|
||||
} else {
|
||||
return fmt.Errorf("a(z) %q mező kitöltése kötelező — használja a Generálás gombot vagy írjon be egy jelszót", field.Label)
|
||||
return "", fmt.Errorf("a(z) %q mező kitöltése kötelező — használja a Generálás gombot vagy írjon be egy jelszót", field.Label)
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -101,13 +140,13 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
|
||||
// Validate required fields
|
||||
if field.Required && value == "" {
|
||||
return fmt.Errorf("a(z) %q (%s) mező kitöltése kötelező", field.Label, field.EnvVar)
|
||||
return "", fmt.Errorf("a(z) %q (%s) mező kitöltése kötelező", field.Label, field.EnvVar)
|
||||
}
|
||||
|
||||
// Validate path fields exist on disk (inside the container's filesystem)
|
||||
if field.Type == "path" && value != "" {
|
||||
if _, err := os.Stat(value); os.IsNotExist(err) {
|
||||
return fmt.Errorf("path %q does not exist for field %q", value, field.Label)
|
||||
return "", fmt.Errorf("path %q does not exist for field %q", value, field.Label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +168,7 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
}
|
||||
|
||||
if err := SaveAppConfig(stackDir, appCfg); err != nil {
|
||||
return fmt.Errorf("saving app config: %w", err)
|
||||
return "", fmt.Errorf("saving app config: %w", err)
|
||||
}
|
||||
|
||||
// Debug: log final env var keys (not values)
|
||||
@@ -140,12 +179,12 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
m.logger.Printf("[INFO] Deploying stack %s with %d env vars: [%s]", req.StackName, len(env), strings.Join(envKeys, ", "))
|
||||
|
||||
// Run docker compose up -d
|
||||
_, err := m.composeExecWithEnv(stackDir, env, "up", "-d")
|
||||
if err != nil {
|
||||
_, composeErr := m.composeExecWithEnv(stackDir, env, "up", "-d")
|
||||
if composeErr != nil {
|
||||
// Deployment failed — keep app.yaml for debugging but mark as not deployed
|
||||
appCfg.Deployed = false
|
||||
_ = SaveAppConfig(stackDir, appCfg)
|
||||
return fmt.Errorf("docker compose up failed: %w", err)
|
||||
return "", fmt.Errorf("docker compose up failed: %w", composeErr)
|
||||
}
|
||||
|
||||
// Update in-memory stack state immediately so the UI reflects the deployment
|
||||
@@ -158,7 +197,7 @@ func (m *Manager) DeployStack(req DeployRequest) error {
|
||||
m.mu.Unlock()
|
||||
|
||||
m.logger.Printf("[INFO] Stack %s deployed successfully", req.StackName)
|
||||
return m.RefreshStatus()
|
||||
return deployWarning, m.RefreshStatus()
|
||||
}
|
||||
|
||||
// UpdateStackConfig updates non-locked fields for a deployed stack.
|
||||
|
||||
Reference in New Issue
Block a user