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