2018-02-05 16:05:59 -05:00
|
|
|
package layer // import "github.com/docker/docker/layer"
|
2015-11-18 17:15:00 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"compress/gzip"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2016-02-28 23:16:10 -05:00
|
|
|
"runtime"
|
2015-11-18 17:15:00 -05:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/docker/docker/daemon/graphdriver"
|
|
|
|
"github.com/docker/docker/pkg/stringid"
|
|
|
|
"github.com/vbatts/tar-split/tar/asm"
|
|
|
|
"github.com/vbatts/tar-split/tar/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
func writeTarSplitFile(name string, tarContent []byte) error {
|
|
|
|
f, err := os.OpenFile(name, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
fz := gzip.NewWriter(f)
|
|
|
|
|
|
|
|
metaPacker := storage.NewJSONPacker(fz)
|
|
|
|
defer fz.Close()
|
|
|
|
|
|
|
|
rdr, err := asm.NewInputTarStream(bytes.NewReader(tarContent), metaPacker, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
if _, err := io.Copy(io.Discard, rdr); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLayerMigration(t *testing.T) {
|
2016-02-28 23:16:10 -05:00
|
|
|
// TODO Windows: Figure out why this is failing
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
t.Skip("Failing on Windows")
|
|
|
|
}
|
2021-08-24 06:10:50 -04:00
|
|
|
td, err := os.MkdirTemp("", "migration-test-")
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
|
|
|
layer1Files := []FileApplier{
|
|
|
|
newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
|
|
|
|
newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
|
|
|
|
}
|
|
|
|
|
|
|
|
layer2Files := []FileApplier{
|
|
|
|
newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
|
|
|
|
}
|
|
|
|
|
|
|
|
tar1, err := tarFromFiles(layer1Files...)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tar2, err := tarFromFiles(layer2Files...)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
graph, err := newVFSGraphDriver(filepath.Join(td, "graphdriver-"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
graphID1 := stringid.GenerateRandomID()
|
2016-11-09 15:59:58 -05:00
|
|
|
if err := graph.Create(graphID1, "", nil); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-10-20 19:40:59 -04:00
|
|
|
if _, err := graph.ApplyDiff(graphID1, "", bytes.NewReader(tar1)); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tf1 := filepath.Join(td, "tar1.json.gz")
|
|
|
|
if err := writeTarSplitFile(tf1, tar1); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-03-05 18:46:21 -05:00
|
|
|
root := filepath.Join(td, "layers")
|
2022-01-25 08:16:43 -05:00
|
|
|
ls, err := newStoreFromGraphDriver(root, graph)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-11-29 22:55:22 -05:00
|
|
|
newTarDataPath := filepath.Join(td, ".migration-tardata")
|
|
|
|
diffID, size, err := ls.(*layerStore).ChecksumForGraphID(graphID1, "", tf1, newTarDataPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
layer1a, err := ls.(*layerStore).RegisterByGraphID(graphID1, "", diffID, newTarDataPath, size)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-09-19 15:14:46 -04:00
|
|
|
layer1b, err := ls.Register(bytes.NewReader(tar1), "")
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertReferences(t, layer1a, layer1b)
|
|
|
|
// Attempt register, should be same
|
2017-09-19 15:14:46 -04:00
|
|
|
layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID())
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
graphID2 := stringid.GenerateRandomID()
|
2016-11-09 15:59:58 -05:00
|
|
|
if err := graph.Create(graphID2, graphID1, nil); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-10-20 19:40:59 -04:00
|
|
|
if _, err := graph.ApplyDiff(graphID2, graphID1, bytes.NewReader(tar2)); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tf2 := filepath.Join(td, "tar2.json.gz")
|
|
|
|
if err := writeTarSplitFile(tf2, tar2); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-11-29 22:55:22 -05:00
|
|
|
diffID, size, err = ls.(*layerStore).ChecksumForGraphID(graphID2, graphID1, tf2, newTarDataPath)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-11-29 22:55:22 -05:00
|
|
|
layer2b, err := ls.(*layerStore).RegisterByGraphID(graphID2, layer1a.ChainID(), diffID, tf2, size)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-11-18 17:15:00 -05:00
|
|
|
assertReferences(t, layer2a, layer2b)
|
|
|
|
|
|
|
|
if metadata, err := ls.Release(layer2a); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if len(metadata) > 0 {
|
|
|
|
t.Fatalf("Unexpected layer removal after first release: %#v", metadata)
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata, err := ls.Release(layer2b)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertMetadata(t, metadata, createMetadata(layer2a))
|
|
|
|
}
|
|
|
|
|
|
|
|
func tarFromFilesInGraph(graph graphdriver.Driver, graphID, parentID string, files ...FileApplier) ([]byte, error) {
|
|
|
|
t, err := tarFromFiles(files...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-11-09 15:59:58 -05:00
|
|
|
if err := graph.Create(graphID, parentID, nil); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-10-20 19:40:59 -04:00
|
|
|
if _, err := graph.ApplyDiff(graphID, parentID, bytes.NewReader(t)); err != nil {
|
2015-11-18 17:15:00 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ar, err := graph.Diff(graphID, parentID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer ar.Close()
|
|
|
|
|
2021-08-24 06:10:50 -04:00
|
|
|
return io.ReadAll(ar)
|
2015-11-18 17:15:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestLayerMigrationNoTarsplit(t *testing.T) {
|
2016-02-28 23:16:10 -05:00
|
|
|
// TODO Windows: Figure out why this is failing
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
t.Skip("Failing on Windows")
|
|
|
|
}
|
2021-08-24 06:10:50 -04:00
|
|
|
td, err := os.MkdirTemp("", "migration-test-")
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
|
|
|
layer1Files := []FileApplier{
|
|
|
|
newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
|
|
|
|
newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
|
|
|
|
}
|
|
|
|
|
|
|
|
layer2Files := []FileApplier{
|
|
|
|
newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
|
|
|
|
}
|
|
|
|
|
|
|
|
graph, err := newVFSGraphDriver(filepath.Join(td, "graphdriver-"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
graphID1 := stringid.GenerateRandomID()
|
|
|
|
graphID2 := stringid.GenerateRandomID()
|
|
|
|
|
|
|
|
tar1, err := tarFromFilesInGraph(graph, graphID1, "", layer1Files...)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tar2, err := tarFromFilesInGraph(graph, graphID2, graphID1, layer2Files...)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-03-05 18:46:21 -05:00
|
|
|
root := filepath.Join(td, "layers")
|
2022-01-25 08:16:43 -05:00
|
|
|
ls, err := newStoreFromGraphDriver(root, graph)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-11-29 22:55:22 -05:00
|
|
|
newTarDataPath := filepath.Join(td, ".migration-tardata")
|
|
|
|
diffID, size, err := ls.(*layerStore).ChecksumForGraphID(graphID1, "", "", newTarDataPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
layer1a, err := ls.(*layerStore).RegisterByGraphID(graphID1, "", diffID, newTarDataPath, size)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-09-19 15:14:46 -04:00
|
|
|
layer1b, err := ls.Register(bytes.NewReader(tar1), "")
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertReferences(t, layer1a, layer1b)
|
|
|
|
|
|
|
|
// Attempt register, should be same
|
2017-09-19 15:14:46 -04:00
|
|
|
layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID())
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-11-29 22:55:22 -05:00
|
|
|
diffID, size, err = ls.(*layerStore).ChecksumForGraphID(graphID2, graphID1, "", newTarDataPath)
|
2015-11-18 17:15:00 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-11-29 22:55:22 -05:00
|
|
|
layer2b, err := ls.(*layerStore).RegisterByGraphID(graphID2, layer1a.ChainID(), diffID, newTarDataPath, size)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-11-18 17:15:00 -05:00
|
|
|
assertReferences(t, layer2a, layer2b)
|
|
|
|
|
|
|
|
if metadata, err := ls.Release(layer2a); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if len(metadata) > 0 {
|
|
|
|
t.Fatalf("Unexpected layer removal after first release: %#v", metadata)
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata, err := ls.Release(layer2b)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
assertMetadata(t, metadata, createMetadata(layer2a))
|
|
|
|
}
|