blob: 89f58a81c60760dd1120bb94dbf5855c45fba163 [file] [edit]
package daemon
import (
"path/filepath"
"runtime"
"testing"
cerrdefs "github.com/containerd/errdefs"
"github.com/moby/moby/api/types/checkpoint"
"github.com/moby/moby/v2/daemon/container"
"github.com/moby/moby/v2/daemon/server/backend"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestValidateCheckpointID(t *testing.T) {
type testCase struct {
name string
checkpointID string
valid bool
}
tests := []testCase{
{name: "restricted name", checkpointID: "a1", valid: true},
{name: "space", checkpointID: "space name"},
{name: "unicode", checkpointID: "unicode-世界"},
{name: "dots", checkpointID: "name.with-dots", valid: true},
{name: "empty", checkpointID: ""},
{name: "dot", checkpointID: "."},
{name: "dot-dot", checkpointID: ".."},
{name: "traversal", checkpointID: "../outside"},
{name: "nested traversal", checkpointID: "foo/../outside"},
{name: "separator", checkpointID: "foo/bar"},
{name: "windows separator", checkpointID: `foo\bar`},
{name: "NUL", checkpointID: "contains\x00nul"},
{name: "absolute path", checkpointID: filepath.Join(string(filepath.Separator), "absolute")},
}
if runtime.GOOS == "windows" {
tests = append(tests,
testCase{name: "dot trailing space", checkpointID: ". "},
testCase{name: "dot-dot trailing space", checkpointID: ".. "},
testCase{name: "trailing dot", checkpointID: "name."},
testCase{name: "trailing space", checkpointID: "name "},
)
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := validateCheckpointID(tc.checkpointID)
if tc.valid {
assert.NilError(t, err)
return
}
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
})
}
}
func TestCheckpointOperationsValidateBeforeContainerLookup(t *testing.T) {
daemon := &Daemon{}
tests := []struct {
name string
operation func() error
}{
{
name: "create",
operation: func() error {
return daemon.CheckpointCreate("missing", checkpoint.CreateRequest{CheckpointID: "../outside"})
},
},
{
name: "delete",
operation: func() error {
return daemon.CheckpointDelete("missing", backend.CheckpointDeleteOptions{CheckpointID: "../outside"})
},
},
{
name: "restore",
operation: func() error {
return daemon.ContainerStart(t.Context(), "missing", "../outside", "")
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := tc.operation()
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
assert.Check(t, is.ErrorContains(err, `invalid checkpoint ID "../outside"`))
})
}
}
func TestCheckpointListMissingDefaultDirectory(t *testing.T) {
t.Parallel()
root := t.TempDir()
ctr := container.NewBaseContainer("container-id", root)
store := container.NewMemoryStore()
store.Add(ctr.ID, ctr)
daemon := &Daemon{containers: store}
checkpoints, err := daemon.CheckpointList(ctr.ID, backend.CheckpointListOptions{})
assert.NilError(t, err)
assert.Assert(t, is.Len(checkpoints, 0))
}