Merge component 'engine' from git@github.com:moby/moby master

This commit is contained in:
GordonTheTurtle
2018-01-26 17:03:45 +00:00
10 changed files with 171 additions and 24 deletions

View File

@ -443,6 +443,10 @@ definitions:
OomKillDisable:
description: "Disable OOM Killer for the container."
type: "boolean"
Init:
description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used."
type: "boolean"
x-nullable: true
PidsLimit:
description: "Tune a container's pids limit. Set -1 for unlimited."
type: "integer"

View File

@ -23,7 +23,7 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
urlStr := "/containers/" + containerID + "/archive"
response, err := cli.head(ctx, urlStr, query, nil)
if err != nil {
return types.ContainerPathStat{}, err
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path)
}
defer ensureReaderClosed(response)
return getContainerPathStatFromHeader(response.header)
@ -31,9 +31,9 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
// CopyToContainer copies content into the container filesystem.
// Note that `content` must be a Reader for a TAR archive
func (cli *Client) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error {
func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options types.CopyToContainerOptions) error {
query := url.Values{}
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
if !options.AllowOverwriteDirWithFile {
query.Set("noOverwriteDirNonDir", "true")
@ -43,11 +43,11 @@ func (cli *Client) CopyToContainer(ctx context.Context, container, path string,
query.Set("copyUIDGID", "true")
}
apiPath := "/containers/" + container + "/archive"
apiPath := "/containers/" + containerID + "/archive"
response, err := cli.putRaw(ctx, apiPath, query, content, nil)
if err != nil {
return err
return wrapResponseError(err, response, "container:path", containerID+":"+dstPath)
}
defer ensureReaderClosed(response)
@ -60,14 +60,14 @@ func (cli *Client) CopyToContainer(ctx context.Context, container, path string,
// CopyFromContainer gets the content from the container and returns it as a Reader
// for a TAR archive to manipulate it in the host. It's up to the caller to close the reader.
func (cli *Client) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
query := make(url.Values, 1)
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
apiPath := "/containers/" + container + "/archive"
apiPath := "/containers/" + containerID + "/archive"
response, err := cli.get(ctx, apiPath, query, nil)
if err != nil {
return nil, types.ContainerPathStat{}, err
return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath)
}
if response.statusCode != http.StatusOK {

View File

@ -25,6 +25,16 @@ func TestContainerStatPathError(t *testing.T) {
}
}
func TestContainerStatPathNotFoundError(t *testing.T) {
client := &Client{
client: newMockClient(errorMock(http.StatusNotFound, "Not found")),
}
_, err := client.ContainerStatPath(context.Background(), "container_id", "path")
if !IsErrNotFound(err) {
t.Fatalf("expected a not found error, got %v", err)
}
}
func TestContainerStatPathNoHeaderError(t *testing.T) {
client := &Client{
client: newMockClient(func(req *http.Request) (*http.Response, error) {
@ -95,6 +105,16 @@ func TestCopyToContainerError(t *testing.T) {
}
}
func TestCopyToContainerNotFoundError(t *testing.T) {
client := &Client{
client: newMockClient(errorMock(http.StatusNotFound, "Not found")),
}
err := client.CopyToContainer(context.Background(), "container_id", "path/to/file", bytes.NewReader([]byte("")), types.CopyToContainerOptions{})
if !IsErrNotFound(err) {
t.Fatalf("expected a not found error, got %v", err)
}
}
func TestCopyToContainerNotStatusOKError(t *testing.T) {
client := &Client{
client: newMockClient(errorMock(http.StatusNoContent, "No content")),
@ -161,6 +181,16 @@ func TestCopyFromContainerError(t *testing.T) {
}
}
func TestCopyFromContainerNotFoundError(t *testing.T) {
client := &Client{
client: newMockClient(errorMock(http.StatusNotFound, "Not found")),
}
_, _, err := client.CopyFromContainer(context.Background(), "container_id", "path/to/file")
if !IsErrNotFound(err) {
t.Fatalf("expected a not found error, got %v", err)
}
}
func TestCopyFromContainerNotStatusOKError(t *testing.T) {
client := &Client{
client: newMockClient(errorMock(http.StatusNoContent, "No content")),

View File

@ -299,7 +299,10 @@ func (svm *serviceVM) createUnionMount(mountName string, mvds ...hcsshim.MappedV
}
var cmd string
if mvds[0].ReadOnly {
if len(mvds) == 1 {
// `FROM SCRATCH` case and the only layer. No overlay required.
cmd = fmt.Sprintf("mount %s %s", mvds[0].ContainerPath, mountName)
} else if mvds[0].ReadOnly {
// Readonly overlay
cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s %s",
strings.Join(lowerLayers, ","),

View File

@ -236,7 +236,9 @@ keywords: "API, Docker, rcli, REST, documentation"
* `POST /secrets/{id}/update` updates the secret `id`.
* `POST /services/(id or name)/update` now accepts service name or prefix of service id as a parameter.
* `POST /containers/create` added 2 built-in log-opts that work on all logging drivers,
`mode` (`blocking`|`non-blocking`), and `max-buffer-size` (e.g. `2m`) which enables a non-blocking log buffer.
`mode` (`blocking`|`non-blocking`), and `max-buffer-size` (e.g. `2m`) which enables a non-blocking log buffer.
* `POST /containers/create` now takes `HostConfig.Init` field to run an init
inside the container that forwards signals and reaps processes.
## v1.24 API changes

View File

@ -142,11 +142,7 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
return err
}
}
if err := tx.setOS(layer.layerStore.os); err != nil {
return err
}
return nil
return tx.setOS(layer.layerStore.os)
}
func newVerifiedReadCloser(rc io.ReadCloser, dgst digest.Digest) (io.ReadCloser, error) {

View File

@ -639,14 +639,10 @@ func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
return errors.Wrap(err, "error unmounting plugin data")
}
removeDir := pluginDir + "-removing"
if err := os.Rename(pluginDir, removeDir); err != nil {
return errors.Wrap(err, "error performing atomic remove of plugin dir")
if err := atomicRemoveAll(pluginDir); err != nil {
return err
}
if err := system.EnsureRemoveAll(removeDir); err != nil {
return errors.Wrap(err, "error removing plugin dir")
}
pm.config.Store.Remove(p)
pm.config.LogPluginEvent(id, name, "remove")
pm.publisher.Publish(EventRemove{Plugin: p.PluginObj})
@ -835,3 +831,35 @@ func splitConfigRootFSFromTar(in io.ReadCloser, config *[]byte) io.ReadCloser {
}()
return pr
}
func atomicRemoveAll(dir string) error {
renamed := dir + "-removing"
err := os.Rename(dir, renamed)
switch {
case os.IsNotExist(err), err == nil:
// even if `dir` doesn't exist, we can still try and remove `renamed`
case os.IsExist(err):
// Some previous remove failed, check if the origin dir exists
if e := system.EnsureRemoveAll(renamed); e != nil {
return errors.Wrap(err, "rename target already exists and could not be removed")
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
// origin doesn't exist, nothing left to do
return nil
}
// attempt to rename again
if err := os.Rename(dir, renamed); err != nil {
return errors.Wrap(err, "failed to rename dir for atomic removal")
}
default:
return errors.Wrap(err, "failed to rename dir for atomic removal")
}
if err := system.EnsureRemoveAll(renamed); err != nil {
os.Rename(renamed, dir)
return err
}
return nil
}

View File

@ -0,0 +1,81 @@
package plugin
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestAtomicRemoveAllNormal(t *testing.T) {
dir, err := ioutil.TempDir("", "atomic-remove-with-normal")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
if err := atomicRemoveAll(dir); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(dir); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
if _, err := os.Stat(dir + "-removing"); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
}
func TestAtomicRemoveAllAlreadyExists(t *testing.T) {
dir, err := ioutil.TempDir("", "atomic-remove-already-exists")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
if err := os.MkdirAll(dir+"-removing", 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir + "-removing")
if err := atomicRemoveAll(dir); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(dir); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
if _, err := os.Stat(dir + "-removing"); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
}
func TestAtomicRemoveAllNotExist(t *testing.T) {
if err := atomicRemoveAll("/not-exist"); err != nil {
t.Fatal(err)
}
dir, err := ioutil.TempDir("", "atomic-remove-already-exists")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // just try to make sure this gets cleaned up
// create the removing dir, but not the "real" one
foo := filepath.Join(dir, "foo")
removing := dir + "-removing"
if err := os.MkdirAll(removing, 0755); err != nil {
t.Fatal(err)
}
if err := atomicRemoveAll(dir); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(foo); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
if _, err := os.Stat(removing); !os.IsNotExist(err) {
t.Fatalf("dir should be gone: %v", err)
}
}

View File

@ -7,7 +7,7 @@ github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
github.com/gorilla/context v1.1
github.com/gorilla/mux v1.1
github.com/Microsoft/opengcs v0.3.5
github.com/Microsoft/opengcs v0.3.6
github.com/kr/pty 5cf931ef8f
github.com/mattn/go-shellwords v1.0.3
github.com/sirupsen/logrus v1.0.3

View File

@ -84,8 +84,11 @@ func (config *Config) RunProcess(commandLine string, stdin io.Reader, stdout io.
}
// Don't need stdin now we've sent everything. This signals GCS that we are finished sending data.
if err := process.Process.CloseStdin(); err != nil {
return nil, err
if err := process.Process.CloseStdin(); err != nil && !hcsshim.IsNotExist(err) && !hcsshim.IsAlreadyClosed(err) {
// This error will occur if the compute system is currently shutting down
if perr, ok := err.(*hcsshim.ProcessError); ok && perr.Err != hcsshim.ErrVmcomputeOperationInvalidState {
return nil, err
}
}
}