Merge component 'engine' from git@github.com:moby/moby master
This commit is contained in:
@ -18,6 +18,7 @@ import (
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
"github.com/docker/libnetwork"
|
||||
@ -155,7 +156,11 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error {
|
||||
if _, ok := err.(libnetwork.NetworkNameError); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// We will continue if CreateManagedNetwork returns PredefinedNetworkError error.
|
||||
// Other callers still can treat it as Error.
|
||||
if _, ok := err.(daemon.PredefinedNetworkError); ok {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,30 +6,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// FIXME: this is copy-pasted from the aufs driver.
|
||||
// It should be moved into the core.
|
||||
|
||||
// Mounted returns true if a mount point exists.
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
var mntpointSt unix.Stat_t
|
||||
if err := unix.Stat(mountpoint, &mntpointSt); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
var parentSt unix.Stat_t
|
||||
if err := unix.Stat(filepath.Join(mountpoint, ".."), &parentSt); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return mntpointSt.Dev != parentSt.Dev, nil
|
||||
}
|
||||
|
||||
type probeData struct {
|
||||
fsName string
|
||||
magic string
|
||||
|
||||
@ -24,6 +24,16 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// PredefinedNetworkError is returned when user tries to create predefined network that already exists.
|
||||
type PredefinedNetworkError string
|
||||
|
||||
func (pnr PredefinedNetworkError) Error() string {
|
||||
return fmt.Sprintf("operation is not permitted on predefined %s network ", string(pnr))
|
||||
}
|
||||
|
||||
// Forbidden denotes the type of this error
|
||||
func (pnr PredefinedNetworkError) Forbidden() {}
|
||||
|
||||
// NetworkControllerEnabled checks if the networking stack is enabled.
|
||||
// This feature depends on OS primitives and it's disabled in systems like Windows.
|
||||
func (daemon *Daemon) NetworkControllerEnabled() bool {
|
||||
@ -267,9 +277,8 @@ func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.N
|
||||
}
|
||||
|
||||
func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
|
||||
if runconfig.IsPreDefinedNetwork(create.Name) && !agent {
|
||||
err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
|
||||
return nil, errdefs.Forbidden(err)
|
||||
if runconfig.IsPreDefinedNetwork(create.Name) {
|
||||
return nil, PredefinedNetworkError(create.Name)
|
||||
}
|
||||
|
||||
var warning string
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -86,7 +87,8 @@ type ImagePushConfig struct {
|
||||
type ImageConfigStore interface {
|
||||
Put([]byte) (digest.Digest, error)
|
||||
Get(digest.Digest) ([]byte, error)
|
||||
RootFSAndOSFromConfig([]byte) (*image.RootFS, string, error)
|
||||
RootFSFromConfig([]byte) (*image.RootFS, error)
|
||||
PlatformFromConfig([]byte) (*specs.Platform, error)
|
||||
}
|
||||
|
||||
// PushLayerProvider provides layers to be pushed by ChainID.
|
||||
@ -140,18 +142,26 @@ func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) {
|
||||
return img.RawJSON(), nil
|
||||
}
|
||||
|
||||
func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
|
||||
func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
||||
var unmarshalledConfig image.Image
|
||||
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
return unmarshalledConfig.RootFS, nil
|
||||
}
|
||||
|
||||
func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) {
|
||||
var unmarshalledConfig image.Image
|
||||
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fail immediately on Windows when downloading a non-Windows image
|
||||
// and vice versa. Exception on Windows if Linux Containers are enabled.
|
||||
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() {
|
||||
return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
||||
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
||||
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
|
||||
return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
||||
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
|
||||
}
|
||||
|
||||
os := unmarshalledConfig.OS
|
||||
@ -159,9 +169,9 @@ func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, strin
|
||||
os = runtime.GOOS
|
||||
}
|
||||
if !system.IsOSSupported(os) {
|
||||
return nil, "", system.ErrNotSupportedOperatingSystem
|
||||
return nil, system.ErrNotSupportedOperatingSystem
|
||||
}
|
||||
return unmarshalledConfig.RootFS, os, nil
|
||||
return &specs.Platform{OS: os, OSVersion: unmarshalledConfig.OSVersion}, nil
|
||||
}
|
||||
|
||||
type storeLayerProvider struct {
|
||||
|
||||
@ -30,6 +30,7 @@ import (
|
||||
refstore "github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
@ -584,11 +585,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
||||
}()
|
||||
|
||||
var (
|
||||
configJSON []byte // raw serialized image config
|
||||
downloadedRootFS *image.RootFS // rootFS from registered layers
|
||||
configRootFS *image.RootFS // rootFS from configuration
|
||||
release func() // release resources from rootFS download
|
||||
configOS string // for LCOW when registering downloaded layers
|
||||
configJSON []byte // raw serialized image config
|
||||
downloadedRootFS *image.RootFS // rootFS from registered layers
|
||||
configRootFS *image.RootFS // rootFS from configuration
|
||||
release func() // release resources from rootFS download
|
||||
configPlatform *specs.Platform // for LCOW when registering downloaded layers
|
||||
)
|
||||
|
||||
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
|
||||
@ -600,14 +601,16 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
||||
// check to block Windows images being pulled on Linux is implemented, it
|
||||
// may be necessary to perform the same type of serialisation.
|
||||
if runtime.GOOS == "windows" {
|
||||
configJSON, configRootFS, configOS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
||||
configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if configRootFS == nil {
|
||||
return "", "", errRootFSInvalid
|
||||
}
|
||||
if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if len(descriptors) != len(configRootFS.DiffIDs) {
|
||||
return "", "", errRootFSMismatch
|
||||
@ -615,8 +618,8 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
||||
|
||||
// Early bath if the requested OS doesn't match that of the configuration.
|
||||
// This avoids doing the download, only to potentially fail later.
|
||||
if !strings.EqualFold(configOS, requestedOS) {
|
||||
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
|
||||
if !strings.EqualFold(configPlatform.OS, requestedOS) {
|
||||
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, requestedOS)
|
||||
}
|
||||
|
||||
// Populate diff ids in descriptors to avoid downloading foreign layers
|
||||
@ -698,16 +701,20 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
||||
return imageID, manifestDigest, nil
|
||||
}
|
||||
|
||||
func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, string, error) {
|
||||
func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) {
|
||||
select {
|
||||
case configJSON := <-configChan:
|
||||
rootfs, os, err := s.RootFSAndOSFromConfig(configJSON)
|
||||
rootfs, err := s.RootFSFromConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return configJSON, rootfs, os, nil
|
||||
platform, err := s.PlatformFromConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return configJSON, rootfs, platform, nil
|
||||
case err := <-errChan:
|
||||
return nil, nil, "", err
|
||||
return nil, nil, nil, err
|
||||
// Don't need a case for ctx.Done in the select because cancellation
|
||||
// will trigger an error in p.pullSchema2ImageConfig.
|
||||
}
|
||||
@ -736,6 +743,10 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
|
||||
}
|
||||
manifestDigest := manifestMatches[0].Digest
|
||||
|
||||
if err := checkImageCompatibility(manifestMatches[0].Platform.OS, manifestMatches[0].Platform.OSVersion); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
manSvc, err := p.repo.Manifests(ctx)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
||||
@ -27,3 +27,8 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []m
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// checkImageCompatibility is a Windows-specific function. No-op on Linux
|
||||
func checkImageCompatibility(imageOS, imageOSVersion string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package distribution // import "github.com/docker/docker/distribution"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
@ -63,7 +65,6 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
|
||||
osVersion := ""
|
||||
if os == "windows" {
|
||||
// TODO: Add UBR (Update Build Release) component after build
|
||||
version := system.GetOSVersion()
|
||||
osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
|
||||
logrus.Debugf("will prefer entries with version %s", osVersion)
|
||||
@ -71,10 +72,11 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []m
|
||||
|
||||
var matches []manifestlist.ManifestDescriptor
|
||||
for _, manifestDescriptor := range manifests {
|
||||
// TODO: Consider filtering out greater versions, including only greater UBR
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
|
||||
matches = append(matches, manifestDescriptor)
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("found match for %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
} else {
|
||||
logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
}
|
||||
}
|
||||
if os == "windows" {
|
||||
@ -107,3 +109,22 @@ func (mbv manifestsByVersion) Len() int {
|
||||
func (mbv manifestsByVersion) Swap(i, j int) {
|
||||
mbv.list[i], mbv.list[j] = mbv.list[j], mbv.list[i]
|
||||
}
|
||||
|
||||
// checkImageCompatibility blocks pulling incompatible images based on a later OS build
|
||||
// Fixes https://github.com/moby/moby/issues/36184.
|
||||
func checkImageCompatibility(imageOS, imageOSVersion string) error {
|
||||
if imageOS == "windows" {
|
||||
hostOSV := system.GetOSVersion()
|
||||
splitImageOSVersion := strings.Split(imageOSVersion, ".") // eg 10.0.16299.nnnn
|
||||
if len(splitImageOSVersion) >= 3 {
|
||||
if imageOSBuild, err := strconv.Atoi(splitImageOSVersion[2]); err == nil {
|
||||
if imageOSBuild > int(hostOSV.Build) {
|
||||
errMsg := fmt.Sprintf("a Windows version %s.%s.%s-based image is incompatible with a %s host", splitImageOSVersion[0], splitImageOSVersion[1], splitImageOSVersion[2], hostOSV.ToString())
|
||||
logrus.Debugf(errMsg)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -118,12 +118,17 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
|
||||
return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
|
||||
}
|
||||
|
||||
rootfs, os, err := p.config.ImageStore.RootFSAndOSFromConfig(imgConfig)
|
||||
rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
|
||||
}
|
||||
|
||||
l, err := p.config.LayerStores[os].Get(rootfs.ChainID())
|
||||
platform, err := p.config.ImageStore.PlatformFromConfig(imgConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get platform for image %s: %s", reference.FamiliarString(ref), err)
|
||||
}
|
||||
|
||||
l, err := p.config.LayerStores[platform.OS].Get(rootfs.ChainID())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get top layer from image: %v", err)
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ type DockerSuite struct {
|
||||
}
|
||||
|
||||
func (s *DockerSuite) OnTimeout(c *check.C) {
|
||||
if !testEnv.IsLocalDaemon() {
|
||||
if testEnv.IsRemoteDaemon() {
|
||||
return
|
||||
}
|
||||
path := filepath.Join(os.Getenv("DEST"), "docker.pid")
|
||||
|
||||
@ -1713,7 +1713,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
|
||||
Type: "bind",
|
||||
Source: notExistPath,
|
||||
Target: destPath}}},
|
||||
msg: "bind source path does not exist",
|
||||
msg: "bind mount source path does not exist: " + notExistPath,
|
||||
},
|
||||
{
|
||||
config: containertypes.Config{
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func (s *DockerSuite) TestEventsAPIEmptyOutput(c *check.C) {
|
||||
type apiResp struct {
|
||||
resp *http.Response
|
||||
err error
|
||||
}
|
||||
chResp := make(chan *apiResp)
|
||||
go func() {
|
||||
resp, body, err := request.Get("/events")
|
||||
body.Close()
|
||||
chResp <- &apiResp{resp, err}
|
||||
}()
|
||||
|
||||
select {
|
||||
case r := <-chResp:
|
||||
c.Assert(r.err, checker.IsNil)
|
||||
c.Assert(r.resp.StatusCode, checker.Equals, http.StatusOK)
|
||||
case <-time.After(3 * time.Second):
|
||||
c.Fatal("timeout waiting for events api to respond, should have responded immediately")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEventsAPIBackwardsCompatible(c *check.C) {
|
||||
since := daemonTime(c).Unix()
|
||||
ts := strconv.FormatInt(since, 10)
|
||||
|
||||
out := runSleepingContainer(c, "--name=foo", "-d")
|
||||
containerID := strings.TrimSpace(out)
|
||||
c.Assert(waitRun(containerID), checker.IsNil)
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("since", ts)
|
||||
|
||||
_, body, err := request.Get("/events?" + q.Encode())
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer body.Close()
|
||||
|
||||
dec := json.NewDecoder(body)
|
||||
var containerCreateEvent *jsonmessage.JSONMessage
|
||||
for {
|
||||
var event jsonmessage.JSONMessage
|
||||
if err := dec.Decode(&event); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
c.Fatal(err)
|
||||
}
|
||||
if event.Status == "create" && event.ID == containerID {
|
||||
containerCreateEvent = &event
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Assert(containerCreateEvent, checker.Not(checker.IsNil))
|
||||
c.Assert(containerCreateEvent.Status, checker.Equals, "create")
|
||||
c.Assert(containerCreateEvent.ID, checker.Equals, containerID)
|
||||
c.Assert(containerCreateEvent.From, checker.Equals, "busybox")
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) {
|
||||
dockerfile1 := `FROM busybox:latest
|
||||
ENTRYPOINT ["true"]`
|
||||
img := "test-container-orphaning"
|
||||
dockerfile2 := `FROM busybox:latest
|
||||
ENTRYPOINT ["true"]
|
||||
MAINTAINER Integration Tests`
|
||||
|
||||
// build first dockerfile
|
||||
buildImageSuccessfully(c, img, build.WithDockerfile(dockerfile1))
|
||||
img1 := getIDByName(c, img)
|
||||
// run container on first image
|
||||
dockerCmd(c, "run", img)
|
||||
// rebuild dockerfile with a small addition at the end
|
||||
buildImageSuccessfully(c, img, build.WithDockerfile(dockerfile2))
|
||||
// try to remove the image, should not error out.
|
||||
out, _, err := dockerCmdWithError("rmi", img)
|
||||
c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out))
|
||||
|
||||
// check if we deleted the first image
|
||||
out, _ = dockerCmd(c, "images", "-q", "--no-trunc")
|
||||
c.Assert(out, checker.Contains, img1, check.Commentf("Orphaned container (could not find %q in docker images): %s", img1, out))
|
||||
|
||||
}
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
@ -26,6 +27,7 @@ import (
|
||||
// the container process, then start dockerd back up and attempt to start the
|
||||
// container again.
|
||||
func TestContainerStartOnDaemonRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", "dockerd", daemon.Config{})
|
||||
|
||||
@ -40,9 +40,9 @@ func TestInspectCpusetInConfigPre120(t *testing.T) {
|
||||
require.NoError(t, err, "unable to unmarshal body for version 1.19: %s", err)
|
||||
|
||||
config, ok := inspectJSON["Config"]
|
||||
assert.Equal(t, ok, true, "Unable to find 'Config'")
|
||||
assert.Equal(t, true, ok, "Unable to find 'Config'")
|
||||
|
||||
cfg := config.(map[string]interface{})
|
||||
_, ok = cfg["Cpuset"]
|
||||
assert.Equal(t, ok, true, "API version 1.19 expected to include Cpuset in 'Config'")
|
||||
assert.Equal(t, true, ok, "API version 1.19 expected to include Cpuset in 'Config'")
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ func TestInspectOomKilledTrue(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.State.OOMKilled, true)
|
||||
assert.Equal(t, true, inspect.State.OOMKilled)
|
||||
}
|
||||
|
||||
func TestInspectOomKilledFalse(t *testing.T) {
|
||||
@ -179,5 +179,5 @@ func TestInspectOomKilledFalse(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.State.OOMKilled, false)
|
||||
assert.Equal(t, false, inspect.State.OOMKilled)
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func TestLinksEtcHostsContentMatch(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
hosts, err := ioutil.ReadFile("/etc/hosts")
|
||||
skip.If(t, os.IsNotExist(err))
|
||||
|
||||
@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
func TestContainerShmNoLeak(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
|
||||
t.Parallel()
|
||||
d := daemon.New(t, "docker", "dockerd", daemon.Config{})
|
||||
client, err := d.NewClient()
|
||||
@ -94,7 +95,7 @@ func TestContainerShmNoLeak(t *testing.T) {
|
||||
|
||||
func TestContainerNetworkMountsNoChown(t *testing.T) {
|
||||
// chown only applies to Linux bind mounted volumes; must be same host to verify
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux" || !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNetworkNat(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
|
||||
@ -40,7 +40,7 @@ func TestNetworkNat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNetworkLocalhostTCPNat(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
|
||||
@ -57,7 +57,7 @@ func TestNetworkLocalhostTCPNat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNetworkLoopbackNat(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
msg := "it works"
|
||||
startServerContainer(t, msg, 8080)
|
||||
|
||||
@ -25,20 +25,19 @@ func TestPause(t *testing.T) {
|
||||
client := request.NewAPIClient(t)
|
||||
ctx := context.Background()
|
||||
|
||||
name := "testeventpause"
|
||||
cID := container.Run(t, ctx, client, container.WithName(name))
|
||||
cID := container.Run(t, ctx, client)
|
||||
poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
|
||||
|
||||
since := request.DaemonUnixTime(ctx, t, client, testEnv)
|
||||
|
||||
err := client.ContainerPause(ctx, name)
|
||||
err := client.ContainerPause(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.State.Paused, true)
|
||||
assert.Equal(t, true, inspect.State.Paused)
|
||||
|
||||
err = client.ContainerUnpause(ctx, name)
|
||||
err = client.ContainerUnpause(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
|
||||
until := request.DaemonUnixTime(ctx, t, client, testEnv)
|
||||
@ -46,9 +45,9 @@ func TestPause(t *testing.T) {
|
||||
messages, errs := client.Events(ctx, types.EventsOptions{
|
||||
Since: since,
|
||||
Until: until,
|
||||
Filters: filters.NewArgs(filters.Arg("container", name)),
|
||||
Filters: filters.NewArgs(filters.Arg("container", cID)),
|
||||
})
|
||||
assert.Equal(t, getEventActions(t, messages, errs), []string{"pause", "unpause"})
|
||||
assert.Equal(t, []string{"pause", "unpause"}, getEventActions(t, messages, errs))
|
||||
}
|
||||
|
||||
func TestPauseFailsOnWindowsServerContainers(t *testing.T) {
|
||||
@ -89,7 +88,7 @@ func getEventActions(t *testing.T, messages <-chan events.Message, errs <-chan e
|
||||
for {
|
||||
select {
|
||||
case err := <-errs:
|
||||
assert.Equal(t, err == nil || err == io.EOF, true)
|
||||
assert.True(t, err == nil || err == io.EOF)
|
||||
return actions
|
||||
case e := <-messages:
|
||||
actions = append(actions, e.Status)
|
||||
|
||||
@ -27,7 +27,7 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
|
||||
|
||||
// Test case for #5244: `docker rm` fails if bind dir doesn't exist anymore
|
||||
func TestRemoveContainerWithRemovedVolume(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
ctx := context.Background()
|
||||
@ -66,7 +66,7 @@ func TestRemoveContainerWithVolume(t *testing.T) {
|
||||
|
||||
insp, _, err := client.ContainerInspectWithRaw(ctx, cID, true)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(insp.Mounts), 1)
|
||||
assert.Equal(t, 1, len(insp.Mounts))
|
||||
volName := insp.Mounts[0].Name
|
||||
|
||||
err = client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{
|
||||
@ -76,7 +76,7 @@ func TestRemoveContainerWithVolume(t *testing.T) {
|
||||
|
||||
volumes, err := client.VolumeList(ctx, filters.NewArgs(filters.Arg("name", volName)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(volumes.Volumes), 0)
|
||||
assert.Equal(t, 0, len(volumes.Volumes))
|
||||
}
|
||||
|
||||
func TestRemoveContainerRunning(t *testing.T) {
|
||||
|
||||
@ -55,7 +55,7 @@ func TestRenameStoppedContainer(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.Name, "/"+oldName)
|
||||
assert.Equal(t, "/"+oldName, inspect.Name)
|
||||
|
||||
newName := "new_name" + stringid.GenerateNonCryptoID()
|
||||
err = client.ContainerRename(ctx, oldName, newName)
|
||||
@ -63,7 +63,7 @@ func TestRenameStoppedContainer(t *testing.T) {
|
||||
|
||||
inspect, err = client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.Name, "/"+newName)
|
||||
assert.Equal(t, "/"+newName, inspect.Name)
|
||||
}
|
||||
|
||||
func TestRenameRunningContainerAndReuse(t *testing.T) {
|
||||
@ -81,7 +81,7 @@ func TestRenameRunningContainerAndReuse(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.Name, "/"+newName)
|
||||
assert.Equal(t, "/"+newName, inspect.Name)
|
||||
|
||||
_, err = client.ContainerInspect(ctx, oldName)
|
||||
testutil.ErrorContains(t, err, "No such container: "+oldName)
|
||||
@ -91,7 +91,7 @@ func TestRenameRunningContainerAndReuse(t *testing.T) {
|
||||
|
||||
inspect, err = client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.Name, "/"+oldName)
|
||||
assert.Equal(t, "/"+oldName, inspect.Name)
|
||||
}
|
||||
|
||||
func TestRenameInvalidName(t *testing.T) {
|
||||
@ -108,7 +108,7 @@ func TestRenameInvalidName(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, oldName)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.ID, cID)
|
||||
assert.Equal(t, cID, inspect.ID)
|
||||
}
|
||||
|
||||
// Test case for GitHub issue 22466
|
||||
@ -133,6 +133,10 @@ func TestRenameAnonymousContainer(t *testing.T) {
|
||||
})
|
||||
err = client.ContainerRename(ctx, cID, "container1")
|
||||
require.NoError(t, err)
|
||||
// Stop/Start the container to get registered
|
||||
// FIXME(vdemeester) this is a really weird behavior as it fails otherwise
|
||||
err = client.ContainerStop(ctx, "container1", nil)
|
||||
require.NoError(t, err)
|
||||
err = client.ContainerStart(ctx, "container1", types.ContainerStartOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -152,7 +156,7 @@ func TestRenameAnonymousContainer(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, cID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.State.ExitCode, 0)
|
||||
assert.Equal(t, 0, inspect.State.ExitCode, "container %s exited with the wrong exitcode: %+v", cID, inspect)
|
||||
}
|
||||
|
||||
// TODO: should be a unit test
|
||||
@ -175,7 +179,7 @@ func TestRenameContainerWithSameName(t *testing.T) {
|
||||
// of the linked container should be updated so that the other
|
||||
// container could still reference to the container that is renamed.
|
||||
func TestRenameContainerWithLinkedContainer(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsLocalDaemon())
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
ctx := context.Background()
|
||||
@ -192,5 +196,5 @@ func TestRenameContainerWithLinkedContainer(t *testing.T) {
|
||||
|
||||
inspect, err := client.ContainerInspect(ctx, "app2/mysql")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, inspect.ID, db1ID)
|
||||
assert.Equal(t, db1ID, inspect.ID)
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ func TestResizeWithInvalidSize(t *testing.T) {
|
||||
endpoint := "/containers/" + cID + "/resize?h=foo&w=bar"
|
||||
res, _, err := req.Post(endpoint)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.StatusCode, http.StatusBadRequest)
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||
}
|
||||
|
||||
func TestResizeWhenContainerNotStarted(t *testing.T) {
|
||||
|
||||
@ -9,9 +9,11 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
func TestDaemonRestartKillContainers(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
|
||||
type testCase struct {
|
||||
desc string
|
||||
config *container.Config
|
||||
|
||||
60
components/engine/integration/image/remove_test.go
Normal file
60
components/engine/integration/image/remove_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package image // import "github.com/docker/docker/integration/image"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/request"
|
||||
"github.com/docker/docker/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRemoveImageOrphaning(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
ctx := context.Background()
|
||||
client := request.NewAPIClient(t)
|
||||
|
||||
img := "test-container-orphaning"
|
||||
|
||||
// Create a container from busybox, and commit a small change so we have a new image
|
||||
cID1 := container.Create(t, ctx, client, container.WithCmd(""))
|
||||
commitResp1, err := client.ContainerCommit(ctx, cID1, types.ContainerCommitOptions{
|
||||
Changes: []string{`ENTRYPOINT ["true"]`},
|
||||
Reference: img,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// verifies that reference now points to first image
|
||||
resp, _, err := client.ImageInspectWithRaw(ctx, img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resp.ID, commitResp1.ID)
|
||||
|
||||
// Create a container from created image, and commit a small change with same reference name
|
||||
cID2 := container.Create(t, ctx, client, container.WithImage(img), container.WithCmd(""))
|
||||
commitResp2, err := client.ContainerCommit(ctx, cID2, types.ContainerCommitOptions{
|
||||
Changes: []string{`LABEL Maintainer="Integration Tests"`},
|
||||
Reference: img,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// verifies that reference now points to second image
|
||||
resp, _, err = client.ImageInspectWithRaw(ctx, img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resp.ID, commitResp2.ID)
|
||||
|
||||
// try to remove the image, should not error out.
|
||||
_, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// check if the first image is still there
|
||||
resp, _, err = client.ImageInspectWithRaw(ctx, commitResp1.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resp.ID, commitResp1.ID)
|
||||
|
||||
// check if the second image has been deleted
|
||||
_, _, err = client.ImageInspectWithRaw(ctx, commitResp2.ID)
|
||||
testutil.ErrorContains(t, err, "No such image:")
|
||||
}
|
||||
@ -20,8 +20,8 @@ func NewAPIClient(t *testing.T, ops ...func(*client.Client) error) client.APICli
|
||||
return clt
|
||||
}
|
||||
|
||||
// daemonTime provides the current time on the daemon host
|
||||
func daemonTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time {
|
||||
// DaemonTime provides the current time on the daemon host
|
||||
func DaemonTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time {
|
||||
if testEnv.IsLocalDaemon() {
|
||||
return time.Now()
|
||||
}
|
||||
@ -37,6 +37,6 @@ func daemonTime(ctx context.Context, t *testing.T, client client.APIClient, test
|
||||
// DaemonUnixTime returns the current time on the daemon host with nanoseconds precision.
|
||||
// It return the time formatted how the client sends timestamps to the server.
|
||||
func DaemonUnixTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) string {
|
||||
dt := daemonTime(ctx, t, client, testEnv)
|
||||
dt := DaemonTime(ctx, t, client, testEnv)
|
||||
return fmt.Sprintf("%d.%09d", dt.Unix(), int64(dt.Nanosecond()))
|
||||
}
|
||||
|
||||
70
components/engine/integration/network/service_test.go
Normal file
70
components/engine/integration/network/service_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
package network // import "github.com/docker/docker/integration/network"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/gotestyourself/gotestyourself/poll"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestServiceWithPredefinedNetwork(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
d := newSwarm(t)
|
||||
defer d.Stop(t)
|
||||
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
require.NoError(t, err)
|
||||
|
||||
hostName := "host"
|
||||
var instances uint64 = 1
|
||||
serviceName := "TestService"
|
||||
serviceSpec := swarmServiceSpec(serviceName, instances)
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: hostName})
|
||||
|
||||
serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
|
||||
QueryRegistry: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
pollSettings := func(config *poll.Settings) {
|
||||
if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
|
||||
config.Timeout = 50 * time.Second
|
||||
config.Delay = 100 * time.Millisecond
|
||||
}
|
||||
}
|
||||
|
||||
serviceID := serviceResp.ID
|
||||
poll.WaitOn(t, serviceRunningCount(client, serviceID, instances), pollSettings)
|
||||
|
||||
_, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.ServiceRemove(context.Background(), serviceID)
|
||||
require.NoError(t, err)
|
||||
|
||||
poll.WaitOn(t, serviceIsRemoved(client, serviceID), pollSettings)
|
||||
poll.WaitOn(t, noTasks(client), pollSettings)
|
||||
|
||||
}
|
||||
|
||||
func serviceRunningCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
|
||||
return func(log poll.LogT) poll.Result {
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("service", serviceID)
|
||||
services, err := client.ServiceList(context.Background(), types.ServiceListOptions{})
|
||||
if err != nil {
|
||||
return poll.Error(err)
|
||||
}
|
||||
|
||||
if len(services) != int(instances) {
|
||||
return poll.Continue("Service count at %d waiting for %d", len(services), instances)
|
||||
}
|
||||
return poll.Success()
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func TestInspect(t *testing.T) {
|
||||
skip.IfCondition(t, !testEnv.IsLocalDaemon())
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon())
|
||||
defer setupTest(t)()
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
|
||||
@ -2,15 +2,22 @@ package system // import "github.com/docker/docker/integration/system"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
req "github.com/docker/docker/integration-cli/request"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/request"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -58,3 +65,55 @@ func TestEvents(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Test case for #18888: Events messages have been switched from generic
|
||||
// `JSONMessage` to `events.Message` types. The switch does not break the
|
||||
// backward compatibility so old `JSONMessage` could still be used.
|
||||
// This test verifies that backward compatibility maintains.
|
||||
func TestEventsBackwardsCompatible(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
ctx := context.Background()
|
||||
client := request.NewAPIClient(t)
|
||||
|
||||
since := request.DaemonTime(ctx, t, client, testEnv)
|
||||
ts := strconv.FormatInt(since.Unix(), 10)
|
||||
|
||||
cID := container.Create(t, ctx, client)
|
||||
|
||||
// In case there is no events, the API should have responded immediately (not blocking),
|
||||
// The test here makes sure the response time is less than 3 sec.
|
||||
expectedTime := time.Now().Add(3 * time.Second)
|
||||
emptyResp, emptyBody, err := req.Get("/events")
|
||||
require.NoError(t, err)
|
||||
defer emptyBody.Close()
|
||||
assert.Equal(t, http.StatusOK, emptyResp.StatusCode)
|
||||
assert.True(t, time.Now().Before(expectedTime), "timeout waiting for events api to respond, should have responded immediately")
|
||||
|
||||
// We also test to make sure the `events.Message` is compatible with `JSONMessage`
|
||||
q := url.Values{}
|
||||
q.Set("since", ts)
|
||||
_, body, err := req.Get("/events?" + q.Encode())
|
||||
require.NoError(t, err)
|
||||
defer body.Close()
|
||||
|
||||
dec := json.NewDecoder(body)
|
||||
var containerCreateEvent *jsonmessage.JSONMessage
|
||||
for {
|
||||
var event jsonmessage.JSONMessage
|
||||
if err := dec.Decode(&event); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
if event.Status == "create" && event.ID == cID {
|
||||
containerCreateEvent = &event
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(t, containerCreateEvent)
|
||||
assert.Equal(t, "create", containerCreateEvent.Status)
|
||||
assert.Equal(t, cID, containerCreateEvent.ID)
|
||||
assert.Equal(t, "busybox", containerCreateEvent.From)
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ func toSlash(path string) string {
|
||||
}
|
||||
|
||||
// IsLocalDaemon is true if the daemon under test is on the same
|
||||
// host as the CLI.
|
||||
// host as the test process.
|
||||
//
|
||||
// Deterministically working out the environment in which CI is running
|
||||
// to evaluate whether the daemon is local or remote is not possible through
|
||||
@ -115,6 +115,12 @@ func (e *Execution) IsLocalDaemon() bool {
|
||||
return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
|
||||
}
|
||||
|
||||
// IsRemoteDaemon is true if the daemon under test is on different host
|
||||
// as the test process.
|
||||
func (e *Execution) IsRemoteDaemon() bool {
|
||||
return !e.IsLocalDaemon()
|
||||
}
|
||||
|
||||
// Print the execution details to stdout
|
||||
// TODO: print everything
|
||||
func (e *Execution) Print() {
|
||||
|
||||
@ -2,6 +2,7 @@ package plugins // import "github.com/docker/docker/pkg/plugins"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -13,7 +14,9 @@ import (
|
||||
|
||||
"github.com/docker/docker/pkg/plugins/transport"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -232,3 +235,43 @@ func TestClientSendFile(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, m, output)
|
||||
}
|
||||
|
||||
func TestClientWithRequestTimeout(t *testing.T) {
|
||||
timeout := 1 * time.Millisecond
|
||||
testHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(timeout + 1*time.Millisecond)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(testHandler))
|
||||
defer srv.Close()
|
||||
|
||||
client := &Client{http: srv.Client(), requestFactory: &testRequestWrapper{srv}}
|
||||
_, err := client.callWithRetry("/Plugin.Hello", nil, false, WithRequestTimeout(timeout))
|
||||
require.Error(t, err, "expected error")
|
||||
|
||||
err = errors.Cause(err)
|
||||
|
||||
switch e := err.(type) {
|
||||
case *url.Error:
|
||||
err = e.Err
|
||||
}
|
||||
require.Equal(t, context.DeadlineExceeded, err)
|
||||
}
|
||||
|
||||
type testRequestWrapper struct {
|
||||
*httptest.Server
|
||||
}
|
||||
|
||||
func (w *testRequestWrapper) NewRequest(path string, data io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest("POST", path, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err := url.Parse(w.Server.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.URL = u
|
||||
return req, nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -53,6 +54,10 @@ func GetOSVersion() OSVersion {
|
||||
return osv
|
||||
}
|
||||
|
||||
func (osv OSVersion) ToString() string {
|
||||
return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build)
|
||||
}
|
||||
|
||||
// IsWindowsClient returns true if the SKU is client
|
||||
// @engine maintainers - this function should not be removed or modified as it
|
||||
// is used to enforce licensing restrictions on Windows.
|
||||
|
||||
@ -33,6 +33,7 @@ import (
|
||||
"github.com/docker/docker/plugin/v2"
|
||||
refstore "github.com/docker/docker/reference"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
@ -146,10 +147,15 @@ func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) {
|
||||
return s.config, nil
|
||||
}
|
||||
|
||||
func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
|
||||
func (s *tempConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
||||
return configToRootFS(c)
|
||||
}
|
||||
|
||||
func (s *tempConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) {
|
||||
// TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS
|
||||
return &specs.Platform{OS: runtime.GOOS}, nil
|
||||
}
|
||||
|
||||
func computePrivileges(c types.PluginConfig) types.PluginPrivileges {
|
||||
var privileges types.PluginPrivileges
|
||||
if c.Network.Type != "null" && c.Network.Type != "bridge" && c.Network.Type != "" {
|
||||
@ -534,10 +540,15 @@ func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) {
|
||||
return ioutil.ReadAll(rwc)
|
||||
}
|
||||
|
||||
func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
|
||||
func (s *pluginConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
||||
return configToRootFS(c)
|
||||
}
|
||||
|
||||
func (s *pluginConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) {
|
||||
// TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS
|
||||
return &specs.Platform{OS: runtime.GOOS}, nil
|
||||
}
|
||||
|
||||
type pluginLayerProvider struct {
|
||||
pm *Manager
|
||||
plugin *v2.Plugin
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/distribution/xfer"
|
||||
"github.com/docker/docker/image"
|
||||
@ -14,6 +15,7 @@ import (
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
@ -178,6 +180,10 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) {
|
||||
func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) {
|
||||
return nil, fmt.Errorf("digest not found")
|
||||
}
|
||||
func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) {
|
||||
func (dm *downloadManager) RootFSFromConfig(c []byte) (*image.RootFS, error) {
|
||||
return configToRootFS(c)
|
||||
}
|
||||
func (dm *downloadManager) PlatformFromConfig(c []byte) (*specs.Platform, error) {
|
||||
// TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS
|
||||
return &specs.Platform{OS: runtime.GOOS}, nil
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -353,19 +352,17 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool {
|
||||
return reflect.DeepEqual(a.Value, b.Value)
|
||||
}
|
||||
|
||||
func configToRootFS(c []byte) (*image.RootFS, string, error) {
|
||||
// TODO @jhowardmsft LCOW - Will need to revisit this.
|
||||
os := runtime.GOOS
|
||||
func configToRootFS(c []byte) (*image.RootFS, error) {
|
||||
var pluginConfig types.PluginConfig
|
||||
if err := json.Unmarshal(c, &pluginConfig); err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
// validation for empty rootfs is in distribution code
|
||||
if pluginConfig.Rootfs == nil {
|
||||
return nil, os, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return rootFSFromPlugin(pluginConfig.Rootfs), os, nil
|
||||
return rootFSFromPlugin(pluginConfig.Rootfs), nil
|
||||
}
|
||||
|
||||
func rootFSFromPlugin(pluginfs *types.PluginConfigRootfs) *image.RootFS {
|
||||
|
||||
@ -322,6 +322,7 @@
|
||||
"stat64",
|
||||
"statfs",
|
||||
"statfs64",
|
||||
"statx",
|
||||
"symlink",
|
||||
"symlinkat",
|
||||
"sync",
|
||||
|
||||
@ -315,6 +315,7 @@ func DefaultProfile() *types.Seccomp {
|
||||
"stat64",
|
||||
"statfs",
|
||||
"statfs64",
|
||||
"statx",
|
||||
"symlink",
|
||||
"symlinkat",
|
||||
"sync",
|
||||
|
||||
@ -83,7 +83,7 @@ func (p *linuxParser) validateMountConfigImpl(mnt *mount.Mount, validateBindSour
|
||||
if validateBindSourceExists {
|
||||
exists, _, _ := currentFileInfoProvider.fileInfo(mnt.Source)
|
||||
if !exists {
|
||||
return &errMountConfig{mnt, errBindNotExist}
|
||||
return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,8 +7,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var errBindNotExist = errors.New("bind source path does not exist")
|
||||
|
||||
type errMountConfig struct {
|
||||
mount *mount.Mount
|
||||
err error
|
||||
@ -18,6 +16,10 @@ func (e *errMountConfig) Error() string {
|
||||
return fmt.Sprintf("invalid mount config for type %q: %v", e.mount.Type, e.err.Error())
|
||||
}
|
||||
|
||||
func errBindSourceDoesNotExist(path string) error {
|
||||
return errors.Errorf("bind mount source path does not exist: %s", path)
|
||||
}
|
||||
|
||||
func errExtraField(name string) error {
|
||||
return errors.Errorf("field %s must not be specified", name)
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func TestValidateMount(t *testing.T) {
|
||||
|
||||
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: testDestinationPath}, nil},
|
||||
{mount.Mount{Type: "invalid", Target: testDestinationPath}, errors.New("mount type unknown")},
|
||||
{mount.Mount{Type: mount.TypeBind, Source: testSourcePath, Target: testDestinationPath}, errBindNotExist},
|
||||
{mount.Mount{Type: mount.TypeBind, Source: testSourcePath, Target: testDestinationPath}, errBindSourceDoesNotExist(testSourcePath)},
|
||||
}
|
||||
|
||||
lcowCases := []struct {
|
||||
@ -44,7 +44,7 @@ func TestValidateMount(t *testing.T) {
|
||||
{mount.Mount{Type: mount.TypeBind}, errMissingField("Target")},
|
||||
{mount.Mount{Type: mount.TypeBind, Target: "/foo"}, errMissingField("Source")},
|
||||
{mount.Mount{Type: mount.TypeBind, Target: "/foo", Source: "c:\\foo", VolumeOptions: &mount.VolumeOptions{}}, errExtraField("VolumeOptions")},
|
||||
{mount.Mount{Type: mount.TypeBind, Source: "c:\\foo", Target: "/foo"}, errBindNotExist},
|
||||
{mount.Mount{Type: mount.TypeBind, Source: "c:\\foo", Target: "/foo"}, errBindSourceDoesNotExist("c:\\foo")},
|
||||
{mount.Mount{Type: mount.TypeBind, Source: testDir, Target: "/foo"}, nil},
|
||||
{mount.Mount{Type: "invalid", Target: "/foo"}, errors.New("mount type unknown")},
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ func TestParseMountRaw(t *testing.T) {
|
||||
`c:\:d:\:xyzzy`: "invalid volume specification: ",
|
||||
`c:`: "cannot be `c:`",
|
||||
`c:\`: "cannot be `c:`",
|
||||
`c:\notexist:d:`: `source path does not exist`,
|
||||
`c:\notexist:d:`: `bind mount source path does not exist: c:\notexist`,
|
||||
`c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`,
|
||||
`name<:d:`: `invalid volume specification`,
|
||||
`name>:d:`: `invalid volume specification`,
|
||||
@ -189,7 +189,7 @@ func TestParseMountRaw(t *testing.T) {
|
||||
`c:\:/foo:xyzzy`: "invalid volume specification: ",
|
||||
`/`: "destination can't be '/'",
|
||||
`/..`: "destination can't be '/'",
|
||||
`c:\notexist:/foo`: `source path does not exist`,
|
||||
`c:\notexist:/foo`: `bind mount source path does not exist: c:\notexist`,
|
||||
`c:\windows\system32\ntdll.dll:/foo`: `source path must be a directory`,
|
||||
`name<:/foo`: `invalid volume specification`,
|
||||
`name>:/foo`: `invalid volume specification`,
|
||||
|
||||
@ -252,7 +252,7 @@ func (p *windowsParser) validateMountConfigReg(mnt *mount.Mount, destRegex strin
|
||||
return &errMountConfig{mnt, err}
|
||||
}
|
||||
if !exists {
|
||||
return &errMountConfig{mnt, errBindNotExist}
|
||||
return &errMountConfig{mnt, errBindSourceDoesNotExist(mnt.Source)}
|
||||
}
|
||||
if !isdir {
|
||||
return &errMountConfig{mnt, fmt.Errorf("source path must be a directory")}
|
||||
|
||||
Reference in New Issue
Block a user