1 package layer
2
3 import (
4 "fmt"
5 "sort"
6
7 "github.com/fluxcd/pkg/ssa"
8 v1 "github.com/google/go-containerregistry/pkg/v1"
9 "github.com/google/go-containerregistry/pkg/v1/partial"
10 "github.com/google/go-containerregistry/pkg/v1/static"
11
12 wh "edge-infra.dev/pkg/f8n/warehouse"
13 "edge-infra.dev/pkg/k8s/unstructured"
14 )
15
16
17
18 var ErrInvalidLayer = fmt.Errorf("OCI layer is not a valid Warehouse layer")
19
20
21
22
23
24 type Layer interface {
25 v1.Layer
26
27
28
29 Descriptor() (*v1.Descriptor, error)
30
31
32
33
34
35 Key() string
36
37
38 Type() Type
39
40
41
42 Unstructured() ([]*unstructured.Unstructured, error)
43
44
45 Annotations() map[string]string
46 }
47
48
49
50 type layer struct {
51 v1.Layer
52 t Type
53 annos map[string]string
54 }
55
56
57 var _ Layer = &layer{}
58
59
60
61 func New(t Type, contents []byte, opts ...Option) (Layer, error) {
62 if err := t.IsValid(); err != nil {
63 return nil, err
64 }
65 o := makeOptions(opts...)
66 if o.cap != "" && t != Runtime {
67 return nil, fmt.Errorf(
68 "layer.New: ForCapability cannot be used with non-Runtime layer types",
69 )
70 }
71
72 l := &layer{
73 static.NewLayer(contents, YAML),
74 t,
75 map[string]string{wh.AnnotationLayerType: string(t)},
76 }
77
78 for k, v := range o.annos {
79 l.annos[k] = v
80 }
81
82 return l, nil
83 }
84
85
86
87 func (l *layer) Descriptor() (*v1.Descriptor, error) {
88 d, err := l.Digest()
89 if err != nil {
90 return nil, err
91 }
92
93 s, err := l.Size()
94 if err != nil {
95 return nil, err
96 }
97
98 mt, err := l.MediaType()
99 if err != nil {
100 return nil, err
101 }
102
103 if l.annos == nil {
104 l.annos = make(map[string]string)
105 }
106
107 return &v1.Descriptor{
108 Digest: d,
109 Size: s,
110 MediaType: mt,
111 Annotations: l.annos,
112 }, nil
113 }
114
115 func (l *layer) Key() string {
116 if c, ok := l.annos[wh.AnnotationLayerRuntimeCapability]; ok {
117 return c
118 }
119 return string(l.t)
120 }
121
122 func (l *layer) Type() Type {
123 return l.t
124 }
125
126 func (l *layer) Unstructured() ([]*unstructured.Unstructured, error) {
127 r, err := l.Uncompressed()
128 if err != nil {
129 return nil, err
130 }
131 return ssa.ReadObjects(r)
132 }
133
134 func (l *layer) Annotations() map[string]string {
135 return l.annos
136 }
137
138
139
140 type layers []Layer
141
142 var _ sort.Interface = layers{}
143
144 func (l layers) Len() int { return len(l) }
145 func (l layers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
146 func (l layers) Less(i, j int) bool {
147 switch {
148
149 case l[j].Key() == Runtime.String():
150 return false
151 case l[i].Key() == Runtime.String():
152 return true
153
154 case l[i].Key() == Infra.String():
155 return true
156 default:
157 return false
158 }
159 }
160
161
162 func Sort(ll []Layer) {
163 sort.Stable(layers(ll))
164 }
165
166
167
168 func FromImage(img v1.Image) ([]Layer, error) {
169 imgLayers, err := img.Layers()
170 if err != nil {
171 return nil, err
172 }
173
174 ll := make([]Layer, 0, len(imgLayers))
175 for _, il := range imgLayers {
176 l, err := FromOCILayer(il)
177 if err == nil {
178 ll = append(ll, l)
179 }
180 }
181 return ll, nil
182 }
183
184
185
186 func FromOCILayer(l v1.Layer) (Layer, error) {
187 d, err := partial.Descriptor(l)
188 if err != nil {
189 return nil, err
190 }
191
192 if d.Annotations[wh.AnnotationLayerType] == "" {
193 return nil, fmt.Errorf("%w: missing %s annotation",
194 ErrInvalidLayer, wh.AnnotationLayerType)
195 }
196
197 return &layer{
198 Layer: l,
199 t: Type(d.Annotations[wh.AnnotationLayerType]),
200 annos: d.Annotations,
201 }, nil
202 }
203
View as plain text