Merge component 'engine' from git@github.com:moby/moby master
This commit is contained in:
@@ -91,21 +91,6 @@ RUN set -x \
|
||||
|
||||
|
||||
|
||||
FROM base AS notary
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION v0.5.0
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -buildmode=pie -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -buildmode=pie -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
|
||||
|
||||
FROM base AS docker-py
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f
|
||||
@@ -248,7 +233,6 @@ COPY --from=containerd /opt/containerd/ /usr/local/bin/
|
||||
COPY --from=proxy /opt/proxy/ /usr/local/bin/
|
||||
COPY --from=dockercli /opt/dockercli /usr/local/cli
|
||||
COPY --from=registry /usr/local/bin/registry* /usr/local/bin/
|
||||
COPY --from=notary /usr/local/bin/notary* /usr/local/bin/
|
||||
COPY --from=criu /opt/criu/ /usr/local/
|
||||
COPY --from=docker-py /docker-py /docker-py
|
||||
# TODO: This is for the docker-py tests, which shouldn't really be needed for
|
||||
|
||||
@@ -34,6 +34,8 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
|
||||
if c.AppArmorProfile != "" {
|
||||
appArmorProfile = c.AppArmorProfile
|
||||
} else if c.HostConfig.Privileged {
|
||||
// `docker exec --privileged` does not currently disable AppArmor
|
||||
// profiles. Privileged configuration of the container is inherited
|
||||
appArmorProfile = "unconfined"
|
||||
} else {
|
||||
appArmorProfile = "docker-default"
|
||||
@@ -50,6 +52,7 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.ApparmorProfile = appArmorProfile
|
||||
}
|
||||
daemon.setRlimits(&specs.Spec{Process: p}, c)
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// +build linux
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func TestExecSetPlatformOpt(t *testing.T) {
|
||||
if !apparmor.IsEnabled() {
|
||||
t.Skip("requires AppArmor to be enabled")
|
||||
}
|
||||
d := &Daemon{}
|
||||
c := &container.Container{AppArmorProfile: "my-custom-profile"}
|
||||
ec := &exec.Config{}
|
||||
p := &specs.Process{}
|
||||
|
||||
err := d.execSetPlatformOpt(c, ec, p)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "my-custom-profile", p.ApparmorProfile)
|
||||
}
|
||||
|
||||
// TestExecSetPlatformOptPrivileged verifies that `docker exec --privileged`
|
||||
// does not disable AppArmor profiles. Exec currently inherits the `Privileged`
|
||||
// configuration of the container. See https://github.com/moby/moby/pull/31773#discussion_r105586900
|
||||
//
|
||||
// This behavior may change in future, but test for the behavior to prevent it
|
||||
// from being changed accidentally.
|
||||
func TestExecSetPlatformOptPrivileged(t *testing.T) {
|
||||
if !apparmor.IsEnabled() {
|
||||
t.Skip("requires AppArmor to be enabled")
|
||||
}
|
||||
d := &Daemon{}
|
||||
c := &container.Container{AppArmorProfile: "my-custom-profile"}
|
||||
ec := &exec.Config{Privileged: true}
|
||||
p := &specs.Process{}
|
||||
|
||||
err := d.execSetPlatformOpt(c, ec, p)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "my-custom-profile", p.ApparmorProfile)
|
||||
|
||||
c.HostConfig = &containertypes.HostConfig{Privileged: true}
|
||||
err = d.execSetPlatformOpt(c, ec, p)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "unconfined", p.ApparmorProfile)
|
||||
}
|
||||
@@ -49,6 +49,9 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if capval <= 0 {
|
||||
return nil, fmt.Errorf("max-size should be a positive numbler")
|
||||
}
|
||||
}
|
||||
var maxFiles = 1
|
||||
if maxFileString, ok := info.Config["max-file"]; ok {
|
||||
@@ -62,6 +65,18 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var compress bool
|
||||
if compressString, ok := info.Config["compress"]; ok {
|
||||
var err error
|
||||
compress, err = strconv.ParseBool(compressString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if compress && (maxFiles == 1 || capval == -1) {
|
||||
return nil, fmt.Errorf("compress cannot be true when max-file is less than 2 or max-size is not set")
|
||||
}
|
||||
}
|
||||
|
||||
attrs, err := info.ExtraAttributes(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -95,7 +110,7 @@ func New(info logger.Info) (logger.Logger, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, marshalFunc, decodeFunc, 0640)
|
||||
writer, err := loggerutils.NewLogFile(info.LogPath, capval, maxFiles, compress, marshalFunc, decodeFunc, 0640)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -139,6 +154,7 @@ func ValidateLogOpt(cfg map[string]string) error {
|
||||
switch key {
|
||||
case "max-file":
|
||||
case "max-size":
|
||||
case "compress":
|
||||
case "labels":
|
||||
case "env":
|
||||
case "env-regex":
|
||||
|
||||
@@ -2,6 +2,7 @@ package jsonfilelog // import "github.com/docker/docker/daemon/logger/jsonfilelo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -142,7 +143,7 @@ func TestJSONFileLoggerWithOpts(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
filename := filepath.Join(tmp, "container.log")
|
||||
config := map[string]string{"max-file": "2", "max-size": "1k"}
|
||||
config := map[string]string{"max-file": "3", "max-size": "1k", "compress": "true"}
|
||||
l, err := New(logger.Info{
|
||||
ContainerID: cid,
|
||||
LogPath: filename,
|
||||
@@ -152,21 +153,55 @@ func TestJSONFileLoggerWithOpts(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := 0; i < 36; i++ {
|
||||
if err := l.Log(&logger.Message{Line: []byte("line" + strconv.Itoa(i)), Source: "src1"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
penUlt, err := ioutil.ReadFile(filename + ".1")
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
file, err := os.Open(filename + ".1.gz")
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
zipReader, err := gzip.NewReader(file)
|
||||
defer zipReader.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
penUlt, err = ioutil.ReadAll(zipReader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Open(filename + ".2.gz")
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
zipReader, err := gzip.NewReader(file)
|
||||
defer zipReader.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
antepenult, err := ioutil.ReadAll(zipReader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedPenultimate := `{"log":"line0\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
expectedAntepenultimate := `{"log":"line0\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line1\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line2\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line3\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
@@ -183,10 +218,27 @@ func TestJSONFileLoggerWithOpts(t *testing.T) {
|
||||
{"log":"line14\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line15\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
`
|
||||
expected := `{"log":"line16\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
expectedPenultimate := `{"log":"line16\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line17\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line18\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line19\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line20\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line21\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line22\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line23\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line24\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line25\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line26\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line27\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line28\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line29\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line30\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line31\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
`
|
||||
expected := `{"log":"line32\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line33\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line34\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
{"log":"line35\n","stream":"src1","time":"0001-01-01T00:00:00Z"}
|
||||
`
|
||||
|
||||
if string(res) != expected {
|
||||
@@ -195,7 +247,9 @@ func TestJSONFileLoggerWithOpts(t *testing.T) {
|
||||
if string(penUlt) != expectedPenultimate {
|
||||
t.Fatalf("Wrong log content: %q, expected %q", penUlt, expectedPenultimate)
|
||||
}
|
||||
|
||||
if string(antepenult) != expectedAntepenultimate {
|
||||
t.Fatalf("Wrong log content: %q, expected %q", antepenult, expectedAntepenultimate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFileLoggerWithLabelsEnv(t *testing.T) {
|
||||
|
||||
@@ -2,17 +2,21 @@ package loggerutils // import "github.com/docker/docker/daemon/logger/loggerutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/loggerutils/multireader"
|
||||
"github.com/docker/docker/pkg/filenotify"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/docker/docker/pkg/pubsub"
|
||||
"github.com/docker/docker/pkg/tailfile"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
@@ -20,24 +24,81 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const tmpLogfileSuffix = ".tmp"
|
||||
|
||||
// rotateFileMetadata is a metadata of the gzip header of the compressed log file
|
||||
type rotateFileMetadata struct {
|
||||
LastTime time.Time `json:"lastTime,omitempty"`
|
||||
}
|
||||
|
||||
// refCounter is a counter of logfile being referenced
|
||||
type refCounter struct {
|
||||
mu sync.Mutex
|
||||
counter map[string]int
|
||||
}
|
||||
|
||||
// Reference increase the reference counter for specified logfile
|
||||
func (rc *refCounter) GetReference(fileName string, openRefFile func(fileName string, exists bool) (*os.File, error)) (*os.File, error) {
|
||||
rc.mu.Lock()
|
||||
defer rc.mu.Unlock()
|
||||
|
||||
var (
|
||||
file *os.File
|
||||
err error
|
||||
)
|
||||
_, ok := rc.counter[fileName]
|
||||
file, err = openRefFile(fileName, ok)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok {
|
||||
rc.counter[fileName]++
|
||||
} else if file != nil {
|
||||
rc.counter[file.Name()] = 1
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// Dereference reduce the reference counter for specified logfile
|
||||
func (rc *refCounter) Dereference(fileName string) error {
|
||||
rc.mu.Lock()
|
||||
defer rc.mu.Unlock()
|
||||
|
||||
rc.counter[fileName]--
|
||||
if rc.counter[fileName] <= 0 {
|
||||
delete(rc.counter, fileName)
|
||||
err := os.Remove(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogFile is Logger implementation for default Docker logging.
|
||||
type LogFile struct {
|
||||
f *os.File // store for closing
|
||||
closed bool
|
||||
mu sync.RWMutex
|
||||
capacity int64 //maximum size of each file
|
||||
currentSize int64 // current size of the latest file
|
||||
maxFiles int //maximum number of files
|
||||
notifyRotate *pubsub.Publisher
|
||||
marshal logger.MarshalFunc
|
||||
createDecoder makeDecoderFunc
|
||||
perms os.FileMode
|
||||
mu sync.RWMutex // protects the logfile access
|
||||
f *os.File // store for closing
|
||||
closed bool
|
||||
rotateMu sync.Mutex // blocks the next rotation until the current rotation is completed
|
||||
capacity int64 // maximum size of each file
|
||||
currentSize int64 // current size of the latest file
|
||||
maxFiles int // maximum number of files
|
||||
compress bool // whether old versions of log files are compressed
|
||||
lastTimestamp time.Time // timestamp of the last log
|
||||
filesRefCounter refCounter // keep reference-counted of decompressed files
|
||||
notifyRotate *pubsub.Publisher
|
||||
marshal logger.MarshalFunc
|
||||
createDecoder makeDecoderFunc
|
||||
perms os.FileMode
|
||||
}
|
||||
|
||||
type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error)
|
||||
|
||||
//NewLogFile creates new LogFile
|
||||
func NewLogFile(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc, perms os.FileMode) (*LogFile, error) {
|
||||
func NewLogFile(logPath string, capacity int64, maxFiles int, compress bool, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc, perms os.FileMode) (*LogFile, error) {
|
||||
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -49,14 +110,16 @@ func NewLogFile(logPath string, capacity int64, maxFiles int, marshaller logger.
|
||||
}
|
||||
|
||||
return &LogFile{
|
||||
f: log,
|
||||
capacity: capacity,
|
||||
currentSize: size,
|
||||
maxFiles: maxFiles,
|
||||
notifyRotate: pubsub.NewPublisher(0, 1),
|
||||
marshal: marshaller,
|
||||
createDecoder: decodeFunc,
|
||||
perms: perms,
|
||||
f: log,
|
||||
capacity: capacity,
|
||||
currentSize: size,
|
||||
maxFiles: maxFiles,
|
||||
compress: compress,
|
||||
filesRefCounter: refCounter{counter: make(map[string]int)},
|
||||
notifyRotate: pubsub.NewPublisher(0, 1),
|
||||
marshal: marshaller,
|
||||
createDecoder: decodeFunc,
|
||||
perms: perms,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -84,6 +147,7 @@ func (w *LogFile) WriteLogEntry(msg *logger.Message) error {
|
||||
n, err := w.f.Write(b)
|
||||
if err == nil {
|
||||
w.currentSize += int64(n)
|
||||
w.lastTimestamp = msg.Timestamp
|
||||
}
|
||||
w.mu.Unlock()
|
||||
return err
|
||||
@@ -95,43 +159,108 @@ func (w *LogFile) checkCapacityAndRotate() error {
|
||||
}
|
||||
|
||||
if w.currentSize >= w.capacity {
|
||||
name := w.f.Name()
|
||||
w.rotateMu.Lock()
|
||||
fname := w.f.Name()
|
||||
if err := w.f.Close(); err != nil {
|
||||
w.rotateMu.Unlock()
|
||||
return errors.Wrap(err, "error closing file")
|
||||
}
|
||||
if err := rotate(name, w.maxFiles); err != nil {
|
||||
if err := rotate(fname, w.maxFiles, w.compress); err != nil {
|
||||
w.rotateMu.Unlock()
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, w.perms)
|
||||
file, err := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, w.perms)
|
||||
if err != nil {
|
||||
w.rotateMu.Unlock()
|
||||
return err
|
||||
}
|
||||
w.f = file
|
||||
w.currentSize = 0
|
||||
w.notifyRotate.Publish(struct{}{})
|
||||
|
||||
if w.maxFiles <= 1 || !w.compress {
|
||||
w.rotateMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
compressFile(fname+".1", w.lastTimestamp)
|
||||
w.rotateMu.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func rotate(name string, maxFiles int) error {
|
||||
func rotate(name string, maxFiles int, compress bool) error {
|
||||
if maxFiles < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var extension string
|
||||
if compress {
|
||||
extension = ".gz"
|
||||
}
|
||||
for i := maxFiles - 1; i > 1; i-- {
|
||||
toPath := name + "." + strconv.Itoa(i)
|
||||
fromPath := name + "." + strconv.Itoa(i-1)
|
||||
toPath := name + "." + strconv.Itoa(i) + extension
|
||||
fromPath := name + "." + strconv.Itoa(i-1) + extension
|
||||
if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "error rotating old log entries")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "error rotating current log")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func compressFile(fileName string, lastTimestamp time.Time) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to open log file: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
file.Close()
|
||||
err := os.Remove(fileName)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to remove source log file: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
outFile, err := os.OpenFile(fileName+".gz", os.O_CREATE|os.O_RDWR, 0640)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to open or create gzip log file: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
outFile.Close()
|
||||
if err != nil {
|
||||
os.Remove(fileName + ".gz")
|
||||
}
|
||||
}()
|
||||
|
||||
compressWriter := gzip.NewWriter(outFile)
|
||||
defer compressWriter.Close()
|
||||
|
||||
// Add the last log entry timestramp to the gzip header
|
||||
extra := rotateFileMetadata{}
|
||||
extra.LastTime = lastTimestamp
|
||||
compressWriter.Header.Extra, err = json.Marshal(&extra)
|
||||
if err != nil {
|
||||
// Here log the error only and don't return since this is just an optimization.
|
||||
logrus.Warningf("Failed to marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
_, err = pools.Copy(compressWriter, file)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithField("module", "container.logs").WithField("file", fileName).Error("Error compressing log file")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFiles return maximum number of files
|
||||
func (w *LogFile) MaxFiles() int {
|
||||
return w.maxFiles
|
||||
@@ -154,18 +283,6 @@ func (w *LogFile) Close() error {
|
||||
// ReadLogs decodes entries from log files and sends them the passed in watcher
|
||||
func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) {
|
||||
w.mu.RLock()
|
||||
files, err := w.openRotatedFiles()
|
||||
if err != nil {
|
||||
w.mu.RUnlock()
|
||||
watcher.Err <- err
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
for _, f := range files {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
currentFile, err := os.Open(w.f.Name())
|
||||
if err != nil {
|
||||
w.mu.RUnlock()
|
||||
@@ -175,14 +292,20 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher)
|
||||
defer currentFile.Close()
|
||||
|
||||
currentChunk, err := newSectionReader(currentFile)
|
||||
w.mu.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
w.mu.RUnlock()
|
||||
watcher.Err <- err
|
||||
return
|
||||
}
|
||||
|
||||
if config.Tail != 0 {
|
||||
files, err := w.openRotatedFiles(config)
|
||||
if err != nil {
|
||||
w.mu.RUnlock()
|
||||
watcher.Err <- err
|
||||
return
|
||||
}
|
||||
w.mu.RUnlock()
|
||||
seekers := make([]io.ReadSeeker, 0, len(files)+1)
|
||||
for _, f := range files {
|
||||
seekers = append(seekers, f)
|
||||
@@ -193,9 +316,20 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher)
|
||||
if len(seekers) > 0 {
|
||||
tailFile(multireader.MultiReadSeeker(seekers...), watcher, w.createDecoder, config)
|
||||
}
|
||||
for _, f := range files {
|
||||
f.Close()
|
||||
fileName := f.Name()
|
||||
if strings.HasSuffix(fileName, tmpLogfileSuffix) {
|
||||
err := w.filesRefCounter.Dereference(fileName)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to dereference the log file %q: %v", fileName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.mu.RLock()
|
||||
}
|
||||
|
||||
w.mu.RLock()
|
||||
if !config.Follow || w.closed {
|
||||
w.mu.RUnlock()
|
||||
return
|
||||
@@ -207,13 +341,22 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher)
|
||||
followLogs(currentFile, watcher, notifyRotate, w.createDecoder, config.Since, config.Until)
|
||||
}
|
||||
|
||||
func (w *LogFile) openRotatedFiles() (files []*os.File, err error) {
|
||||
func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, err error) {
|
||||
w.rotateMu.Lock()
|
||||
defer w.rotateMu.Unlock()
|
||||
|
||||
defer func() {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
for _, f := range files {
|
||||
f.Close()
|
||||
if strings.HasSuffix(f.Name(), tmpLogfileSuffix) {
|
||||
err := os.Remove(f.Name())
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logrus.Warningf("Failed to remove the logfile %q: %v", f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -223,6 +366,28 @@ func (w *LogFile) openRotatedFiles() (files []*os.File, err error) {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("%s.%d.gz", w.f.Name(), i-1)
|
||||
decompressedFileName := fileName + tmpLogfileSuffix
|
||||
tmpFile, err := w.filesRefCounter.GetReference(decompressedFileName, func(refFileName string, exists bool) (*os.File, error) {
|
||||
if exists {
|
||||
return os.Open(refFileName)
|
||||
}
|
||||
return decompressfile(fileName, refFileName, config.Since)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tmpFile == nil {
|
||||
// The log before `config.Since` does not need to read
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, tmpFile)
|
||||
continue
|
||||
}
|
||||
files = append(files, f)
|
||||
@@ -231,6 +396,44 @@ func (w *LogFile) openRotatedFiles() (files []*os.File, err error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func decompressfile(fileName, destFileName string, since time.Time) (*os.File, error) {
|
||||
cf, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cf.Close()
|
||||
|
||||
rc, err := gzip.NewReader(cf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// Extract the last log entry timestramp from the gzip header
|
||||
extra := &rotateFileMetadata{}
|
||||
err = json.Unmarshal(rc.Header.Extra, extra)
|
||||
if err == nil && extra.LastTime.Before(since) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rs, err := os.OpenFile(destFileName, os.O_CREATE|os.O_RDWR, 0640)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = pools.Copy(rs, rc)
|
||||
if err != nil {
|
||||
rs.Close()
|
||||
rErr := os.Remove(rs.Name())
|
||||
if rErr != nil && os.IsNotExist(rErr) {
|
||||
logrus.Errorf("Failed to remove the logfile %q: %v", rs.Name(), rErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func newSectionReader(f *os.File) (*io.SectionReader, error) {
|
||||
// seek to the end to get the size
|
||||
// we'll leave this at the end of the file since section reader does not advance the reader
|
||||
|
||||
@@ -13,8 +13,8 @@ export DOCKER_ENGINE_GOARCH=${DOCKER_ENGINE_GOARCH:-${ARCH}}
|
||||
: ${TESTDEBUG:=}
|
||||
|
||||
integration_api_dirs=${TEST_INTEGRATION_DIR:-"$(
|
||||
find ./integration -type d |
|
||||
grep -vE '(^./integration($|/internal)|/testdata)')"}
|
||||
find /tests/integration -type d |
|
||||
grep -vE '(^/tests/integration($|/internal)|/testdata)')"}
|
||||
|
||||
run_test_integration() {
|
||||
[[ "$TESTFLAGS" != *-check.f* ]] && run_test_integration_suites
|
||||
@@ -35,7 +35,7 @@ run_test_integration_suites() {
|
||||
run_test_integration_legacy_suites() {
|
||||
(
|
||||
flags="-check.v -check.timeout=${TIMEOUT} -test.timeout=360m $TESTFLAGS"
|
||||
cd test/integration-cli
|
||||
cd /tests/integration-cli
|
||||
echo "Running $PWD"
|
||||
test_env ./test.main $flags
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli/config"
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakestorage"
|
||||
@@ -386,74 +385,6 @@ func (s *DockerSwarmSuite) TearDownTest(c *check.C) {
|
||||
s.ds.TearDownTest(c)
|
||||
}
|
||||
|
||||
func init() {
|
||||
check.Suite(&DockerTrustSuite{
|
||||
ds: &DockerSuite{},
|
||||
})
|
||||
}
|
||||
|
||||
type DockerTrustSuite struct {
|
||||
ds *DockerSuite
|
||||
reg *registry.V2
|
||||
not *testNotary
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) OnTimeout(c *check.C) {
|
||||
s.ds.OnTimeout(c)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) SetUpTest(c *check.C) {
|
||||
testRequires(c, registry.Hosting, NotaryServerHosting)
|
||||
s.reg = setupRegistry(c, false, "", "")
|
||||
s.not = setupNotary(c)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TearDownTest(c *check.C) {
|
||||
if s.reg != nil {
|
||||
s.reg.Close()
|
||||
}
|
||||
if s.not != nil {
|
||||
s.not.Close()
|
||||
}
|
||||
|
||||
// Remove trusted keys and metadata after test
|
||||
os.RemoveAll(filepath.Join(config.Dir(), "trust"))
|
||||
s.ds.TearDownTest(c)
|
||||
}
|
||||
|
||||
func init() {
|
||||
ds := &DockerSuite{}
|
||||
check.Suite(&DockerTrustedSwarmSuite{
|
||||
trustSuite: DockerTrustSuite{
|
||||
ds: ds,
|
||||
},
|
||||
swarmSuite: DockerSwarmSuite{
|
||||
ds: ds,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type DockerTrustedSwarmSuite struct {
|
||||
swarmSuite DockerSwarmSuite
|
||||
trustSuite DockerTrustSuite
|
||||
reg *registry.V2
|
||||
not *testNotary
|
||||
}
|
||||
|
||||
func (s *DockerTrustedSwarmSuite) SetUpTest(c *check.C) {
|
||||
s.swarmSuite.SetUpTest(c)
|
||||
s.trustSuite.SetUpTest(c)
|
||||
}
|
||||
|
||||
func (s *DockerTrustedSwarmSuite) TearDownTest(c *check.C) {
|
||||
s.trustSuite.TearDownTest(c)
|
||||
s.swarmSuite.TearDownTest(c)
|
||||
}
|
||||
|
||||
func (s *DockerTrustedSwarmSuite) OnTimeout(c *check.C) {
|
||||
s.swarmSuite.OnTimeout(c)
|
||||
}
|
||||
|
||||
func init() {
|
||||
check.Suite(&DockerPluginSuite{
|
||||
ds: &DockerSuite{},
|
||||
|
||||
@@ -4048,140 +4048,6 @@ func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedBuild(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-build")
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN []
|
||||
`, repoName)
|
||||
|
||||
name := "testtrustedbuild"
|
||||
|
||||
buildImage(name, trustedBuild, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{
|
||||
Out: fmt.Sprintf("FROM %s@sha", repoName[:len(repoName)-7]),
|
||||
})
|
||||
|
||||
// We should also have a tag reference for the image.
|
||||
dockerCmd(c, "inspect", repoName)
|
||||
|
||||
// We should now be able to remove the tag reference.
|
||||
dockerCmd(c, "rmi", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedBuildUntrustedTag(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/build-untrusted-tag:latest", privateRegistryURL)
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN []
|
||||
`, repoName)
|
||||
|
||||
name := "testtrustedbuilduntrustedtag"
|
||||
|
||||
buildImage(name, trustedBuild, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "does not have trust data for",
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME(vdemeester) should migrate to docker/cli e2e tests
|
||||
func (s *DockerTrustSuite) TestBuildContextDirIsSymlink(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
tempDir, err := ioutil.TempDir("", "test-build-dir-is-symlink-")
|
||||
c.Assert(err, check.IsNil)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Make a real context directory in this temp directory with a simple
|
||||
// Dockerfile.
|
||||
realContextDirname := filepath.Join(tempDir, "context")
|
||||
if err := os.Mkdir(realContextDirname, os.FileMode(0755)); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(
|
||||
filepath.Join(realContextDirname, "Dockerfile"),
|
||||
[]byte(`
|
||||
FROM busybox
|
||||
RUN echo hello world
|
||||
`),
|
||||
os.FileMode(0644),
|
||||
); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Make a symlink to the real context directory.
|
||||
contextSymlinkName := filepath.Join(tempDir, "context_link")
|
||||
if err := os.Symlink(realContextDirname, contextSymlinkName); err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// Executing the build with the symlink as the specified context should
|
||||
// *not* fail.
|
||||
dockerCmd(c, "build", contextSymlinkName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedBuildTagFromReleasesRole(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
|
||||
latestTag := s.setupTrustedImage(c, "trusted-build-releases-role")
|
||||
repoName := strings.TrimSuffix(latestTag, ":latest")
|
||||
|
||||
// Now create the releases role
|
||||
s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// push a different tag to the releases role
|
||||
otherTag := fmt.Sprintf("%s:other", repoName)
|
||||
cli.DockerCmd(c, "tag", "busybox", otherTag)
|
||||
|
||||
cli.Docker(cli.Args("push", otherTag), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "other", "targets/releases")
|
||||
s.assertTargetNotInRoles(c, repoName, "other", "targets")
|
||||
|
||||
cli.DockerCmd(c, "rmi", otherTag)
|
||||
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN []
|
||||
`, otherTag)
|
||||
name := "testtrustedbuildreleasesrole"
|
||||
cli.BuildCmd(c, name, trustedCmd, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{
|
||||
Out: fmt.Sprintf("FROM %s@sha", repoName),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedBuildTagIgnoresOtherDelegationRoles(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
|
||||
latestTag := s.setupTrustedImage(c, "trusted-build-releases-role")
|
||||
repoName := strings.TrimSuffix(latestTag, ":latest")
|
||||
|
||||
// Now create a non-releases delegation role
|
||||
s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// push a different tag to the other role
|
||||
otherTag := fmt.Sprintf("%s:other", repoName)
|
||||
cli.DockerCmd(c, "tag", "busybox", otherTag)
|
||||
|
||||
cli.Docker(cli.Args("push", otherTag), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "other", "targets/other")
|
||||
s.assertTargetNotInRoles(c, repoName, "other", "targets")
|
||||
|
||||
cli.DockerCmd(c, "rmi", otherTag)
|
||||
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM %s
|
||||
RUN []
|
||||
`, otherTag)
|
||||
|
||||
name := "testtrustedbuildotherrole"
|
||||
cli.Docker(cli.Build(name), trustedCmd, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
})
|
||||
}
|
||||
|
||||
// Issue #15634: COPY fails when path starts with "null"
|
||||
func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
|
||||
name := "testbuildnullstringinaddcopyvolume"
|
||||
@@ -6018,28 +5884,6 @@ func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) {
|
||||
cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestBuildMultiStageTrusted(c *check.C) {
|
||||
img1 := s.setupTrustedImage(c, "trusted-build1")
|
||||
img2 := s.setupTrustedImage(c, "trusted-build2")
|
||||
dockerFile := fmt.Sprintf(`
|
||||
FROM %s AS build-base
|
||||
RUN echo ok > /foo
|
||||
FROM %s
|
||||
COPY --from=build-base foo bar`, img1, img2)
|
||||
|
||||
name := "testcopyfromtrustedbuild"
|
||||
|
||||
r := buildImage(name, trustedBuild, build.WithDockerfile(dockerFile))
|
||||
r.Assert(c, icmd.Expected{
|
||||
Out: fmt.Sprintf("FROM %s@sha", img1[:len(img1)-7]),
|
||||
})
|
||||
r.Assert(c, icmd.Expected{
|
||||
Out: fmt.Sprintf("FROM %s@sha", img2[:len(img2)-7]),
|
||||
})
|
||||
|
||||
dockerCmdWithResult("run", name, "cat", "bar").Assert(c, icmd.Expected{Out: "ok"})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) {
|
||||
testRequires(c, DaemonIsWindows)
|
||||
dockerfile := `
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
)
|
||||
|
||||
// Make sure we can create a simple container with some args
|
||||
@@ -292,75 +290,6 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedCreate(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-create")
|
||||
|
||||
// Try create
|
||||
cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try untrusted create to ensure we pushed the tag to the registry
|
||||
cli.Docker(cli.Args("create", "--disable-content-trust=true", repoName)).Assert(c, SuccessDownloadedOnStderr)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/createtest", privateRegistryURL)
|
||||
withTagName := fmt.Sprintf("%s:latest", repoName)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", withTagName)
|
||||
cli.DockerCmd(c, "push", withTagName)
|
||||
cli.DockerCmd(c, "rmi", withTagName)
|
||||
|
||||
// Try trusted create on untrusted tag
|
||||
cli.Docker(cli.Args("create", withTagName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: fmt.Sprintf("does not have trust data for %s", repoName),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedIsolatedCreate(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-isolated-create")
|
||||
|
||||
// Try create
|
||||
cli.Docker(cli.Args("--config", "/tmp/docker-isolated-create", "create", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
defer os.RemoveAll("/tmp/docker-isolated-create")
|
||||
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclievilcreate/trusted:latest", privateRegistryURL)
|
||||
evilLocalConfigDir, err := ioutil.TempDir("", "evilcreate-local-config-dir")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try create
|
||||
cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Kill the notary server, start a new "evil" one.
|
||||
s.not.Close()
|
||||
s.not, err = newTestNotary(c)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// In order to make an evil server, lets re-init a client (with a different trust dir) and push new data.
|
||||
// tag an image and upload it to the private registry
|
||||
cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName)
|
||||
|
||||
// Push up to the new server
|
||||
cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Now, try creating with the original client from this new trust server. This should fail because the new root is invalid.
|
||||
cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "could not rotate trust to a new trusted root",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestCreateStopSignal(c *check.C) {
|
||||
name := "test_create_stop_signal"
|
||||
dockerCmd(c, "create", "--name", name, "--stop-signal", "9", "busybox")
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -352,51 +351,6 @@ func (s *DockerSuite) TestPluginInspectOnWindows(c *check.C) {
|
||||
c.Assert(err.Error(), checker.Contains, "plugins are not supported on this platform")
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestPluginTrustedInstall(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
|
||||
trustedName := s.setupTrustedplugin(c, pNameWithTag, "trusted-plugin-install")
|
||||
|
||||
cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", trustedName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Out: trustedName,
|
||||
})
|
||||
|
||||
out := cli.DockerCmd(c, "plugin", "ls").Combined()
|
||||
c.Assert(out, checker.Contains, "true")
|
||||
|
||||
out = cli.DockerCmd(c, "plugin", "disable", trustedName).Combined()
|
||||
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
|
||||
|
||||
out = cli.DockerCmd(c, "plugin", "enable", trustedName).Combined()
|
||||
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
|
||||
|
||||
out = cli.DockerCmd(c, "plugin", "rm", "-f", trustedName).Combined()
|
||||
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
|
||||
|
||||
// Try untrusted pull to ensure we pushed the tag to the registry
|
||||
cli.Docker(cli.Args("plugin", "install", "--disable-content-trust=true", "--grant-all-permissions", trustedName), trustedCmd).Assert(c, SuccessDownloaded)
|
||||
|
||||
out = cli.DockerCmd(c, "plugin", "ls").Combined()
|
||||
c.Assert(out, checker.Contains, "true")
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestPluginUntrustedInstall(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, IsAmd64, Network)
|
||||
|
||||
pluginName := fmt.Sprintf("%v/dockercliuntrusted/plugintest:latest", privateRegistryURL)
|
||||
// install locally and push to private registry
|
||||
cli.DockerCmd(c, "plugin", "install", "--grant-all-permissions", "--alias", pluginName, pNameWithTag)
|
||||
cli.DockerCmd(c, "plugin", "push", pluginName)
|
||||
cli.DockerCmd(c, "plugin", "rm", "-f", pluginName)
|
||||
|
||||
// Try trusted install on untrusted plugin
|
||||
cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", pluginName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: remote trust data does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) {
|
||||
name := "test"
|
||||
client := testEnv.APIClient()
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
)
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPull(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-pull")
|
||||
|
||||
// Try pull
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
// Try untrusted pull to ensure we pushed the tag to the registry
|
||||
cli.Docker(cli.Args("pull", "--disable-content-trust=true", repoName), trustedCmd).Assert(c, SuccessDownloaded)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedIsolatedPull(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-isolated-pull")
|
||||
|
||||
// Try pull (run from isolated directory without trust information)
|
||||
cli.Docker(cli.Args("--config", "/tmp/docker-isolated", "pull", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/pulltest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.DockerCmd(c, "push", repoName)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try trusted pull on untrusted tag
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: remote trust data does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPullFromBadTrustServer(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclievilpull/trusted:latest", privateRegistryURL)
|
||||
evilLocalConfigDir, err := ioutil.TempDir("", "evil-local-config-dir")
|
||||
if err != nil {
|
||||
c.Fatalf("Failed to create local temp dir")
|
||||
}
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try pull
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Kill the notary server, start a new "evil" one.
|
||||
s.not.Close()
|
||||
s.not, err = newTestNotary(c)
|
||||
|
||||
c.Assert(err, check.IsNil, check.Commentf("Restarting notary server failed."))
|
||||
|
||||
// In order to make an evil server, lets re-init a client (with a different trust dir) and push new data.
|
||||
// tag an image and upload it to the private registry
|
||||
cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName)
|
||||
|
||||
// Push up to the new server
|
||||
cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Now, try pulling with the original client from this new trust server. This should fail because the new root is invalid.
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "could not rotate trust to a new trusted root",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedOfflinePull(c *check.C) {
|
||||
repoName := s.setupTrustedImage(c, "trusted-offline-pull")
|
||||
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmdWithServer("https://invalidnotaryserver")).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "error contacting notary server",
|
||||
})
|
||||
// Do valid trusted pull to warm cache
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try pull again with invalid notary server, should use cache
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmdWithServer("https://invalidnotaryserver")).Assert(c, SuccessTagging)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPullDelete(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, "trusted-pull-delete")
|
||||
// tag the image and upload it to the private registry
|
||||
cli.BuildCmd(c, repoName, build.WithDockerfile(`
|
||||
FROM busybox
|
||||
CMD echo trustedpulldelete
|
||||
`))
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try pull
|
||||
result := cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Success)
|
||||
|
||||
matches := digestRegex.FindStringSubmatch(result.Combined())
|
||||
c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", result.Combined()))
|
||||
pullDigest := matches[1]
|
||||
|
||||
imageID := inspectField(c, repoName, "Id")
|
||||
|
||||
imageByDigest := repoName + "@" + pullDigest
|
||||
byDigestID := inspectField(c, imageByDigest, "Id")
|
||||
|
||||
c.Assert(byDigestID, checker.Equals, imageID)
|
||||
|
||||
// rmi of tag should also remove the digest reference
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
_, err := inspectFieldWithError(imageByDigest, "Id")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("digest reference should have been removed"))
|
||||
|
||||
_, err = inspectFieldWithError(imageID, "Id")
|
||||
c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPullReadsFromReleasesRole(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
repoName := fmt.Sprintf("%v/dockerclireleasesdelegationpulling/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
|
||||
// Push with targets first, initializing the repo
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets")
|
||||
|
||||
// Try pull, check we retrieve from targets role
|
||||
cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Err: "retrieving target for targets role",
|
||||
})
|
||||
|
||||
// Now we'll create the releases role, and try pushing and pulling
|
||||
s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// try a pull, check that we can still pull because we can still read the
|
||||
// old tag in the targets role
|
||||
cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Err: "retrieving target for targets role",
|
||||
})
|
||||
|
||||
// try a pull -a, check that it succeeds because we can still pull from the
|
||||
// targets role
|
||||
cli.Docker(cli.Args("-D", "pull", "-a", repoName), trustedCmd).Assert(c, icmd.Success)
|
||||
|
||||
// Push, should sign with targets/releases
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases")
|
||||
|
||||
// Try pull, check we retrieve from targets/releases role
|
||||
cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Err: "retrieving target for targets/releases role",
|
||||
})
|
||||
|
||||
// Create another delegation that we'll sign with
|
||||
s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[1].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/other", s.not.keys[1].Private)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases", "targets/other")
|
||||
|
||||
// Try pull, check we retrieve from targets/releases role
|
||||
cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Err: "retrieving target for targets/releases role",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPullIgnoresOtherDelegationRoles(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
repoName := fmt.Sprintf("%v/dockerclipullotherdelegation/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
|
||||
// We'll create a repo first with a non-release delegation role, so that when we
|
||||
// push we'll sign it into the delegation role
|
||||
s.notaryInitRepo(c, repoName)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// Push should write to the delegation role, not targets
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets/other")
|
||||
s.assertTargetNotInRoles(c, repoName, "latest", "targets")
|
||||
|
||||
// Try pull - we should fail, since pull will only pull from the targets/releases
|
||||
// role or the targets role
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No trust data for",
|
||||
})
|
||||
|
||||
// try a pull -a: we should fail since pull will only pull from the targets/releases
|
||||
// role or the targets role
|
||||
cli.Docker(cli.Args("-D", "pull", "-a", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No trusted tags for",
|
||||
})
|
||||
}
|
||||
@@ -7,14 +7,11 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/cli/config"
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
@@ -281,225 +278,6 @@ func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c
|
||||
c.Assert(out3, check.Equals, "hello world")
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Try pull after push
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Out: "Status: Image is up to date",
|
||||
})
|
||||
|
||||
// Assert that we rotated the snapshot key to the server by checking our local keystore
|
||||
contents, err := ioutil.ReadDir(filepath.Join(config.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest"))
|
||||
c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files"))
|
||||
// Check that we only have 1 key (targets key)
|
||||
c.Assert(contents, checker.HasLen, 1)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "12345678")).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Try pull after push
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Out: "Status: Image is up to date",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
// Using a name that doesn't resolve to an address makes this test faster
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmdWithServer("https://server.invalid:81/")).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "error contacting notary server",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
result := cli.Docker(cli.Args("push", "--disable-content-trust", repoName), trustedCmdWithServer("https://server.invalid:81/"))
|
||||
result.Assert(c, icmd.Success)
|
||||
c.Assert(result.Combined(), check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:"))
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.DockerCmd(c, "push", repoName)
|
||||
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Try pull after push
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Out: "Status: Image is up to date",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
// Do a trusted push
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Do another trusted push
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try pull to ensure the double push did not break our ability to pull
|
||||
cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessDownloaded)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
// Push with default passphrases
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Push with wrong passphrases
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "87654321")).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "could not find necessary signing keys",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
s.notaryInitRepo(c, repoName)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private)
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
// check to make sure that the target has been added to targets/releases and not targets
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets/releases")
|
||||
s.assertTargetNotInRoles(c, repoName, "latest", "targets")
|
||||
|
||||
// Try pull after push
|
||||
os.RemoveAll(filepath.Join(config.Dir(), "trust"))
|
||||
|
||||
cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
|
||||
Out: "Status: Image is up to date",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
s.notaryInitRepo(c, repoName)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public)
|
||||
|
||||
// import everything except the third key
|
||||
s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
|
||||
s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
|
||||
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public)
|
||||
s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private)
|
||||
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// check to make sure that the target has been added to targets/role1 and targets/role2, and
|
||||
// not targets (because there are delegations) or targets/role3 (due to missing key) or
|
||||
// targets/role1/subrole (due to it being a second level delegation)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2")
|
||||
s.assertTargetNotInRoles(c, repoName, "latest", "targets")
|
||||
|
||||
// Try pull after push
|
||||
os.RemoveAll(filepath.Join(config.Dir(), "trust"))
|
||||
|
||||
// pull should fail because none of these are the releases role
|
||||
cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
s.notaryInitRepo(c, repoName)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z")
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y")
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest")
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest")
|
||||
|
||||
// import everything except the third key
|
||||
s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
|
||||
s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
|
||||
s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private)
|
||||
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// check to make sure that the target has been added to targets/role1 and targets/role4, and
|
||||
// not targets (because there are delegations) or targets/role2 (due to path restrictions) or
|
||||
// targets/role3 (due to missing key)
|
||||
s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4")
|
||||
s.assertTargetNotInRoles(c, repoName, "latest", "targets")
|
||||
|
||||
// Try pull after push
|
||||
os.RemoveAll(filepath.Join(config.Dir(), "trust"))
|
||||
|
||||
// pull should fail because none of these are the releases role
|
||||
cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) {
|
||||
testRequires(c, NotaryHosting)
|
||||
repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL)
|
||||
targetName := fmt.Sprintf("%s:latest", repoName)
|
||||
s.notaryInitRepo(c, repoName)
|
||||
s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
|
||||
s.notaryPublish(c, repoName)
|
||||
|
||||
// do not import any delegations key
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", targetName)
|
||||
|
||||
cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "no valid signing keys",
|
||||
})
|
||||
s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1")
|
||||
}
|
||||
|
||||
func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
|
||||
repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
@@ -3140,75 +3140,6 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedRun(c *check.C) {
|
||||
// Windows does not support this functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
repoName := s.setupTrustedImage(c, "trusted-run")
|
||||
|
||||
// Try run
|
||||
cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try untrusted run to ensure we pushed the tag to the registry
|
||||
cli.Docker(cli.Args("run", "--disable-content-trust=true", repoName), trustedCmd).Assert(c, SuccessDownloadedOnStderr)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) {
|
||||
// Windows does not support this functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/runtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.DockerCmd(c, "push", repoName)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try trusted run on untrusted tag
|
||||
cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "does not have trust data for",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedRunFromBadTrustServer(c *check.C) {
|
||||
// Windows does not support this functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
repoName := fmt.Sprintf("%v/dockerclievilrun/trusted:latest", privateRegistryURL)
|
||||
evilLocalConfigDir, err := ioutil.TempDir("", "evilrun-local-config-dir")
|
||||
if err != nil {
|
||||
c.Fatalf("Failed to create local temp dir")
|
||||
}
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Try run
|
||||
cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, SuccessTagging)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
// Kill the notary server, start a new "evil" one.
|
||||
s.not.Close()
|
||||
s.not, err = newTestNotary(c)
|
||||
if err != nil {
|
||||
c.Fatalf("Restarting notary server failed.")
|
||||
}
|
||||
|
||||
// In order to make an evil server, lets re-init a client (with a different trust dir) and push new data.
|
||||
// tag an image and upload it to the private registry
|
||||
cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName)
|
||||
|
||||
// Push up to the new server
|
||||
cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
// Now, try running with the original client from this new trust server. This should fail because the new root is invalid.
|
||||
cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, icmd.Expected{
|
||||
ExitCode: 125,
|
||||
Err: "could not rotate trust to a new trusted root",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestPtraceContainerProcsFromHost(c *check.C) {
|
||||
// Not applicable on Windows as uses Unix specific functionality
|
||||
testRequires(c, DaemonIsLinux, SameHostDaemon)
|
||||
|
||||
@@ -1560,78 +1560,6 @@ func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) {
|
||||
c.Assert(strings.TrimSpace(out), checker.Contains, "com.docker.network.ipam.serial:true")
|
||||
}
|
||||
|
||||
func (s *DockerTrustedSwarmSuite) TestTrustedServiceCreate(c *check.C) {
|
||||
d := s.swarmSuite.AddDaemon(c, true, true)
|
||||
|
||||
// Attempt creating a service from an image that is known to notary.
|
||||
repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
|
||||
|
||||
name := "trusted"
|
||||
cli.Docker(cli.Args("-D", "service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
|
||||
Err: "resolved image tag to",
|
||||
})
|
||||
|
||||
out, err := d.Cmd("service", "inspect", "--pretty", name)
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out))
|
||||
|
||||
// Try trusted service create on an untrusted tag.
|
||||
|
||||
repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.DockerCmd(c, "push", repoName)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
name = "untrusted"
|
||||
cli.Docker(cli.Args("service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: remote trust data does not exist",
|
||||
})
|
||||
|
||||
out, err = d.Cmd("service", "inspect", "--pretty", name)
|
||||
c.Assert(err, checker.NotNil, check.Commentf(out))
|
||||
}
|
||||
|
||||
func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) {
|
||||
d := s.swarmSuite.AddDaemon(c, true, true)
|
||||
|
||||
// Attempt creating a service from an image that is known to notary.
|
||||
repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
|
||||
|
||||
name := "myservice"
|
||||
|
||||
// Create a service without content trust
|
||||
cli.Docker(cli.Args("service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
|
||||
|
||||
result := cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon))
|
||||
c.Assert(result.Error, checker.IsNil, check.Commentf(result.Combined()))
|
||||
// Daemon won't insert the digest because this is disabled by
|
||||
// DOCKER_SERVICE_PREFER_OFFLINE_IMAGE.
|
||||
c.Assert(result.Combined(), check.Not(checker.Contains), repoName+"@", check.Commentf(result.Combined()))
|
||||
|
||||
cli.Docker(cli.Args("-D", "service", "update", "--detach", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
|
||||
Err: "resolved image tag to",
|
||||
})
|
||||
|
||||
cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
|
||||
Out: repoName + "@",
|
||||
})
|
||||
|
||||
// Try trusted service update on an untrusted tag.
|
||||
|
||||
repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.DockerCmd(c, "push", repoName)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
|
||||
cli.Docker(cli.Args("service", "update", "--detach", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "Error: remote trust data does not exist",
|
||||
})
|
||||
}
|
||||
|
||||
// Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID.
|
||||
// e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1".
|
||||
func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) {
|
||||
|
||||
@@ -202,12 +202,6 @@ func buildImage(name string, cmdOperators ...cli.CmdOperator) *icmd.Result {
|
||||
return cli.Docker(cli.Build(name), cmdOperators...)
|
||||
}
|
||||
|
||||
// Deprecated: use trustedcmd
|
||||
func trustedBuild(cmd *icmd.Cmd) func() {
|
||||
trustedCmd(cmd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write `content` to the file at path `dst`, creating it if necessary,
|
||||
// as well as any missing directories.
|
||||
// The file is truncated if it already exists.
|
||||
@@ -306,13 +300,6 @@ func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *registry.V2
|
||||
return reg
|
||||
}
|
||||
|
||||
func setupNotary(c *check.C) *testNotary {
|
||||
ts, err := newTestNotary(c)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
return ts
|
||||
}
|
||||
|
||||
// appendBaseEnv appends the minimum set of environment variables to exec the
|
||||
// docker cli binary for testing with correct configuration to the given env
|
||||
// list.
|
||||
|
||||
@@ -112,22 +112,6 @@ func Apparmor() bool {
|
||||
return err == nil && len(buf) > 1 && buf[0] == 'Y'
|
||||
}
|
||||
|
||||
func NotaryHosting() bool {
|
||||
// for now notary binary is built only if we're running inside
|
||||
// container through `make test`. Figure that out by testing if
|
||||
// notary-server binary is in PATH.
|
||||
_, err := exec.LookPath(notaryServerBinary)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func NotaryServerHosting() bool {
|
||||
// for now notary-server binary is built only if we're running inside
|
||||
// container through `make test`. Figure that out by testing if
|
||||
// notary-server binary is in PATH.
|
||||
_, err := exec.LookPath(notaryServerBinary)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func Devicemapper() bool {
|
||||
return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper")
|
||||
}
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
cliconfig "github.com/docker/docker/cli/config"
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/fixtures/plugin"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gotestyourself/gotestyourself/icmd"
|
||||
)
|
||||
|
||||
var notaryBinary = "notary"
|
||||
var notaryServerBinary = "notary-server"
|
||||
|
||||
type keyPair struct {
|
||||
Public string
|
||||
Private string
|
||||
}
|
||||
|
||||
type testNotary struct {
|
||||
cmd *exec.Cmd
|
||||
dir string
|
||||
keys []keyPair
|
||||
}
|
||||
|
||||
const notaryHost = "localhost:4443"
|
||||
const notaryURL = "https://" + notaryHost
|
||||
|
||||
var SuccessTagging = icmd.Expected{
|
||||
Out: "Tagging",
|
||||
}
|
||||
|
||||
var SuccessSigningAndPushing = icmd.Expected{
|
||||
Out: "Signing and pushing trust metadata",
|
||||
}
|
||||
|
||||
var SuccessDownloaded = icmd.Expected{
|
||||
Out: "Status: Downloaded",
|
||||
}
|
||||
|
||||
var SuccessDownloadedOnStderr = icmd.Expected{
|
||||
Err: "Status: Downloaded",
|
||||
}
|
||||
|
||||
func newTestNotary(c *check.C) (*testNotary, error) {
|
||||
// generate server config
|
||||
template := `{
|
||||
"server": {
|
||||
"http_addr": "%s",
|
||||
"tls_key_file": "%s",
|
||||
"tls_cert_file": "%s"
|
||||
},
|
||||
"trust_service": {
|
||||
"type": "local",
|
||||
"hostname": "",
|
||||
"port": "",
|
||||
"key_algorithm": "ed25519"
|
||||
},
|
||||
"logging": {
|
||||
"level": "debug"
|
||||
},
|
||||
"storage": {
|
||||
"backend": "memory"
|
||||
}
|
||||
}`
|
||||
tmp, err := ioutil.TempDir("", "notary-test-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confPath := filepath.Join(tmp, "config.json")
|
||||
config, err := os.Create(confPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer config.Close()
|
||||
|
||||
workingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fmt.Fprintf(config, template, notaryHost, filepath.Join(workingDir, "fixtures/notary/localhost.key"), filepath.Join(workingDir, "fixtures/notary/localhost.cert")); err != nil {
|
||||
os.RemoveAll(tmp)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// generate client config
|
||||
clientConfPath := filepath.Join(tmp, "client-config.json")
|
||||
clientConfig, err := os.Create(clientConfPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer clientConfig.Close()
|
||||
|
||||
template = `{
|
||||
"trust_dir" : "%s",
|
||||
"remote_server": {
|
||||
"url": "%s",
|
||||
"skipTLSVerify": true
|
||||
}
|
||||
}`
|
||||
if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.Dir(), "trust"), notaryURL); err != nil {
|
||||
os.RemoveAll(tmp)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load key fixture filenames
|
||||
var keys []keyPair
|
||||
for i := 1; i < 5; i++ {
|
||||
keys = append(keys, keyPair{
|
||||
Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)),
|
||||
Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)),
|
||||
})
|
||||
}
|
||||
|
||||
// run notary-server
|
||||
cmd := exec.Command(notaryServerBinary, "-config", confPath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
os.RemoveAll(tmp)
|
||||
if os.IsNotExist(err) {
|
||||
c.Skip(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
testNotary := &testNotary{
|
||||
cmd: cmd,
|
||||
dir: tmp,
|
||||
keys: keys,
|
||||
}
|
||||
|
||||
// Wait for notary to be ready to serve requests.
|
||||
for i := 1; i <= 20; i++ {
|
||||
if err = testNotary.Ping(); err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond * time.Duration(i*i))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Fatalf("Timeout waiting for test notary to become available: %s", err)
|
||||
}
|
||||
|
||||
return testNotary, nil
|
||||
}
|
||||
|
||||
func (t *testNotary) Ping() error {
|
||||
tlsConfig := tlsconfig.ClientDefault()
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
}
|
||||
resp, err := client.Get(fmt.Sprintf("%s/v2/", notaryURL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("notary ping replied with an unexpected status code %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testNotary) Close() {
|
||||
t.cmd.Process.Kill()
|
||||
t.cmd.Process.Wait()
|
||||
os.RemoveAll(t.dir)
|
||||
}
|
||||
|
||||
func trustedCmd(cmd *icmd.Cmd) func() {
|
||||
pwd := "12345678"
|
||||
cmd.Env = append(cmd.Env, trustEnv(notaryURL, pwd, pwd)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func trustedCmdWithServer(server string) func(*icmd.Cmd) func() {
|
||||
return func(cmd *icmd.Cmd) func() {
|
||||
pwd := "12345678"
|
||||
cmd.Env = append(cmd.Env, trustEnv(server, pwd, pwd)...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func trustedCmdWithPassphrases(rootPwd, repositoryPwd string) func(*icmd.Cmd) func() {
|
||||
return func(cmd *icmd.Cmd) func() {
|
||||
cmd.Env = append(cmd.Env, trustEnv(notaryURL, rootPwd, repositoryPwd)...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func trustEnv(server, rootPwd, repositoryPwd string) []string {
|
||||
env := append(os.Environ(), []string{
|
||||
"DOCKER_CONTENT_TRUST=1",
|
||||
fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server),
|
||||
fmt.Sprintf("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=%s", rootPwd),
|
||||
fmt.Sprintf("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=%s", repositoryPwd),
|
||||
}...)
|
||||
return env
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string {
|
||||
repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name)
|
||||
// tag the image and upload it to the private registry
|
||||
cli.DockerCmd(c, "tag", "busybox", repoName)
|
||||
cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
cli.DockerCmd(c, "rmi", repoName)
|
||||
return repoName
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) setupTrustedplugin(c *check.C, source, name string) string {
|
||||
repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name)
|
||||
|
||||
client := testEnv.APIClient()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
err := plugin.Create(ctx, client, repoName)
|
||||
cancel()
|
||||
c.Assert(err, checker.IsNil, check.Commentf("could not create test plugin"))
|
||||
|
||||
// tag the image and upload it to the private registry
|
||||
// TODO: shouldn't need to use the CLI to do trust
|
||||
cli.Docker(cli.Args("plugin", "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing)
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second)
|
||||
err = client.PluginRemove(ctx, repoName, types.PluginRemoveOptions{Force: true})
|
||||
cancel()
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to cleanup test plugin for trust suite"))
|
||||
return repoName
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryCmd(c *check.C, args ...string) string {
|
||||
pwd := "12345678"
|
||||
env := []string{
|
||||
fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd),
|
||||
fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd),
|
||||
fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd),
|
||||
fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd),
|
||||
}
|
||||
result := icmd.RunCmd(icmd.Cmd{
|
||||
Command: append([]string{notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json")}, args...),
|
||||
Env: append(os.Environ(), env...),
|
||||
})
|
||||
result.Assert(c, icmd.Success)
|
||||
return result.Combined()
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) {
|
||||
s.notaryCmd(c, "init", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) {
|
||||
pathsArg := "--all-paths"
|
||||
if len(paths) > 0 {
|
||||
pathsArg = "--paths=" + strings.Join(paths, ",")
|
||||
}
|
||||
|
||||
s.notaryCmd(c, "delegation", "add", repoName, role, pubKey, pathsArg)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) {
|
||||
s.notaryCmd(c, "publish", repoName)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) {
|
||||
s.notaryCmd(c, "key", "import", privKey, "-g", repoName, "-r", role)
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string {
|
||||
out := s.notaryCmd(c, "list", repoName, "-r", role)
|
||||
|
||||
// should look something like:
|
||||
// NAME DIGEST SIZE (BYTES) ROLE
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
// latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets
|
||||
|
||||
targets := make(map[string]string)
|
||||
|
||||
// no target
|
||||
lines := strings.Split(strings.TrimSpace(out), "\n")
|
||||
if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") {
|
||||
return targets
|
||||
}
|
||||
|
||||
// otherwise, there is at least one target
|
||||
c.Assert(len(lines), checker.GreaterOrEqualThan, 3)
|
||||
|
||||
for _, line := range lines[2:] {
|
||||
tokens := strings.Fields(line)
|
||||
c.Assert(tokens, checker.HasLen, 4)
|
||||
targets[tokens[0]] = tokens[3]
|
||||
}
|
||||
|
||||
return targets
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) assertTargetInRoles(c *check.C, repoName, target string, roles ...string) {
|
||||
// check all the roles
|
||||
for _, role := range roles {
|
||||
targets := s.notaryListTargetsInRole(c, repoName, role)
|
||||
roleName, ok := targets[target]
|
||||
c.Assert(ok, checker.True)
|
||||
c.Assert(roleName, checker.Equals, role)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerTrustSuite) assertTargetNotInRoles(c *check.C, repoName, target string, roles ...string) {
|
||||
targets := s.notaryListTargetsInRole(c, repoName, "targets")
|
||||
|
||||
roleName, ok := targets[target]
|
||||
if ok {
|
||||
for _, role := range roles {
|
||||
c.Assert(roleName, checker.Not(checker.Equals), role)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@@ -12,11 +13,13 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakecontext"
|
||||
"github.com/docker/docker/integration/internal/request"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
func TestBuildWithRemoveAndForceRemove(t *testing.T) {
|
||||
@@ -304,6 +307,9 @@ COPY bar /`
|
||||
// docker/for-linux#135
|
||||
// #35641
|
||||
func TestBuildMultiStageLayerLeak(t *testing.T) {
|
||||
fmt.Println(testEnv.DaemonAPIVersion())
|
||||
skip.IfCondition(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"),
|
||||
"Don't run on API lower than 1.38 as it has been fixed starting from that version")
|
||||
ctx := context.TODO()
|
||||
defer setupTest(t)()
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ func TestNetworkLocalhostTCPNat(t *testing.T) {
|
||||
func TestNetworkLoopbackNat(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
|
||||
defer setupTest(t)()
|
||||
|
||||
msg := "it works"
|
||||
startServerContainer(t, msg, 8080)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,6 +22,7 @@ const (
|
||||
|
||||
// NewSwarm creates a swarm daemon for testing
|
||||
func NewSwarm(t *testing.T, testEnv *environment.Execution) *daemon.Swarm {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon())
|
||||
d := &daemon.Swarm{
|
||||
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package network // import "github.com/docker/docker/integration/network"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/docker/docker/integration/internal/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/poll"
|
||||
"golang.org/x/net/context"
|
||||
@@ -21,7 +20,7 @@ const dockerdBinary = "dockerd"
|
||||
|
||||
func TestInspectNetwork(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
d := newSwarm(t)
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
assert.NilError(t, err)
|
||||
@@ -38,8 +37,9 @@ func TestInspectNetwork(t *testing.T) {
|
||||
|
||||
var instances uint64 = 4
|
||||
serviceName := "TestService"
|
||||
// FIXME(vdemeester) consolidate with swarm.CreateService
|
||||
serviceSpec := swarmServiceSpec(serviceName, instances)
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: overlayName})
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarmtypes.NetworkAttachmentConfig{Target: overlayName})
|
||||
|
||||
serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
|
||||
QueryRegistry: false,
|
||||
@@ -107,38 +107,19 @@ func TestInspectNetwork(t *testing.T) {
|
||||
poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
|
||||
}
|
||||
|
||||
func newSwarm(t *testing.T) *daemon.Swarm {
|
||||
d := &daemon.Swarm{
|
||||
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
|
||||
}),
|
||||
// TODO: better method of finding an unused port
|
||||
Port: defaultSwarmPort,
|
||||
}
|
||||
// TODO: move to a NewSwarm constructor
|
||||
d.ListenAddr = fmt.Sprintf("0.0.0.0:%d", d.Port)
|
||||
|
||||
// avoid networking conflicts
|
||||
args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
|
||||
d.StartWithBusybox(t, args...)
|
||||
|
||||
assert.NilError(t, d.Init(swarm.InitRequest{}))
|
||||
return d
|
||||
}
|
||||
|
||||
func swarmServiceSpec(name string, replicas uint64) swarm.ServiceSpec {
|
||||
return swarm.ServiceSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
func swarmServiceSpec(name string, replicas uint64) swarmtypes.ServiceSpec {
|
||||
return swarmtypes.ServiceSpec{
|
||||
Annotations: swarmtypes.Annotations{
|
||||
Name: name,
|
||||
},
|
||||
TaskTemplate: swarm.TaskSpec{
|
||||
ContainerSpec: &swarm.ContainerSpec{
|
||||
TaskTemplate: swarmtypes.TaskSpec{
|
||||
ContainerSpec: &swarmtypes.ContainerSpec{
|
||||
Image: "busybox:latest",
|
||||
Command: []string{"/bin/top"},
|
||||
},
|
||||
},
|
||||
Mode: swarm.ServiceMode{
|
||||
Replicated: &swarm.ReplicatedService{
|
||||
Mode: swarmtypes.ServiceMode{
|
||||
Replicated: &swarmtypes.ReplicatedService{
|
||||
Replicas: &replicas,
|
||||
},
|
||||
},
|
||||
@@ -157,7 +138,7 @@ func serviceRunningTasksCount(client client.ServiceAPIClient, serviceID string,
|
||||
return poll.Error(err)
|
||||
case len(tasks) == int(instances):
|
||||
for _, task := range tasks {
|
||||
if task.Status.State != swarm.TaskStateRunning {
|
||||
if task.Status.State != swarmtypes.TaskStateRunning {
|
||||
return poll.Continue("waiting for tasks to enter run state")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration/internal/swarm"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/poll"
|
||||
"golang.org/x/net/context"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
|
||||
func TestServiceWithPredefinedNetwork(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
d := newSwarm(t)
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
assert.NilError(t, err)
|
||||
@@ -25,7 +26,7 @@ func TestServiceWithPredefinedNetwork(t *testing.T) {
|
||||
var instances uint64 = 1
|
||||
serviceName := "TestService"
|
||||
serviceSpec := swarmServiceSpec(serviceName, instances)
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: hostName})
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarmtypes.NetworkAttachmentConfig{Target: hostName})
|
||||
|
||||
serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{
|
||||
QueryRegistry: false,
|
||||
@@ -60,7 +61,7 @@ const ingressNet = "ingress"
|
||||
|
||||
func TestServiceWithIngressNetwork(t *testing.T) {
|
||||
defer setupTest(t)()
|
||||
d := newSwarm(t)
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
|
||||
client, err := client.NewClientWithOpts(client.WithHost((d.Sock())))
|
||||
@@ -81,13 +82,13 @@ func TestServiceWithIngressNetwork(t *testing.T) {
|
||||
var instances uint64 = 1
|
||||
serviceName := "TestIngressService"
|
||||
serviceSpec := swarmServiceSpec(serviceName, instances)
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: ingressNet})
|
||||
serviceSpec.EndpointSpec = &swarm.EndpointSpec{
|
||||
Ports: []swarm.PortConfig{
|
||||
serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarmtypes.NetworkAttachmentConfig{Target: ingressNet})
|
||||
serviceSpec.EndpointSpec = &swarmtypes.EndpointSpec{
|
||||
Ports: []swarmtypes.PortConfig{
|
||||
{
|
||||
Protocol: swarm.PortConfigProtocolTCP,
|
||||
Protocol: swarmtypes.PortConfigProtocolTCP,
|
||||
TargetPort: 80,
|
||||
PublishMode: swarm.PortConfigPublishModeIngress,
|
||||
PublishMode: swarmtypes.PortConfigPublishModeIngress,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -48,6 +49,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) func() {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
|
||||
environment.ProtectAll(t, testEnv)
|
||||
|
||||
d = daemon.New(t, "", dockerdBinary, daemon.Config{
|
||||
|
||||
@@ -14,8 +14,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
var pluginBuildLock = locker.New()
|
||||
|
||||
func ensurePlugin(t *testing.T, name string) string {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package logging // import "github.com/docker/docker/integration/plugin/logging"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/internal/test/environment"
|
||||
)
|
||||
|
||||
var (
|
||||
testEnv *environment.Execution
|
||||
)
|
||||
|
||||
const dockerdBinary = "dockerd"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
testEnv, err = environment.New()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = environment.EnsureFrozenImagesLinux(testEnv)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
testEnv.Print()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/integration-cli/daemon"
|
||||
"github.com/gotestyourself/gotestyourself/assert"
|
||||
"github.com/gotestyourself/gotestyourself/skip"
|
||||
)
|
||||
|
||||
// Regression test for #35553
|
||||
// Ensure that a daemon with a log plugin set as the default logger for containers
|
||||
// does not keep the daemon from starting.
|
||||
func TestDaemonStartWithLogOpt(t *testing.T) {
|
||||
skip.IfCondition(t, testEnv.IsRemoteDaemon(), "cannot run daemon when remote daemon")
|
||||
t.Parallel()
|
||||
|
||||
d := daemon.New(t, "", dockerdBinary, daemon.Config{})
|
||||
|
||||
@@ -121,6 +121,15 @@ func (e *Execution) IsRemoteDaemon() bool {
|
||||
return !e.IsLocalDaemon()
|
||||
}
|
||||
|
||||
// DaemonAPIVersion returns the negociated daemon api version
|
||||
func (e *Execution) DaemonAPIVersion() string {
|
||||
version, err := e.APIClient().ServerVersion(context.TODO())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return version.APIVersion
|
||||
}
|
||||
|
||||
// Print the execution details to stdout
|
||||
// TODO: print everything
|
||||
func (e *Execution) Print() {
|
||||
|
||||
@@ -45,9 +45,6 @@ var (
|
||||
// IndexName is the name of the index
|
||||
IndexName = "docker.io"
|
||||
|
||||
// NotaryServer is the endpoint serving the Notary trust server
|
||||
NotaryServer = "https://notary.docker.io"
|
||||
|
||||
// DefaultV2Registry is the URI of the default v2 registry
|
||||
DefaultV2Registry = &url.URL{
|
||||
Scheme: "https",
|
||||
|
||||
Reference in New Issue
Block a user