internal/helm/repos.go
Rama main · réplica de lectura del repositorio original.
package helm
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sigs.k8s.io/yaml"
"github.com/sophotechlabs/spinoza/internal/charts"
"github.com/sophotechlabs/spinoza/internal/filetx"
)
const (
repoConfigEnv = "HELM_REPOSITORY_CONFIG"
repoCacheEnv = "HELM_REPOSITORY_CACHE"
maxRepositoryConfigSize = 8 << 20
)
type repoFile struct {
Repositories []struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"repositories"`
}
func RepositoryConfig() string {
fromEnv := os.Getenv(repoConfigEnv)
if fromEnv != "" {
return fromEnv
}
dir := configHome()
if dir == "" {
return ""
}
return filepath.Join(dir, "helm", "repositories.yaml")
}
func RepositoryCache() string {
fromEnv := os.Getenv(repoCacheEnv)
if fromEnv != "" {
return fromEnv
}
dir, err := os.UserCacheDir()
if err != nil {
return ""
}
return filepath.Join(dir, "helm", "repository")
}
func configHome() string {
fromXDG := os.Getenv("XDG_CONFIG_HOME")
if fromXDG != "" {
return fromXDG
}
home, err := os.UserHomeDir()
if err != nil {
return ""
}
if runtime.GOOS == "darwin" {
return filepath.Join(home, "Library", "Preferences")
}
return filepath.Join(home, ".config")
}
type RepoEntry struct {
Name string
Repo charts.Repo
}
func (s *Service) admitsRepository(raw string, oci bool) error {
err := charts.CheckRepoURL(raw)
if err != nil {
return err
}
for _, entry := range s.repos {
configured := strings.TrimSuffix(entry.Repo.URL, "/")
requested := strings.TrimSuffix(raw, "/")
if configured != requested {
continue
}
if entry.Repo.OCI != oci {
return fmt.Errorf("repository %q does not match the requested protocol", raw)
}
return nil
}
return fmt.Errorf("repository %q is not configured", raw)
}
func Repositories(path string) []RepoEntry {
if path == "" {
return nil
}
body, err := filetx.Read(path, maxRepositoryConfigSize)
if err != nil {
return nil
}
var parsed repoFile
unmarshalErr := yaml.Unmarshal(body, &parsed)
if unmarshalErr != nil {
return nil
}
out := make([]RepoEntry, 0, len(parsed.Repositories))
for _, entry := range parsed.Repositories {
if entry.URL == "" {
continue
}
repo := charts.Repo{URL: entry.URL, OCI: strings.HasPrefix(entry.URL, "oci://")}
out = append(out, RepoEntry{Name: entry.Name, Repo: repo})
}
return out
}
func SeedRepositoryCache(index *charts.Cache, repos []RepoEntry, dir string) error {
if index == nil || dir == "" {
return nil
}
failures := []error{}
for _, entry := range repos {
if entry.Name == "" || entry.Repo.OCI {
continue
}
if filepath.Base(entry.Name) != entry.Name {
failures = append(failures, fmt.Errorf("repository name %q is not a cache filename", entry.Name))
continue
}
path := filepath.Join(dir, entry.Name+"-index.yaml")
err := seedRepository(index, entry.Repo, path)
if err != nil && !errors.Is(err, os.ErrNotExist) {
failures = append(failures, err)
}
}
return errors.Join(failures...)
}
func seedRepository(index *charts.Cache, repo charts.Repo, path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer func() { _ = file.Close() }()
info, err := file.Stat()
if err != nil {
return fmt.Errorf("stat repository cache %s: %w", path, err)
}
seedErr := index.Seed(repo, file, info.ModTime())
if seedErr != nil {
return fmt.Errorf("read repository cache %s: %w", path, seedErr)
}
return nil
}