Browse code

graph: use tar archive entries for TarLayer

if there is a tar-data.json.gz present for an image layer, then use it
to create the tar archive, instead of the traditional graphdriver Diff.

Signed-off-by: Vincent Batts <vbatts@redhat.com>

Conflicts:
graph/graph.go

Vincent Batts authored on 2015/06/24 08:44:49
Showing 3 changed files
... ...
@@ -87,7 +87,7 @@ const (
87 87
 	jsonFileName      = "json"
88 88
 	layersizeFileName = "layersize"
89 89
 	digestFileName    = "checksum"
90
-	tardataFileName   = "tar-data.json.gz"
90
+	tarDataFileName   = "tar-data.json.gz"
91 91
 )
92 92
 
93 93
 var (
... ...
@@ -6,14 +6,18 @@ import (
6 6
 	"compress/gzip"
7 7
 	"encoding/json"
8 8
 	"fmt"
9
+	"io"
9 10
 	"os"
10 11
 	"path/filepath"
11 12
 	"strings"
12 13
 	"syscall"
13 14
 
15
+	"github.com/Sirupsen/logrus"
14 16
 	"github.com/docker/docker/image"
15 17
 	"github.com/docker/docker/pkg/archive"
16 18
 	"github.com/docker/docker/pkg/system"
19
+	"github.com/vbatts/tar-split/tar/asm"
20
+	"github.com/vbatts/tar-split/tar/storage"
17 21
 )
18 22
 
19 23
 // setupInitLayer populates a directory with mountpoints suitable
... ...
@@ -92,14 +96,14 @@ func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader
92 92
 	// Store the layer. If layerData is not nil, unpack it into the new layer
93 93
 	if layerData != nil {
94 94
 		// this is saving the tar-split metadata
95
-		mf, err := os.OpenFile(filepath.Join(root, tardataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
95
+		mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
96 96
 		if err != nil {
97 97
 			return err
98 98
 		}
99 99
 		defer mf.Close()
100 100
 		mfz := gzip.NewWriter(mf)
101 101
 		defer mfz.Close()
102
-		metaPacker := storage.NewJSONPacker(mf)
102
+		metaPacker := storage.NewJSONPacker(mfz)
103 103
 
104 104
 		inflatedLayerData, err := archive.DecompressStream(layerData)
105 105
 		if err != nil {
... ...
@@ -134,6 +138,49 @@ func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader
134 134
 
135 135
 // TarLayer returns a tar archive of the image's filesystem layer.
136 136
 func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error) {
137
-	// TODO(vbatts) let's reassemble!
138
-	return graph.driver.Diff(img.ID, img.Parent)
137
+	root := graph.imageRoot(img.ID)
138
+	mFileName := filepath.Join(root, tarDataFileName)
139
+	mf, err := os.Open(mFileName)
140
+	if err != nil {
141
+		if !os.IsNotExist(err) {
142
+			logrus.Errorf("failed to open %q: %s", mFileName, err)
143
+		}
144
+		logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
145
+		return graph.driver.Diff(img.ID, img.Parent)
146
+	}
147
+	pR, pW := io.Pipe()
148
+	// this will need to be in a goroutine, as we are returning the stream of a
149
+	// tar archive, but can not close the metadata reader early (when this
150
+	// function returns)...
151
+	go func() {
152
+		defer mf.Close()
153
+		// let's reassemble!
154
+		logrus.Debugf("[graph] TarLayer with reassembly: %s", img.ID)
155
+		mfz, err := gzip.NewReader(mf)
156
+		if err != nil {
157
+			pW.CloseWithError(fmt.Errorf("[graph] error with %s:  %s", mFileName, err))
158
+			return
159
+		}
160
+		defer mfz.Close()
161
+
162
+		// get our relative path to the container
163
+		fsLayer, err := graph.driver.Get(img.ID, "")
164
+		if err != nil {
165
+			pW.CloseWithError(err)
166
+			return
167
+		}
168
+		defer graph.driver.Put(img.ID)
169
+
170
+		metaUnpacker := storage.NewJSONUnpacker(mfz)
171
+		fileGetter := storage.NewPathFileGetter(fsLayer)
172
+		logrus.Debugf("[graph] %s is at %q", img.ID, fsLayer)
173
+		ots := asm.NewOutputTarStream(fileGetter, metaUnpacker)
174
+		defer ots.Close()
175
+		if _, err := io.Copy(pW, ots); err != nil {
176
+			pW.CloseWithError(err)
177
+			return
178
+		}
179
+		pW.Close()
180
+	}()
181
+	return pR, nil
139 182
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"compress/gzip"
7 7
 	"encoding/json"
8 8
 	"fmt"
9
+	"io"
9 10
 	"os"
10 11
 	"path/filepath"
11 12
 
... ...
@@ -13,6 +14,8 @@ import (
13 13
 	"github.com/docker/docker/daemon/graphdriver/windows"
14 14
 	"github.com/docker/docker/image"
15 15
 	"github.com/docker/docker/pkg/archive"
16
+	"github.com/vbatts/tar-split/tar/asm"
17
+	"github.com/vbatts/tar-split/tar/storage"
16 18
 )
17 19
 
18 20
 // setupInitLayer populates a directory with mountpoints suitable
... ...
@@ -118,14 +121,14 @@ func (graph *Graph) storeImage(img *image.Image, layerData archive.ArchiveReader
118 118
 		// Store the layer. If layerData is not nil, unpack it into the new layer
119 119
 		if layerData != nil {
120 120
 			// this is saving the tar-split metadata
121
-			mf, err := os.OpenFile(filepath.Join(root, tardataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
121
+			mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
122 122
 			if err != nil {
123 123
 				return err
124 124
 			}
125 125
 			defer mf.Close()
126 126
 			mfz := gzip.NewWriter(mf)
127 127
 			defer mfz.Close()
128
-			metaPacker := storage.NewJSONPacker(mf)
128
+			metaPacker := storage.NewJSONPacker(mfz)
129 129
 
130 130
 			inflatedLayerData, err := archive.DecompressStream(layerData)
131 131
 			if err != nil {
... ...
@@ -180,7 +183,50 @@ func (graph *Graph) TarLayer(img *image.Image) (arch archive.Archive, err error)
180 180
 		// We keep this functionality here so that we can still work with the VFS
181 181
 		// driver during development. VFS is not supported (and just will not work)
182 182
 		// for Windows containers.
183
-		// TODO(vbatts) let's reassemble!
184
-		return graph.driver.Diff(img.ID, img.Parent)
183
+		root := graph.imageRoot(img.ID)
184
+		mFileName := filepath.Join(root, tarDataFileName)
185
+		mf, err := os.Open(mFileName)
186
+		if err != nil {
187
+			if !os.IsNotExist(err) {
188
+				logrus.Errorf("failed to open %q: %s", mFileName, err)
189
+			}
190
+			logrus.Debugf("[graph] TarLayer with traditional differ: %s", img.ID)
191
+			return graph.driver.Diff(img.ID, img.Parent)
192
+		}
193
+		pR, pW := io.Pipe()
194
+		// this will need to be in a goroutine, as we are returning the stream of a
195
+		// tar archive, but can not close the metadata reader early (when this
196
+		// function returns)...
197
+		go func() {
198
+			defer mf.Close()
199
+			// let's reassemble!
200
+			logrus.Debugf("[graph] TarLayer with reassembly: %s", img.ID)
201
+			mfz, err := gzip.NewReader(mf)
202
+			if err != nil {
203
+				pW.CloseWithError(fmt.Errorf("[graph] error with %s:  %s", mFileName, err))
204
+				return
205
+			}
206
+			defer mfz.Close()
207
+
208
+			// get our relative path to the container
209
+			fsLayer, err := graph.driver.Get(img.ID, "")
210
+			if err != nil {
211
+				pW.CloseWithError(err)
212
+				return
213
+			}
214
+			defer graph.driver.Put(img.ID)
215
+
216
+			metaUnpacker := storage.NewJSONUnpacker(mfz)
217
+			fileGetter := storage.NewPathFileGetter(fsLayer)
218
+			logrus.Debugf("[graph] %s is at %q", img.ID, fsLayer)
219
+			ots := asm.NewOutputTarStream(fileGetter, metaUnpacker)
220
+			defer ots.Close()
221
+			if _, err := io.Copy(pW, ots); err != nil {
222
+				pW.CloseWithError(err)
223
+				return
224
+			}
225
+			pW.Close()
226
+		}()
227
+		return pR, nil
185 228
 	}
186 229
 }