From 8b5183d98a53b2544e993bfd7a9afbe981547642 Mon Sep 17 00:00:00 2001 From: "Othman H. Suseno" Date: Thu, 18 Dec 2025 12:41:51 +0700 Subject: [PATCH] still fixing UI issue --- internal/httpapp/api_handlers.go | 16 ++++++--- internal/zfs/service.go | 58 ++++++++++++++++++++------------ web/templates/storage.html | 18 +++++----- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/internal/httpapp/api_handlers.go b/internal/httpapp/api_handlers.go index b1125c3..5fcdc10 100644 --- a/internal/httpapp/api_handlers.go +++ b/internal/httpapp/api_handlers.go @@ -7,6 +7,7 @@ import ( "net/http" "strconv" "strings" + "time" "gitea.avt.data-center.id/othman.suseno/atlas/internal/auth" "gitea.avt.data-center.id/othman.suseno/atlas/internal/errors" @@ -78,14 +79,21 @@ func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) { err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options) - // Always check if pool exists, even if creation reported an error - // Sometimes pool is created despite errors (e.g., mountpoint issues) + // CRITICAL: Always check if pool exists, regardless of reported error + // ZFS often reports mountpoint errors but pool is still created + // Wait a brief moment for pool to be fully registered + time.Sleep(200 * time.Millisecond) + pool, getErr := a.zfs.GetPool(req.Name) if getErr == nil { - // Pool exists - return success even if creation reported an error + // Pool exists - this is success! if err != nil { - log.Printf("create pool reported error but pool exists: %v", err) + log.Printf("info: pool %s created successfully despite reported error: %v", req.Name, err) } + // Set cache-control headers + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + w.Header().Set("Pragma", "no-cache") + w.Header().Set("Expires", "0") writeJSON(w, http.StatusCreated, pool) return } diff --git a/internal/zfs/service.go b/internal/zfs/service.go index 7b7d44e..c35bea1 100644 --- a/internal/zfs/service.go +++ b/internal/zfs/service.go @@ -212,7 +212,13 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str options["canmount"] = "noauto" } - // Add options + // IMPORTANT: Don't set mountpoint during pool creation + // ZFS tries to mount immediately during creation, which can fail + // We'll set mountpoint after pool is created + mountpointOption := options["mountpoint"] + delete(options, "mountpoint") // Remove from options temporarily + + // Add remaining options for k, v := range options { args = append(args, "-o", fmt.Sprintf("%s=%s", k, v)) } @@ -220,33 +226,41 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str args = append(args, name) args = append(args, vdevs...) - // Create the pool + // Create the pool (without mountpoint to avoid mount errors) _, err := s.execCommand(s.zpoolPath, args...) - // Even if creation reports an error, check if pool actually exists - // Sometimes ZFS reports errors but pool is still created - if err != nil { - // Check if pool exists despite the error - if existingPools, listErr := s.ListPools(); listErr == nil { - for _, pool := range existingPools { - if pool.Name == name { - // Pool exists! Log the original error but don't fail - log.Printf("warning: pool creation reported error but pool %s exists: %v", name, err) - err = nil // Clear error since pool was created - break - } + // CRITICAL: Always check if pool exists, even if creation reported an error + // ZFS often reports mountpoint errors but pool is still created successfully + poolExists := false + if existingPools, listErr := s.ListPools(); listErr == nil { + for _, pool := range existingPools { + if pool.Name == name { + poolExists = true + break } } - - // If pool doesn't exist and we still have an error, return it - if err != nil { - return err - } } - // Pool created successfully - now try to set mountpoint and mount if needed - if mountpoint != "none" { - // Set mountpoint property (in case it wasn't set during creation) + if poolExists { + // Pool exists! This is success, regardless of any reported errors + if err != nil { + log.Printf("info: pool %s created successfully despite reported error: %v", name, err) + } + // Clear error since pool was created + err = nil + } else if err != nil { + // Pool doesn't exist and we have an error - return it + return err + } + + // Pool created successfully - now set mountpoint and mount if needed + if mountpoint != "none" && mountpointOption != "" && poolExists { + // Ensure mountpoint directory exists + if err := s.createMountpointWithSudo(mountpoint); err != nil { + log.Printf("warning: failed to create mountpoint %s: %v", mountpoint, err) + } + + // Set mountpoint property on the root filesystem of the pool setMountpointArgs := []string{"set", fmt.Sprintf("mountpoint=%s", mountpoint), name} if _, setErr := s.execCommand(s.zfsPath, setMountpointArgs...); setErr != nil { log.Printf("warning: failed to set mountpoint property: %v (pool created but not mounted)", setErr) diff --git a/web/templates/storage.html b/web/templates/storage.html index 54e6514..5370da5 100644 --- a/web/templates/storage.html +++ b/web/templates/storage.html @@ -518,21 +518,19 @@ async function createPool(e) { const data = await res.json().catch(() => null); + // Always refresh pool list after creation attempt, regardless of response + // Wait a moment for pool creation to complete + await new Promise(resolve => setTimeout(resolve, 1000)); + await loadPools(); + if (res.ok) { closeModal('create-pool-modal'); e.target.reset(); - // Wait a moment for pool to be fully created, then refresh - await new Promise(resolve => setTimeout(resolve, 500)); - await loadPools(); alert('Pool created successfully'); } else { - // Even if error, check if pool was actually created - // Sometimes creation reports error but pool exists - await new Promise(resolve => setTimeout(resolve, 1000)); - await loadPools(); - - const err = await res.json().catch(() => ({ error: 'Failed to create pool' })); - alert(`Error: ${err.error || 'Failed to create pool'}\n\nNote: Please check if the pool was created despite the error.`); + // Check if pool appears in the list (might have been created despite error) + const err = data?.error || 'Failed to create pool'; + alert(`Error: ${err}\n\nNote: The pool list has been refreshed. Please check if the pool was created.`); } } catch (err) { // On network error, still try to refresh to see if pool was created