166 lines · 4.6 KB · Go
RawDownload
// Copyright 2022 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package container import (	"fmt"	"io"	"strings" 	"forgejo.org/modules/json"	"forgejo.org/modules/packages/container/helm"	"forgejo.org/modules/validation" 	oci "github.com/opencontainers/image-spec/specs-go/v1") const (	PropertyRepository        = "container.repository"	PropertyDigest            = "container.digest"	PropertyMediaType         = "container.mediatype"	PropertyManifestTagged    = "container.manifest.tagged"	PropertyManifestReference = "container.manifest.reference" 	DefaultPlatform = "linux/amd64" 	labelLicenses      = "org.opencontainers.image.licenses"	labelURL           = "org.opencontainers.image.url"	labelSource        = "org.opencontainers.image.source"	labelDocumentation = "org.opencontainers.image.documentation"	labelDescription   = "org.opencontainers.image.description"	labelAuthors       = "org.opencontainers.image.authors") type ImageType string const (	TypeOCI  ImageType = "oci"	TypeHelm ImageType = "helm") // Name gets the name of the image typefunc (it ImageType) Name() string {	switch it {	case TypeHelm:		return "Helm Chart"	default:		return "OCI / Docker"	}} // Metadata represents the metadata of a Container packagetype Metadata struct {	Type             ImageType         `json:"type"`	IsTagged         bool              `json:"is_tagged"`	Platform         string            `json:"platform,omitempty"`	Description      string            `json:"description,omitempty"`	Authors          []string          `json:"authors,omitempty"`	Licenses         string            `json:"license,omitempty"`	ProjectURL       string            `json:"project_url,omitempty"`	RepositoryURL    string            `json:"repository_url,omitempty"`	DocumentationURL string            `json:"documentation_url,omitempty"`	Labels           map[string]string `json:"labels,omitempty"`	ImageLayers      []string          `json:"layer_creation,omitempty"`	Manifests        []*Manifest       `json:"manifests,omitempty"`} type Manifest struct {	Platform string `json:"platform"`	Digest   string `json:"digest"`	Size     int64  `json:"size"`} // ParseImageConfig parses the metadata of an image configfunc ParseImageConfig(mt string, r io.Reader) (*Metadata, error) {	if strings.EqualFold(mt, helm.ConfigMediaType) {		return parseHelmConfig(r)	} 	// fallback to OCI Image Config	return parseOCIImageConfig(r)} func parseOCIImageConfig(r io.Reader) (*Metadata, error) {	var image oci.Image	if err := json.NewDecoder(r).Decode(&image); err != nil {		return nil, err	} 	platform := DefaultPlatform	if image.OS != "" && image.Architecture != "" {		platform = fmt.Sprintf("%s/%s", image.OS, image.Architecture)		if image.Variant != "" {			platform = fmt.Sprintf("%s/%s", platform, image.Variant)		}	} 	imageLayers := make([]string, 0, len(image.History))	for _, history := range image.History {		cmd := history.CreatedBy		if i := strings.Index(cmd, "#(nop) "); i != -1 {			cmd = strings.TrimSpace(cmd[i+7:])		}		if cmd != "" {			imageLayers = append(imageLayers, cmd)		}	} 	metadata := &Metadata{		Type:             TypeOCI,		Platform:         platform,		Licenses:         image.Config.Labels[labelLicenses],		ProjectURL:       image.Config.Labels[labelURL],		RepositoryURL:    image.Config.Labels[labelSource],		DocumentationURL: image.Config.Labels[labelDocumentation],		Description:      image.Config.Labels[labelDescription],		Labels:           image.Config.Labels,		ImageLayers:      imageLayers,	} 	if authors, ok := image.Config.Labels[labelAuthors]; ok {		metadata.Authors = []string{authors}	} 	if !validation.IsValidURL(metadata.ProjectURL) {		metadata.ProjectURL = ""	}	if !validation.IsValidURL(metadata.RepositoryURL) {		metadata.RepositoryURL = ""	}	if !validation.IsValidURL(metadata.DocumentationURL) {		metadata.DocumentationURL = ""	} 	return metadata, nil} func parseHelmConfig(r io.Reader) (*Metadata, error) {	var config helm.Metadata	if err := json.NewDecoder(r).Decode(&config); err != nil {		return nil, err	} 	metadata := &Metadata{		Type:        TypeHelm,		Description: config.Description,		ProjectURL:  config.Home,	} 	if len(config.Maintainers) > 0 {		authors := make([]string, 0, len(config.Maintainers))		for _, maintainer := range config.Maintainers {			authors = append(authors, maintainer.Name)		}		metadata.Authors = authors	} 	if len(config.Sources) > 0 && validation.IsValidURL(config.Sources[0]) {		metadata.RepositoryURL = config.Sources[0]	}	if !validation.IsValidURL(metadata.ProjectURL) {		metadata.ProjectURL = ""	} 	return metadata, nil}