Skip to content
This repository was archived by the owner on Mar 27, 2024. It is now read-only.

Commit ee6af32

Browse files
authored
Merge pull request #107 from dlorenc/mem
Unpack layers in memory instead of extracting to disk first.
2 parents c7acb1a + 9c7e841 commit ee6af32

File tree

4 files changed

+64
-43
lines changed

4 files changed

+64
-43
lines changed

pkg/util/docker_utils.go

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ limitations under the License.
1717
package util
1818

1919
import (
20+
"archive/tar"
21+
"bytes"
2022
"context"
2123
"encoding/json"
22-
"errors"
2324
"fmt"
25+
"io"
2426
"io/ioutil"
2527
"os"
26-
"path/filepath"
28+
"strings"
2729

2830
"github.com/docker/docker/client"
2931
"github.com/golang/glog"
@@ -49,56 +51,73 @@ func NewClient() (*client.Client, error) {
4951
return cli, nil
5052
}
5153

52-
func getLayersFromManifest(manifestPath string) ([]string, error) {
54+
func getLayersFromManifest(r io.Reader) ([]string, error) {
5355
type Manifest struct {
5456
Layers []string
5557
}
5658

57-
manifestJSON, err := ioutil.ReadFile(manifestPath)
59+
manifestJSON, err := ioutil.ReadAll(r)
5860
if err != nil {
59-
errMsg := fmt.Sprintf("Could not open manifest to get layer order: %s", err)
60-
return []string{}, errors.New(errMsg)
61+
return nil, err
6162
}
6263

6364
var imageManifest []Manifest
64-
err = json.Unmarshal(manifestJSON, &imageManifest)
65-
if err != nil {
66-
errMsg := fmt.Sprintf("Could not unmarshal manifest to get layer order: %s", err)
67-
return []string{}, errors.New(errMsg)
65+
if err := json.Unmarshal(manifestJSON, &imageManifest); err != nil {
66+
return []string{}, fmt.Errorf("Could not unmarshal manifest to get layer order: %s", err)
6867
}
6968
return imageManifest[0].Layers, nil
7069
}
7170

7271
func unpackDockerSave(tarPath string, target string) error {
7372
if _, ok := os.Stat(target); ok != nil {
74-
os.MkdirAll(target, 0777)
73+
os.MkdirAll(target, 0775)
7574
}
76-
77-
tempLayerDir, err := ioutil.TempDir("", ".container-diff")
75+
f, err := os.Open(tarPath)
7876
if err != nil {
7977
return err
8078
}
81-
defer os.RemoveAll(tempLayerDir)
8279

83-
if err := UnTar(tarPath, tempLayerDir); err != nil {
84-
errMsg := fmt.Sprintf("Could not unpack saved Docker image %s: %s", tarPath, err)
85-
return errors.New(errMsg)
86-
}
80+
tr := tar.NewReader(f)
8781

88-
manifest := filepath.Join(tempLayerDir, "manifest.json")
89-
layers, err := getLayersFromManifest(manifest)
90-
if err != nil {
91-
return err
92-
}
82+
// Unpack the layers into a map, since we need to sort out the order later.
83+
var layers []string
84+
layerMap := map[string][]byte{}
85+
for {
86+
hdr, err := tr.Next()
87+
if err == io.EOF {
88+
break
89+
}
90+
if err != nil {
91+
return err
92+
}
9393

94-
for _, layer := range layers {
95-
layerTar := filepath.Join(tempLayerDir, layer)
96-
if _, err := os.Stat(layerTar); err != nil {
97-
glog.Infof("Did not unpack layer %s because no layer.tar found", layer)
94+
// Docker save contains files and directories. Ignore the directories.
95+
// We care about the layers and the manifest. The layers look like:
96+
// $SHA/layer.tar
97+
// and they are referenced that way in the manifest.
98+
switch t := hdr.Typeflag; t {
99+
case tar.TypeReg:
100+
if hdr.Name == "manifest.json" {
101+
layers, err = getLayersFromManifest(tr)
102+
if err != nil {
103+
return err
104+
}
105+
} else if strings.HasSuffix(hdr.Name, ".tar") {
106+
layerMap[hdr.Name], err = ioutil.ReadAll(tr)
107+
if err != nil {
108+
return err
109+
}
110+
}
111+
case tar.TypeDir:
98112
continue
113+
default:
114+
return fmt.Errorf("unsupported file type %v found in file %s tar %s", t, hdr.Name, tarPath)
99115
}
100-
if err = UnTar(layerTar, target); err != nil {
101-
glog.Errorf("Could not unpack layer %s: %s", layer, err)
116+
}
117+
118+
for _, layer := range layers {
119+
if err = UnTar(bytes.NewReader(layerMap[layer]), target); err != nil {
120+
return fmt.Errorf("Could not unpack layer %s: %s", layer, err)
102121
}
103122
}
104123
return nil

pkg/util/tar_prepper.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ func (p TarPrepper) GetConfig() (ConfigSchema, error) {
5555
return ConfigSchema{}, nil
5656
}
5757
defer os.RemoveAll(tempDir)
58-
if err := UnTar(p.Source, tempDir); err != nil {
58+
f, err := os.Open(p.Source)
59+
if err != nil {
60+
return ConfigSchema{}, err
61+
}
62+
defer f.Close()
63+
if err := UnTar(f, tempDir); err != nil {
5964
return ConfigSchema{}, err
6065
}
6166

pkg/util/tar_utils.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,13 @@ func unpackTar(tr *tar.Reader, path string) error {
9595

9696
// UnTar takes in a path to a tar file and writes the untarred version to the provided target.
9797
// Only untars one level, does not untar nested tars.
98-
func UnTar(filename string, target string) error {
98+
func UnTar(r io.Reader, target string) error {
9999
if _, ok := os.Stat(target); ok != nil {
100-
os.MkdirAll(target, 0777)
100+
os.MkdirAll(target, 0775)
101101
}
102-
file, err := os.Open(filename)
103-
if err != nil {
104-
return err
105-
}
106-
defer file.Close()
107-
tr := tar.NewReader(file)
108-
err = unpackTar(tr, target)
109-
if err != nil {
110-
glog.Error(err)
102+
103+
tr := tar.NewReader(r)
104+
if err := unpackTar(tr, target); err != nil {
111105
return err
112106
}
113107
return nil

util/tar_utils_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ func TestUnTar(t *testing.T) {
8282
if test.starter != "" {
8383
CopyDir(test.starter, test.target)
8484
}
85-
err := pkgutil.UnTar(test.tarPath, test.target)
86-
if err != nil && !test.err {
85+
r, err := os.Open(test.tarPath)
86+
if err != nil {
87+
t.Errorf("Error opening tar: %s", err)
88+
}
89+
if err := pkgutil.UnTar(r, test.target); err != nil && !test.err {
8790
t.Errorf(test.descrip, "Got unexpected error: %s", err)
8891
remove = false
8992
}

0 commit comments

Comments
 (0)