Merge component 'engine' from git@github.com:moby/moby master
This commit is contained in:
@@ -18,7 +18,7 @@ type execBackend interface {
|
||||
ContainerExecCreate(name string, config *types.ExecConfig) (string, error)
|
||||
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
||||
ContainerExecResize(name string, height, width int) error
|
||||
ContainerExecStart(ctx context.Context, name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
|
||||
ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
|
||||
ExecExists(name string) (bool, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||
config := &backend.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OutStream: w,
|
||||
Version: string(httputils.VersionFromContext(ctx)),
|
||||
Version: httputils.VersionFromContext(ctx),
|
||||
}
|
||||
|
||||
return s.backend.ContainerStats(ctx, vars["name"], config)
|
||||
|
||||
@@ -66,9 +66,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{
|
||||
ID: string(imgID),
|
||||
})
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
|
||||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
|
||||
@@ -2,6 +2,7 @@ package swarm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
|
||||
// swarmLogs takes an http response, request, and selector, and writes the logs
|
||||
// specified by the selector to the response
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, selector *backend.LogSelector) error {
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w io.Writer, r *http.Request, selector *backend.LogSelector) error {
|
||||
// Args are validated before the stream starts because when it starts we're
|
||||
// sending HTTP 200 by writing an empty chunk of data to tell the client that
|
||||
// daemon is going to stream. By sending this initial HTTP 200 we can't report
|
||||
|
||||
@@ -555,7 +555,7 @@ func parseOptInterval(f *Flag) (time.Duration, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if d < time.Duration(container.MinimumDuration) {
|
||||
if d < container.MinimumDuration {
|
||||
return 0, fmt.Errorf("Interval %#v cannot be less than %s", f.name, container.MinimumDuration)
|
||||
}
|
||||
return d, nil
|
||||
|
||||
@@ -348,7 +348,7 @@ func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.
|
||||
}
|
||||
fmt.Fprint(b.Stdout, " ---> Using cache\n")
|
||||
|
||||
dispatchState.imageID = string(cachedID)
|
||||
dispatchState.imageID = cachedID
|
||||
b.buildStages.update(dispatchState.imageID)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func GetWithStatusError(address string) (resp *http.Response, err error) {
|
||||
// - an io.Reader for the response body
|
||||
// - an error value which will be non-nil either when something goes wrong while
|
||||
// reading bytes from r or when the detected content-type is not acceptable.
|
||||
func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadCloser, error) {
|
||||
func inspectResponse(ct string, r io.Reader, clen int64) (string, io.ReadCloser, error) {
|
||||
plen := clen
|
||||
if plen <= 0 || plen > maxPreambleLength {
|
||||
plen = maxPreambleLength
|
||||
@@ -119,10 +119,10 @@ func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadClo
|
||||
preamble := make([]byte, plen, plen)
|
||||
rlen, err := r.Read(preamble)
|
||||
if rlen == 0 {
|
||||
return ct, r, errors.New("empty response")
|
||||
return ct, ioutil.NopCloser(r), errors.New("empty response")
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return ct, r, err
|
||||
return ct, ioutil.NopCloser(r), err
|
||||
}
|
||||
|
||||
preambleR := bytes.NewReader(preamble[:rlen])
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -85,7 +85,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||
return response, err
|
||||
}
|
||||
|
||||
func imageDigestAndPlatforms(ctx context.Context, cli *Client, image, encodedAuth string) (string, []swarm.Platform, error) {
|
||||
func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) {
|
||||
distributionInspect, err := cli.DistributionInspect(ctx, image, encodedAuth)
|
||||
imageWithDigest := image
|
||||
var platforms []swarm.Platform
|
||||
|
||||
@@ -539,7 +539,7 @@ func initRouter(opts routerOptions) {
|
||||
}
|
||||
|
||||
// TODO: remove this from cli and return the authzMiddleware
|
||||
func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config, pluginStore *plugin.Store) error {
|
||||
func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config, pluginStore plugingetter.PluginGetter) error {
|
||||
v := cfg.Version
|
||||
|
||||
exp := middleware.NewExperimentalMiddleware(cli.Config.Experimental)
|
||||
|
||||
@@ -655,8 +655,12 @@ func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwor
|
||||
return nil
|
||||
}
|
||||
|
||||
type named interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// UpdateJoinInfo updates network settings when container joins network n with endpoint ep.
|
||||
func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
|
||||
func (container *Container) UpdateJoinInfo(n named, ep libnetwork.Endpoint) error {
|
||||
if err := container.buildPortMapInfo(ep); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -684,7 +688,7 @@ func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox)
|
||||
}
|
||||
|
||||
// BuildJoinOptions builds endpoint Join options from a given network.
|
||||
func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
|
||||
func (container *Container) BuildJoinOptions(n named) ([]libnetwork.EndpointOption, error) {
|
||||
var joinOptions []libnetwork.EndpointOption
|
||||
if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
||||
for _, str := range epConfig.Links {
|
||||
|
||||
@@ -3,7 +3,6 @@ package convert
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
types "github.com/docker/docker/api/types/swarm"
|
||||
swarmapi "github.com/docker/swarmkit/api"
|
||||
@@ -115,7 +114,7 @@ func MergeSwarmSpecToGRPC(s types.Spec, spec swarmapi.ClusterSpec) (swarmapi.Clu
|
||||
spec.Raft.ElectionTick = uint32(s.Raft.ElectionTick)
|
||||
}
|
||||
if s.Dispatcher.HeartbeatPeriod != 0 {
|
||||
spec.Dispatcher.HeartbeatPeriod = gogotypes.DurationProto(time.Duration(s.Dispatcher.HeartbeatPeriod))
|
||||
spec.Dispatcher.HeartbeatPeriod = gogotypes.DurationProto(s.Dispatcher.HeartbeatPeriod)
|
||||
}
|
||||
if s.CAConfig.NodeCertExpiry != 0 {
|
||||
spec.CAConfig.NodeCertExpiry = gogotypes.DurationProto(s.CAConfig.NodeCertExpiry)
|
||||
|
||||
@@ -659,7 +659,7 @@ func (e *exitError) Error() string {
|
||||
}
|
||||
|
||||
func (e *exitError) ExitCode() int {
|
||||
return int(e.code)
|
||||
return e.code
|
||||
}
|
||||
|
||||
func (e *exitError) Cause() error {
|
||||
|
||||
@@ -142,7 +142,7 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str
|
||||
// ContainerExecStart starts a previously set up exec instance. The
|
||||
// std streams are set up.
|
||||
// If ctx is cancelled, the process is terminated.
|
||||
func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) (err error) {
|
||||
func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (err error) {
|
||||
var (
|
||||
cStdin io.ReadCloser
|
||||
cStdout, cStderr io.Writer
|
||||
|
||||
@@ -158,8 +158,8 @@ func copyDir(srcDir, dstDir string, flags copyFlags) error {
|
||||
|
||||
// system.Chtimes doesn't support a NOFOLLOW flag atm
|
||||
if !isSymlink {
|
||||
aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
|
||||
mTime := time.Unix(int64(stat.Mtim.Sec), int64(stat.Mtim.Nsec))
|
||||
aTime := time.Unix(stat.Atim.Sec, stat.Atim.Nsec)
|
||||
mTime := time.Unix(stat.Mtim.Sec, stat.Mtim.Nsec)
|
||||
if err := system.Chtimes(dstPath, aTime, mTime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -97,10 +97,10 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||
drivers = strings.TrimSpace(drivers)
|
||||
v := &types.Info{
|
||||
ID: daemon.ID,
|
||||
Containers: int(cRunning + cPaused + cStopped),
|
||||
ContainersRunning: int(cRunning),
|
||||
ContainersPaused: int(cPaused),
|
||||
ContainersStopped: int(cStopped),
|
||||
Containers: cRunning + cPaused + cStopped,
|
||||
ContainersRunning: cRunning,
|
||||
ContainersPaused: cPaused,
|
||||
ContainersStopped: cStopped,
|
||||
Images: imageCount,
|
||||
Driver: drivers,
|
||||
DriverStatus: daemon.stores[p].layerStore.DriverStatus(),
|
||||
|
||||
@@ -86,7 +86,7 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) {
|
||||
return nil, fmt.Errorf("failed to parse systemd fd address: should be a number: %v", addr)
|
||||
}
|
||||
fdOffset := fdNum - 3
|
||||
if len(listeners) < int(fdOffset)+1 {
|
||||
if len(listeners) < fdOffset+1 {
|
||||
return nil, fmt.Errorf("too few socket activated files passed in by systemd")
|
||||
}
|
||||
if listeners[fdOffset] == nil {
|
||||
|
||||
@@ -833,7 +833,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
||||
s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj
|
||||
s.Linux.MountLabel = c.MountLabel
|
||||
|
||||
return (*specs.Spec)(&s), nil
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func clearReadOnly(m *specs.Mount) {
|
||||
|
||||
@@ -84,7 +84,7 @@ func ComputeV2MetadataHMACKey(authConfig *types.AuthConfig) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(digest.FromBytes([]byte(buf))), nil
|
||||
return []byte(digest.FromBytes(buf)), nil
|
||||
}
|
||||
|
||||
// authConfigKeyInput is a reduced AuthConfig structure holding just relevant credential data eligible for
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/docker/docker/pkg/system"
|
||||
refstore "github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
@@ -435,7 +435,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
||||
func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
|
||||
var verifiedManifest *schema1.Manifest
|
||||
verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
|
||||
if err != nil {
|
||||
@@ -838,7 +838,7 @@ func allowV1Fallback(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Named) (m *schema1.Manifest, err error) {
|
||||
func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
|
||||
// If pull by digest, then verify the manifest digest. NOTE: It is
|
||||
// important to do this first, before any other content validation. If the
|
||||
// digest cannot be verified, don't even bother with those other things.
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -651,6 +651,7 @@ func (bla byLikeness) Swap(i, j int) {
|
||||
}
|
||||
func (bla byLikeness) Len() int { return len(bla.arr) }
|
||||
|
||||
// nolint: interfacer
|
||||
func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, marr []metadata.V2Metadata) {
|
||||
// reverse the metadata array to shift the newest entries to the beginning
|
||||
for i := 0; i < len(marr)/2; i++ {
|
||||
|
||||
@@ -8,12 +8,17 @@
|
||||
"api/types/container/container_.*",
|
||||
"integration-cli/"
|
||||
],
|
||||
"Skip": [
|
||||
"integration-cli/"
|
||||
],
|
||||
|
||||
"Enable": [
|
||||
"deadcode",
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"golint",
|
||||
"interfacer",
|
||||
"unconvert",
|
||||
"vet"
|
||||
],
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -212,15 +212,12 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
|
||||
return l.ls.Register(inflatedLayerData, rootFS.ChainID(), platform)
|
||||
}
|
||||
|
||||
func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
|
||||
func (l *tarexporter) setLoadedTag(ref reference.Named, imgID digest.Digest, outStream io.Writer) error {
|
||||
if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
|
||||
fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", reference.FamiliarString(ref), string(prevID)) // todo: this message is wrong in case of multiple tags
|
||||
}
|
||||
|
||||
if err := l.rs.AddTag(ref, imgID, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return l.rs.AddTag(ref, imgID, true)
|
||||
}
|
||||
|
||||
func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error {
|
||||
|
||||
@@ -2,10 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -20,6 +22,7 @@ import (
|
||||
"github.com/docker/docker/integration-cli/environment"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
"github.com/docker/docker/integration-cli/registry"
|
||||
ienv "github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/go-check/check"
|
||||
"golang.org/x/net/context"
|
||||
@@ -57,20 +60,14 @@ func init() {
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dockerBinary = testEnv.DockerBinary()
|
||||
|
||||
if testEnv.LocalDaemon() {
|
||||
fmt.Println("INFO: Testing against a local daemon")
|
||||
} else {
|
||||
fmt.Println("INFO: Testing against a remote daemon")
|
||||
}
|
||||
exitCode := m.Run()
|
||||
os.Exit(exitCode)
|
||||
testEnv.Print()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
cli.EnsureTestEnvIsLoaded(t)
|
||||
fakestorage.EnsureTestEnvIsLoaded(t)
|
||||
environment.ProtectImages(t, testEnv)
|
||||
cli.SetTestEnvironment(testEnv)
|
||||
fakestorage.SetTestEnvironment(&testEnv.Execution)
|
||||
ienv.ProtectImages(t, &testEnv.Execution)
|
||||
check.TestingT(t)
|
||||
}
|
||||
|
||||
@@ -82,13 +79,25 @@ type DockerSuite struct {
|
||||
}
|
||||
|
||||
func (s *DockerSuite) OnTimeout(c *check.C) {
|
||||
if testEnv.DaemonPID() > 0 && testEnv.LocalDaemon() {
|
||||
daemon.SignalDaemonDump(testEnv.DaemonPID())
|
||||
path := filepath.Join(os.Getenv("DEST"), "docker.pid")
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
c.Fatalf("Failed to get daemon PID from %s\n", path)
|
||||
}
|
||||
|
||||
rawPid, err := strconv.ParseInt(string(b), 10, 32)
|
||||
if err != nil {
|
||||
c.Fatalf("Failed to parse pid from %s: %s\n", path, err)
|
||||
}
|
||||
|
||||
daemonPid := int(rawPid)
|
||||
if daemonPid > 0 && testEnv.IsLocalDaemon() {
|
||||
daemon.SignalDaemonDump(daemonPid)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TearDownTest(c *check.C) {
|
||||
testEnv.Clean(c, dockerBinary)
|
||||
testEnv.Clean(c)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -11,9 +11,11 @@ import (
|
||||
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakecontext"
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakestorage"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
require.TestingT
|
||||
logT
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
|
||||
@@ -30,7 +30,7 @@ func ensureHTTPServerImage(t testingT) {
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
goos := testEnv.DaemonPlatform()
|
||||
goos := testEnv.DaemonInfo.OSType
|
||||
if goos == "" {
|
||||
goos = "linux"
|
||||
}
|
||||
|
||||
@@ -8,39 +8,20 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakecontext"
|
||||
"github.com/docker/docker/integration-cli/environment"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
testEnv *environment.Execution
|
||||
onlyOnce sync.Once
|
||||
)
|
||||
|
||||
// EnsureTestEnvIsLoaded make sure the test environment is loaded for this package
|
||||
func EnsureTestEnvIsLoaded(t testingT) {
|
||||
var doIt bool
|
||||
var err error
|
||||
onlyOnce.Do(func() {
|
||||
doIt = true
|
||||
})
|
||||
|
||||
if !doIt {
|
||||
return
|
||||
}
|
||||
testEnv, err = environment.New()
|
||||
if err != nil {
|
||||
t.Fatalf("error loading testenv : %v", err)
|
||||
}
|
||||
}
|
||||
var testEnv *environment.Execution
|
||||
|
||||
type testingT interface {
|
||||
require.TestingT
|
||||
logT
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
@@ -58,11 +39,20 @@ type Fake interface {
|
||||
CtxDir() string
|
||||
}
|
||||
|
||||
// SetTestEnvironment sets a static test environment
|
||||
// TODO: decouple this package from environment
|
||||
func SetTestEnvironment(env *environment.Execution) {
|
||||
testEnv = env
|
||||
}
|
||||
|
||||
// New returns a static file server that will be use as build context.
|
||||
func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
|
||||
if testEnv == nil {
|
||||
t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
|
||||
}
|
||||
ctx := fakecontext.New(t, dir, modifiers...)
|
||||
if testEnv.LocalDaemon() {
|
||||
return newLocalFakeStorage(t, ctx)
|
||||
if testEnv.IsLocalDaemon() {
|
||||
return newLocalFakeStorage(ctx)
|
||||
}
|
||||
return newRemoteFileServer(t, ctx)
|
||||
}
|
||||
@@ -86,7 +76,7 @@ func (s *localFileStorage) Close() error {
|
||||
return s.Fake.Close()
|
||||
}
|
||||
|
||||
func newLocalFakeStorage(t testingT, ctx *fakecontext.Fake) *localFileStorage {
|
||||
func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
|
||||
handler := http.FileServer(http.Dir(ctx.Dir))
|
||||
server := httptest.NewServer(handler)
|
||||
return &localFileStorage{
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
@@ -13,26 +12,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
testEnv *environment.Execution
|
||||
onlyOnce sync.Once
|
||||
)
|
||||
var testEnv *environment.Execution
|
||||
|
||||
// EnsureTestEnvIsLoaded make sure the test environment is loaded for this package
|
||||
func EnsureTestEnvIsLoaded(t testingT) {
|
||||
var doIt bool
|
||||
var err error
|
||||
onlyOnce.Do(func() {
|
||||
doIt = true
|
||||
})
|
||||
|
||||
if !doIt {
|
||||
return
|
||||
}
|
||||
testEnv, err = environment.New()
|
||||
if err != nil {
|
||||
t.Fatalf("error loading testenv : %v", err)
|
||||
}
|
||||
// SetTestEnvironment sets a static test environment
|
||||
// TODO: decouple this package from environment
|
||||
func SetTestEnvironment(env *environment.Execution) {
|
||||
testEnv = env
|
||||
}
|
||||
|
||||
// CmdOperator defines functions that can modify a command
|
||||
@@ -130,7 +115,7 @@ func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result {
|
||||
// validateArgs is a checker to ensure tests are not running commands which are
|
||||
// not supported on platforms. Specifically on Windows this is 'busybox top'.
|
||||
func validateArgs(args ...string) error {
|
||||
if testEnv.DaemonPlatform() != "windows" {
|
||||
if testEnv.DaemonInfo.OSType != "windows" {
|
||||
return nil
|
||||
}
|
||||
foundBusybox := -1
|
||||
|
||||
@@ -69,6 +69,7 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: move to unit test
|
||||
func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
|
||||
runSleepingContainer(c, "--name", "myname")
|
||||
|
||||
@@ -76,18 +77,6 @@ func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
|
||||
c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
|
||||
c.Assert(out, checker.Contains, "Invalid container name", check.Commentf("%v", err))
|
||||
|
||||
out, _, err = dockerCmdWithError("rename", "myname")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
|
||||
c.Assert(out, checker.Contains, "requires exactly 2 argument(s).", check.Commentf("%v", err))
|
||||
|
||||
out, _, err = dockerCmdWithError("rename", "myname", "")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
|
||||
c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))
|
||||
|
||||
out, _, err = dockerCmdWithError("rename", "", "newname")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("Renaming container with empty name should have failed: %s", out))
|
||||
c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))
|
||||
|
||||
out, _ = dockerCmd(c, "ps", "-a")
|
||||
c.Assert(out, checker.Contains, "myname", check.Commentf("Output of docker ps should have included 'myname': %s", out))
|
||||
}
|
||||
|
||||
@@ -2215,7 +2215,7 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
|
||||
|
||||
out, err = inspectMountSourceField("dark_helmet", prefix+slash+`foo`)
|
||||
c.Assert(err, check.IsNil)
|
||||
if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.VolumesConfigPath())) {
|
||||
if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
|
||||
c.Fatalf("Volume was not defined for %s/foo\n%q", prefix, out)
|
||||
}
|
||||
|
||||
@@ -2226,7 +2226,7 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
|
||||
|
||||
out, err = inspectMountSourceField("dark_helmet", prefix+slash+"bar")
|
||||
c.Assert(err, check.IsNil)
|
||||
if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.VolumesConfigPath())) {
|
||||
if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
|
||||
c.Fatalf("Volume was not defined for %s/bar\n%q", prefix, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ func readFile(src string, c *check.C) (content string) {
|
||||
}
|
||||
|
||||
func containerStorageFile(containerID, basename string) string {
|
||||
return filepath.Join(testEnv.ContainerStoragePath(), containerID, basename)
|
||||
return filepath.Join(testEnv.PlatformDefaults.ContainerStoragePath, containerID, basename)
|
||||
}
|
||||
|
||||
// docker commands that use this function must be run with the '-d' switch.
|
||||
@@ -266,7 +266,7 @@ func readContainerFileWithExec(c *check.C, containerID, filename string) []byte
|
||||
|
||||
// daemonTime provides the current time on the daemon host
|
||||
func daemonTime(c *check.C) time.Time {
|
||||
if testEnv.LocalDaemon() {
|
||||
if testEnv.IsLocalDaemon() {
|
||||
return time.Now()
|
||||
}
|
||||
cli, err := client.NewEnvClient()
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
logT
|
||||
Fatalf(string, ...interface{})
|
||||
}
|
||||
|
||||
type logT interface {
|
||||
Logf(string, ...interface{})
|
||||
}
|
||||
|
||||
// Clean the environment, preserving protected objects (images, containers, ...)
|
||||
// and removing everything else. It's meant to run after any tests so that they don't
|
||||
// depend on each others.
|
||||
func (e *Execution) Clean(t testingT, dockerBinary string) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if (e.DaemonPlatform() != "windows") || (e.DaemonPlatform() == "windows" && e.Isolation() == "hyperv") {
|
||||
unpauseAllContainers(t, dockerBinary)
|
||||
}
|
||||
deleteAllContainers(t, dockerBinary)
|
||||
deleteAllImages(t, dockerBinary, e.protectedElements.images)
|
||||
deleteAllVolumes(t, cli)
|
||||
deleteAllNetworks(t, cli, e.DaemonPlatform())
|
||||
if e.DaemonPlatform() == "linux" {
|
||||
deleteAllPlugins(t, cli, dockerBinary)
|
||||
}
|
||||
}
|
||||
|
||||
func unpauseAllContainers(t testingT, dockerBinary string) {
|
||||
containers := getPausedContainers(t, dockerBinary)
|
||||
if len(containers) > 0 {
|
||||
icmd.RunCommand(dockerBinary, append([]string{"unpause"}, containers...)...).Assert(t, icmd.Success)
|
||||
}
|
||||
}
|
||||
|
||||
func getPausedContainers(t testingT, dockerBinary string) []string {
|
||||
result := icmd.RunCommand(dockerBinary, "ps", "-f", "status=paused", "-q", "-a")
|
||||
result.Assert(t, icmd.Success)
|
||||
return strings.Fields(result.Combined())
|
||||
}
|
||||
|
||||
var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
|
||||
|
||||
func deleteAllContainers(t testingT, dockerBinary string) {
|
||||
containers := getAllContainers(t, dockerBinary)
|
||||
if len(containers) > 0 {
|
||||
result := icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, containers...)...)
|
||||
if result.Error != nil {
|
||||
// If the error is "No such container: ..." this means the container doesn't exists anymore,
|
||||
// or if it is "... removal of container ... is already in progress" it will be removed eventually.
|
||||
// We can safely ignore those.
|
||||
if strings.Contains(result.Stderr(), "No such container") || alreadyExists.MatchString(result.Stderr()) {
|
||||
return
|
||||
}
|
||||
t.Fatalf("error removing containers %v : %v (%s)", containers, result.Error, result.Combined())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAllContainers(t testingT, dockerBinary string) []string {
|
||||
result := icmd.RunCommand(dockerBinary, "ps", "-q", "-a")
|
||||
result.Assert(t, icmd.Success)
|
||||
return strings.Fields(result.Combined())
|
||||
}
|
||||
|
||||
func deleteAllImages(t testingT, dockerBinary string, protectedImages map[string]struct{}) {
|
||||
result := icmd.RunCommand(dockerBinary, "images", "--digests")
|
||||
result.Assert(t, icmd.Success)
|
||||
lines := strings.Split(string(result.Combined()), "\n")[1:]
|
||||
imgMap := map[string]struct{}{}
|
||||
for _, l := range lines {
|
||||
if l == "" {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(l)
|
||||
imgTag := fields[0] + ":" + fields[1]
|
||||
if _, ok := protectedImages[imgTag]; !ok {
|
||||
if fields[0] == "<none>" || fields[1] == "<none>" {
|
||||
if fields[2] != "<none>" {
|
||||
imgMap[fields[0]+"@"+fields[2]] = struct{}{}
|
||||
} else {
|
||||
imgMap[fields[3]] = struct{}{}
|
||||
}
|
||||
// continue
|
||||
} else {
|
||||
imgMap[imgTag] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(imgMap) != 0 {
|
||||
imgs := make([]string, 0, len(imgMap))
|
||||
for k := range imgMap {
|
||||
imgs = append(imgs, k)
|
||||
}
|
||||
icmd.RunCommand(dockerBinary, append([]string{"rmi", "-f"}, imgs...)...).Assert(t, icmd.Success)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAllVolumes(t testingT, c client.APIClient) {
|
||||
var errs []string
|
||||
volumes, err := getAllVolumes(c)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
for _, v := range volumes {
|
||||
err := c.VolumeRemove(context.Background(), v.Name, true)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%v", strings.Join(errs, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func getAllVolumes(c client.APIClient) ([]*types.Volume, error) {
|
||||
volumes, err := c.VolumeList(context.Background(), filters.Args{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return volumes.Volumes, nil
|
||||
}
|
||||
|
||||
func deleteAllNetworks(t testingT, c client.APIClient, daemonPlatform string) {
|
||||
networks, err := getAllNetworks(c)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
var errs []string
|
||||
for _, n := range networks {
|
||||
if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
|
||||
continue
|
||||
}
|
||||
if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
|
||||
// nat is a pre-defined network on Windows and cannot be removed
|
||||
continue
|
||||
}
|
||||
err := c.NetworkRemove(context.Background(), n.ID)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%v", strings.Join(errs, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func getAllNetworks(c client.APIClient) ([]types.NetworkResource, error) {
|
||||
networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func deleteAllPlugins(t testingT, c client.APIClient, dockerBinary string) {
|
||||
plugins, err := getAllPlugins(c)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
var errs []string
|
||||
for _, p := range plugins {
|
||||
err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%v", strings.Join(errs, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func getAllPlugins(c client.APIClient) (types.PluginsListResponse, error) {
|
||||
plugins, err := c.PluginList(context.Background(), filters.Args{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plugins, nil
|
||||
}
|
||||
@@ -1,19 +1,11 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/opts"
|
||||
"golang.org/x/net/context"
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,89 +15,28 @@ var (
|
||||
|
||||
func init() {
|
||||
if DefaultClientBinary == "" {
|
||||
// TODO: to be removed once we no longer depend on the docker cli for integration tests
|
||||
//panic("TEST_CLIENT_BINARY must be set")
|
||||
DefaultClientBinary = "docker"
|
||||
}
|
||||
}
|
||||
|
||||
// Execution holds informations about the test execution environment.
|
||||
// Execution contains information about the current test execution and daemon
|
||||
// under test
|
||||
type Execution struct {
|
||||
daemonPlatform string
|
||||
localDaemon bool
|
||||
experimentalDaemon bool
|
||||
daemonStorageDriver string
|
||||
isolation container.Isolation
|
||||
daemonPid int
|
||||
daemonKernelVersion string
|
||||
// For a local daemon on Linux, these values will be used for testing
|
||||
// user namespace support as the standard graph path(s) will be
|
||||
// appended with the root remapped uid.gid prefix
|
||||
dockerBasePath string
|
||||
volumesConfigPath string
|
||||
containerStoragePath string
|
||||
// baseImage is the name of the base image for testing
|
||||
// Environment variable WINDOWS_BASE_IMAGE can override this
|
||||
baseImage string
|
||||
environment.Execution
|
||||
dockerBinary string
|
||||
|
||||
protectedElements protectedElements
|
||||
}
|
||||
|
||||
// New creates a new Execution struct
|
||||
// DockerBinary returns the docker binary for this testing environment
|
||||
func (e *Execution) DockerBinary() string {
|
||||
return e.dockerBinary
|
||||
}
|
||||
|
||||
// New returns details about the testing environment
|
||||
func New() (*Execution, error) {
|
||||
localDaemon := true
|
||||
// Deterministically working out the environment in which CI is running
|
||||
// to evaluate whether the daemon is local or remote is not possible through
|
||||
// a build tag.
|
||||
//
|
||||
// For example Windows to Linux CI under Jenkins tests the 64-bit
|
||||
// Windows binary build with the daemon build tag, but calls a remote
|
||||
// Linux daemon.
|
||||
//
|
||||
// We can't just say if Windows then assume the daemon is local as at
|
||||
// some point, we will be testing the Windows CLI against a Windows daemon.
|
||||
//
|
||||
// Similarly, it will be perfectly valid to also run CLI tests from
|
||||
// a Linux CLI (built with the daemon tag) against a Windows daemon.
|
||||
if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
|
||||
localDaemon = false
|
||||
}
|
||||
info, err := getDaemonDockerInfo()
|
||||
env, err := environment.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
daemonPlatform := info.OSType
|
||||
if daemonPlatform != "linux" && daemonPlatform != "windows" {
|
||||
return nil, fmt.Errorf("Cannot run tests against platform: %s", daemonPlatform)
|
||||
}
|
||||
baseImage := "scratch"
|
||||
volumesConfigPath := filepath.Join(info.DockerRootDir, "volumes")
|
||||
containerStoragePath := filepath.Join(info.DockerRootDir, "containers")
|
||||
// Make sure in context of daemon, not the local platform. Note we can't
|
||||
// use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
|
||||
if daemonPlatform == "windows" {
|
||||
volumesConfigPath = strings.Replace(volumesConfigPath, `/`, `\`, -1)
|
||||
containerStoragePath = strings.Replace(containerStoragePath, `/`, `\`, -1)
|
||||
|
||||
baseImage = "microsoft/windowsservercore"
|
||||
if len(os.Getenv("WINDOWS_BASE_IMAGE")) > 0 {
|
||||
baseImage = os.Getenv("WINDOWS_BASE_IMAGE")
|
||||
fmt.Println("INFO: Windows Base image is ", baseImage)
|
||||
}
|
||||
} else {
|
||||
volumesConfigPath = strings.Replace(volumesConfigPath, `\`, `/`, -1)
|
||||
containerStoragePath = strings.Replace(containerStoragePath, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
var daemonPid int
|
||||
dest := os.Getenv("DEST")
|
||||
b, err := ioutil.ReadFile(filepath.Join(dest, "docker.pid"))
|
||||
if err == nil {
|
||||
if p, err := strconv.ParseInt(string(b), 10, 32); err == nil {
|
||||
daemonPid = int(p)
|
||||
}
|
||||
}
|
||||
|
||||
dockerBinary, err := exec.LookPath(DefaultClientBinary)
|
||||
if err != nil {
|
||||
@@ -113,117 +44,36 @@ func New() (*Execution, error) {
|
||||
}
|
||||
|
||||
return &Execution{
|
||||
localDaemon: localDaemon,
|
||||
daemonPlatform: daemonPlatform,
|
||||
daemonStorageDriver: info.Driver,
|
||||
daemonKernelVersion: info.KernelVersion,
|
||||
dockerBasePath: info.DockerRootDir,
|
||||
volumesConfigPath: volumesConfigPath,
|
||||
containerStoragePath: containerStoragePath,
|
||||
isolation: info.Isolation,
|
||||
daemonPid: daemonPid,
|
||||
experimentalDaemon: info.ExperimentalBuild,
|
||||
baseImage: baseImage,
|
||||
dockerBinary: dockerBinary,
|
||||
protectedElements: protectedElements{
|
||||
images: map[string]struct{}{},
|
||||
},
|
||||
Execution: *env,
|
||||
dockerBinary: dockerBinary,
|
||||
}, nil
|
||||
}
|
||||
func getDaemonDockerInfo() (types.Info, error) {
|
||||
// FIXME(vdemeester) should be safe to use as is
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
return types.Info{}, err
|
||||
}
|
||||
return client.Info(context.Background())
|
||||
|
||||
// DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker)
|
||||
// TODO: remove
|
||||
// Deprecated: use Execution.DaemonInfo.DockerRootDir
|
||||
func (e *Execution) DockerBasePath() string {
|
||||
return e.DaemonInfo.DockerRootDir
|
||||
}
|
||||
|
||||
// LocalDaemon is true if the daemon under test is on the same
|
||||
// host as the CLI.
|
||||
func (e *Execution) LocalDaemon() bool {
|
||||
return e.localDaemon
|
||||
// ExperimentalDaemon tell whether the main daemon has
|
||||
// experimental features enabled or not
|
||||
// Deprecated: use DaemonInfo.ExperimentalBuild
|
||||
func (e *Execution) ExperimentalDaemon() bool {
|
||||
return e.DaemonInfo.ExperimentalBuild
|
||||
}
|
||||
|
||||
// DaemonPlatform is held globally so that tests can make intelligent
|
||||
// decisions on how to configure themselves according to the platform
|
||||
// of the daemon. This is initialized in docker_utils by sending
|
||||
// a version call to the daemon and examining the response header.
|
||||
// Deprecated: use Execution.DaemonInfo.OSType
|
||||
func (e *Execution) DaemonPlatform() string {
|
||||
return e.daemonPlatform
|
||||
}
|
||||
|
||||
// DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker)
|
||||
func (e *Execution) DockerBasePath() string {
|
||||
return e.dockerBasePath
|
||||
}
|
||||
|
||||
// VolumesConfigPath is the path of the volume configuration for the testing daemon
|
||||
func (e *Execution) VolumesConfigPath() string {
|
||||
return e.volumesConfigPath
|
||||
}
|
||||
|
||||
// ContainerStoragePath is the path where the container are stored for the testing daemon
|
||||
func (e *Execution) ContainerStoragePath() string {
|
||||
return e.containerStoragePath
|
||||
}
|
||||
|
||||
// DaemonStorageDriver is held globally so that tests can know the storage
|
||||
// driver of the daemon. This is initialized in docker_utils by sending
|
||||
// a version call to the daemon and examining the response header.
|
||||
func (e *Execution) DaemonStorageDriver() string {
|
||||
return e.daemonStorageDriver
|
||||
}
|
||||
|
||||
// Isolation is the isolation mode of the daemon under test
|
||||
func (e *Execution) Isolation() container.Isolation {
|
||||
return e.isolation
|
||||
}
|
||||
|
||||
// DaemonPID is the pid of the main test daemon
|
||||
func (e *Execution) DaemonPID() int {
|
||||
return e.daemonPid
|
||||
}
|
||||
|
||||
// ExperimentalDaemon tell whether the main daemon has
|
||||
// experimental features enabled or not
|
||||
func (e *Execution) ExperimentalDaemon() bool {
|
||||
return e.experimentalDaemon
|
||||
return e.DaemonInfo.OSType
|
||||
}
|
||||
|
||||
// MinimalBaseImage is the image used for minimal builds (it depends on the platform)
|
||||
// Deprecated: use Execution.PlatformDefaults.BaseImage
|
||||
func (e *Execution) MinimalBaseImage() string {
|
||||
return e.baseImage
|
||||
}
|
||||
|
||||
// DaemonKernelVersion is the kernel version of the daemon as a string, as returned
|
||||
// by an INFO call to the daemon.
|
||||
func (e *Execution) DaemonKernelVersion() string {
|
||||
return e.daemonKernelVersion
|
||||
}
|
||||
|
||||
// DaemonKernelVersionNumeric is the kernel version of the daemon as an integer.
|
||||
// Mostly useful on Windows where DaemonKernelVersion holds the full string such
|
||||
// as `10.0 14393 (14393.447.amd64fre.rs1_release_inmarket.161102-0100)`, but
|
||||
// integration tests really only need the `14393` piece to make decisions.
|
||||
func (e *Execution) DaemonKernelVersionNumeric() int {
|
||||
if e.daemonPlatform != "windows" {
|
||||
return -1
|
||||
}
|
||||
v, _ := strconv.Atoi(strings.Split(e.daemonKernelVersion, " ")[1])
|
||||
return v
|
||||
}
|
||||
|
||||
// DockerBinary returns the docker binary for this testing environment
|
||||
func (e *Execution) DockerBinary() string {
|
||||
return e.dockerBinary
|
||||
}
|
||||
|
||||
// DaemonHost return the daemon host string for this test execution
|
||||
func DaemonHost() string {
|
||||
daemonURLStr := "unix://" + opts.DefaultUnixSocket
|
||||
if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
|
||||
daemonURLStr = daemonHostVar
|
||||
}
|
||||
return daemonURLStr
|
||||
return e.PlatformDefaults.BaseImage
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/integration-cli/fixtures/load"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
)
|
||||
|
||||
type protectedElements struct {
|
||||
images map[string]struct{}
|
||||
}
|
||||
|
||||
// ProtectImage adds the specified image(s) to be protected in case of clean
|
||||
func (e *Execution) ProtectImage(t testingT, images ...string) {
|
||||
for _, image := range images {
|
||||
e.protectedElements.images[image] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// ProtectImages protects existing images and on linux frozen images from being
|
||||
// cleaned up at the end of test runs
|
||||
func ProtectImages(t testingT, testEnv *Execution) {
|
||||
images := getExistingImages(t, testEnv)
|
||||
|
||||
if testEnv.DaemonPlatform() == "linux" {
|
||||
images = append(images, ensureFrozenImagesLinux(t, testEnv)...)
|
||||
}
|
||||
testEnv.ProtectImage(t, images...)
|
||||
}
|
||||
|
||||
func getExistingImages(t testingT, testEnv *Execution) []string {
|
||||
// TODO: use API instead of cli
|
||||
result := icmd.RunCommand(testEnv.dockerBinary, "images", "-f", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}")
|
||||
result.Assert(t, icmd.Success)
|
||||
return strings.Split(strings.TrimSpace(result.Stdout()), "\n")
|
||||
}
|
||||
|
||||
func ensureFrozenImagesLinux(t testingT, testEnv *Execution) []string {
|
||||
images := []string{"busybox:latest", "hello-world:frozen", "debian:jessie"}
|
||||
err := load.FrozenImagesLinux(testEnv.DockerBinary(), images...)
|
||||
if err != nil {
|
||||
result := icmd.RunCommand(testEnv.DockerBinary(), "image", "ls")
|
||||
t.Logf(result.String())
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
return images
|
||||
}
|
||||
@@ -9,21 +9,27 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var frozenImgDir = "/docker-frozen-images"
|
||||
const frozenImgDir = "/docker-frozen-images"
|
||||
|
||||
// FrozenImagesLinux loads the frozen image set for the integration suite
|
||||
// If the images are not available locally it will download them
|
||||
// TODO: This loads whatever is in the frozen image dir, regardless of what
|
||||
// images were passed in. If the images need to be downloaded, then it will respect
|
||||
// the passed in images
|
||||
func FrozenImagesLinux(dockerBinary string, images ...string) error {
|
||||
func FrozenImagesLinux(client client.APIClient, images ...string) error {
|
||||
imgNS := os.Getenv("TEST_IMAGE_NAMESPACE")
|
||||
var loadImages []struct{ srcName, destName string }
|
||||
for _, img := range images {
|
||||
if err := exec.Command(dockerBinary, "inspect", "--type=image", img).Run(); err != nil {
|
||||
if !imageExists(client, img) {
|
||||
srcName := img
|
||||
// hello-world:latest gets re-tagged as hello-world:frozen
|
||||
// there are some tests that use hello-world:latest specifically so it pulls
|
||||
@@ -46,35 +52,41 @@ func FrozenImagesLinux(dockerBinary string, images ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
fi, err := os.Stat(frozenImgDir)
|
||||
if err != nil || !fi.IsDir() {
|
||||
srcImages := make([]string, 0, len(loadImages))
|
||||
for _, img := range loadImages {
|
||||
srcImages = append(srcImages, img.srcName)
|
||||
}
|
||||
if err := pullImages(dockerBinary, srcImages); err != nil {
|
||||
if err := pullImages(ctx, client, srcImages); err != nil {
|
||||
return errors.Wrap(err, "error pulling image list")
|
||||
}
|
||||
} else {
|
||||
if err := loadFrozenImages(dockerBinary); err != nil {
|
||||
if err := loadFrozenImages(ctx, client); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, img := range loadImages {
|
||||
if img.srcName != img.destName {
|
||||
if out, err := exec.Command(dockerBinary, "tag", img.srcName, img.destName).CombinedOutput(); err != nil {
|
||||
return errors.Errorf("%v: %s", err, string(out))
|
||||
if err := client.ImageTag(ctx, img.srcName, img.destName); err != nil {
|
||||
return errors.Wrapf(err, "failed to tag %s as %s", img.srcName, img.destName)
|
||||
}
|
||||
if out, err := exec.Command(dockerBinary, "rmi", img.srcName).CombinedOutput(); err != nil {
|
||||
return errors.Errorf("%v: %s", err, string(out))
|
||||
if _, err := client.ImageRemove(ctx, img.srcName, types.ImageRemoveOptions{}); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove %s", img.srcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadFrozenImages(dockerBinary string) error {
|
||||
func imageExists(client client.APIClient, name string) bool {
|
||||
_, _, err := client.ImageInspectWithRaw(context.Background(), name)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func loadFrozenImages(ctx context.Context, client client.APIClient) error {
|
||||
tar, err := exec.LookPath("tar")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not find tar binary")
|
||||
@@ -90,15 +102,16 @@ func loadFrozenImages(dockerBinary string) error {
|
||||
tarCmd.Start()
|
||||
defer tarCmd.Wait()
|
||||
|
||||
cmd := exec.Command(dockerBinary, "load")
|
||||
cmd.Stdin = out
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return errors.Errorf("%v: %s", err, string(out))
|
||||
resp, err := client.ImageLoad(ctx, out, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load frozen images")
|
||||
}
|
||||
return nil
|
||||
defer resp.Body.Close()
|
||||
fd, isTerminal := term.GetFdInfo(os.Stdout)
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp.Body, os.Stdout, fd, isTerminal, nil)
|
||||
}
|
||||
|
||||
func pullImages(dockerBinary string, images []string) error {
|
||||
func pullImages(ctx context.Context, client client.APIClient, images []string) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting path to dockerfile")
|
||||
@@ -119,16 +132,8 @@ func pullImages(dockerBinary string, images []string) error {
|
||||
wg.Add(1)
|
||||
go func(tag, ref string) {
|
||||
defer wg.Done()
|
||||
if out, err := exec.Command(dockerBinary, "pull", ref).CombinedOutput(); err != nil {
|
||||
chErr <- errors.Errorf("%v: %s", string(out), err)
|
||||
return
|
||||
}
|
||||
if out, err := exec.Command(dockerBinary, "tag", ref, tag).CombinedOutput(); err != nil {
|
||||
chErr <- errors.Errorf("%v: %s", string(out), err)
|
||||
return
|
||||
}
|
||||
if out, err := exec.Command(dockerBinary, "rmi", ref).CombinedOutput(); err != nil {
|
||||
chErr <- errors.Errorf("%v: %s", string(out), err)
|
||||
if err := pullTagAndRemove(ctx, client, ref, tag); err != nil {
|
||||
chErr <- err
|
||||
return
|
||||
}
|
||||
}(tag, ref)
|
||||
@@ -138,6 +143,25 @@ func pullImages(dockerBinary string, images []string) error {
|
||||
return <-chErr
|
||||
}
|
||||
|
||||
func pullTagAndRemove(ctx context.Context, client client.APIClient, ref string, tag string) error {
|
||||
resp, err := client.ImagePull(ctx, ref, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to pull %s", ref)
|
||||
}
|
||||
defer resp.Close()
|
||||
fd, isTerminal := term.GetFdInfo(os.Stdout)
|
||||
if err := jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerminal, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.ImageTag(ctx, ref, tag); err != nil {
|
||||
return errors.Wrapf(err, "failed to tag %s as %s", ref, tag)
|
||||
}
|
||||
_, err = client.ImageRemove(ctx, ref, types.ImageRemoveOptions{})
|
||||
return errors.Wrapf(err, "failed to remove %s", ref)
|
||||
|
||||
}
|
||||
|
||||
func readFrozenImageList(dockerfilePath string, images []string) (map[string]string, error) {
|
||||
f, err := os.Open(dockerfilePath)
|
||||
if err != nil {
|
||||
@@ -156,11 +180,6 @@ func readFrozenImageList(dockerfilePath string, images []string) (map[string]str
|
||||
continue
|
||||
}
|
||||
|
||||
frozenImgDir = line[2]
|
||||
if line[2] == frozenImgDir {
|
||||
frozenImgDir = filepath.Join(os.Getenv("DEST"), "frozen-images")
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
img := strings.TrimSpace(scanner.Text())
|
||||
img = strings.TrimSuffix(img, "\\")
|
||||
|
||||
@@ -79,7 +79,7 @@ func ensureSyscallTest(c *check.C) {
|
||||
}
|
||||
|
||||
func ensureSyscallTestBuild(c *check.C) {
|
||||
err := load.FrozenImagesLinux(dockerBinary, "buildpack-deps:jessie")
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
var buildArgs []string
|
||||
@@ -126,7 +126,7 @@ func ensureNNPTest(c *check.C) {
|
||||
}
|
||||
|
||||
func ensureNNPTestBuild(c *check.C) {
|
||||
err := load.FrozenImagesLinux(dockerBinary, "buildpack-deps:jessie")
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
var buildArgs []string
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type skipT interface {
|
||||
// SkipT is the interface required to skip tests
|
||||
type SkipT interface {
|
||||
Skip(reason string)
|
||||
}
|
||||
|
||||
@@ -17,7 +18,7 @@ type Test func() bool
|
||||
|
||||
// Is checks if the environment satisfies the requirements
|
||||
// for the test to run or skips the tests.
|
||||
func Is(s skipT, requirements ...Test) {
|
||||
func Is(s SkipT, requirements ...Test) {
|
||||
for _, r := range requirements {
|
||||
isValid := r()
|
||||
if !isValid {
|
||||
|
||||
@@ -6,57 +6,47 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/integration-cli/requirement"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func PlatformIs(platform string) bool {
|
||||
return testEnv.DaemonPlatform() == platform
|
||||
}
|
||||
|
||||
func ArchitectureIs(arch string) bool {
|
||||
return os.Getenv("DOCKER_ENGINE_GOARCH") == arch
|
||||
}
|
||||
|
||||
func ArchitectureIsNot(arch string) bool {
|
||||
return os.Getenv("DOCKER_ENGINE_GOARCH") != arch
|
||||
}
|
||||
|
||||
func StorageDriverIs(storageDriver string) bool {
|
||||
return strings.HasPrefix(testEnv.DaemonStorageDriver(), storageDriver)
|
||||
}
|
||||
|
||||
func StorageDriverIsNot(storageDriver string) bool {
|
||||
return !strings.HasPrefix(testEnv.DaemonStorageDriver(), storageDriver)
|
||||
}
|
||||
|
||||
func DaemonIsWindows() bool {
|
||||
return PlatformIs("windows")
|
||||
return testEnv.DaemonInfo.OSType == "windows"
|
||||
}
|
||||
|
||||
func DaemonIsWindowsAtLeastBuild(buildNumber int) func() bool {
|
||||
return func() bool {
|
||||
return DaemonIsWindows() && testEnv.DaemonKernelVersionNumeric() >= buildNumber
|
||||
if testEnv.DaemonInfo.OSType != "windows" {
|
||||
return false
|
||||
}
|
||||
version := testEnv.DaemonInfo.KernelVersion
|
||||
numVersion, _ := strconv.Atoi(strings.Split(version, " ")[1])
|
||||
return numVersion >= buildNumber
|
||||
}
|
||||
}
|
||||
|
||||
func DaemonIsLinux() bool {
|
||||
return PlatformIs("linux")
|
||||
return testEnv.DaemonInfo.OSType == "linux"
|
||||
}
|
||||
|
||||
// Deprecated: use skip.IfCondition(t, !testEnv.DaemonInfo.ExperimentalBuild)
|
||||
func ExperimentalDaemon() bool {
|
||||
return testEnv.ExperimentalDaemon()
|
||||
return testEnv.DaemonInfo.ExperimentalBuild
|
||||
}
|
||||
|
||||
func NotExperimentalDaemon() bool {
|
||||
return !testEnv.ExperimentalDaemon()
|
||||
return !testEnv.DaemonInfo.ExperimentalBuild
|
||||
}
|
||||
|
||||
func IsAmd64() bool {
|
||||
return ArchitectureIs("amd64")
|
||||
return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64"
|
||||
}
|
||||
|
||||
func NotArm() bool {
|
||||
@@ -76,7 +66,7 @@ func NotS390X() bool {
|
||||
}
|
||||
|
||||
func SameHostDaemon() bool {
|
||||
return testEnv.LocalDaemon()
|
||||
return testEnv.IsLocalDaemon()
|
||||
}
|
||||
|
||||
func UnixCli() bool {
|
||||
@@ -127,12 +117,8 @@ func NotaryServerHosting() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func NotOverlay() bool {
|
||||
return StorageDriverIsNot("overlay")
|
||||
}
|
||||
|
||||
func Devicemapper() bool {
|
||||
return StorageDriverIs("devicemapper")
|
||||
return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper")
|
||||
}
|
||||
|
||||
func IPv6() bool {
|
||||
@@ -177,21 +163,21 @@ func UserNamespaceInKernel() bool {
|
||||
}
|
||||
|
||||
func IsPausable() bool {
|
||||
if testEnv.DaemonPlatform() == "windows" {
|
||||
return testEnv.Isolation() == "hyperv"
|
||||
if testEnv.DaemonInfo.OSType == "windows" {
|
||||
return testEnv.DaemonInfo.Isolation == "hyperv"
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func NotPausable() bool {
|
||||
if testEnv.DaemonPlatform() == "windows" {
|
||||
return testEnv.Isolation() == "process"
|
||||
if testEnv.DaemonInfo.OSType == "windows" {
|
||||
return testEnv.DaemonInfo.Isolation == "process"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsolationIs(expectedIsolation string) bool {
|
||||
return testEnv.DaemonPlatform() == "windows" && string(testEnv.Isolation()) == expectedIsolation
|
||||
return testEnv.DaemonInfo.OSType == "windows" && string(testEnv.DaemonInfo.Isolation) == expectedIsolation
|
||||
}
|
||||
|
||||
func IsolationIsHyperv() bool {
|
||||
@@ -204,6 +190,6 @@ func IsolationIsProcess() bool {
|
||||
|
||||
// testRequires checks if the environment satisfies the requirements
|
||||
// for the test to run or skips the tests.
|
||||
func testRequires(c *check.C, requirements ...requirement.Test) {
|
||||
func testRequires(c requirement.SkipT, requirements ...requirement.Test) {
|
||||
requirement.Is(c, requirements...)
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func overlay2Supported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
daemonV, err := kernel.ParseRelease(testEnv.DaemonKernelVersion())
|
||||
daemonV, err := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -5,12 +5,10 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/integration-cli/environment"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
)
|
||||
|
||||
var (
|
||||
testEnv *environment.Execution
|
||||
)
|
||||
var testEnv *environment.Execution
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
@@ -20,18 +18,11 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// TODO: replace this with `testEnv.Print()` to print the full env
|
||||
if testEnv.LocalDaemon() {
|
||||
fmt.Println("INFO: Testing against a local daemon")
|
||||
} else {
|
||||
fmt.Println("INFO: Testing against a remote daemon")
|
||||
}
|
||||
|
||||
res := m.Run()
|
||||
os.Exit(res)
|
||||
testEnv.Print()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) func() {
|
||||
environment.ProtectImages(t, testEnv)
|
||||
return func() { testEnv.Clean(t, testEnv.DockerBinary()) }
|
||||
return func() { testEnv.Clean(t) }
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ const defaultSwarmPort = 2477
|
||||
func newSwarm(t *testing.T) *daemon.Swarm {
|
||||
d := &daemon.Swarm{
|
||||
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.ExperimentalDaemon(),
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
}),
|
||||
// TODO: better method of finding an unused port
|
||||
Port: defaultSwarmPort,
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/integration-cli/environment"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
)
|
||||
|
||||
var testEnv *environment.Execution
|
||||
@@ -20,18 +20,11 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// TODO: replace this with `testEnv.Print()` to print the full env
|
||||
if testEnv.LocalDaemon() {
|
||||
fmt.Println("INFO: Testing against a local daemon")
|
||||
} else {
|
||||
fmt.Println("INFO: Testing against a remote daemon")
|
||||
}
|
||||
|
||||
res := m.Run()
|
||||
os.Exit(res)
|
||||
testEnv.Print()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) func() {
|
||||
environment.ProtectImages(t, testEnv)
|
||||
return func() { testEnv.Clean(t, testEnv.DockerBinary()) }
|
||||
return func() { testEnv.Clean(t) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type testingT interface {
|
||||
require.TestingT
|
||||
logT
|
||||
Fatalf(string, ...interface{})
|
||||
}
|
||||
|
||||
type logT interface {
|
||||
Logf(string, ...interface{})
|
||||
}
|
||||
|
||||
// Clean the environment, preserving protected objects (images, containers, ...)
|
||||
// and removing everything else. It's meant to run after any tests so that they don't
|
||||
// depend on each others.
|
||||
func (e *Execution) Clean(t testingT) {
|
||||
client := e.APIClient()
|
||||
|
||||
platform := e.DaemonInfo.OSType
|
||||
if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
|
||||
unpauseAllContainers(t, client)
|
||||
}
|
||||
deleteAllContainers(t, client)
|
||||
deleteAllImages(t, client, e.protectedElements.images)
|
||||
deleteAllVolumes(t, client)
|
||||
deleteAllNetworks(t, client, platform)
|
||||
if platform == "linux" {
|
||||
deleteAllPlugins(t, client)
|
||||
}
|
||||
}
|
||||
|
||||
func unpauseAllContainers(t assert.TestingT, client client.ContainerAPIClient) {
|
||||
ctx := context.Background()
|
||||
containers := getPausedContainers(ctx, t, client)
|
||||
if len(containers) > 0 {
|
||||
for _, container := range containers {
|
||||
err := client.ContainerUnpause(ctx, container.ID)
|
||||
assert.NoError(t, err, "failed to unpause container %s", container.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPausedContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("status", "paused")
|
||||
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
||||
Filters: filter,
|
||||
Quiet: true,
|
||||
All: true,
|
||||
})
|
||||
assert.NoError(t, err, "failed to list containers")
|
||||
return containers
|
||||
}
|
||||
|
||||
var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
|
||||
|
||||
func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient) {
|
||||
ctx := context.Background()
|
||||
containers := getAllContainers(ctx, t, apiclient)
|
||||
if len(containers) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
RemoveVolumes: true,
|
||||
})
|
||||
if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) {
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err, "failed to remove %s", container.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func getAllContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
|
||||
containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
||||
Quiet: true,
|
||||
All: true,
|
||||
})
|
||||
assert.NoError(t, err, "failed to list containers")
|
||||
return containers
|
||||
}
|
||||
|
||||
func deleteAllImages(t testingT, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
|
||||
images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
|
||||
assert.NoError(t, err, "failed to list images")
|
||||
|
||||
ctx := context.Background()
|
||||
for _, image := range images {
|
||||
tags := tagsFromImageSummary(image)
|
||||
if len(tags) == 0 {
|
||||
t.Logf("Removing image %s", image.ID)
|
||||
removeImage(ctx, t, apiclient, image.ID)
|
||||
continue
|
||||
}
|
||||
for _, tag := range tags {
|
||||
if _, ok := protectedImages[tag]; !ok {
|
||||
t.Logf("Removing image %s", tag)
|
||||
removeImage(ctx, t, apiclient, tag)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageAPIClient, ref string) {
|
||||
_, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
if client.IsErrNotFound(err) {
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err, "failed to remove image %s", ref)
|
||||
}
|
||||
|
||||
func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient) {
|
||||
volumes, err := c.VolumeList(context.Background(), filters.Args{})
|
||||
assert.NoError(t, err, "failed to list volumes")
|
||||
|
||||
for _, v := range volumes.Volumes {
|
||||
err := c.VolumeRemove(context.Background(), v.Name, true)
|
||||
assert.NoError(t, err, "failed to remove volume %s", v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string) {
|
||||
networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
|
||||
assert.NoError(t, err, "failed to list networks")
|
||||
|
||||
for _, n := range networks {
|
||||
if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
|
||||
continue
|
||||
}
|
||||
if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
|
||||
// nat is a pre-defined network on Windows and cannot be removed
|
||||
continue
|
||||
}
|
||||
err := c.NetworkRemove(context.Background(), n.ID)
|
||||
assert.NoError(t, err, "failed to remove network %s", n.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient) {
|
||||
plugins, err := c.PluginList(context.Background(), filters.Args{})
|
||||
assert.NoError(t, err, "failed to list plugins")
|
||||
|
||||
for _, p := range plugins {
|
||||
err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
|
||||
assert.NoError(t, err, "failed to remove plugin %s", p.ID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Execution contains information about the current test execution and daemon
|
||||
// under test
|
||||
type Execution struct {
|
||||
client client.APIClient
|
||||
DaemonInfo types.Info
|
||||
PlatformDefaults PlatformDefaults
|
||||
protectedElements protectedElements
|
||||
}
|
||||
|
||||
// PlatformDefaults are defaults values for the platform of the daemon under test
|
||||
type PlatformDefaults struct {
|
||||
BaseImage string
|
||||
VolumesConfigPath string
|
||||
ContainerStoragePath string
|
||||
}
|
||||
|
||||
// New creates a new Execution struct
|
||||
func New() (*Execution, error) {
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create client")
|
||||
}
|
||||
|
||||
info, err := client.Info(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get info from daemon")
|
||||
}
|
||||
|
||||
return &Execution{
|
||||
client: client,
|
||||
DaemonInfo: info,
|
||||
PlatformDefaults: getPlatformDefaults(info),
|
||||
protectedElements: newProtectedElements(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getPlatformDefaults(info types.Info) PlatformDefaults {
|
||||
volumesPath := filepath.Join(info.DockerRootDir, "volumes")
|
||||
containersPath := filepath.Join(info.DockerRootDir, "containers")
|
||||
|
||||
switch info.OSType {
|
||||
case "linux":
|
||||
return PlatformDefaults{
|
||||
BaseImage: "scratch",
|
||||
VolumesConfigPath: toSlash(volumesPath),
|
||||
ContainerStoragePath: toSlash(containersPath),
|
||||
}
|
||||
case "windows":
|
||||
baseImage := "microsoft/windowsservercore"
|
||||
if override := os.Getenv("WINDOWS_BASE_IMAGE"); override != "" {
|
||||
baseImage = override
|
||||
fmt.Println("INFO: Windows Base image is ", baseImage)
|
||||
}
|
||||
return PlatformDefaults{
|
||||
BaseImage: baseImage,
|
||||
VolumesConfigPath: filepath.FromSlash(volumesPath),
|
||||
ContainerStoragePath: filepath.FromSlash(containersPath),
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown info.OSType for daemon: %s", info.OSType))
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure in context of daemon, not the local platform. Note we can't
|
||||
// use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
|
||||
func toSlash(path string) string {
|
||||
return strings.Replace(path, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
// IsLocalDaemon is true if the daemon under test is on the same
|
||||
// host as the CLI.
|
||||
//
|
||||
// Deterministically working out the environment in which CI is running
|
||||
// to evaluate whether the daemon is local or remote is not possible through
|
||||
// a build tag.
|
||||
//
|
||||
// For example Windows to Linux CI under Jenkins tests the 64-bit
|
||||
// Windows binary build with the daemon build tag, but calls a remote
|
||||
// Linux daemon.
|
||||
//
|
||||
// We can't just say if Windows then assume the daemon is local as at
|
||||
// some point, we will be testing the Windows CLI against a Windows daemon.
|
||||
//
|
||||
// Similarly, it will be perfectly valid to also run CLI tests from
|
||||
// a Linux CLI (built with the daemon tag) against a Windows daemon.
|
||||
func (e *Execution) IsLocalDaemon() bool {
|
||||
return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
|
||||
}
|
||||
|
||||
// Print the execution details to stdout
|
||||
// TODO: print everything
|
||||
func (e *Execution) Print() {
|
||||
if e.IsLocalDaemon() {
|
||||
fmt.Println("INFO: Testing against a local daemon")
|
||||
} else {
|
||||
fmt.Println("INFO: Testing against a remote daemon")
|
||||
}
|
||||
}
|
||||
|
||||
// APIClient returns an APIClient connected to the daemon under test
|
||||
func (e *Execution) APIClient() client.APIClient {
|
||||
return e.client
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/integration-cli/fixtures/load"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type protectedElements struct {
|
||||
images map[string]struct{}
|
||||
}
|
||||
|
||||
// ProtectImage adds the specified image(s) to be protected in case of clean
|
||||
func (e *Execution) ProtectImage(t testingT, images ...string) {
|
||||
for _, image := range images {
|
||||
e.protectedElements.images[image] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func newProtectedElements() protectedElements {
|
||||
return protectedElements{
|
||||
images: map[string]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// ProtectImages protects existing images and on linux frozen images from being
|
||||
// cleaned up at the end of test runs
|
||||
func ProtectImages(t testingT, testEnv *Execution) {
|
||||
images := getExistingImages(t, testEnv)
|
||||
|
||||
if testEnv.DaemonInfo.OSType == "linux" {
|
||||
images = append(images, ensureFrozenImagesLinux(t, testEnv)...)
|
||||
}
|
||||
testEnv.ProtectImage(t, images...)
|
||||
}
|
||||
|
||||
func getExistingImages(t require.TestingT, testEnv *Execution) []string {
|
||||
client := testEnv.APIClient()
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("dangling", "false")
|
||||
imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
|
||||
Filters: filter,
|
||||
})
|
||||
require.NoError(t, err, "failed to list images")
|
||||
|
||||
images := []string{}
|
||||
for _, image := range imageList {
|
||||
images = append(images, tagsFromImageSummary(image)...)
|
||||
}
|
||||
return images
|
||||
}
|
||||
|
||||
func tagsFromImageSummary(image types.ImageSummary) []string {
|
||||
result := []string{}
|
||||
for _, tag := range image.RepoTags {
|
||||
if tag != "<none>:<none>" {
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
for _, digest := range image.RepoDigests {
|
||||
if digest != "<none>@<none>" {
|
||||
result = append(result, digest)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ensureFrozenImagesLinux(t testingT, testEnv *Execution) []string {
|
||||
images := []string{"busybox:latest", "hello-world:frozen", "debian:jessie"}
|
||||
err := load.FrozenImagesLinux(testEnv.APIClient(), images...)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load frozen images: %s", err)
|
||||
}
|
||||
return images
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir
|
||||
return fmt.Errorf("Container %s is already active", containerID)
|
||||
}
|
||||
|
||||
uid, gid, err := getRootIDs(specs.Spec(spec))
|
||||
uid, gid, err := getRootIDs(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,35 +8,117 @@ import (
|
||||
)
|
||||
|
||||
func TestValidateEnv(t *testing.T) {
|
||||
valids := map[string]string{
|
||||
"a": "a",
|
||||
"something": "something",
|
||||
"_=a": "_=a",
|
||||
"env1=value1": "env1=value1",
|
||||
"_env1=value1": "_env1=value1",
|
||||
"env2=value2=value3": "env2=value2=value3",
|
||||
"env3=abc!qwe": "env3=abc!qwe",
|
||||
"env_4=value 4": "env_4=value 4",
|
||||
"PATH": fmt.Sprintf("PATH=%v", os.Getenv("PATH")),
|
||||
"PATH=something": "PATH=something",
|
||||
"asd!qwe": "asd!qwe",
|
||||
"1asd": "1asd",
|
||||
"123": "123",
|
||||
"some space": "some space",
|
||||
" some space before": " some space before",
|
||||
"some space after ": "some space after ",
|
||||
testcase := []struct {
|
||||
value string
|
||||
expected string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
value: "a",
|
||||
expected: "a",
|
||||
},
|
||||
{
|
||||
value: "something",
|
||||
expected: "something",
|
||||
},
|
||||
{
|
||||
value: "_=a",
|
||||
expected: "_=a",
|
||||
},
|
||||
{
|
||||
value: "env1=value1",
|
||||
expected: "env1=value1",
|
||||
},
|
||||
{
|
||||
value: "_env1=value1",
|
||||
expected: "_env1=value1",
|
||||
},
|
||||
{
|
||||
value: "env2=value2=value3",
|
||||
expected: "env2=value2=value3",
|
||||
},
|
||||
{
|
||||
value: "env3=abc!qwe",
|
||||
expected: "env3=abc!qwe",
|
||||
},
|
||||
{
|
||||
value: "env_4=value 4",
|
||||
expected: "env_4=value 4",
|
||||
},
|
||||
{
|
||||
value: "PATH",
|
||||
expected: fmt.Sprintf("PATH=%v", os.Getenv("PATH")),
|
||||
},
|
||||
{
|
||||
value: "=a",
|
||||
err: fmt.Errorf(fmt.Sprintf("invalid environment variable: %s", "=a")),
|
||||
},
|
||||
{
|
||||
value: "PATH=something",
|
||||
expected: "PATH=something",
|
||||
},
|
||||
{
|
||||
value: "asd!qwe",
|
||||
expected: "asd!qwe",
|
||||
},
|
||||
{
|
||||
value: "1asd",
|
||||
expected: "1asd",
|
||||
},
|
||||
{
|
||||
value: "123",
|
||||
expected: "123",
|
||||
},
|
||||
{
|
||||
value: "some space",
|
||||
expected: "some space",
|
||||
},
|
||||
{
|
||||
value: " some space before",
|
||||
expected: " some space before",
|
||||
},
|
||||
{
|
||||
value: "some space after ",
|
||||
expected: "some space after ",
|
||||
},
|
||||
{
|
||||
value: "=",
|
||||
err: fmt.Errorf(fmt.Sprintf("invalid environment variable: %s", "=")),
|
||||
},
|
||||
}
|
||||
|
||||
// Environment variables are case in-sensitive on Windows
|
||||
if runtime.GOOS == "windows" {
|
||||
valids["PaTh"] = fmt.Sprintf("PaTh=%v", os.Getenv("PATH"))
|
||||
}
|
||||
for value, expected := range valids {
|
||||
actual, err := ValidateEnv(value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
tmp := struct {
|
||||
value string
|
||||
expected string
|
||||
err error
|
||||
}{
|
||||
value: "PaTh",
|
||||
expected: fmt.Sprintf("PaTh=%v", os.Getenv("PATH")),
|
||||
}
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected [%v], got [%v]", expected, actual)
|
||||
testcase = append(testcase, tmp)
|
||||
|
||||
}
|
||||
|
||||
for _, r := range testcase {
|
||||
actual, err := ValidateEnv(r.value)
|
||||
|
||||
if err != nil {
|
||||
if r.err == nil {
|
||||
t.Fatalf("Expected err is nil, got err[%v]", err)
|
||||
}
|
||||
if err.Error() != r.err.Error() {
|
||||
t.Fatalf("Expected err[%v], got err[%v]", r.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && r.err != nil {
|
||||
t.Fatalf("Expected err[%v], but err is nil", r.err)
|
||||
}
|
||||
|
||||
if actual != r.expected {
|
||||
t.Fatalf("Expected [%v], got [%v]", r.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func (opts *MapOpts) GetAll() map[string]string {
|
||||
}
|
||||
|
||||
func (opts *MapOpts) String() string {
|
||||
return fmt.Sprintf("%v", map[string]string((opts.values)))
|
||||
return fmt.Sprintf("%v", opts.values)
|
||||
}
|
||||
|
||||
// Type returns a string name for this Option type
|
||||
|
||||
@@ -18,7 +18,7 @@ func (s *QuotedString) Type() string {
|
||||
}
|
||||
|
||||
func (s *QuotedString) String() string {
|
||||
return string(*s.value)
|
||||
return *s.value
|
||||
}
|
||||
|
||||
func trimQuotes(value string) string {
|
||||
|
||||
@@ -50,8 +50,8 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (
|
||||
// Currently go does not fill in the major/minors
|
||||
if s.Mode&unix.S_IFBLK != 0 ||
|
||||
s.Mode&unix.S_IFCHR != 0 {
|
||||
hdr.Devmajor = int64(major(uint64(s.Rdev)))
|
||||
hdr.Devminor = int64(minor(uint64(s.Rdev)))
|
||||
hdr.Devmajor = int64(major(s.Rdev))
|
||||
hdr.Devminor = int64(minor(s.Rdev))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
||||
s, ok := stat.(*syscall.Stat_t)
|
||||
|
||||
if ok {
|
||||
inode = uint64(s.Ino)
|
||||
inode = s.Ino
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -294,7 +294,7 @@ func OverlayChanges(layers []string, rw string) ([]Change, error) {
|
||||
func overlayDeletedFile(root, path string, fi os.FileInfo) (string, error) {
|
||||
if fi.Mode()&os.ModeCharDevice != 0 {
|
||||
s := fi.Sys().(*syscall.Stat_t)
|
||||
if major(uint64(s.Rdev)) == 0 && minor(uint64(s.Rdev)) == 0 {
|
||||
if major(s.Rdev) == 0 && minor(s.Rdev) == 0 {
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func (info *FileInfo) isDir() bool {
|
||||
}
|
||||
|
||||
func getIno(fi os.FileInfo) uint64 {
|
||||
return uint64(fi.Sys().(*syscall.Stat_t).Ino)
|
||||
return fi.Sys().(*syscall.Stat_t).Ino
|
||||
}
|
||||
|
||||
func hasHardlinks(fi os.FileInfo) bool {
|
||||
|
||||
@@ -74,7 +74,7 @@ type DefaultLogger struct {
|
||||
// DMLog is the logging callback containing all of the information from
|
||||
// devicemapper. The interface is identical to the C libdm counterpart.
|
||||
func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
|
||||
if int(level) <= l.Level {
|
||||
if level <= l.Level {
|
||||
// Forward the log to the correct logrus level, if allowed by dmLogLevel.
|
||||
logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
||||
switch level {
|
||||
|
||||
@@ -34,11 +34,11 @@ func Size(dir string) (size int64, err error) {
|
||||
// Check inode to handle hard links correctly
|
||||
inode := fileInfo.Sys().(*syscall.Stat_t).Ino
|
||||
// inode is not a uint64 on all platforms. Cast it to avoid issues.
|
||||
if _, exists := data[uint64(inode)]; exists {
|
||||
if _, exists := data[inode]; exists {
|
||||
return nil
|
||||
}
|
||||
// inode is not a uint64 on all platforms. Cast it to avoid issues.
|
||||
data[uint64(inode)] = struct{}{}
|
||||
data[inode] = struct{}{}
|
||||
|
||||
size += s
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import "syscall"
|
||||
// fromStatT converts a syscall.Stat_t type to a system.Stat_t type
|
||||
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
|
||||
return &StatT{size: s.Size,
|
||||
mode: uint32(s.Mode),
|
||||
mode: s.Mode,
|
||||
uid: s.Uid,
|
||||
gid: s.Gid,
|
||||
rdev: uint64(s.Rdev),
|
||||
rdev: s.Rdev,
|
||||
mtim: s.Mtim}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ next:
|
||||
return nil, fmt.Errorf("Unknown character: '%s'", key)
|
||||
}
|
||||
} else {
|
||||
codes = append(codes, byte(key[0]))
|
||||
codes = append(codes, key[0])
|
||||
}
|
||||
}
|
||||
return codes, nil
|
||||
|
||||
@@ -3,28 +3,18 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// GetWinsize returns the window size based on the specified file descriptor.
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
// Skipp errno = 0
|
||||
if err == 0 {
|
||||
return ws, nil
|
||||
}
|
||||
uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
|
||||
ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
|
||||
return ws, err
|
||||
}
|
||||
|
||||
// SetWinsize tries to set the specified window size for the specified file descriptor.
|
||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(unix.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
// Skipp errno = 0
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y}
|
||||
return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/plugin/v2"
|
||||
"github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -62,7 +61,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
|
||||
if err := pm.containerdClient.Create(p.GetID(), "", "", *spec, attachToLog(p.GetID())); err != nil {
|
||||
if p.PropagatedMount != "" {
|
||||
if err := mount.Unmount(p.PropagatedMount); err != nil {
|
||||
logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
|
||||
|
||||
@@ -247,6 +247,7 @@ func (err PingResponseError) Error() string {
|
||||
// challenge manager for the supported authentication types and
|
||||
// whether v2 was confirmed by the response. If a response is received but
|
||||
// cannot be interpreted a PingResponseError will be returned.
|
||||
// nolint: interfacer
|
||||
func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.Manager, bool, error) {
|
||||
var (
|
||||
foundV2 = false
|
||||
|
||||
Reference in New Issue
Block a user