Files
distributor/internal/bundle/manifest.go

111 lines
3.0 KiB
Go

package bundle
import (
"bytes"
"encoding/json"
"fmt"
"io"
"time"
)
const ManifestName = "manifest.json"
type Manifest struct {
SchemaVersion int `json:"schema_version"`
ID string `json:"id"`
Digest string `json:"digest"`
Created time.Time `json:"created"`
Files []ManifestFile `json:"files"`
}
type ManifestFile struct {
Path string `json:"path"`
SHA256 string `json:"sha256"`
Size int64 `json:"size"`
}
type Bundle struct {
RootRelativePath string
Manifest Manifest
}
type rawManifest struct {
SchemaVersion *int `json:"schema_version"`
ID *string `json:"id"`
Digest *string `json:"digest"`
Created *string `json:"created"`
Files []rawManifestFile `json:"files"`
}
type rawManifestFile struct {
Path *string `json:"path"`
SHA256 *string `json:"sha256"`
Size *int64 `json:"size"`
}
func ParseManifest(data []byte) (Manifest, error) {
decoder := json.NewDecoder(bytes.NewReader(data))
var raw rawManifest
if err := decoder.Decode(&raw); err != nil {
return Manifest{}, fmt.Errorf("parse manifest: %w", err)
}
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
return Manifest{}, fmt.Errorf("parse manifest: trailing data")
}
var manifest Manifest
if raw.SchemaVersion == nil {
return Manifest{}, fmt.Errorf("manifest schema_version is required")
}
manifest.SchemaVersion = *raw.SchemaVersion
if raw.ID == nil || *raw.ID == "" {
return Manifest{}, fmt.Errorf("manifest id is required")
}
manifest.ID = *raw.ID
if raw.Digest == nil || *raw.Digest == "" {
return Manifest{}, fmt.Errorf("manifest digest is required")
}
manifest.Digest = *raw.Digest
if raw.Created == nil || *raw.Created == "" {
return Manifest{}, fmt.Errorf("manifest created is required")
}
created, err := time.Parse(time.RFC3339, *raw.Created)
if err != nil {
return Manifest{}, fmt.Errorf("manifest created must be RFC3339: %w", err)
}
manifest.Created = created
if len(raw.Files) == 0 {
return Manifest{}, fmt.Errorf("manifest files is required")
}
for index, rawFile := range raw.Files {
file, err := parseManifestFile(index, rawFile)
if err != nil {
return Manifest{}, err
}
manifest.Files = append(manifest.Files, file)
}
if err := ValidateManifest(manifest); err != nil {
return Manifest{}, fmt.Errorf("manifest %w", err)
}
return manifest, nil
}
func parseManifestFile(index int, raw rawManifestFile) (ManifestFile, error) {
if raw.Path == nil || *raw.Path == "" {
return ManifestFile{}, fmt.Errorf("manifest files[%d].path is required", index)
}
if raw.SHA256 == nil || *raw.SHA256 == "" {
return ManifestFile{}, fmt.Errorf("manifest files[%d].sha256 is required", index)
}
if raw.Size == nil {
return ManifestFile{}, fmt.Errorf("manifest files[%d].size is required", index)
}
return ManifestFile{
Path: *raw.Path,
SHA256: *raw.SHA256,
Size: *raw.Size,
}, nil
}