From 95b2dbac04d789aa26beb28ecae88d39b7216cb0 Mon Sep 17 00:00:00 2001 From: "Othman H. Suseno" Date: Thu, 18 Dec 2025 12:49:10 +0700 Subject: [PATCH] still fixing UI issue --- internal/httpapp/api_handlers.go | 23 +++++++++++----- internal/zfs/service.go | 46 ++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/internal/httpapp/api_handlers.go b/internal/httpapp/api_handlers.go index 5fcdc10..2ac83d8 100644 --- a/internal/httpapp/api_handlers.go +++ b/internal/httpapp/api_handlers.go @@ -77,18 +77,23 @@ func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) { req.Options = make(map[string]string) } + log.Printf("creating pool: name=%s, vdevs=%v, options=%v", req.Name, req.VDEVs, req.Options) + err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options) // CRITICAL: Always check if pool exists, regardless of reported error // ZFS often reports mountpoint errors but pool is still created + // The CreatePool function already does retries, but we double-check here // Wait a brief moment for pool to be fully registered - time.Sleep(200 * time.Millisecond) + time.Sleep(300 * time.Millisecond) pool, getErr := a.zfs.GetPool(req.Name) if getErr == nil { // Pool exists - this is success! if err != nil { - log.Printf("info: pool %s created successfully despite reported error: %v", req.Name, err) + log.Printf("info: pool %s created successfully despite CreatePool reporting error: %v", req.Name, err) + } else { + log.Printf("info: pool %s created successfully", req.Name) } // Set cache-control headers w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") @@ -98,15 +103,21 @@ func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) { return } - // Pool doesn't exist - return the error + // Pool doesn't exist - return the error with detailed context if err != nil { - log.Printf("create pool error: %v", err) - writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) + log.Printf("error: pool %s creation failed - CreatePool error: %v, GetPool error: %v", req.Name, err, getErr) + writeJSON(w, http.StatusInternalServerError, map[string]string{ + "error": err.Error(), + "details": fmt.Sprintf("Pool '%s' was not created. Check logs for zpool command output.", req.Name), + }) return } // No error but pool doesn't exist (shouldn't happen, but handle it) - writeJSON(w, http.StatusCreated, map[string]string{"message": "pool created", "name": req.Name}) + log.Printf("warning: pool %s creation reported no error but pool was not found", req.Name) + writeJSON(w, http.StatusInternalServerError, map[string]string{ + "error": fmt.Sprintf("Pool '%s' creation reported success but pool was not found", req.Name), + }) } func (a *App) handleGetPool(w http.ResponseWriter, r *http.Request) { diff --git a/internal/zfs/service.go b/internal/zfs/service.go index c35bea1..54e3503 100644 --- a/internal/zfs/service.go +++ b/internal/zfs/service.go @@ -226,31 +226,61 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str args = append(args, name) args = append(args, vdevs...) + // Log the command we're about to run for debugging + log.Printf("executing: %s %v", s.zpoolPath, args) + // Create the pool (without mountpoint to avoid mount errors) - _, err := s.execCommand(s.zpoolPath, args...) + createOutput, err := s.execCommand(s.zpoolPath, args...) + + // Log the command output for debugging + if err != nil { + log.Printf("zpool create failed - output: %s, error: %v", createOutput, err) + } else { + log.Printf("zpool create succeeded - output: %s", createOutput) + } // CRITICAL: Always check if pool exists, even if creation reported an error // ZFS often reports mountpoint errors but pool is still created successfully + // Retry checking pool existence up to 3 times with delays poolExists := false - if existingPools, listErr := s.ListPools(); listErr == nil { - for _, pool := range existingPools { - if pool.Name == name { - poolExists = true - break + for i := 0; i < 3; i++ { + if i > 0 { + // Wait before retry (100ms, 200ms, 300ms) + time.Sleep(time.Duration(i*100) * time.Millisecond) + } + + if existingPools, listErr := s.ListPools(); listErr == nil { + for _, pool := range existingPools { + if pool.Name == name { + poolExists = true + log.Printf("pool %s found after %d check(s)", name, i+1) + break + } } } + + if poolExists { + break + } } 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) + } else { + log.Printf("info: pool %s created successfully", name) } // 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 doesn't exist and we have an error - return it with full context + log.Printf("error: pool %s creation failed and pool does not exist: %v", name, err) + return fmt.Errorf("failed to create pool %s: %v", name, err) + } else { + // No error reported but pool doesn't exist - this shouldn't happen + log.Printf("warning: pool %s creation reported no error but pool does not exist", name) + return fmt.Errorf("pool %s creation reported success but pool was not found", name) } // Pool created successfully - now set mountpoint and mount if needed