github.com/docker/cnab-to-oci@v0.3.0-beta4/converter/types.go (about) 1 package converter 2 3 import ( 4 "github.com/cnabio/cnab-go/bundle" 5 "github.com/docker/distribution" 6 "github.com/docker/distribution/manifest/schema2" 7 "github.com/docker/go/canonical/json" 8 digest "github.com/opencontainers/go-digest" 9 ocischema "github.com/opencontainers/image-spec/specs-go" 10 ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" 11 ) 12 13 const ( 14 // CNABConfigMediaType is the config media type of the CNAB config image manifest 15 CNABConfigMediaType = "application/vnd.cnab.config.v1+json" 16 ) 17 18 // PreparedBundleConfig contains the config blob, image manifest (and fallback), and descriptors for a CNAB config 19 type PreparedBundleConfig struct { 20 ConfigBlob []byte 21 ConfigBlobDescriptor ocischemav1.Descriptor 22 Manifest []byte 23 ManifestDescriptor ocischemav1.Descriptor 24 Fallback *PreparedBundleConfig 25 } 26 27 // PrepareForPush serializes a bundle config, generates its image manifest, and its manifest descriptor 28 func PrepareForPush(b *bundle.Bundle) (*PreparedBundleConfig, error) { 29 blob, err := json.MarshalCanonical(b) 30 if err != nil { 31 return nil, err 32 } 33 fallbackChain := []bundleConfigPreparer{ 34 prepareOCIBundleConfig(CNABConfigMediaType), 35 prepareOCIBundleConfig(ocischemav1.MediaTypeImageConfig), 36 prepareNonOCIBundleConfig, 37 } 38 var first, current *PreparedBundleConfig 39 for _, preparer := range fallbackChain { 40 cfg, err := preparer(blob) 41 if err != nil { 42 return nil, err 43 } 44 if current == nil { 45 first = cfg 46 } else { 47 current.Fallback = cfg 48 } 49 current = cfg 50 } 51 return first, nil 52 } 53 54 func descriptorOf(payload []byte, mediaType string) ocischemav1.Descriptor { 55 return ocischemav1.Descriptor{ 56 MediaType: mediaType, 57 Digest: digest.FromBytes(payload), 58 Size: int64(len(payload)), 59 } 60 } 61 62 type bundleConfigPreparer func(blob []byte) (*PreparedBundleConfig, error) 63 64 func prepareOCIBundleConfig(mediaType string) bundleConfigPreparer { 65 return func(blob []byte) (*PreparedBundleConfig, error) { 66 manifest := ocischemav1.Manifest{ 67 Versioned: ocischema.Versioned{ 68 SchemaVersion: OCIIndexSchemaVersion, 69 }, 70 Config: descriptorOf(blob, mediaType), 71 } 72 manifestBytes, err := json.Marshal(&manifest) 73 if err != nil { 74 return nil, err 75 } 76 return &PreparedBundleConfig{ 77 ConfigBlob: blob, 78 ConfigBlobDescriptor: manifest.Config, 79 Manifest: manifestBytes, 80 ManifestDescriptor: descriptorOf(manifestBytes, ocischemav1.MediaTypeImageManifest), 81 }, nil 82 } 83 } 84 85 func nonOCIDescriptorOf(blob []byte) distribution.Descriptor { 86 return distribution.Descriptor{ 87 MediaType: schema2.MediaTypeImageConfig, 88 Size: int64(len(blob)), 89 Digest: digest.FromBytes(blob), 90 } 91 } 92 93 func prepareNonOCIBundleConfig(blob []byte) (*PreparedBundleConfig, error) { 94 desc := nonOCIDescriptorOf(blob) 95 man, err := schema2.FromStruct(schema2.Manifest{ 96 Versioned: schema2.SchemaVersion, 97 // Add a descriptor for the configuration because some registries 98 // require the layers property to be defined and non-empty 99 Layers: []distribution.Descriptor{ 100 desc, 101 }, 102 Config: desc, 103 }) 104 if err != nil { 105 return nil, err 106 } 107 manBytes, err := man.MarshalJSON() 108 if err != nil { 109 return nil, err 110 } 111 return &PreparedBundleConfig{ 112 ConfigBlob: blob, 113 ConfigBlobDescriptor: descriptorOf(blob, schema2.MediaTypeImageConfig), 114 Manifest: manBytes, 115 ManifestDescriptor: descriptorOf(manBytes, schema2.MediaTypeManifest), 116 }, nil 117 }