/
layermodifier.go
139 lines (115 loc) · 3.34 KB
/
layermodifier.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package layermodifier
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"github.com/google/go-containerregistry/pkg/v1/tarball"
digest "github.com/opencontainers/go-digest"
oci "github.com/opencontainers/image-spec/specs-go/v1"
)
//go:generate counterfeiter -o fakes/oci_directory.go --fake-name OCIDirectory . OCIDirectory
type OCIDirectory interface {
AddBlob(srcPath string, blobDescriptor oci.Descriptor) error
RemoveTopBlob(sha256 string) error
ClearMetadata() error
ReadMetadata() (oci.Manifest, oci.Image, error)
WriteMetadata(layers []oci.Descriptor, diffIds []digest.Digest, layerAdded bool) error
}
type LayerModifier struct {
ociDirectory OCIDirectory
}
func New(ociDirectory OCIDirectory) *LayerModifier {
return &LayerModifier{
ociDirectory: ociDirectory,
}
}
func (l *LayerModifier) AddLayer(layerTgzPath string) error {
descriptor, diffId, err := l.getLayerDescriptor(layerTgzPath)
if err != nil {
return err
}
if err := l.ociDirectory.AddBlob(layerTgzPath, descriptor); err != nil {
return err
}
manifest, config, err := l.ociDirectory.ReadMetadata()
if err != nil {
return err
}
if err := l.ociDirectory.ClearMetadata(); err != nil {
return err
}
newLayers := append(manifest.Layers, descriptor)
newDiffIDs := append(config.RootFS.DiffIDs, diffId)
layerAdded := true
return l.ociDirectory.WriteMetadata(newLayers, newDiffIDs, layerAdded)
}
func (l *LayerModifier) RemoveHydratorLayer() error {
manifest, config, err := l.ociDirectory.ReadMetadata()
if err != nil {
return err
}
if _, ok := manifest.Annotations["hydrator.layerAdded"]; !ok {
return nil
}
if err := l.ociDirectory.ClearMetadata(); err != nil {
return err
}
lastLayer := manifest.Layers[len(manifest.Layers)-1]
layerDigest := (strings.Split(string(lastLayer.Digest), ":"))[1] //lastLayer.Digest = "sha256:LAYER_SHA"
if err := l.ociDirectory.RemoveTopBlob(layerDigest); err != nil {
return err
}
newLayers := manifest.Layers[:len(manifest.Layers)-1]
newDiffIDs := config.RootFS.DiffIDs[:len(config.RootFS.DiffIDs)-1]
layerAdded := false
return l.ociDirectory.WriteMetadata(newLayers, newDiffIDs, layerAdded)
}
func (l *LayerModifier) getLayerDescriptor(layerTgzPath string) (oci.Descriptor, digest.Digest, error) {
layerfd, err := os.Open(layerTgzPath)
if err != nil {
return oci.Descriptor{}, "", err
}
defer layerfd.Close()
compressed, err := isGzipped(layerfd)
if err != nil {
return oci.Descriptor{}, "", err
}
if !compressed {
return oci.Descriptor{}, "", fmt.Errorf("invalid layer %s: not gzipped", layerTgzPath)
}
layer, err := tarball.LayerFromFile(layerTgzPath)
if err != nil {
return oci.Descriptor{}, "", err
}
layerDigest, err := layer.Digest()
if err != nil {
return oci.Descriptor{}, "", err
}
size, err := layer.Size()
if err != nil {
return oci.Descriptor{}, "", err
}
diffID, err := layer.DiffID()
if err != nil {
return oci.Descriptor{}, "", err
}
return oci.Descriptor{
Digest: digest.Digest(layerDigest.String()),
MediaType: oci.MediaTypeImageLayerGzip,
Size: size,
}, digest.Digest(diffID.String()), nil
}
var gzipHeader = []byte{'\x1f', '\x8b'}
func isGzipped(file *os.File) (bool, error) {
header := make([]byte, 2)
n, err := file.Read(header)
if n == 0 && err == io.EOF {
return false, nil
}
if err != nil {
return false, err
}
return bytes.Equal(header, gzipHeader), nil
}