Merge pull request #1969 from darrenstahlmsft/WindowsSnapshotter5
Implement Windows snapshotter and differ
This commit is contained in:
commit
7e4403540d
@ -9,7 +9,6 @@ import (
|
|||||||
golog "log"
|
golog "log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -17,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/sys"
|
||||||
"github.com/containerd/containerd/testutil"
|
"github.com/containerd/containerd/testutil"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -62,7 +62,7 @@ func TestMain(m *testing.M) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if !noDaemon {
|
if !noDaemon {
|
||||||
os.RemoveAll(defaultRoot)
|
sys.ForceRemoveAll(defaultRoot)
|
||||||
|
|
||||||
err := ctrd.start("containerd", address, []string{
|
err := ctrd.start("containerd", address, []string{
|
||||||
"--root", defaultRoot,
|
"--root", defaultRoot,
|
||||||
@ -99,19 +99,12 @@ func TestMain(m *testing.M) {
|
|||||||
}).Info("running tests against containerd")
|
}).Info("running tests against containerd")
|
||||||
|
|
||||||
// pull a seed image
|
// pull a seed image
|
||||||
if runtime.GOOS != "windows" { // TODO: remove once pull is supported on windows
|
|
||||||
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
|
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
|
||||||
ctrd.Stop()
|
ctrd.Stop()
|
||||||
ctrd.Wait()
|
ctrd.Wait()
|
||||||
fmt.Fprintf(os.Stderr, "%s: %s\n", err, buf.String())
|
fmt.Fprintf(os.Stderr, "%s: %s\n", err, buf.String())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := platformTestSetup(client); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "platform test setup failed", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.Close(); err != nil {
|
if err := client.Close(); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "failed to close client", err)
|
fmt.Fprintln(os.Stderr, "failed to close client", err)
|
||||||
@ -132,7 +125,7 @@ func TestMain(m *testing.M) {
|
|||||||
fmt.Fprintln(os.Stderr, "failed to wait for containerd", err)
|
fmt.Fprintln(os.Stderr, "failed to wait for containerd", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := os.RemoveAll(defaultRoot); err != nil {
|
if err := sys.ForceRemoveAll(defaultRoot); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "failed to remove test root dir", err)
|
fmt.Fprintln(os.Stderr, "failed to remove test root dir", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@ -169,11 +162,6 @@ func TestNewClient(t *testing.T) {
|
|||||||
|
|
||||||
// All the container's tests depends on this, we need it to run first.
|
// All the container's tests depends on this, we need it to run first.
|
||||||
func TestImagePull(t *testing.T) {
|
func TestImagePull(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// TODO: remove once Windows has a snapshotter
|
|
||||||
t.Skip("Windows does not have a snapshotter yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := newClient(t, address)
|
client, err := newClient(t, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -16,10 +16,6 @@ var (
|
|||||||
testImage string
|
testImage string
|
||||||
)
|
)
|
||||||
|
|
||||||
func platformTestSetup(client *Client) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
case "386":
|
case "386":
|
||||||
|
@ -1,88 +1,16 @@
|
|||||||
package containerd
|
package containerd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultAddress = `\\.\pipe\containerd-containerd-test`
|
defaultAddress = `\\.\pipe\containerd-containerd-test`
|
||||||
testImage = "docker.io/library/go:nanoserver"
|
testImage = "docker.io/microsoft/nanoserver:latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dockerLayerFolders []string
|
|
||||||
|
|
||||||
defaultRoot = filepath.Join(os.Getenv("programfiles"), "containerd", "root-test")
|
defaultRoot = filepath.Join(os.Getenv("programfiles"), "containerd", "root-test")
|
||||||
defaultState = filepath.Join(os.Getenv("programfiles"), "containerd", "state-test")
|
defaultState = filepath.Join(os.Getenv("programfiles"), "containerd", "state-test")
|
||||||
)
|
)
|
||||||
|
|
||||||
func platformTestSetup(client *Client) error {
|
|
||||||
var (
|
|
||||||
roots []string
|
|
||||||
layerChains = make(map[string]string)
|
|
||||||
)
|
|
||||||
// Since we can't pull images yet, we'll piggyback on the default
|
|
||||||
// docker's images
|
|
||||||
wfPath := `C:\ProgramData\docker\windowsfilter`
|
|
||||||
wf, err := os.Open(wfPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to access docker layers @ %s", wfPath)
|
|
||||||
}
|
|
||||||
defer wf.Close()
|
|
||||||
entries, err := wf.Readdirnames(0)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read %s entries", wfPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fn := range entries {
|
|
||||||
layerChainPath := filepath.Join(wfPath, fn, "layerchain.json")
|
|
||||||
lfi, err := os.Stat(layerChainPath)
|
|
||||||
switch {
|
|
||||||
case err == nil && lfi.Mode().IsRegular():
|
|
||||||
f, err := os.OpenFile(layerChainPath, os.O_RDONLY, 0660)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr,
|
|
||||||
errors.Wrapf(err, "failed to open %s", layerChainPath))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
l := make([]string, 0)
|
|
||||||
if err := json.NewDecoder(f).Decode(&l); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr,
|
|
||||||
errors.Wrapf(err, "failed to decode %s", layerChainPath))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case len(l) == 1:
|
|
||||||
layerChains[l[0]] = filepath.Join(wfPath, fn)
|
|
||||||
case len(l) > 1:
|
|
||||||
fmt.Fprintf(os.Stderr, "Too many entries in %s: %d", layerChainPath, len(l))
|
|
||||||
case len(l) == 0:
|
|
||||||
roots = append(roots, filepath.Join(wfPath, fn))
|
|
||||||
}
|
|
||||||
case os.IsNotExist(err):
|
|
||||||
// keep on going
|
|
||||||
default:
|
|
||||||
return errors.Wrapf(err, "error trying to access %s", layerChainPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// They'll be 2 roots, just take the first one
|
|
||||||
l := roots[0]
|
|
||||||
dockerLayerFolders = append(dockerLayerFolders, l)
|
|
||||||
for {
|
|
||||||
l = layerChains[l]
|
|
||||||
if l == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
dockerLayerFolders = append([]string{l}, dockerLayerFolders...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/containerd/containerd/diff/windows"
|
||||||
_ "github.com/containerd/containerd/snapshots/windows"
|
_ "github.com/containerd/containerd/snapshots/windows"
|
||||||
_ "github.com/containerd/containerd/windows"
|
_ "github.com/containerd/containerd/windows"
|
||||||
)
|
)
|
||||||
|
@ -7,32 +7,12 @@ import (
|
|||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/cmd/ctr/commands"
|
"github.com/containerd/containerd/cmd/ctr/commands"
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/oci"
|
"github.com/containerd/containerd/oci"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
Command.Flags = append(Command.Flags, cli.StringSliceFlag{
|
|
||||||
Name: "layer",
|
|
||||||
Usage: "HCSSHIM Layers to be used",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func withLayers(context *cli.Context) oci.SpecOpts {
|
|
||||||
return func(ctx gocontext.Context, client oci.Client, c *containers.Container, s *specs.Spec) error {
|
|
||||||
l := context.StringSlice("layer")
|
|
||||||
if l == nil {
|
|
||||||
return errors.Wrap(errdefs.ErrInvalidArgument, "base layers must be specified with `--layer`")
|
|
||||||
}
|
|
||||||
s.Windows.LayerFolders = l
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func withTTY(terminal bool) oci.SpecOpts {
|
func withTTY(terminal bool) oci.SpecOpts {
|
||||||
if !terminal {
|
if !terminal {
|
||||||
return func(ctx gocontext.Context, client oci.Client, c *containers.Container, s *specs.Spec) error {
|
return func(ctx gocontext.Context, client oci.Client, c *containers.Container, s *specs.Spec) error {
|
||||||
@ -51,36 +31,39 @@ func withTTY(terminal bool) oci.SpecOpts {
|
|||||||
|
|
||||||
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
|
||||||
var (
|
var (
|
||||||
// ref = context.Args().First()
|
ref = context.Args().First()
|
||||||
id = context.Args().Get(1)
|
id = context.Args().Get(1)
|
||||||
args = context.Args()[2:]
|
args = context.Args()[2:]
|
||||||
tty = context.Bool("tty")
|
|
||||||
labelStrings = context.StringSlice("label")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
labels := commands.LabelArgs(labelStrings)
|
image, err := client.GetImage(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
// TODO(mlaventure): get base image once we have a snapshotter
|
return nil, err
|
||||||
|
|
||||||
opts := []oci.SpecOpts{
|
|
||||||
// TODO(mlaventure): use oci.WithImageConfig once we have a snapshotter
|
|
||||||
withLayers(context),
|
|
||||||
oci.WithEnv(context.StringSlice("env")),
|
|
||||||
withMounts(context),
|
|
||||||
withTTY(tty),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
opts []oci.SpecOpts
|
||||||
|
cOpts []containerd.NewContainerOpts
|
||||||
|
)
|
||||||
|
opts = append(opts, oci.WithImageConfig(image))
|
||||||
|
opts = append(opts, oci.WithEnv(context.StringSlice("env")))
|
||||||
|
opts = append(opts, withMounts(context))
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
opts = append(opts, oci.WithProcessArgs(args...))
|
opts = append(opts, oci.WithProcessArgs(args...))
|
||||||
}
|
}
|
||||||
if cwd := context.String("cwd"); cwd != "" {
|
if cwd := context.String("cwd"); cwd != "" {
|
||||||
opts = append(opts, oci.WithProcessCwd(cwd))
|
opts = append(opts, oci.WithProcessCwd(cwd))
|
||||||
}
|
}
|
||||||
return client.NewContainer(ctx, id,
|
opts = append(opts, withTTY(context.Bool("tty")))
|
||||||
containerd.WithNewSpec(opts...),
|
|
||||||
containerd.WithContainerLabels(labels),
|
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
|
||||||
containerd.WithRuntime(context.String("runtime"), nil),
|
cOpts = append(cOpts, containerd.WithImage(image))
|
||||||
// TODO(mlaventure): containerd.WithImage(image),
|
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
|
||||||
)
|
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
|
||||||
|
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
|
||||||
|
|
||||||
|
cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...)
|
||||||
|
return client.NewContainer(ctx, id, cOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNewTaskOpts(_ *cli.Context) []containerd.NewTaskOpts {
|
func getNewTaskOpts(_ *cli.Context) []containerd.NewTaskOpts {
|
||||||
|
@ -187,7 +187,7 @@ func TestDaemonRestart(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "30")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@ func TestContainerAttach(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withCat()), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -427,8 +427,8 @@ func TestContainerUsername(t *testing.T) {
|
|||||||
|
|
||||||
// squid user in the alpine image has a uid of 31
|
// squid user in the alpine image has a uid of 31
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithNewSpec(withImageConfig(image), oci.WithUsername("squid"), oci.WithProcessArgs("id", "-u")),
|
WithNewSpec(oci.WithImageConfig(image), oci.WithUsername("squid"), oci.WithProcessArgs("id", "-u")),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -487,7 +487,7 @@ func TestContainerAttachProcess(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -620,8 +620,8 @@ func TestContainerUserID(t *testing.T) {
|
|||||||
|
|
||||||
// adm user in the alpine image has a uid of 3 and gid of 4.
|
// adm user in the alpine image has a uid of 3 and gid of 4.
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithNewSpec(withImageConfig(image), oci.WithUserID(3), oci.WithProcessArgs("sh", "-c", "echo $(id -u):$(id -g)")),
|
WithNewSpec(oci.WithImageConfig(image), oci.WithUserID(3), oci.WithProcessArgs("sh", "-c", "echo $(id -u):$(id -g)")),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -674,8 +674,8 @@ func TestContainerKillAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithNewSpec(withImageConfig(image),
|
WithNewSpec(oci.WithImageConfig(image),
|
||||||
withProcessArgs("sh", "-c", "top"),
|
withProcessArgs("sh", "-c", "top"),
|
||||||
oci.WithHostNamespace(specs.PIDNamespace),
|
oci.WithHostNamespace(specs.PIDNamespace),
|
||||||
),
|
),
|
||||||
@ -730,7 +730,7 @@ func TestShimSigkilled(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image)), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image)), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -793,7 +793,7 @@ func TestDaemonRestartWithRunningShim(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -877,8 +877,8 @@ func TestContainerRuntimeOptions(t *testing.T) {
|
|||||||
|
|
||||||
container, err := client.NewContainer(
|
container, err := client.NewContainer(
|
||||||
ctx, id,
|
ctx, id,
|
||||||
WithNewSpec(withImageConfig(image), withExitStatus(7)),
|
WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)),
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithRuntime("io.containerd.runtime.v1.linux", &runctypes.RuncOptions{Runtime: "no-runc"}),
|
WithRuntime("io.containerd.runtime.v1.linux", &runctypes.RuncOptions{Runtime: "no-runc"}),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -917,8 +917,8 @@ func TestContainerKillInitPidHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
WithNewSpec(withImageConfig(image),
|
WithNewSpec(oci.WithImageConfig(image),
|
||||||
withProcessArgs("sh", "-c", "sleep 42; echo hi"),
|
withProcessArgs("sh", "-c", "sleep 42; echo hi"),
|
||||||
oci.WithHostNamespace(specs.PIDNamespace),
|
oci.WithHostNamespace(specs.PIDNamespace),
|
||||||
),
|
),
|
||||||
@ -1007,7 +1007,7 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []NewContainerOpts{WithNewSpec(withImageConfig(image),
|
opts := []NewContainerOpts{WithNewSpec(oci.WithImageConfig(image),
|
||||||
withExitStatus(7),
|
withExitStatus(7),
|
||||||
oci.WithUserNamespace(0, 1000, 10000),
|
oci.WithUserNamespace(0, 1000, 10000),
|
||||||
)}
|
)}
|
||||||
@ -1081,13 +1081,11 @@ func TestTaskResize(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)), WithNewSnapshot(id, image))
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withExitStatus(7)), withNewSnapshot(id, image))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -97,13 +97,11 @@ func TestContainerStart(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)), WithNewSnapshot(id, image))
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withExitStatus(7)), withNewSnapshot(id, image))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -163,13 +161,11 @@ func TestContainerOutput(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("echo", expected)), WithNewSnapshot(id, image))
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("echo", expected)), withNewSnapshot(id, image))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -233,14 +229,12 @@ func TestContainerExec(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -320,14 +314,12 @@ func TestContainerLargeExecArgs(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -398,14 +390,12 @@ func TestContainerPids(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -477,14 +467,12 @@ func TestContainerCloseIO(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withCat()), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -535,14 +523,12 @@ func TestDeleteRunningContainer(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -592,14 +578,12 @@ func TestContainerKill(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "10")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "10")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -649,16 +633,14 @@ func TestContainerNoBinaryExists(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
WithNewSpec(withImageConfig(image), oci.WithProcessArgs("nothing")),
|
WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("nothing")),
|
||||||
withNewSnapshot(id, image))
|
WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -698,14 +680,12 @@ func TestContainerExecNoBinaryExists(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -765,14 +745,12 @@ func TestWaitStoppedTask(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withExitStatus(7)), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -830,14 +808,12 @@ func TestWaitStoppedProcess(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -920,13 +896,11 @@ func TestTaskForceDelete(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "30")), WithNewSnapshot(id, image))
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -963,13 +937,11 @@ func TestProcessForceDelete(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "30")), WithNewSnapshot(id, image))
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1034,18 +1006,16 @@ func TestContainerHostname(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image),
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image),
|
||||||
withProcessArgs("hostname"),
|
withProcessArgs("hostname"),
|
||||||
oci.WithHostname(expected),
|
oci.WithHostname(expected),
|
||||||
),
|
),
|
||||||
withNewSnapshot(id, image))
|
WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1105,14 +1075,12 @@ func TestContainerExitedAtSet(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withTrue()), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withTrue()), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1167,14 +1135,12 @@ func TestDeleteContainerExecCreated(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image))
|
container, err := client.NewContainer(ctx, id, WithNewSpec(oci.WithImageConfig(image), withProcessArgs("sleep", "100")), WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1239,15 +1205,13 @@ func TestContainerMetrics(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
WithNewSpec(withImageConfig(image), oci.WithProcessArgs("sleep", "30")),
|
WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "30")),
|
||||||
withNewSnapshot(id, image))
|
WithNewSnapshot(id, image))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1297,15 +1261,13 @@ func TestDeletedContainerMetrics(t *testing.T) {
|
|||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
image, err = client.GetImage(ctx, testImage)
|
image, err = client.GetImage(ctx, testImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
container, err := client.NewContainer(ctx, id,
|
container, err := client.NewContainer(ctx, id,
|
||||||
WithNewSpec(withImageConfig(image), withExitStatus(0)),
|
WithNewSpec(oci.WithImageConfig(image), withExitStatus(0)),
|
||||||
withNewSnapshot(id, image),
|
WithNewSnapshot(id, image),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/archive"
|
"github.com/containerd/containerd/archive"
|
||||||
@ -75,19 +74,11 @@ func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts
|
|||||||
}).Debugf("diff applied")
|
}).Debugf("diff applied")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var isCompressed bool
|
|
||||||
switch desc.MediaType {
|
isCompressed, err := images.IsCompressedDiff(ctx, desc.MediaType)
|
||||||
case ocispec.MediaTypeImageLayer, images.MediaTypeDockerSchema2Layer:
|
if err != nil {
|
||||||
case ocispec.MediaTypeImageLayerGzip, images.MediaTypeDockerSchema2LayerGzip:
|
|
||||||
isCompressed = true
|
|
||||||
default:
|
|
||||||
// Still apply all generic media types *.tar[.+]gzip and *.tar
|
|
||||||
if strings.HasSuffix(desc.MediaType, ".tar.gzip") || strings.HasSuffix(desc.MediaType, ".tar+gzip") {
|
|
||||||
isCompressed = true
|
|
||||||
} else if !strings.HasSuffix(desc.MediaType, ".tar") {
|
|
||||||
return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", desc.MediaType)
|
return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", desc.MediaType)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var ocidesc ocispec.Descriptor
|
var ocidesc ocispec.Descriptor
|
||||||
if err := mount.WithTempMount(ctx, mounts, func(root string) error {
|
if err := mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||||
|
160
diff/windows/windows.go
Normal file
160
diff/windows/windows.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
"github.com/containerd/containerd/archive"
|
||||||
|
"github.com/containerd/containerd/archive/compression"
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/diff"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/metadata"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.DiffPlugin,
|
||||||
|
ID: "windows",
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.MetadataPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
md, err := ic.Get(plugin.MetadataPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
|
||||||
|
return NewWindowsDiff(md.(*metadata.DB).ContentStore())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type windowsDiff struct {
|
||||||
|
store content.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyDesc = ocispec.Descriptor{}
|
||||||
|
|
||||||
|
// NewWindowsDiff is the Windows container layer implementation of diff.Differ.
|
||||||
|
func NewWindowsDiff(store content.Store) (diff.Differ, error) {
|
||||||
|
return &windowsDiff{
|
||||||
|
store: store,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply applies the content associated with the provided digests onto the
|
||||||
|
// provided mounts. Archive content will be extracted and decompressed if
|
||||||
|
// necessary.
|
||||||
|
func (s *windowsDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount) (d ocispec.Descriptor, err error) {
|
||||||
|
t1 := time.Now()
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
log.G(ctx).WithFields(logrus.Fields{
|
||||||
|
"d": time.Now().Sub(t1),
|
||||||
|
"dgst": desc.Digest,
|
||||||
|
"size": desc.Size,
|
||||||
|
"media": desc.MediaType,
|
||||||
|
}).Debugf("diff applied")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
isCompressed, err := images.IsCompressedDiff(ctx, desc.MediaType)
|
||||||
|
if err != nil {
|
||||||
|
return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", desc.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
ra, err := s.store.ReaderAt(ctx, desc.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return emptyDesc, errors.Wrap(err, "failed to get reader from content store")
|
||||||
|
}
|
||||||
|
defer ra.Close()
|
||||||
|
|
||||||
|
r := content.NewReader(ra)
|
||||||
|
if isCompressed {
|
||||||
|
ds, err := compression.DecompressStream(r)
|
||||||
|
if err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
defer ds.Close()
|
||||||
|
r = ds
|
||||||
|
}
|
||||||
|
|
||||||
|
digester := digest.Canonical.Digester()
|
||||||
|
rc := &readCounter{
|
||||||
|
r: io.TeeReader(r, digester.Hash()),
|
||||||
|
}
|
||||||
|
|
||||||
|
layer, parentLayerPaths, err := mountsToLayerAndParents(mounts)
|
||||||
|
if err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO darrenstahlmsft: When this is done isolated, we should disable these.
|
||||||
|
// it currently cannot be disabled, unless we add ref counting. Since this is
|
||||||
|
// temporary, leaving it enabled is OK for now.
|
||||||
|
if err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := archive.Apply(ctx, layer, rc, archive.WithParentLayers(parentLayerPaths), archive.AsWindowsContainerLayer()); err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read any trailing data
|
||||||
|
if _, err := io.Copy(ioutil.Discard, rc); err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ocispec.Descriptor{
|
||||||
|
MediaType: ocispec.MediaTypeImageLayer,
|
||||||
|
Size: rc.c,
|
||||||
|
Digest: digester.Digest(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffMounts creates a diff between the given mounts and uploads the result
|
||||||
|
// to the content store.
|
||||||
|
func (s *windowsDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, opts ...diff.Opt) (d ocispec.Descriptor, err error) {
|
||||||
|
return emptyDesc, errdefs.ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
type readCounter struct {
|
||||||
|
r io.Reader
|
||||||
|
c int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *readCounter) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = rc.r.Read(p)
|
||||||
|
rc.c += int64(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountsToLayerAndParents(mounts []mount.Mount) (string, []string, error) {
|
||||||
|
if len(mounts) != 1 {
|
||||||
|
return "", nil, errors.Wrap(errdefs.ErrInvalidArgument, "number of mounts should always be 1 for Windows layers")
|
||||||
|
}
|
||||||
|
layer := mounts[0].Source
|
||||||
|
|
||||||
|
parentLayerPaths, err := mounts[0].GetParentPaths()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer, parentLayerPaths, nil
|
||||||
|
}
|
@ -39,8 +39,3 @@ func withExecExitStatus(s *specs.Process, es int) {
|
|||||||
func withExecArgs(s *specs.Process, args ...string) {
|
func withExecArgs(s *specs.Process, args ...string) {
|
||||||
s.Args = args
|
s.Args = args
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
withNewSnapshot = WithNewSnapshot
|
|
||||||
withImageConfig = oci.WithImageConfig
|
|
||||||
)
|
|
||||||
|
@ -39,17 +39,3 @@ func withExecExitStatus(s *specs.Process, es int) {
|
|||||||
func withExecArgs(s *specs.Process, args ...string) {
|
func withExecArgs(s *specs.Process, args ...string) {
|
||||||
s.Args = append([]string{"powershell", "-noprofile"}, args...)
|
s.Args = append([]string{"powershell", "-noprofile"}, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withImageConfig(i Image) oci.SpecOpts {
|
|
||||||
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
|
|
||||||
s.Windows.LayerFolders = dockerLayerFolders
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func withNewSnapshot(id string, i Image) NewContainerOpts {
|
|
||||||
// TODO: when windows has a snapshotter remove the withNewSnapshot helper
|
|
||||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ package images
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
@ -359,3 +360,22 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D
|
|||||||
}
|
}
|
||||||
return config.RootFS.DiffIDs, nil
|
return config.RootFS.DiffIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCompressedDiff returns true if mediaType is a known compressed diff media type.
|
||||||
|
// It returns false if the media type is a diff, but not compressed. If the media type
|
||||||
|
// is not a known diff type, it returns errdefs.ErrNotImplemented
|
||||||
|
func IsCompressedDiff(ctx context.Context, mediaType string) (bool, error) {
|
||||||
|
switch mediaType {
|
||||||
|
case ocispec.MediaTypeImageLayer, MediaTypeDockerSchema2Layer:
|
||||||
|
case ocispec.MediaTypeImageLayerGzip, MediaTypeDockerSchema2LayerGzip:
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
// Still apply all generic media types *.tar[.+]gzip and *.tar
|
||||||
|
if strings.HasSuffix(mediaType, ".tar.gzip") || strings.HasSuffix(mediaType, ".tar+gzip") {
|
||||||
|
return true, nil
|
||||||
|
} else if !strings.HasSuffix(mediaType, ".tar") {
|
||||||
|
return false, errdefs.ErrNotImplemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
package mount
|
package mount
|
||||||
|
|
||||||
import "github.com/pkg/errors"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNotImplementOnWindows is returned when an action is not implemented for windows
|
// ErrNotImplementOnWindows is returned when an action is not implemented for windows
|
||||||
@ -9,15 +16,70 @@ var (
|
|||||||
|
|
||||||
// Mount to the provided target
|
// Mount to the provided target
|
||||||
func (m *Mount) Mount(target string) error {
|
func (m *Mount) Mount(target string) error {
|
||||||
return ErrNotImplementOnWindows
|
home, layerID := filepath.Split(m.Source)
|
||||||
|
|
||||||
|
parentLayerPaths, err := m.GetParentPaths()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var di = hcsshim.DriverInfo{
|
||||||
|
HomeDir: home,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = hcsshim.ActivateLayer(di, layerID); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to activate layer %s", m.Source)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
hcsshim.DeactivateLayer(di, layerID)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = hcsshim.PrepareLayer(di, layerID, parentLayerPaths); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to prepare layer %s", m.Source)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentLayerPathsFlag is the options flag used to represent the JSON encoded
|
||||||
|
// list of parent layers required to use the layer
|
||||||
|
const ParentLayerPathsFlag = "parentLayerPaths="
|
||||||
|
|
||||||
|
// GetParentPaths of the mount
|
||||||
|
func (m *Mount) GetParentPaths() ([]string, error) {
|
||||||
|
var parentLayerPaths []string
|
||||||
|
for _, option := range m.Options {
|
||||||
|
if strings.HasPrefix(option, ParentLayerPathsFlag) {
|
||||||
|
err := json.Unmarshal([]byte(option[len(ParentLayerPathsFlag):]), &parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to unmarshal parent layer paths from mount")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parentLayerPaths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount the mount at the provided path
|
// Unmount the mount at the provided path
|
||||||
func Unmount(mount string, flags int) error {
|
func Unmount(mount string, flags int) error {
|
||||||
return ErrNotImplementOnWindows
|
var (
|
||||||
|
home, layerID = filepath.Split(mount)
|
||||||
|
di = hcsshim.DriverInfo{
|
||||||
|
HomeDir: home,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := hcsshim.UnprepareLayer(di, layerID); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to unprepare layer %s", mount)
|
||||||
|
}
|
||||||
|
if err := hcsshim.DeactivateLayer(di, layerID); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to deactivate layer %s", mount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmountAll mounts at the provided path
|
return nil
|
||||||
func UnmountAll(mount string, flags int) error {
|
}
|
||||||
return ErrNotImplementOnWindows
|
|
||||||
|
// UnmountAll unmounts from the provided path
|
||||||
|
func UnmountAll(mount string, flags int) error {
|
||||||
|
return Unmount(mount, flags)
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,7 @@ func init() {
|
|||||||
Requires: []plugin.Type{
|
Requires: []plugin.Type{
|
||||||
plugin.DiffPlugin,
|
plugin.DiffPlugin,
|
||||||
},
|
},
|
||||||
Config: &config{
|
Config: defaultDifferConfig,
|
||||||
Order: []string{"walking"},
|
|
||||||
},
|
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
differs, err := ic.GetByType(plugin.DiffPlugin)
|
differs, err := ic.GetByType(plugin.DiffPlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
7
services/diff/service_unix.go
Normal file
7
services/diff/service_unix.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package diff
|
||||||
|
|
||||||
|
var defaultDifferConfig = &config{
|
||||||
|
Order: []string{"walking"},
|
||||||
|
}
|
7
services/diff/service_windows.go
Normal file
7
services/diff/service_windows.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package diff
|
||||||
|
|
||||||
|
var defaultDifferConfig = &config{
|
||||||
|
Order: []string{"windows"},
|
||||||
|
}
|
@ -4,16 +4,23 @@ package windows
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/fs"
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/snapshots"
|
"github.com/containerd/containerd/snapshots"
|
||||||
|
"github.com/containerd/containerd/snapshots/storage"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrNotImplemented is returned when an action is not implemented
|
|
||||||
ErrNotImplemented = errors.New("not implemented")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -28,12 +35,38 @@ func init() {
|
|||||||
|
|
||||||
type snapshotter struct {
|
type snapshotter struct {
|
||||||
root string
|
root string
|
||||||
|
info hcsshim.DriverInfo
|
||||||
|
ms *storage.MetaStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshotter returns a new windows snapshotter
|
// NewSnapshotter returns a new windows snapshotter
|
||||||
func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
|
func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
|
||||||
|
fsType, err := getFileSystemType(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if strings.ToLower(fsType) != "ntfs" {
|
||||||
|
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%s is not on an NTFS volume - only NTFS volumes are supported", root)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil && !os.IsExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &snapshotter{
|
return &snapshotter{
|
||||||
|
info: hcsshim.DriverInfo{
|
||||||
|
HomeDir: filepath.Join(root, "snapshots"),
|
||||||
|
},
|
||||||
root: root,
|
root: root,
|
||||||
|
ms: ms,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,50 +75,270 @@ func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
|
|||||||
//
|
//
|
||||||
// Should be used for parent resolution, existence checks and to discern
|
// Should be used for parent resolution, existence checks and to discern
|
||||||
// the kind of snapshot.
|
// the kind of snapshot.
|
||||||
func (o *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
|
func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
_, info, _, err := storage.GetInfo(ctx, key)
|
||||||
|
return info, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
|
func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
if err := t.Commit(); err != nil {
|
||||||
panic("not implemented")
|
return snapshots.Info{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
return info, nil
|
||||||
panic("not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
func (s *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
_, info, usage, err := storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Kind == snapshots.KindActive {
|
||||||
|
du := fs.Usage{
|
||||||
|
Size: 0,
|
||||||
|
}
|
||||||
|
usage = snapshots.Usage(du)
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return s.createSnapshot(ctx, snapshots.KindActive, key, parent, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return s.createSnapshot(ctx, snapshots.KindView, key, parent, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mounts returns the mounts for the transaction identified by key. Can be
|
// Mounts returns the mounts for the transaction identified by key. Can be
|
||||||
// called on an read-write or readonly transaction.
|
// called on an read-write or readonly transaction.
|
||||||
//
|
//
|
||||||
// This can be used to recover mounts after calling View or Prepare.
|
// This can be used to recover mounts after calling View or Prepare.
|
||||||
func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
|
func (s *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
snapshot, err := storage.GetSnapshot(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get snapshot mount")
|
||||||
|
}
|
||||||
|
return s.mounts(snapshot), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
usage := fs.Usage{
|
||||||
|
Size: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to commit snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove abandons the transaction identified by key. All resources
|
// Remove abandons the transaction identified by key. All resources
|
||||||
// associated with the key will be removed.
|
// associated with the key will be removed.
|
||||||
func (o *snapshotter) Remove(ctx context.Context, key string) error {
|
func (s *snapshotter) Remove(ctx context.Context, key string) error {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
id, _, err := storage.Remove(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to remove")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := s.getSnapshotDir(id)
|
||||||
|
renamedID := "rm-" + id
|
||||||
|
renamed := filepath.Join(s.root, "snapshots", "rm-"+id)
|
||||||
|
if err := os.Rename(path, renamed); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Commit(); err != nil {
|
||||||
|
if err1 := os.Rename(renamed, path); err1 != nil {
|
||||||
|
// May cause inconsistent data on disk
|
||||||
|
log.G(ctx).WithError(err1).WithField("path", renamed).Errorf("Failed to rename after failed commit")
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "failed to commit")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hcsshim.DestroyLayer(s.info, renamedID); err != nil {
|
||||||
|
// Must be cleaned up, any "rm-*" could be removed if no active transactions
|
||||||
|
log.G(ctx).WithError(err).WithField("path", renamed).Warnf("Failed to remove root filesystem")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the committed snapshots.
|
// Walk the committed snapshots.
|
||||||
func (o *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
|
func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
|
||||||
panic("not implemented")
|
ctx, t, err := s.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
return storage.WalkInfo(ctx, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the snapshotter
|
// Close closes the snapshotter
|
||||||
func (o *snapshotter) Close() error {
|
func (s *snapshotter) Close() error {
|
||||||
panic("not implemented")
|
return s.ms.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) mounts(sn storage.Snapshot) []mount.Mount {
|
||||||
|
var (
|
||||||
|
roFlag string
|
||||||
|
source string
|
||||||
|
parentLayerPaths []string
|
||||||
|
)
|
||||||
|
|
||||||
|
if sn.Kind == snapshots.KindView {
|
||||||
|
roFlag = "ro"
|
||||||
|
} else {
|
||||||
|
roFlag = "rw"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sn.ParentIDs) == 0 || sn.Kind == snapshots.KindActive {
|
||||||
|
source = s.getSnapshotDir(sn.ID)
|
||||||
|
parentLayerPaths = s.parentIDsToParentPaths(sn.ParentIDs)
|
||||||
|
} else {
|
||||||
|
source = s.getSnapshotDir(sn.ParentIDs[0])
|
||||||
|
parentLayerPaths = s.parentIDsToParentPaths(sn.ParentIDs[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// error is not checked here, as a string array will never fail to Marshal
|
||||||
|
parentLayersJSON, _ := json.Marshal(parentLayerPaths)
|
||||||
|
parentLayersOption := mount.ParentLayerPathsFlag + string(parentLayersJSON)
|
||||||
|
|
||||||
|
var mounts []mount.Mount
|
||||||
|
mounts = append(mounts, mount.Mount{
|
||||||
|
Source: source,
|
||||||
|
Type: "windows-layer",
|
||||||
|
Options: []string{
|
||||||
|
roFlag,
|
||||||
|
parentLayersOption,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return mounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) getSnapshotDir(id string) string {
|
||||||
|
return filepath.Join(s.root, "snapshots", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
ctx, t, err := s.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
|
||||||
|
newSnapshot, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to create snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == snapshots.KindActive {
|
||||||
|
parentLayerPaths := s.parentIDsToParentPaths(newSnapshot.ParentIDs)
|
||||||
|
|
||||||
|
var parentPath string
|
||||||
|
if len(parentLayerPaths) != 0 {
|
||||||
|
parentPath = parentLayerPaths[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hcsshim.CreateSandboxLayer(s.info, newSnapshot.ID, parentPath, parentLayerPaths); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to create sandbox layer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(darrenstahlmsft): Allow changing sandbox size
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Commit(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "commit failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.mounts(newSnapshot), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotter) parentIDsToParentPaths(parentIDs []string) []string {
|
||||||
|
var parentLayerPaths []string
|
||||||
|
for _, ID := range parentIDs {
|
||||||
|
parentLayerPaths = append(parentLayerPaths, s.getSnapshotDir(ID))
|
||||||
|
}
|
||||||
|
return parentLayerPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFileSystemType obtains the type of a file system through GetVolumeInformation
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx
|
||||||
|
func getFileSystemType(path string) (fsType string, hr error) {
|
||||||
|
drive := filepath.VolumeName(path)
|
||||||
|
if len(drive) != 2 {
|
||||||
|
return "", errors.New("getFileSystemType path must start with a drive letter")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
procGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW")
|
||||||
|
buf = make([]uint16, 255)
|
||||||
|
size = windows.MAX_PATH + 1
|
||||||
|
)
|
||||||
|
drive += `\`
|
||||||
|
n := uintptr(unsafe.Pointer(nil))
|
||||||
|
r0, _, _ := syscall.Syscall9(procGetVolumeInformation.Addr(), 8, uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(drive))), n, n, n, n, n, uintptr(unsafe.Pointer(&buf[0])), uintptr(size), 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = syscall.Errno(win32FromHresult(r0))
|
||||||
|
}
|
||||||
|
fsType = windows.UTF16ToString(buf)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32FromHresult is a helper function to get the win32 error code from an HRESULT
|
||||||
|
func win32FromHresult(hr uintptr) uintptr {
|
||||||
|
if hr&0x1fff0000 == 0x00070000 {
|
||||||
|
return hr & 0xffff
|
||||||
|
}
|
||||||
|
return hr
|
||||||
}
|
}
|
||||||
|
10
sys/filesys_unix.go
Normal file
10
sys/filesys_unix.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package sys
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// ForceRemoveAll on unix is just a wrapper for os.RemoveAll
|
||||||
|
func ForceRemoveAll(path string) error {
|
||||||
|
return os.RemoveAll(path)
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
winio "github.com/Microsoft/go-winio"
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
|
// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
|
||||||
@ -234,3 +235,13 @@ func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle,
|
|||||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
|
||||||
return h, e
|
return h, e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order
|
||||||
|
// to delete container layers.
|
||||||
|
func ForceRemoveAll(path string) error {
|
||||||
|
info := hcsshim.DriverInfo{
|
||||||
|
HomeDir: filepath.Dir(path),
|
||||||
|
}
|
||||||
|
|
||||||
|
return hcsshim.DestroyLayer(info, filepath.Base(path))
|
||||||
|
}
|
||||||
|
@ -4,14 +4,12 @@ package windows
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Microsoft/hcsshim"
|
"github.com/Microsoft/hcsshim"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@ -49,14 +47,14 @@ func newWindowsContainerConfig(ctx context.Context, owner, id string, spec *spec
|
|||||||
}
|
}
|
||||||
conf.IgnoreFlushesDuringBoot = spec.Windows.IgnoreFlushesDuringBoot
|
conf.IgnoreFlushesDuringBoot = spec.Windows.IgnoreFlushesDuringBoot
|
||||||
|
|
||||||
if len(spec.Windows.LayerFolders) < 1 {
|
if len(spec.Windows.LayerFolders) < 2 {
|
||||||
return nil, errors.Wrap(errdefs.ErrInvalidArgument,
|
return nil, errors.Wrap(errdefs.ErrInvalidArgument,
|
||||||
"spec.Windows.LayerFolders must have at least 1 layers")
|
"spec.Windows.LayerFolders must have at least 2 layers")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
layerFolders = spec.Windows.LayerFolders
|
layerFolderPath = spec.Windows.LayerFolders[0]
|
||||||
homeDir = filepath.Dir(layerFolders[0])
|
layerFolders = spec.Windows.LayerFolders[1:]
|
||||||
layerFolderPath = filepath.Join(homeDir, id)
|
layerID = filepath.Base(layerFolderPath)
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: use the create request Mount for those
|
// TODO: use the create request Mount for those
|
||||||
@ -71,39 +69,12 @@ func newWindowsContainerConfig(ctx context.Context, owner, id string, spec *spec
|
|||||||
Path: layerPath,
|
Path: layerPath,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
di = hcsshim.DriverInfo{
|
|
||||||
Flavour: 1, // filter driver
|
|
||||||
HomeDir: homeDir,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
conf.LayerFolderPath = layerFolderPath
|
conf.LayerFolderPath = layerFolderPath
|
||||||
|
|
||||||
// TODO: Once there is a snapshotter for windows, this can be deleted.
|
var di = hcsshim.DriverInfo{
|
||||||
// The R/W Layer should come from the Rootfs Mounts provided
|
HomeDir: filepath.Dir(layerFolderPath),
|
||||||
//
|
|
||||||
// Windows doesn't support creating a container with a readonly
|
|
||||||
// filesystem, so always create a RW one
|
|
||||||
if err = hcsshim.CreateSandboxLayer(di, id, layerFolders[0], layerFolders); err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
|
|
||||||
id, layerFolders, di)
|
|
||||||
}
|
}
|
||||||
defer func() {
|
conf.VolumePath, err = hcsshim.GetLayerMountPath(di, layerID)
|
||||||
if err != nil {
|
|
||||||
removeLayer(ctx, conf.LayerFolderPath)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err = hcsshim.ActivateLayer(di, id); err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to activate layer %s", conf.LayerFolderPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = hcsshim.PrepareLayer(di, id, layerFolders); err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to prepare layer %s", conf.LayerFolderPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.VolumePath, err = hcsshim.GetLayerMountPath(di, id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
|
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
|
||||||
}
|
}
|
||||||
@ -146,41 +117,6 @@ func newWindowsContainerConfig(ctx context.Context, owner, id string, spec *spec
|
|||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeLayer deletes the given layer, all associated containers must have
|
|
||||||
// been shutdown for this to succeed.
|
|
||||||
func removeLayer(ctx context.Context, path string) error {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
layerID = filepath.Base(path)
|
|
||||||
parentPath = filepath.Dir(path)
|
|
||||||
di = hcsshim.DriverInfo{
|
|
||||||
Flavour: 1, // filter driver
|
|
||||||
HomeDir: parentPath,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if err = hcsshim.UnprepareLayer(di, layerID); err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = hcsshim.DeactivateLayer(di, layerID); err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID))
|
|
||||||
if err = os.Rename(path, removePath); err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path)
|
|
||||||
removePath = path
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = hcsshim.DestroyLayer(di, removePath); err != nil {
|
|
||||||
log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig {
|
func newProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig {
|
||||||
conf := &hcsshim.ProcessConfig{
|
conf := &hcsshim.ProcessConfig{
|
||||||
EmulateConsole: pset.src.Terminal,
|
EmulateConsole: pset.src.Terminal,
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package windows
|
|
||||||
|
|
||||||
// TODO: remove this file (i.e. meta.go) once we have a snapshotter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newLayerFolderStore(tx *bolt.Tx) *layerFolderStore {
|
|
||||||
return &layerFolderStore{tx}
|
|
||||||
}
|
|
||||||
|
|
||||||
type layerFolderStore struct {
|
|
||||||
tx *bolt.Tx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *layerFolderStore) Create(id, layer string) error {
|
|
||||||
bkt, err := s.tx.CreateBucketIfNotExists([]byte(pluginID))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create bucket %s", pluginID)
|
|
||||||
}
|
|
||||||
err = bkt.Put([]byte(id), []byte(layer))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to store entry %s:%s", id, layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *layerFolderStore) Get(id string) (string, error) {
|
|
||||||
bkt := s.tx.Bucket([]byte(pluginID))
|
|
||||||
if bkt == nil {
|
|
||||||
return "", errors.Wrapf(errdefs.ErrNotFound, "bucket %s", pluginID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bkt.Get([]byte(id))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *layerFolderStore) Delete(id string) error {
|
|
||||||
bkt := s.tx.Bucket([]byte(pluginID))
|
|
||||||
if bkt == nil {
|
|
||||||
return errors.Wrapf(errdefs.ErrNotFound, "bucket %s", pluginID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bkt.Delete([]byte(id)); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to delete entry %s", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -10,13 +10,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Microsoft/hcsshim"
|
"github.com/Microsoft/hcsshim"
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
containerdtypes "github.com/containerd/containerd/api/types"
|
"github.com/containerd/containerd/api/types"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/events"
|
"github.com/containerd/containerd/events"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/metadata"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
"github.com/containerd/containerd/runtime"
|
"github.com/containerd/containerd/runtime"
|
||||||
@ -55,12 +54,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
|
|||||||
if err := os.MkdirAll(ic.Root, 0700); err != nil {
|
if err := os.MkdirAll(ic.Root, 0700); err != nil {
|
||||||
return nil, errors.Wrapf(err, "could not create state directory at %s", ic.Root)
|
return nil, errors.Wrapf(err, "could not create state directory at %s", ic.Root)
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := ic.Get(plugin.MetadataPlugin)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &windowsRuntime{
|
r := &windowsRuntime{
|
||||||
root: ic.Root,
|
root: ic.Root,
|
||||||
pidPool: newPidPool(),
|
pidPool: newPidPool(),
|
||||||
@ -70,7 +63,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
|
|||||||
// TODO(mlaventure): windows needs a stat monitor
|
// TODO(mlaventure): windows needs a stat monitor
|
||||||
monitor: nil,
|
monitor: nil,
|
||||||
tasks: runtime.NewTaskList(),
|
tasks: runtime.NewTaskList(),
|
||||||
db: m.(*metadata.DB),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load our existing containers and kill/delete them. We don't support
|
// Load our existing containers and kill/delete them. We don't support
|
||||||
@ -91,7 +83,6 @@ type windowsRuntime struct {
|
|||||||
|
|
||||||
monitor runtime.TaskMonitor
|
monitor runtime.TaskMonitor
|
||||||
tasks *runtime.TaskList
|
tasks *runtime.TaskList
|
||||||
db *metadata.DB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *windowsRuntime) ID() string {
|
func (r *windowsRuntime) ID() string {
|
||||||
@ -125,7 +116,17 @@ func (r *windowsRuntime) Create(ctx context.Context, id string, opts runtime.Cre
|
|||||||
createOpts.TerminateDuration = defaultTerminateDuration
|
createOpts.TerminateDuration = defaultTerminateDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.newTask(ctx, namespace, id, spec, opts.IO, createOpts)
|
if len(opts.Rootfs) == 0 {
|
||||||
|
return nil, errors.Wrap(errdefs.ErrInvalidArgument, "rootfs was not provided to container create")
|
||||||
|
}
|
||||||
|
spec.Windows.LayerFolders = append(spec.Windows.LayerFolders, opts.Rootfs[0].Source)
|
||||||
|
parentLayerPaths, err := opts.Rootfs[0].GetParentPaths()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
spec.Windows.LayerFolders = append(spec.Windows.LayerFolders, parentLayerPaths...)
|
||||||
|
|
||||||
|
return r.newTask(ctx, namespace, id, opts.Rootfs, spec, opts.IO, createOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *windowsRuntime) Get(ctx context.Context, id string) (runtime.Task, error) {
|
func (r *windowsRuntime) Get(ctx context.Context, id string) (runtime.Task, error) {
|
||||||
@ -209,14 +210,19 @@ func (r *windowsRuntime) Delete(ctx context.Context, t runtime.Task) (*runtime.E
|
|||||||
ns, _ := namespaces.Namespace(ctx)
|
ns, _ := namespaces.Namespace(ctx)
|
||||||
serviceCtx := log.WithLogger(context.Background(), log.GetLogger(ctx))
|
serviceCtx := log.WithLogger(context.Background(), log.GetLogger(ctx))
|
||||||
serviceCtx = namespaces.WithNamespace(serviceCtx, ns)
|
serviceCtx = namespaces.WithNamespace(serviceCtx, ns)
|
||||||
r.serviceTask(serviceCtx, ns, wt.id+"_servicing", wt.spec)
|
r.serviceTask(serviceCtx, ns, wt.id+"_servicing", wt.rootfs, wt.spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mount.UnmountAll(wt.rootfs[0].Source, 0); err != nil {
|
||||||
|
log.G(ctx).WithError(err).WithField("path", wt.rootfs[0].Source).
|
||||||
|
Warn("failed to unmount rootfs on failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We were never started, return failure
|
// We were never started, return failure
|
||||||
return rtExit, nil
|
return rtExit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec *specs.Spec, io runtime.IO, createOpts *hcsshimtypes.CreateOptions) (*task, error) {
|
func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, rootfs []mount.Mount, spec *specs.Spec, io runtime.IO, createOpts *hcsshimtypes.CreateOptions) (*task, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
pset *pipeSet
|
pset *pipeSet
|
||||||
@ -241,6 +247,18 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := mount.All(rootfs, ""); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to mount rootfs")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err := mount.UnmountAll(rootfs[0].Source, 0); err != nil {
|
||||||
|
log.G(ctx).WithError(err).WithField("path", rootfs[0].Source).
|
||||||
|
Warn("failed to unmount rootfs on failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
conf *hcsshim.ContainerConfig
|
conf *hcsshim.ContainerConfig
|
||||||
nsid = namespace + "-" + id
|
nsid = namespace + "-" + id
|
||||||
@ -248,31 +266,6 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
|
|||||||
if conf, err = newWindowsContainerConfig(ctx, hcsshimOwner, nsid, spec); err != nil {
|
if conf, err = newWindowsContainerConfig(ctx, hcsshimOwner, nsid, spec); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
removeLayer(ctx, conf.LayerFolderPath)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// TODO: remove this once we have a windows snapshotter
|
|
||||||
// Store the LayerFolder in the db so we can clean it if we die
|
|
||||||
if err = r.db.Update(func(tx *bolt.Tx) error {
|
|
||||||
s := newLayerFolderStore(tx)
|
|
||||||
return s.Create(nsid, conf.LayerFolderPath)
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if dbErr := r.db.Update(func(tx *bolt.Tx) error {
|
|
||||||
s := newLayerFolderStore(tx)
|
|
||||||
return s.Delete(nsid)
|
|
||||||
}); dbErr != nil {
|
|
||||||
log.G(ctx).WithField("id", id).
|
|
||||||
Error("failed to remove key from metadata")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ctr, err := hcsshim.CreateContainer(nsid, conf)
|
ctr, err := hcsshim.CreateContainer(nsid, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -301,6 +294,7 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
|
|||||||
hyperV: spec.Windows.HyperV != nil,
|
hyperV: spec.Windows.HyperV != nil,
|
||||||
publisher: r.publisher,
|
publisher: r.publisher,
|
||||||
rwLayer: conf.LayerFolderPath,
|
rwLayer: conf.LayerFolderPath,
|
||||||
|
rootfs: rootfs,
|
||||||
pidPool: r.pidPool,
|
pidPool: r.pidPool,
|
||||||
hcsContainer: ctr,
|
hcsContainer: ctr,
|
||||||
terminateDuration: createOpts.TerminateDuration,
|
terminateDuration: createOpts.TerminateDuration,
|
||||||
@ -312,11 +306,12 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
|
|||||||
}
|
}
|
||||||
r.tasks.Add(ctx, t)
|
r.tasks.Add(ctx, t)
|
||||||
|
|
||||||
var rootfs []*containerdtypes.Mount
|
var eventRootfs []*types.Mount
|
||||||
for _, l := range append([]string{t.rwLayer}, spec.Windows.LayerFolders...) {
|
for _, m := range rootfs {
|
||||||
rootfs = append(rootfs, &containerdtypes.Mount{
|
eventRootfs = append(eventRootfs, &types.Mount{
|
||||||
Type: "windows-layer",
|
Type: m.Type,
|
||||||
Source: l,
|
Source: m.Source,
|
||||||
|
Options: m.Options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +326,7 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
|
|||||||
Terminal: io.Terminal,
|
Terminal: io.Terminal,
|
||||||
},
|
},
|
||||||
Pid: t.pid,
|
Pid: t.pid,
|
||||||
Rootfs: rootfs,
|
Rootfs: eventRootfs,
|
||||||
// TODO: what should be in Bundle for windows?
|
// TODO: what should be in Bundle for windows?
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -360,34 +355,10 @@ func (r *windowsRuntime) cleanup(ctx context.Context) {
|
|||||||
container.Wait()
|
container.Wait()
|
||||||
}
|
}
|
||||||
container.Close()
|
container.Close()
|
||||||
|
|
||||||
// TODO: remove this once we have a windows snapshotter
|
|
||||||
var layerFolderPath string
|
|
||||||
if err := r.db.View(func(tx *bolt.Tx) error {
|
|
||||||
s := newLayerFolderStore(tx)
|
|
||||||
l, e := s.Get(p.ID)
|
|
||||||
if err == nil {
|
|
||||||
layerFolderPath = l
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}); err == nil && layerFolderPath != "" {
|
|
||||||
removeLayer(ctx, layerFolderPath)
|
|
||||||
if dbErr := r.db.Update(func(tx *bolt.Tx) error {
|
|
||||||
s := newLayerFolderStore(tx)
|
|
||||||
return s.Delete(p.ID)
|
|
||||||
}); dbErr != nil {
|
|
||||||
log.G(ctx).WithField("id", p.ID).
|
|
||||||
Error("failed to remove key from metadata")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.G(ctx).WithField("id", p.ID).
|
|
||||||
Debug("key not found in metadata, R/W layer may be leaked")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *windowsRuntime) serviceTask(ctx context.Context, namespace, id string, spec *specs.Spec) {
|
func (r *windowsRuntime) serviceTask(ctx context.Context, namespace, id string, rootfs []mount.Mount, spec *specs.Spec) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
t *task
|
t *task
|
||||||
@ -397,7 +368,7 @@ func (r *windowsRuntime) serviceTask(ctx context.Context, namespace, id string,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
t, err = r.newTask(ctx, namespace, id, spec, io, createOpts)
|
t, err = r.newTask(ctx, namespace, id, rootfs, spec, io, createOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.G(ctx).WithError(err).WithField("id", id).
|
log.G(ctx).WithError(err).WithField("id", id).
|
||||||
Warn("failed to created servicing task")
|
Warn("failed to created servicing task")
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
eventstypes "github.com/containerd/containerd/api/events"
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/events"
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/runtime"
|
"github.com/containerd/containerd/runtime"
|
||||||
"github.com/containerd/containerd/windows/hcsshimtypes"
|
"github.com/containerd/containerd/windows/hcsshimtypes"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
@ -33,6 +34,7 @@ type task struct {
|
|||||||
|
|
||||||
publisher events.Publisher
|
publisher events.Publisher
|
||||||
rwLayer string
|
rwLayer string
|
||||||
|
rootfs []mount.Mount
|
||||||
|
|
||||||
pidPool *pidPool
|
pidPool *pidPool
|
||||||
hcsContainer hcsshim.Container
|
hcsContainer hcsshim.Container
|
||||||
@ -406,6 +408,5 @@ func (t *task) cleanup() {
|
|||||||
for _, p := range t.processes {
|
for _, p := range t.processes {
|
||||||
t.removeProcessNL(p.id)
|
t.removeProcessNL(p.id)
|
||||||
}
|
}
|
||||||
removeLayer(context.Background(), t.rwLayer)
|
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user