mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-12-19 13:46:17 +01:00
Provide fallback in archive manager in case fd is not available
This commit is contained in:
@@ -9,9 +9,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -39,28 +41,47 @@ func parseDate(in string) int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// countJobs counts the total number of jobs in the source archive using external fd command.
|
// parseArchivePath extracts the path from the source config JSON.
|
||||||
// It requires the fd binary to be available in PATH.
|
func parseArchivePath(srcConfig string) (string, error) {
|
||||||
// The srcConfig parameter should be the JSON configuration string containing the archive path.
|
|
||||||
func countJobs(srcConfig string) (int, error) {
|
|
||||||
fdPath, err := exec.LookPath("fd")
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("fd binary not found in PATH: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var config struct {
|
var config struct {
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal([]byte(srcConfig), &config); err != nil {
|
if err := json.Unmarshal([]byte(srcConfig), &config); err != nil {
|
||||||
return 0, fmt.Errorf("failed to parse source config: %w", err)
|
return "", fmt.Errorf("failed to parse source config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Path == "" {
|
if config.Path == "" {
|
||||||
return 0, fmt.Errorf("no path found in source config")
|
return "", fmt.Errorf("no path found in source config")
|
||||||
}
|
}
|
||||||
|
|
||||||
fdCmd := exec.Command(fdPath, "meta.json", config.Path)
|
return config.Path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countJobsNative counts jobs using native Go filepath.WalkDir.
|
||||||
|
// This is used as a fallback when fd/fdfind is not available.
|
||||||
|
func countJobsNative(archivePath string) (int, error) {
|
||||||
|
count := 0
|
||||||
|
err := filepath.WalkDir(archivePath, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return nil // Skip directories we can't access
|
||||||
|
}
|
||||||
|
if !d.IsDir() && d.Name() == "meta.json" {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to walk directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countJobsWithFd counts jobs using the external fd command.
|
||||||
|
func countJobsWithFd(fdPath, archivePath string) (int, error) {
|
||||||
|
fdCmd := exec.Command(fdPath, "meta.json", archivePath)
|
||||||
wcCmd := exec.Command("wc", "-l")
|
wcCmd := exec.Command("wc", "-l")
|
||||||
|
|
||||||
pipe, err := fdCmd.StdoutPipe()
|
pipe, err := fdCmd.StdoutPipe()
|
||||||
@@ -91,6 +112,31 @@ func countJobs(srcConfig string) (int, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// countJobs counts the total number of jobs in the source archive.
|
||||||
|
// It tries to use external fd/fdfind command for speed, falling back to
|
||||||
|
// native Go filepath.WalkDir if neither is available.
|
||||||
|
// The srcConfig parameter should be the JSON configuration string containing the archive path.
|
||||||
|
func countJobs(srcConfig string) (int, error) {
|
||||||
|
archivePath, err := parseArchivePath(srcConfig)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try fd first (common name)
|
||||||
|
if fdPath, err := exec.LookPath("fd"); err == nil {
|
||||||
|
return countJobsWithFd(fdPath, archivePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try fdfind (Debian/Ubuntu package name)
|
||||||
|
if fdPath, err := exec.LookPath("fdfind"); err == nil {
|
||||||
|
return countJobsWithFd(fdPath, archivePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to native Go implementation
|
||||||
|
cclog.Debug("fd/fdfind not found, using native Go file walker")
|
||||||
|
return countJobsNative(archivePath)
|
||||||
|
}
|
||||||
|
|
||||||
// formatDuration formats a duration as a human-readable string.
|
// formatDuration formats a duration as a human-readable string.
|
||||||
func formatDuration(d time.Duration) string {
|
func formatDuration(d time.Duration) string {
|
||||||
if d < time.Minute {
|
if d < time.Minute {
|
||||||
|
|||||||
Reference in New Issue
Block a user