gitea源码

metadata.go 5.4KB

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