manifestlist.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package manifestlist
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/docker/distribution"
  7. "github.com/docker/distribution/digest"
  8. "github.com/docker/distribution/manifest"
  9. )
  10. // MediaTypeManifestList specifies the mediaType for manifest lists.
  11. const MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
  12. // SchemaVersion provides a pre-initialized version structure for this
  13. // packages version of the manifest.
  14. var SchemaVersion = manifest.Versioned{
  15. SchemaVersion: 2,
  16. MediaType: MediaTypeManifestList,
  17. }
  18. func init() {
  19. manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
  20. m := new(DeserializedManifestList)
  21. err := m.UnmarshalJSON(b)
  22. if err != nil {
  23. return nil, distribution.Descriptor{}, err
  24. }
  25. dgst := digest.FromBytes(b)
  26. return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
  27. }
  28. err := distribution.RegisterManifestSchema(MediaTypeManifestList, manifestListFunc)
  29. if err != nil {
  30. panic(fmt.Sprintf("Unable to register manifest: %s", err))
  31. }
  32. }
  33. // PlatformSpec specifies a platform where a particular image manifest is
  34. // applicable.
  35. type PlatformSpec struct {
  36. // Architecture field specifies the CPU architecture, for example
  37. // `amd64` or `ppc64`.
  38. Architecture string `json:"architecture"`
  39. // OS specifies the operating system, for example `linux` or `windows`.
  40. OS string `json:"os"`
  41. // OSVersion is an optional field specifying the operating system
  42. // version, for example `10.0.10586`.
  43. OSVersion string `json:"os.version,omitempty"`
  44. // OSFeatures is an optional field specifying an array of strings,
  45. // each listing a required OS feature (for example on Windows `win32k`).
  46. OSFeatures []string `json:"os.features,omitempty"`
  47. // Variant is an optional field specifying a variant of the CPU, for
  48. // example `ppc64le` to specify a little-endian version of a PowerPC CPU.
  49. Variant string `json:"variant,omitempty"`
  50. // Features is an optional field specifying an array of strings, each
  51. // listing a required CPU feature (for example `sse4` or `aes`).
  52. Features []string `json:"features,omitempty"`
  53. }
  54. // A ManifestDescriptor references a platform-specific manifest.
  55. type ManifestDescriptor struct {
  56. distribution.Descriptor
  57. // Platform specifies which platform the manifest pointed to by the
  58. // descriptor runs on.
  59. Platform PlatformSpec `json:"platform"`
  60. }
  61. // ManifestList references manifests for various platforms.
  62. type ManifestList struct {
  63. manifest.Versioned
  64. // Config references the image configuration as a blob.
  65. Manifests []ManifestDescriptor `json:"manifests"`
  66. }
  67. // References returnes the distribution descriptors for the referenced image
  68. // manifests.
  69. func (m ManifestList) References() []distribution.Descriptor {
  70. dependencies := make([]distribution.Descriptor, len(m.Manifests))
  71. for i := range m.Manifests {
  72. dependencies[i] = m.Manifests[i].Descriptor
  73. }
  74. return dependencies
  75. }
  76. // DeserializedManifestList wraps ManifestList with a copy of the original
  77. // JSON.
  78. type DeserializedManifestList struct {
  79. ManifestList
  80. // canonical is the canonical byte representation of the Manifest.
  81. canonical []byte
  82. }
  83. // FromDescriptors takes a slice of descriptors, and returns a
  84. // DeserializedManifestList which contains the resulting manifest list
  85. // and its JSON representation.
  86. func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
  87. m := ManifestList{
  88. Versioned: SchemaVersion,
  89. }
  90. m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors))
  91. copy(m.Manifests, descriptors)
  92. deserialized := DeserializedManifestList{
  93. ManifestList: m,
  94. }
  95. var err error
  96. deserialized.canonical, err = json.MarshalIndent(&m, "", " ")
  97. return &deserialized, err
  98. }
  99. // UnmarshalJSON populates a new ManifestList struct from JSON data.
  100. func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error {
  101. m.canonical = make([]byte, len(b), len(b))
  102. // store manifest list in canonical
  103. copy(m.canonical, b)
  104. // Unmarshal canonical JSON into ManifestList object
  105. var manifestList ManifestList
  106. if err := json.Unmarshal(m.canonical, &manifestList); err != nil {
  107. return err
  108. }
  109. m.ManifestList = manifestList
  110. return nil
  111. }
  112. // MarshalJSON returns the contents of canonical. If canonical is empty,
  113. // marshals the inner contents.
  114. func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) {
  115. if len(m.canonical) > 0 {
  116. return m.canonical, nil
  117. }
  118. return nil, errors.New("JSON representation not initialized in DeserializedManifestList")
  119. }
  120. // Payload returns the raw content of the manifest list. The contents can be
  121. // used to calculate the content identifier.
  122. func (m DeserializedManifestList) Payload() (string, []byte, error) {
  123. return m.MediaType, m.canonical, nil
  124. }