diff --git a/internal/zfs/service.go b/internal/zfs/service.go index 54e3503..c363568 100644 --- a/internal/zfs/service.go +++ b/internal/zfs/service.go @@ -80,6 +80,9 @@ func (s *Service) execCommand(name string, args ...string) (string, error) { err := cmd.Run() if err != nil && useSudo { + // Log that sudo failed + log.Printf("sudo command failed, trying direct execution: %s %v (error: %v, stderr: %s)", name, args, err, stderr.String()) + // If sudo failed, try running the command directly // (user might already have permissions or be root) directCmd := exec.Command(name, args...) @@ -89,13 +92,17 @@ func (s *Service) execCommand(name string, args ...string) (string, error) { if directErr := directCmd.Run(); directErr == nil { // Direct execution succeeded, return that result + log.Printf("direct command execution succeeded (without sudo)") return strings.TrimSpace(directStdout.String()), nil } - // Both sudo and direct failed, return the original sudo error - return "", fmt.Errorf("%s: %v: %s", name, err, stderr.String()) + // Both sudo and direct failed, return detailed error + log.Printf("both sudo and direct execution failed - sudo error: %v, direct error: %v", err, directErr) + log.Printf("sudo stderr: %s, direct stderr: %s", stderr.String(), directStderr.String()) + return "", fmt.Errorf("%s: sudo failed (%v: %s), direct execution also failed (%v: %s)", name, err, stderr.String(), directErr, directStderr.String()) } if err != nil { + log.Printf("command execution failed: %s %v (error: %v, stderr: %s)", name, args, err, stderr.String()) return "", fmt.Errorf("%s: %v: %s", name, err, stderr.String()) } @@ -224,19 +231,41 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str } args = append(args, name) - args = append(args, vdevs...) + + // Normalize vdev paths - ensure they start with /dev/ if they don't already + normalizedVdevs := make([]string, 0, len(vdevs)) + for _, vdev := range vdevs { + vdev = strings.TrimSpace(vdev) + if vdev == "" { + continue + } + // If vdev doesn't start with /dev/ or /, assume it's a device name and add /dev/ + if !strings.HasPrefix(vdev, "/dev/") && !strings.HasPrefix(vdev, "/") { + vdev = "/dev/" + vdev + } + normalizedVdevs = append(normalizedVdevs, vdev) + } + + if len(normalizedVdevs) == 0 { + return fmt.Errorf("no valid vdevs provided after normalization") + } + + args = append(args, normalizedVdevs...) // Log the command we're about to run for debugging - log.Printf("executing: %s %v", s.zpoolPath, args) + // Note: execCommand will use sudo automatically for zpool commands + log.Printf("executing zpool create: %s %v", s.zpoolPath, args) + log.Printf("pool name: %s, original vdevs: %v, normalized vdevs: %v", name, vdevs, normalizedVdevs) // Create the pool (without mountpoint to avoid mount errors) 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) + log.Printf("zpool create command failed - output: %s, error: %v", createOutput, err) + log.Printf("this error might be a false positive - checking if pool was actually created...") } else { - log.Printf("zpool create succeeded - output: %s", createOutput) + log.Printf("zpool create command succeeded - output: %s", createOutput) } // CRITICAL: Always check if pool exists, even if creation reported an error @@ -250,6 +279,7 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str } if existingPools, listErr := s.ListPools(); listErr == nil { + log.Printf("checking pool existence (attempt %d/%d): found %d pools", i+1, 3, len(existingPools)) for _, pool := range existingPools { if pool.Name == name { poolExists = true @@ -257,6 +287,8 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str break } } + } else { + log.Printf("warning: failed to list pools during existence check (attempt %d): %v", i+1, listErr) } if poolExists { @@ -275,12 +307,16 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str err = nil } else if err != nil { // 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) + log.Printf("error: pool %s creation failed and pool does not exist", name) + log.Printf("error details: %v", err) + log.Printf("command that failed: %s %v", s.zpoolPath, args) + log.Printf("command output: %s", createOutput) + return fmt.Errorf("failed to create pool %s: %v (command: %s %v)", name, err, s.zpoolPath, args) } 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) + log.Printf("command output: %s", createOutput) + return fmt.Errorf("pool %s creation reported success but pool was not found (command: %s %v)", name, s.zpoolPath, args) } // Pool created successfully - now set mountpoint and mount if needed