package catalog import ( "context" "fmt" "io" "net/http" "os" "sigs.k8s.io/yaml" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1" ) const ( DefaultCachePath = "/var/lib/monok8s/catalog.yaml" ) // ResolveCatalog resolves catalog using priority: // Inline > ConfigMap > URL > cached func ResolveCatalog(ctx context.Context, kubeClient kubernetes.Interface, namespace string, src *monov1alpha1.VersionCatalogSource, ) (*VersionCatalog, error) { // Inline if src != nil && src.Inline != "" { return parseCatalog([]byte(src.Inline)) } // ConfigMap if src != nil && src.ConfigMap != "" { cm, err := kubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, src.ConfigMap, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("get catalog configmap: %w", err) } data, ok := cm.Data["catalog.yaml"] if !ok { return nil, fmt.Errorf("configmap %s missing key catalog.yaml", src.ConfigMap) } return parseCatalog([]byte(data)) } // URL if src != nil && src.URL != "" { cat, err := fetchCatalog(src.URL) if err == nil { _ = os.WriteFile(DefaultCachePath, mustMarshal(cat), 0644) return cat, nil } // fallback to cache if cached, err2 := loadCached(); err2 == nil { return cached, nil } return nil, fmt.Errorf("fetch catalog failed and no cache: %w", err) } // fallback cache if cached, err := loadCached(); err == nil { return cached, nil } return nil, fmt.Errorf("no catalog source available") } func fetchCatalog(url string) (*VersionCatalog, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("http %d", resp.StatusCode) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return parseCatalog(body) } func loadCached() (*VersionCatalog, error) { data, err := os.ReadFile(DefaultCachePath) if err != nil { return nil, err } return parseCatalog(data) } func parseCatalog(data []byte) (*VersionCatalog, error) { var c VersionCatalog if err := yaml.Unmarshal(data, &c); err != nil { return nil, fmt.Errorf("parse catalog: %w", err) } return &c, nil } func mustMarshal(c *VersionCatalog) []byte { b, _ := yaml.Marshal(c) return b }