manifest.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package schema1
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/docker/distribution"
  6. "github.com/docker/distribution/digest"
  7. "github.com/docker/distribution/manifest"
  8. "github.com/docker/libtrust"
  9. )
  10. const (
  11. // MediaTypeManifest specifies the mediaType for the current version. Note
  12. // that for schema version 1, the the media is optionally "application/json".
  13. MediaTypeManifest = "application/vnd.docker.distribution.manifest.v1+json"
  14. // MediaTypeSignedManifest specifies the mediatype for current SignedManifest version
  15. MediaTypeSignedManifest = "application/vnd.docker.distribution.manifest.v1+prettyjws"
  16. // MediaTypeManifestLayer specifies the media type for manifest layers
  17. MediaTypeManifestLayer = "application/vnd.docker.container.image.rootfs.diff+x-gtar"
  18. )
  19. var (
  20. // SchemaVersion provides a pre-initialized version structure for this
  21. // packages version of the manifest.
  22. SchemaVersion = manifest.Versioned{
  23. SchemaVersion: 1,
  24. }
  25. )
  26. func init() {
  27. schema1Func := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
  28. sm := new(SignedManifest)
  29. err := sm.UnmarshalJSON(b)
  30. if err != nil {
  31. return nil, distribution.Descriptor{}, err
  32. }
  33. desc := distribution.Descriptor{
  34. Digest: digest.FromBytes(sm.Canonical),
  35. Size: int64(len(sm.Canonical)),
  36. MediaType: MediaTypeSignedManifest,
  37. }
  38. return sm, desc, err
  39. }
  40. err := distribution.RegisterManifestSchema(MediaTypeSignedManifest, schema1Func)
  41. if err != nil {
  42. panic(fmt.Sprintf("Unable to register manifest: %s", err))
  43. }
  44. err = distribution.RegisterManifestSchema("", schema1Func)
  45. if err != nil {
  46. panic(fmt.Sprintf("Unable to register manifest: %s", err))
  47. }
  48. err = distribution.RegisterManifestSchema("application/json", schema1Func)
  49. if err != nil {
  50. panic(fmt.Sprintf("Unable to register manifest: %s", err))
  51. }
  52. }
  53. // FSLayer is a container struct for BlobSums defined in an image manifest
  54. type FSLayer struct {
  55. // BlobSum is the tarsum of the referenced filesystem image layer
  56. BlobSum digest.Digest `json:"blobSum"`
  57. }
  58. // History stores unstructured v1 compatibility information
  59. type History struct {
  60. // V1Compatibility is the raw v1 compatibility information
  61. V1Compatibility string `json:"v1Compatibility"`
  62. }
  63. // Manifest provides the base accessible fields for working with V2 image
  64. // format in the registry.
  65. type Manifest struct {
  66. manifest.Versioned
  67. // Name is the name of the image's repository
  68. Name string `json:"name"`
  69. // Tag is the tag of the image specified by this manifest
  70. Tag string `json:"tag"`
  71. // Architecture is the host architecture on which this image is intended to
  72. // run
  73. Architecture string `json:"architecture"`
  74. // FSLayers is a list of filesystem layer blobSums contained in this image
  75. FSLayers []FSLayer `json:"fsLayers"`
  76. // History is a list of unstructured historical data for v1 compatibility
  77. History []History `json:"history"`
  78. }
  79. // SignedManifest provides an envelope for a signed image manifest, including
  80. // the format sensitive raw bytes.
  81. type SignedManifest struct {
  82. Manifest
  83. // Canonical is the canonical byte representation of the ImageManifest,
  84. // without any attached signatures. The manifest byte
  85. // representation cannot change or it will have to be re-signed.
  86. Canonical []byte `json:"-"`
  87. // all contains the byte representation of the Manifest including signatures
  88. // and is returned by Payload()
  89. all []byte
  90. }
  91. // UnmarshalJSON populates a new SignedManifest struct from JSON data.
  92. func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
  93. sm.all = make([]byte, len(b), len(b))
  94. // store manifest and signatures in all
  95. copy(sm.all, b)
  96. jsig, err := libtrust.ParsePrettySignature(b, "signatures")
  97. if err != nil {
  98. return err
  99. }
  100. // Resolve the payload in the manifest.
  101. bytes, err := jsig.Payload()
  102. if err != nil {
  103. return err
  104. }
  105. // sm.Canonical stores the canonical manifest JSON
  106. sm.Canonical = make([]byte, len(bytes), len(bytes))
  107. copy(sm.Canonical, bytes)
  108. // Unmarshal canonical JSON into Manifest object
  109. var manifest Manifest
  110. if err := json.Unmarshal(sm.Canonical, &manifest); err != nil {
  111. return err
  112. }
  113. sm.Manifest = manifest
  114. return nil
  115. }
  116. // References returnes the descriptors of this manifests references
  117. func (sm SignedManifest) References() []distribution.Descriptor {
  118. dependencies := make([]distribution.Descriptor, len(sm.FSLayers))
  119. for i, fsLayer := range sm.FSLayers {
  120. dependencies[i] = distribution.Descriptor{
  121. MediaType: "application/vnd.docker.container.image.rootfs.diff+x-gtar",
  122. Digest: fsLayer.BlobSum,
  123. }
  124. }
  125. return dependencies
  126. }
  127. // MarshalJSON returns the contents of raw. If Raw is nil, marshals the inner
  128. // contents. Applications requiring a marshaled signed manifest should simply
  129. // use Raw directly, since the the content produced by json.Marshal will be
  130. // compacted and will fail signature checks.
  131. func (sm *SignedManifest) MarshalJSON() ([]byte, error) {
  132. if len(sm.all) > 0 {
  133. return sm.all, nil
  134. }
  135. // If the raw data is not available, just dump the inner content.
  136. return json.Marshal(&sm.Manifest)
  137. }
  138. // Payload returns the signed content of the signed manifest.
  139. func (sm SignedManifest) Payload() (string, []byte, error) {
  140. return MediaTypeSignedManifest, sm.all, nil
  141. }
  142. // Signatures returns the signatures as provided by
  143. // (*libtrust.JSONSignature).Signatures. The byte slices are opaque jws
  144. // signatures.
  145. func (sm *SignedManifest) Signatures() ([][]byte, error) {
  146. jsig, err := libtrust.ParsePrettySignature(sm.all, "signatures")
  147. if err != nil {
  148. return nil, err
  149. }
  150. // Resolve the payload in the manifest.
  151. return jsig.Signatures()
  152. }