code.gitea.io/gitea@v1.19.3/modules/packages/container/metadata.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package container 5 6 import ( 7 "fmt" 8 "io" 9 "strings" 10 11 "code.gitea.io/gitea/modules/json" 12 "code.gitea.io/gitea/modules/packages/container/helm" 13 "code.gitea.io/gitea/modules/validation" 14 15 oci "github.com/opencontainers/image-spec/specs-go/v1" 16 ) 17 18 const ( 19 PropertyRepository = "container.repository" 20 PropertyDigest = "container.digest" 21 PropertyMediaType = "container.mediatype" 22 PropertyManifestTagged = "container.manifest.tagged" 23 PropertyManifestReference = "container.manifest.reference" 24 25 DefaultPlatform = "linux/amd64" 26 27 labelLicenses = "org.opencontainers.image.licenses" 28 labelURL = "org.opencontainers.image.url" 29 labelSource = "org.opencontainers.image.source" 30 labelDocumentation = "org.opencontainers.image.documentation" 31 labelDescription = "org.opencontainers.image.description" 32 labelAuthors = "org.opencontainers.image.authors" 33 ) 34 35 type ImageType string 36 37 const ( 38 TypeOCI ImageType = "oci" 39 TypeHelm ImageType = "helm" 40 ) 41 42 // Name gets the name of the image type 43 func (it ImageType) Name() string { 44 switch it { 45 case TypeHelm: 46 return "Helm Chart" 47 default: 48 return "OCI / Docker" 49 } 50 } 51 52 // Metadata represents the metadata of a Container package 53 type Metadata struct { 54 Type ImageType `json:"type"` 55 IsTagged bool `json:"is_tagged"` 56 Platform string `json:"platform,omitempty"` 57 Description string `json:"description,omitempty"` 58 Authors []string `json:"authors,omitempty"` 59 Licenses string `json:"license,omitempty"` 60 ProjectURL string `json:"project_url,omitempty"` 61 RepositoryURL string `json:"repository_url,omitempty"` 62 DocumentationURL string `json:"documentation_url,omitempty"` 63 Labels map[string]string `json:"labels,omitempty"` 64 ImageLayers []string `json:"layer_creation,omitempty"` 65 MultiArch map[string]string `json:"multiarch,omitempty"` 66 } 67 68 // ParseImageConfig parses the metadata of an image config 69 func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) { 70 if strings.EqualFold(mt, helm.ConfigMediaType) { 71 return parseHelmConfig(r) 72 } 73 74 // fallback to OCI Image Config 75 return parseOCIImageConfig(r) 76 } 77 78 func parseOCIImageConfig(r io.Reader) (*Metadata, error) { 79 var image oci.Image 80 if err := json.NewDecoder(r).Decode(&image); err != nil { 81 return nil, err 82 } 83 84 platform := DefaultPlatform 85 if image.OS != "" && image.Architecture != "" { 86 platform = fmt.Sprintf("%s/%s", image.OS, image.Architecture) 87 if image.Variant != "" { 88 platform = fmt.Sprintf("%s/%s", platform, image.Variant) 89 } 90 } 91 92 imageLayers := make([]string, 0, len(image.History)) 93 for _, history := range image.History { 94 cmd := history.CreatedBy 95 if i := strings.Index(cmd, "#(nop) "); i != -1 { 96 cmd = strings.TrimSpace(cmd[i+7:]) 97 } 98 if cmd != "" { 99 imageLayers = append(imageLayers, cmd) 100 } 101 } 102 103 metadata := &Metadata{ 104 Type: TypeOCI, 105 Platform: platform, 106 Licenses: image.Config.Labels[labelLicenses], 107 ProjectURL: image.Config.Labels[labelURL], 108 RepositoryURL: image.Config.Labels[labelSource], 109 DocumentationURL: image.Config.Labels[labelDocumentation], 110 Description: image.Config.Labels[labelDescription], 111 Labels: image.Config.Labels, 112 ImageLayers: imageLayers, 113 } 114 115 if authors, ok := image.Config.Labels[labelAuthors]; ok { 116 metadata.Authors = []string{authors} 117 } 118 119 if !validation.IsValidURL(metadata.ProjectURL) { 120 metadata.ProjectURL = "" 121 } 122 if !validation.IsValidURL(metadata.RepositoryURL) { 123 metadata.RepositoryURL = "" 124 } 125 if !validation.IsValidURL(metadata.DocumentationURL) { 126 metadata.DocumentationURL = "" 127 } 128 129 return metadata, nil 130 } 131 132 func parseHelmConfig(r io.Reader) (*Metadata, error) { 133 var config helm.Metadata 134 if err := json.NewDecoder(r).Decode(&config); err != nil { 135 return nil, err 136 } 137 138 metadata := &Metadata{ 139 Type: TypeHelm, 140 Description: config.Description, 141 ProjectURL: config.Home, 142 } 143 144 if len(config.Maintainers) > 0 { 145 authors := make([]string, 0, len(config.Maintainers)) 146 for _, maintainer := range config.Maintainers { 147 authors = append(authors, maintainer.Name) 148 } 149 metadata.Authors = authors 150 } 151 152 if len(config.Sources) > 0 && validation.IsValidURL(config.Sources[0]) { 153 metadata.RepositoryURL = config.Sources[0] 154 } 155 if !validation.IsValidURL(metadata.ProjectURL) { 156 metadata.ProjectURL = "" 157 } 158 159 return metadata, nil 160 }