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  }