mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
149 lines
4.2 KiB
Go
149 lines
4.2 KiB
Go
package holdclient
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"atcr.io/pkg/atproto"
|
|
)
|
|
|
|
// HelmChartMeta is the parsed Chart.yaml-equivalent metadata extracted from a
|
|
// helm chart's OCI config blob (media type
|
|
// application/vnd.cncf.helm.config.v1+json). The helm config blob is Chart.yaml
|
|
// rendered as JSON, so the field names mirror Chart.yaml.
|
|
type HelmChartMeta struct {
|
|
Name string
|
|
Version string
|
|
AppVersion string
|
|
Type string // "application" | "library" (empty in older charts; treat as "application")
|
|
Description string
|
|
KubeVersion string
|
|
Home string
|
|
Icon string
|
|
Sources []string
|
|
Keywords []string
|
|
Maintainers []HelmMaintainer
|
|
Dependencies []HelmDependency
|
|
Annotations map[string]string
|
|
Deprecated bool
|
|
}
|
|
|
|
type HelmMaintainer struct {
|
|
Name string
|
|
Email string
|
|
URL string
|
|
}
|
|
|
|
type HelmDependency struct {
|
|
Name string
|
|
Version string
|
|
Repository string
|
|
Alias string
|
|
Condition string
|
|
}
|
|
|
|
// helmConfigJSON matches the on-the-wire shape of a helm OCI config blob.
|
|
// Field names track Chart.yaml's JSON form (lowerCamelCase for some fields,
|
|
// kebab-case for none — helm's CLI marshals Chart.yaml struct directly).
|
|
type helmConfigJSON struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
AppVersion string `json:"appVersion"`
|
|
Type string `json:"type"`
|
|
Description string `json:"description"`
|
|
KubeVersion string `json:"kubeVersion"`
|
|
Home string `json:"home"`
|
|
Icon string `json:"icon"`
|
|
Sources []string `json:"sources"`
|
|
Keywords []string `json:"keywords"`
|
|
Maintainers []helmMaintainerJSON `json:"maintainers"`
|
|
Dependencies []helmDependencyJSON `json:"dependencies"`
|
|
Annotations map[string]string `json:"annotations"`
|
|
Deprecated bool `json:"deprecated"`
|
|
}
|
|
|
|
type helmMaintainerJSON struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
type helmDependencyJSON struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Repository string `json:"repository"`
|
|
Alias string `json:"alias"`
|
|
Condition string `json:"condition"`
|
|
}
|
|
|
|
// FetchHelmChartMeta fetches a helm chart's config blob from the hold and
|
|
// parses it as Chart.yaml metadata. Uses the same getImageConfig XRPC as
|
|
// FetchImageConfig but applies a helm-specific schema to the JSON.
|
|
func FetchHelmChartMeta(ctx context.Context, holdURL, manifestDigest string) (*HelmChartMeta, error) {
|
|
reqURL := fmt.Sprintf("%s%s?digest=%s",
|
|
strings.TrimSuffix(holdURL, "/"),
|
|
atproto.HoldGetImageConfig,
|
|
url.QueryEscape(manifestDigest),
|
|
)
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer cancel()
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build request: %w", err)
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetch helm chart config: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("hold returned status %d for %s", resp.StatusCode, reqURL)
|
|
}
|
|
|
|
var record struct {
|
|
ConfigJSON string `json:"configJson"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&record); err != nil {
|
|
return nil, fmt.Errorf("parse image config response: %w", err)
|
|
}
|
|
|
|
var raw helmConfigJSON
|
|
if err := json.Unmarshal([]byte(record.ConfigJSON), &raw); err != nil {
|
|
return nil, fmt.Errorf("parse helm chart config JSON: %w", err)
|
|
}
|
|
|
|
meta := &HelmChartMeta{
|
|
Name: raw.Name,
|
|
Version: raw.Version,
|
|
AppVersion: raw.AppVersion,
|
|
Type: raw.Type,
|
|
Description: raw.Description,
|
|
KubeVersion: raw.KubeVersion,
|
|
Home: raw.Home,
|
|
Icon: raw.Icon,
|
|
Sources: raw.Sources,
|
|
Keywords: raw.Keywords,
|
|
Annotations: raw.Annotations,
|
|
Deprecated: raw.Deprecated,
|
|
}
|
|
if meta.Type == "" {
|
|
meta.Type = "application"
|
|
}
|
|
for _, m := range raw.Maintainers {
|
|
meta.Maintainers = append(meta.Maintainers, HelmMaintainer(m))
|
|
}
|
|
for _, d := range raw.Dependencies {
|
|
meta.Dependencies = append(meta.Dependencies, HelmDependency(d))
|
|
}
|
|
return meta, nil
|
|
}
|