Add direct unpack support for overlay and aufs
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
parent
f06e605f1a
commit
81386df917
122
archive/tar.go
122
archive/tar.go
@ -19,9 +19,7 @@ package archive
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -91,11 +89,6 @@ const (
|
|||||||
// archives.
|
// archives.
|
||||||
whiteoutMetaPrefix = whiteoutPrefix + whiteoutPrefix
|
whiteoutMetaPrefix = whiteoutPrefix + whiteoutPrefix
|
||||||
|
|
||||||
// whiteoutLinkDir is a directory AUFS uses for storing hardlink links to other
|
|
||||||
// layers. Normally these should not go into exported archives and all changed
|
|
||||||
// hardlinks should be copied to the top layer.
|
|
||||||
whiteoutLinkDir = whiteoutMetaPrefix + "plnk"
|
|
||||||
|
|
||||||
// whiteoutOpaqueDir file means directory has been made opaque - meaning
|
// whiteoutOpaqueDir file means directory has been made opaque - meaning
|
||||||
// readdir calls to this directory do not follow to lower layers.
|
// readdir calls to this directory do not follow to lower layers.
|
||||||
whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq"
|
whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq"
|
||||||
@ -130,10 +123,6 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
|
|||||||
// Used for handling opaque directory markers which
|
// Used for handling opaque directory markers which
|
||||||
// may occur out of order
|
// may occur out of order
|
||||||
unpackedPaths = make(map[string]struct{})
|
unpackedPaths = make(map[string]struct{})
|
||||||
|
|
||||||
// Used for aufs plink directory
|
|
||||||
aufsTempdir = ""
|
|
||||||
aufsHardlinks = make(map[string]*tar.Header)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
@ -201,40 +190,15 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip AUFS metadata dirs
|
// Naive whiteout convert function which handles whiteout files by
|
||||||
if strings.HasPrefix(hdr.Name, whiteoutMetaPrefix) {
|
// removing the target files.
|
||||||
// Regular files inside /.wh..wh.plnk can be used as hardlink targets
|
convertWhiteout := func(hdr *tar.Header, path string) (bool, error) {
|
||||||
// We don't want this directory, but we need the files in them so that
|
base := filepath.Base(path)
|
||||||
// such hardlinks can be resolved.
|
|
||||||
if strings.HasPrefix(hdr.Name, whiteoutLinkDir) && hdr.Typeflag == tar.TypeReg {
|
|
||||||
basename := filepath.Base(hdr.Name)
|
|
||||||
aufsHardlinks[basename] = hdr
|
|
||||||
if aufsTempdir == "" {
|
|
||||||
if aufsTempdir, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "dockerplnk"); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(aufsTempdir)
|
|
||||||
}
|
|
||||||
p, err := fs.RootPath(aufsTempdir, basename)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if err := createTarFile(ctx, p, root, hdr, tr); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hdr.Name != whiteoutOpaqueDir {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(base, whiteoutPrefix) {
|
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
if base == whiteoutOpaqueDir {
|
if base == whiteoutOpaqueDir {
|
||||||
_, err := os.Lstat(dir)
|
_, err := os.Lstat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return false, err
|
||||||
}
|
}
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -252,26 +216,29 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
return false, err
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
originalBase := base[len(whiteoutPrefix):]
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
||||||
originalPath := filepath.Join(dir, originalBase)
|
originalBase := base[len(whiteoutPrefix):]
|
||||||
|
originalPath := filepath.Join(dir, originalBase)
|
||||||
|
|
||||||
// Ensure originalPath is under dir
|
return false, os.RemoveAll(originalPath)
|
||||||
if dir[len(dir)-1] != filepath.Separator {
|
|
||||||
dir += string(filepath.Separator)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(originalPath, dir) {
|
|
||||||
return 0, errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.RemoveAll(originalPath); err != nil {
|
return true, nil
|
||||||
return 0, err
|
}
|
||||||
}
|
if options.ConvertWhiteout != nil {
|
||||||
|
convertWhiteout = options.ConvertWhiteout
|
||||||
|
}
|
||||||
|
if err := validateWhiteout(path); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
writeFile, err := convertWhiteout(hdr, path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrapf(err, "failed to convert whiteout file %q", hdr.Name)
|
||||||
|
}
|
||||||
|
if !writeFile {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If path exits we almost always just want to remove and replace it.
|
// If path exits we almost always just want to remove and replace it.
|
||||||
@ -289,26 +256,6 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
|
|||||||
srcData := io.Reader(tr)
|
srcData := io.Reader(tr)
|
||||||
srcHdr := hdr
|
srcHdr := hdr
|
||||||
|
|
||||||
// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
|
|
||||||
// we manually retarget these into the temporary files we extracted them into
|
|
||||||
if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), whiteoutLinkDir) {
|
|
||||||
linkBasename := filepath.Base(hdr.Linkname)
|
|
||||||
srcHdr = aufsHardlinks[linkBasename]
|
|
||||||
if srcHdr == nil {
|
|
||||||
return 0, fmt.Errorf("invalid aufs hardlink")
|
|
||||||
}
|
|
||||||
p, err := fs.RootPath(aufsTempdir, linkBasename)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
tmpFile, err := os.Open(p)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer tmpFile.Close()
|
|
||||||
srcData = tmpFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := createTarFile(ctx, path, root, srcHdr, srcData); err != nil {
|
if err := createTarFile(ctx, path, root, srcHdr, srcData); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -684,3 +631,26 @@ func hardlinkRootPath(root, linkname string) (string, error) {
|
|||||||
}
|
}
|
||||||
return targetPath, nil
|
return targetPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateWhiteout(path string) error {
|
||||||
|
base := filepath.Base(path)
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
|
if base == whiteoutOpaqueDir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
||||||
|
originalBase := base[len(whiteoutPrefix):]
|
||||||
|
originalPath := filepath.Join(dir, originalBase)
|
||||||
|
|
||||||
|
// Ensure originalPath is under dir
|
||||||
|
if dir[len(dir)-1] != filepath.Separator {
|
||||||
|
dir += string(filepath.Separator)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(originalPath, dir) {
|
||||||
|
return errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -24,6 +24,9 @@ type ApplyOpt func(options *ApplyOptions) error
|
|||||||
// Filter specific files from the archive
|
// Filter specific files from the archive
|
||||||
type Filter func(*tar.Header) (bool, error)
|
type Filter func(*tar.Header) (bool, error)
|
||||||
|
|
||||||
|
// ConvertWhiteout converts whiteout files from the archive
|
||||||
|
type ConvertWhiteout func(*tar.Header, string) (bool, error)
|
||||||
|
|
||||||
// all allows all files
|
// all allows all files
|
||||||
func all(_ *tar.Header) (bool, error) {
|
func all(_ *tar.Header) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -36,3 +39,11 @@ func WithFilter(f Filter) ApplyOpt {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithConvertWhiteout uses the convert function to convert the whiteout files.
|
||||||
|
func WithConvertWhiteout(c ConvertWhiteout) ApplyOpt {
|
||||||
|
return func(options *ApplyOptions) error {
|
||||||
|
options.ConvertWhiteout = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
65
archive/tar_opts_linux.go
Normal file
65
archive/tar_opts_linux.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
|
type ApplyOptions struct {
|
||||||
|
Filter Filter // Filter tar headers
|
||||||
|
ConvertWhiteout ConvertWhiteout // Convert whiteout files
|
||||||
|
}
|
||||||
|
|
||||||
|
// AufsConvertWhiteout converts whiteout files for aufs.
|
||||||
|
func AufsConvertWhiteout(_ *tar.Header, _ string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OverlayConvertWhiteout converts whiteout files for overlay.
|
||||||
|
func OverlayConvertWhiteout(hdr *tar.Header, path string) (bool, error) {
|
||||||
|
base := filepath.Base(path)
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
|
// if a directory is marked as opaque, we need to translate that to overlay
|
||||||
|
if base == whiteoutOpaqueDir {
|
||||||
|
// don't write the file itself
|
||||||
|
return false, unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a file was deleted and we are using overlay, we need to create a character device
|
||||||
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
||||||
|
originalBase := base[len(whiteoutPrefix):]
|
||||||
|
originalPath := filepath.Join(dir, originalBase)
|
||||||
|
|
||||||
|
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// don't write the file itself
|
||||||
|
return false, os.Chown(originalPath, hdr.Uid, hdr.Gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// +build !windows
|
// +build !linux,!windows
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright The containerd Authors.
|
Copyright The containerd Authors.
|
||||||
@ -20,5 +20,6 @@ package archive
|
|||||||
|
|
||||||
// ApplyOptions provides additional options for an Apply operation
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
type ApplyOptions struct {
|
type ApplyOptions struct {
|
||||||
Filter Filter // Filter tar headers
|
Filter Filter // Filter tar headers
|
||||||
|
ConvertWhiteout ConvertWhiteout // Convert whiteout files
|
||||||
}
|
}
|
@ -20,9 +20,10 @@ package archive
|
|||||||
|
|
||||||
// ApplyOptions provides additional options for an Apply operation
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
type ApplyOptions struct {
|
type ApplyOptions struct {
|
||||||
ParentLayerPaths []string // Parent layer paths used for Windows layer apply
|
ParentLayerPaths []string // Parent layer paths used for Windows layer apply
|
||||||
IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer
|
IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer
|
||||||
Filter Filter // Filter tar headers
|
Filter Filter // Filter tar headers
|
||||||
|
ConvertWhiteout ConvertWhiteout // Convert whiteout files
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithParentLayers adds parent layers to the apply process this is required
|
// WithParentLayers adds parent layers to the apply process this is required
|
||||||
|
@ -19,10 +19,8 @@ package apply
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/archive"
|
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/diff"
|
"github.com/containerd/containerd/diff"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
@ -94,15 +92,8 @@ func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts [
|
|||||||
rc := &readCounter{
|
rc := &readCounter{
|
||||||
r: io.TeeReader(processor, digester.Hash()),
|
r: io.TeeReader(processor, digester.Hash()),
|
||||||
}
|
}
|
||||||
if err := mount.WithTempMount(ctx, mounts, func(root string) error {
|
|
||||||
if _, err := archive.Apply(ctx, root, rc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read any trailing data
|
if err := apply(ctx, mounts, rc); err != nil {
|
||||||
_, err := io.Copy(ioutil.Discard, rc)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return emptyDesc, err
|
return emptyDesc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
diff/apply/apply_linux.go
Normal file
90
diff/apply/apply_linux.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/archive"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader) error {
|
||||||
|
switch {
|
||||||
|
case len(mounts) == 1 && mounts[0].Type == "overlay":
|
||||||
|
path, err := getOverlayPath(mounts[0].Options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = archive.Apply(ctx, path, r,
|
||||||
|
archive.WithConvertWhiteout(archive.OverlayConvertWhiteout))
|
||||||
|
return err
|
||||||
|
case len(mounts) == 1 && mounts[0].Type == "aufs":
|
||||||
|
path, err := getAufsPath(mounts[0].Options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = archive.Apply(ctx, path, r,
|
||||||
|
archive.WithConvertWhiteout(archive.AufsConvertWhiteout))
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||||
|
_, err := archive.Apply(ctx, root, r)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOverlayPath(options []string) (string, error) {
|
||||||
|
const upperdirPrefix = "upperdir="
|
||||||
|
for _, o := range options {
|
||||||
|
if strings.HasPrefix(o, upperdirPrefix) {
|
||||||
|
return strings.TrimPrefix(o, upperdirPrefix), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("upperdir not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAufsPath(options []string) (string, error) {
|
||||||
|
const (
|
||||||
|
sep = ":"
|
||||||
|
brPrefix1 = "br:"
|
||||||
|
brPrefix2 = "br="
|
||||||
|
rwSuffix = "=rw"
|
||||||
|
)
|
||||||
|
for _, o := range options {
|
||||||
|
if strings.HasPrefix(o, brPrefix1) {
|
||||||
|
o = strings.TrimPrefix(o, brPrefix1)
|
||||||
|
} else if strings.HasPrefix(o, brPrefix2) {
|
||||||
|
o = strings.TrimPrefix(o, brPrefix2)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, b := range strings.Split(o, sep) {
|
||||||
|
if strings.HasSuffix(b, rwSuffix) {
|
||||||
|
return strings.TrimSuffix(b, rwSuffix), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return "", errors.New("rw branch not found")
|
||||||
|
}
|
79
diff/apply/apply_linux_test.go
Normal file
79
diff/apply/apply_linux_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetOverlayPath(t *testing.T) {
|
||||||
|
good := []string{"upperdir=/test/upper", "lowerdir=/test/lower", "workdir=/test/work"}
|
||||||
|
path, err := getOverlayPath(good)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Get overlay path failed: %v", err)
|
||||||
|
}
|
||||||
|
if path != "/test/upper" {
|
||||||
|
t.Fatalf("Unexpected upperdir: %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
bad := []string{"lowerdir=/test/lower"}
|
||||||
|
_, err = getOverlayPath(bad)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("An error is expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAufsPath(t *testing.T) {
|
||||||
|
rwDir := "/test/rw"
|
||||||
|
for _, test := range []struct {
|
||||||
|
options []string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
options: []string{"random:option", "br:" + rwDir + "=rw:/test/ro=ro+wh"},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
options: []string{"random:option", "br=" + rwDir + "=rw:/test/ro=ro+wh"},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
options: []string{"random:option"},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
options: []string{"br:/test/ro=ro+wh"},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
path, err := getAufsPath(test.options)
|
||||||
|
if test.expectErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("An error is expected")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Get aufs path failed: %v", err)
|
||||||
|
}
|
||||||
|
if path != rwDir {
|
||||||
|
t.Fatalf("Unexpected rw dir: %q", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
diff/apply/apply_other.go
Normal file
34
diff/apply/apply_other.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/archive"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader) error {
|
||||||
|
return mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||||
|
_, err := archive.Apply(ctx, root, r)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user