From 7a06d0ee3ed12643d2c12aafeafffeda2bd91fcf Mon Sep 17 00:00:00 2001 From: Ye Yin Date: Wed, 23 Nov 2016 16:46:04 +0800 Subject: [PATCH] Use hardlink to copy layer between oci and bt engine --- cmd/daemon/main.go | 5 ++++ daemon/config.go | 1 + daemon/daemon.go | 72 +++++++++++++++++++++++++++++----------------- oci/dir.go | 8 ++++++ oci/layout.go | 3 ++ 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index 3077f85..b4a9e21 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -71,6 +71,10 @@ var daemonFlags = []cli.Flag{ Value: 1 * time.Second, Usage: "GRPC connection timeout", }, + cli.BoolFlag{ + Name: "hardlink", + Usage: "use hard link to copy layer between oci engine and bt engine", + }, } // DumpStacks dumps the runtime stack. @@ -138,6 +142,7 @@ func runDaemon(context *cli.Context) error { BtSeederServer: context.StringSlice("seeder-addr"), UploadRateLimit: context.Int("upload-rate"), DownloadRateLimit: context.Int("download-rate"), + UseHardlink: context.Bool("hardlink"), } s := make(chan os.Signal, 2048) signal.Notify(s, syscall.SIGTERM, syscall.SIGINT) diff --git a/daemon/config.go b/daemon/config.go index 3979845..5255725 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -8,6 +8,7 @@ type Config struct { Pidfile string Root string ConnTimeout time.Duration + UseHardlink bool BtEnable bool BtSeeder bool diff --git a/daemon/daemon.go b/daemon/daemon.go index 1f19c7e..5948766 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -196,27 +196,37 @@ func (daemon *Daemon) startSeedingLayer(ctx context.Context, ociImg *OciImage, d id := distdigests.Digest(digest).Hex() // Write layer to file fn := daemon.btEngine.GetFilePath(id) - layerFile, err := os.Create(fn) - if err != nil { - return fmt.Errorf("Create layer file %s failed: %v", fn, err) - } - defer layerFile.Close() + if daemon.config.UseHardlink { + src, err := ociImg.layout.GetBlobPath(ctx, digest) + if err != nil { + return fmt.Errorf("Get oci blob path error: %v", err) + } + if err = os.Link(src, fn); err != nil { + return fmt.Errorf("Create hardlink error: %v", err) + } + } else { + layerFile, err := os.Create(fn) + if err != nil { + return fmt.Errorf("Create layer file %s failed: %v", fn, err) + } + defer layerFile.Close() - // Copy from OCI directory - srcFile, err := ociImg.layout.GetBlob(ctx, digest) - if err != nil { - return fmt.Errorf("Open oci layer %s error: %v", digest, err) - } - defer srcFile.Close() + // Copy from OCI directory + srcFile, err := ociImg.layout.GetBlob(ctx, digest) + if err != nil { + return fmt.Errorf("Open oci layer %s error: %v", digest, err) + } + defer srcFile.Close() - _, err = io.Copy(layerFile, srcFile) - if err != nil { - return fmt.Errorf("Copy oci layer %s error: %v", digest, err) + _, err = io.Copy(layerFile, srcFile) + if err != nil { + return fmt.Errorf("Copy oci layer %s error: %v", digest, err) + } } writeReport("Start seeding %s\n", id) // Seed layer file - if err = daemon.btEngine.StartSeed(id); err != nil { + if err := daemon.btEngine.StartSeed(id); err != nil { log.Errorf("Seed layer %s failed: %v", id, err) } else { log.Infof("Seed layer %s success", id) @@ -336,19 +346,29 @@ func (daemon *Daemon) startLeechingLayer(ctx context.Context, ociImg *OciImage, writeReport("%s: Copy to OCI directory\n", id) fn := daemon.btEngine.GetFilePath(id) - layerFile, err := os.Open(fn) - if err != nil { - return fmt.Errorf("Open layer file %s failed: %v", fn, err) - } - defer layerFile.Close() + if daemon.config.UseHardlink { + dst, err := ociImg.layout.GetBlobPath(ctx, layer.Digest) + if err != nil { + return fmt.Errorf("Get oci blob path error: %v", err) + } + if err = os.Link(fn, dst); err != nil { + return fmt.Errorf("Create hardlink error: %v", err) + } + } else { + layerFile, err := os.Open(fn) + if err != nil { + return fmt.Errorf("Open layer file %s failed: %v", fn, err) + } + defer layerFile.Close() - digest, _, err := ociImg.layout.PutBlob(ctx, layerFile) - if err != nil { - return fmt.Errorf("Error to put blob %s: %v", layer.Digest, err) - } + digest, _, err := ociImg.layout.PutBlob(ctx, layerFile) + if err != nil { + return fmt.Errorf("Error to put blob %s: %v", layer.Digest, err) + } - if digest != layer.Digest { - return fmt.Errorf("Digest not match, src: %s, dest: %s", layer.Digest, digest) + if digest != layer.Digest { + return fmt.Errorf("Digest not match, src: %s, dest: %s", layer.Digest, digest) + } } return nil } diff --git a/oci/dir.go b/oci/dir.go index 9f9dc83..9b4f979 100644 --- a/oci/dir.go +++ b/oci/dir.go @@ -297,6 +297,14 @@ func (e dirLayout) Close() error { return os.RemoveAll(e.temp) } +func (e dirLayout) GetBlobPath(ctx context.Context, digest string) (path string, err error) { + path, err = blobPath(digest) + if err != nil { + return "", err + } + return filepath.Join(e.path, path), nil +} + func newDirLayout(path string) (*dirLayout, error) { layout := &dirLayout{ path: path, diff --git a/oci/layout.go b/oci/layout.go index b50a0cf..381b9b5 100644 --- a/oci/layout.go +++ b/oci/layout.go @@ -87,6 +87,9 @@ type Layout interface { // Close releases all references held by the engine. Subsequent operations // may fail. Close() (err error) + + // GetBlobPath returns a path of a blob from the image + GetBlobPath(ctx context.Context, digest string) (path string, err error) } // Open will create an Layout reference to the OCI image at the provided