manifest.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package schema2
  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. const (
  11. // MediaTypeManifest specifies the mediaType for the current version.
  12. MediaTypeManifest = "application/vnd.docker.distribution.manifest.v2+json"
  13. // MediaTypeConfig specifies the mediaType for the image configuration.
  14. MediaTypeConfig = "application/vnd.docker.container.image.v1+json"
  15. // MediaTypeLayer is the mediaType used for layers referenced by the
  16. // manifest.
  17. MediaTypeLayer = "application/vnd.docker.image.rootfs.diff.tar.gzip"
  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: 2,
  24. MediaType: MediaTypeManifest,
  25. }
  26. )
  27. func init() {
  28. schema2Func := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
  29. m := new(DeserializedManifest)
  30. err := m.UnmarshalJSON(b)
  31. if err != nil {
  32. return nil, distribution.Descriptor{}, err
  33. }
  34. dgst := digest.FromBytes(b)
  35. return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifest}, err
  36. }
  37. err := distribution.RegisterManifestSchema(MediaTypeManifest, schema2Func)
  38. if err != nil {
  39. panic(fmt.Sprintf("Unable to register manifest: %s", err))
  40. }
  41. }
  42. // Manifest defines a schema2 manifest.
  43. type Manifest struct {
  44. manifest.Versioned
  45. // Config references the image configuration as a blob.
  46. Config distribution.Descriptor `json:"config"`
  47. // Layers lists descriptors for the layers referenced by the
  48. // configuration.
  49. Layers []distribution.Descriptor `json:"layers"`
  50. }
  51. // References returnes the descriptors of this manifests references.
  52. func (m Manifest) References() []distribution.Descriptor {
  53. return m.Layers
  54. }
  55. // Target returns the target of this signed manifest.
  56. func (m Manifest) Target() distribution.Descriptor {
  57. return m.Config
  58. }
  59. // DeserializedManifest wraps Manifest with a copy of the original JSON.
  60. // It satisfies the distribution.Manifest interface.
  61. type DeserializedManifest struct {
  62. Manifest
  63. // canonical is the canonical byte representation of the Manifest.
  64. canonical []byte
  65. }
  66. // FromStruct takes a Manifest structure, marshals it to JSON, and returns a
  67. // DeserializedManifest which contains the manifest and its JSON representation.
  68. func FromStruct(m Manifest) (*DeserializedManifest, error) {
  69. var deserialized DeserializedManifest
  70. deserialized.Manifest = m
  71. var err error
  72. deserialized.canonical, err = json.MarshalIndent(&m, "", " ")
  73. return &deserialized, err
  74. }
  75. // UnmarshalJSON populates a new Manifest struct from JSON data.
  76. func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
  77. m.canonical = make([]byte, len(b), len(b))
  78. // store manifest in canonical
  79. copy(m.canonical, b)
  80. // Unmarshal canonical JSON into Manifest object
  81. var manifest Manifest
  82. if err := json.Unmarshal(m.canonical, &manifest); err != nil {
  83. return err
  84. }
  85. m.Manifest = manifest
  86. return nil
  87. }
  88. // MarshalJSON returns the contents of canonical. If canonical is empty,
  89. // marshals the inner contents.
  90. func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
  91. if len(m.canonical) > 0 {
  92. return m.canonical, nil
  93. }
  94. return nil, errors.New("JSON representation not initialized in DeserializedManifest")
  95. }
  96. // Payload returns the raw content of the manifest. The contents can be used to
  97. // calculate the content identifier.
  98. func (m DeserializedManifest) Payload() (string, []byte, error) {
  99. return m.MediaType, m.canonical, nil
  100. }