mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-12 08:23:48 +02:00
Merge pull request #509 from andruby/fix-docker-build
Rename go dependencies on dotcloud/docker to docker/docker
This commit is contained in:
commit
22268eb2e6
16
Godeps/Godeps.json
generated
16
Godeps/Godeps.json
generated
@ -124,27 +124,17 @@
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dotcloud/docker/archive",
|
||||
"ImportPath": "github.com/docker/docker/archive",
|
||||
"Comment": "v1.2.0-576-gf68f5fd",
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dotcloud/docker/pkg/parsers",
|
||||
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
||||
"Comment": "v1.2.0-576-gf68f5fd",
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dotcloud/docker/pkg/stdcopy",
|
||||
"Comment": "v1.2.0-576-gf68f5fd",
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dotcloud/docker/pkg/term",
|
||||
"Comment": "v1.2.0-576-gf68f5fd",
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dotcloud/docker/utils",
|
||||
"ImportPath": "github.com/docker/docker/pkg/stdcopy",
|
||||
"Comment": "v1.2.0-576-gf68f5fd",
|
||||
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
|
||||
},
|
||||
|
1
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/MAINTAINERS
generated
vendored
1
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/MAINTAINERS
generated
vendored
@ -1 +0,0 @@
|
||||
Solomon Hykes <solomon@docker.com> (@shykes)
|
103
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/term.go
generated
vendored
103
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/term.go
generated
vendored
@ -1,103 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidState = errors.New("Invalid terminal state")
|
||||
)
|
||||
|
||||
type State struct {
|
||||
termios Termios
|
||||
}
|
||||
|
||||
type Winsize struct {
|
||||
Height uint16
|
||||
Width uint16
|
||||
x uint16
|
||||
y uint16
|
||||
}
|
||||
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
// Skipp errno = 0
|
||||
if err == 0 {
|
||||
return ws, nil
|
||||
}
|
||||
return ws, err
|
||||
}
|
||||
|
||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
// Skipp errno = 0
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
|
||||
return err == 0
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func RestoreTerminal(fd uintptr, state *State) error {
|
||||
if state == nil {
|
||||
return ErrInvalidState
|
||||
}
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
|
||||
if err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SaveState(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
func DisableEcho(fd uintptr, state *State) error {
|
||||
newState := state.termios
|
||||
newState.Lflag &^= syscall.ECHO
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return err
|
||||
}
|
||||
handleInterrupt(fd, state)
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||
oldState, err := MakeRaw(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handleInterrupt(fd, oldState)
|
||||
return oldState, err
|
||||
}
|
||||
|
||||
func handleInterrupt(fd uintptr, state *State) {
|
||||
sigchan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigchan, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
_ = <-sigchan
|
||||
RestoreTerminal(fd, state)
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
65
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_darwin.go
generated
vendored
65
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_darwin.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
|
||||
IGNBRK = syscall.IGNBRK
|
||||
PARMRK = syscall.PARMRK
|
||||
INLCR = syscall.INLCR
|
||||
IGNCR = syscall.IGNCR
|
||||
ECHONL = syscall.ECHONL
|
||||
CSIZE = syscall.CSIZE
|
||||
ICRNL = syscall.ICRNL
|
||||
ISTRIP = syscall.ISTRIP
|
||||
PARENB = syscall.PARENB
|
||||
ECHO = syscall.ECHO
|
||||
ICANON = syscall.ICANON
|
||||
ISIG = syscall.ISIG
|
||||
IXON = syscall.IXON
|
||||
BRKINT = syscall.BRKINT
|
||||
INPCK = syscall.INPCK
|
||||
OPOST = syscall.OPOST
|
||||
CS8 = syscall.CS8
|
||||
IEXTEN = syscall.IEXTEN
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint64
|
||||
Oflag uint64
|
||||
Cflag uint64
|
||||
Lflag uint64
|
||||
Cc [20]byte
|
||||
Ispeed uint64
|
||||
Ospeed uint64
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||
newState.Oflag &^= OPOST
|
||||
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||
newState.Cflag &^= (CSIZE | PARENB)
|
||||
newState.Cflag |= CS8
|
||||
newState.Cc[syscall.VMIN] = 1
|
||||
newState.Cc[syscall.VTIME] = 0
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
65
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_freebsd.go
generated
vendored
65
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_freebsd.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
|
||||
IGNBRK = syscall.IGNBRK
|
||||
PARMRK = syscall.PARMRK
|
||||
INLCR = syscall.INLCR
|
||||
IGNCR = syscall.IGNCR
|
||||
ECHONL = syscall.ECHONL
|
||||
CSIZE = syscall.CSIZE
|
||||
ICRNL = syscall.ICRNL
|
||||
ISTRIP = syscall.ISTRIP
|
||||
PARENB = syscall.PARENB
|
||||
ECHO = syscall.ECHO
|
||||
ICANON = syscall.ICANON
|
||||
ISIG = syscall.ISIG
|
||||
IXON = syscall.IXON
|
||||
BRKINT = syscall.BRKINT
|
||||
INPCK = syscall.INPCK
|
||||
OPOST = syscall.OPOST
|
||||
CS8 = syscall.CS8
|
||||
IEXTEN = syscall.IEXTEN
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||
newState.Oflag &^= OPOST
|
||||
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||
newState.Cflag &^= (CSIZE | PARENB)
|
||||
newState.Cflag |= CS8
|
||||
newState.Cc[syscall.VMIN] = 1
|
||||
newState.Cc[syscall.VTIME] = 0
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
44
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_linux.go
generated
vendored
44
Godeps/_workspace/src/github.com/dotcloud/docker/pkg/term/termios_linux.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
getTermios = syscall.TCGETS
|
||||
setTermios = syscall.TCSETS
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var oldState State
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := oldState.termios
|
||||
|
||||
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
|
||||
newState.Oflag &^= syscall.OPOST
|
||||
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||
newState.Cflag |= syscall.CS8
|
||||
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &oldState, nil
|
||||
}
|
36
Godeps/_workspace/src/github.com/dotcloud/docker/utils/daemon.go
generated
vendored
36
Godeps/_workspace/src/github.com/dotcloud/docker/utils/daemon.go
generated
vendored
@ -1,36 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func CreatePidFile(pidfile string) error {
|
||||
if pidString, err := ioutil.ReadFile(pidfile); err == nil {
|
||||
pid, err := strconv.Atoi(string(pidString))
|
||||
if err == nil {
|
||||
if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
|
||||
return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(pidfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = fmt.Fprintf(file, "%d", os.Getpid())
|
||||
return err
|
||||
}
|
||||
|
||||
func RemovePidFile(pidfile string) {
|
||||
if err := os.Remove(pidfile); err != nil {
|
||||
log.Printf("Error removing %s: %s", pidfile, err)
|
||||
}
|
||||
}
|
164
Godeps/_workspace/src/github.com/dotcloud/docker/utils/http.go
generated
vendored
164
Godeps/_workspace/src/github.com/dotcloud/docker/utils/http.go
generated
vendored
@ -1,164 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/log"
|
||||
)
|
||||
|
||||
// VersionInfo is used to model entities which has a version.
|
||||
// It is basically a tupple with name and version.
|
||||
type VersionInfo interface {
|
||||
Name() string
|
||||
Version() string
|
||||
}
|
||||
|
||||
func validVersion(version VersionInfo) bool {
|
||||
const stopChars = " \t\r\n/"
|
||||
name := version.Name()
|
||||
vers := version.Version()
|
||||
if len(name) == 0 || strings.ContainsAny(name, stopChars) {
|
||||
return false
|
||||
}
|
||||
if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Convert versions to a string and append the string to the string base.
|
||||
//
|
||||
// Each VersionInfo will be converted to a string in the format of
|
||||
// "product/version", where the "product" is get from the Name() method, while
|
||||
// version is get from the Version() method. Several pieces of verson information
|
||||
// will be concatinated and separated by space.
|
||||
func appendVersions(base string, versions ...VersionInfo) string {
|
||||
if len(versions) == 0 {
|
||||
return base
|
||||
}
|
||||
|
||||
verstrs := make([]string, 0, 1+len(versions))
|
||||
if len(base) > 0 {
|
||||
verstrs = append(verstrs, base)
|
||||
}
|
||||
|
||||
for _, v := range versions {
|
||||
if !validVersion(v) {
|
||||
continue
|
||||
}
|
||||
verstrs = append(verstrs, v.Name()+"/"+v.Version())
|
||||
}
|
||||
return strings.Join(verstrs, " ")
|
||||
}
|
||||
|
||||
// HTTPRequestDecorator is used to change an instance of
|
||||
// http.Request. It could be used to add more header fields,
|
||||
// change body, etc.
|
||||
type HTTPRequestDecorator interface {
|
||||
// ChangeRequest() changes the request accordingly.
|
||||
// The changed request will be returned or err will be non-nil
|
||||
// if an error occur.
|
||||
ChangeRequest(req *http.Request) (newReq *http.Request, err error)
|
||||
}
|
||||
|
||||
// HTTPUserAgentDecorator appends the product/version to the user agent field
|
||||
// of a request.
|
||||
type HTTPUserAgentDecorator struct {
|
||||
versions []VersionInfo
|
||||
}
|
||||
|
||||
func NewHTTPUserAgentDecorator(versions ...VersionInfo) HTTPRequestDecorator {
|
||||
return &HTTPUserAgentDecorator{
|
||||
versions: versions,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
||||
if req == nil {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
userAgent := appendVersions(req.UserAgent(), h.versions...)
|
||||
if len(userAgent) > 0 {
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
type HTTPMetaHeadersDecorator struct {
|
||||
Headers map[string][]string
|
||||
}
|
||||
|
||||
func (h *HTTPMetaHeadersDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
||||
if h.Headers == nil {
|
||||
return req, nil
|
||||
}
|
||||
for k, v := range h.Headers {
|
||||
req.Header[k] = v
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
type HTTPAuthDecorator struct {
|
||||
login string
|
||||
password string
|
||||
}
|
||||
|
||||
func NewHTTPAuthDecorator(login, password string) HTTPRequestDecorator {
|
||||
return &HTTPAuthDecorator{
|
||||
login: login,
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HTTPAuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
||||
req.SetBasicAuth(self.login, self.password)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// HTTPRequestFactory creates an HTTP request
|
||||
// and applies a list of decorators on the request.
|
||||
type HTTPRequestFactory struct {
|
||||
decorators []HTTPRequestDecorator
|
||||
}
|
||||
|
||||
func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
|
||||
return &HTTPRequestFactory{
|
||||
decorators: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HTTPRequestFactory) AddDecorator(d ...HTTPRequestDecorator) {
|
||||
self.decorators = append(self.decorators, d...)
|
||||
}
|
||||
|
||||
// NewRequest() creates a new *http.Request,
|
||||
// applies all decorators in the HTTPRequestFactory on the request,
|
||||
// then applies decorators provided by d on the request.
|
||||
func (h *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, urlStr, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// By default, a nil factory should work.
|
||||
if h == nil {
|
||||
return req, nil
|
||||
}
|
||||
for _, dec := range h.decorators {
|
||||
req, err = dec.ChangeRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, dec := range d {
|
||||
req, err = dec.ChangeRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
log.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
|
||||
return req, err
|
||||
}
|
169
Godeps/_workspace/src/github.com/dotcloud/docker/utils/jsonmessage.go
generated
vendored
169
Godeps/_workspace/src/github.com/dotcloud/docker/utils/jsonmessage.go
generated
vendored
@ -1,169 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
"github.com/docker/docker/pkg/units"
|
||||
)
|
||||
|
||||
type JSONError struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (e *JSONError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
type JSONProgress struct {
|
||||
terminalFd uintptr
|
||||
Current int `json:"current,omitempty"`
|
||||
Total int `json:"total,omitempty"`
|
||||
Start int64 `json:"start,omitempty"`
|
||||
}
|
||||
|
||||
func (p *JSONProgress) String() string {
|
||||
var (
|
||||
width = 200
|
||||
pbBox string
|
||||
numbersBox string
|
||||
timeLeftBox string
|
||||
)
|
||||
|
||||
ws, err := term.GetWinsize(p.terminalFd)
|
||||
if err == nil {
|
||||
width = int(ws.Width)
|
||||
}
|
||||
|
||||
if p.Current <= 0 && p.Total <= 0 {
|
||||
return ""
|
||||
}
|
||||
current := units.HumanSize(int64(p.Current))
|
||||
if p.Total <= 0 {
|
||||
return fmt.Sprintf("%8v", current)
|
||||
}
|
||||
total := units.HumanSize(int64(p.Total))
|
||||
percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
|
||||
if width > 110 {
|
||||
// this number can't be negetive gh#7136
|
||||
numSpaces := 0
|
||||
if 50-percentage > 0 {
|
||||
numSpaces = 50 - percentage
|
||||
}
|
||||
pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
|
||||
}
|
||||
numbersBox = fmt.Sprintf("%8v/%v", current, total)
|
||||
|
||||
if p.Current > 0 && p.Start > 0 && percentage < 50 {
|
||||
fromStart := time.Now().UTC().Sub(time.Unix(int64(p.Start), 0))
|
||||
perEntry := fromStart / time.Duration(p.Current)
|
||||
left := time.Duration(p.Total-p.Current) * perEntry
|
||||
left = (left / time.Second) * time.Second
|
||||
|
||||
if width > 50 {
|
||||
timeLeftBox = " " + left.String()
|
||||
}
|
||||
}
|
||||
return pbBox + numbersBox + timeLeftBox
|
||||
}
|
||||
|
||||
type JSONMessage struct {
|
||||
Stream string `json:"stream,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Progress *JSONProgress `json:"progressDetail,omitempty"`
|
||||
ProgressMessage string `json:"progress,omitempty"` //deprecated
|
||||
ID string `json:"id,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Error *JSONError `json:"errorDetail,omitempty"`
|
||||
ErrorMessage string `json:"error,omitempty"` //deprecated
|
||||
}
|
||||
|
||||
func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
|
||||
if jm.Error != nil {
|
||||
if jm.Error.Code == 401 {
|
||||
return fmt.Errorf("Authentication is required.")
|
||||
}
|
||||
return jm.Error
|
||||
}
|
||||
var endl string
|
||||
if isTerminal && jm.Stream == "" && jm.Progress != nil {
|
||||
// <ESC>[2K = erase entire current line
|
||||
fmt.Fprintf(out, "%c[2K\r", 27)
|
||||
endl = "\r"
|
||||
} else if jm.Progress != nil { //disable progressbar in non-terminal
|
||||
return nil
|
||||
}
|
||||
if jm.Time != 0 {
|
||||
fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(timeutils.RFC3339NanoFixed))
|
||||
}
|
||||
if jm.ID != "" {
|
||||
fmt.Fprintf(out, "%s: ", jm.ID)
|
||||
}
|
||||
if jm.From != "" {
|
||||
fmt.Fprintf(out, "(from %s) ", jm.From)
|
||||
}
|
||||
if jm.Progress != nil {
|
||||
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
|
||||
} else if jm.ProgressMessage != "" { //deprecated
|
||||
fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
|
||||
} else if jm.Stream != "" {
|
||||
fmt.Fprintf(out, "%s%s", jm.Stream, endl)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool) error {
|
||||
var (
|
||||
dec = json.NewDecoder(in)
|
||||
ids = make(map[string]int)
|
||||
diff = 0
|
||||
)
|
||||
for {
|
||||
var jm JSONMessage
|
||||
if err := dec.Decode(&jm); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if jm.Progress != nil {
|
||||
jm.Progress.terminalFd = terminalFd
|
||||
}
|
||||
if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
|
||||
line, ok := ids[jm.ID]
|
||||
if !ok {
|
||||
line = len(ids)
|
||||
ids[jm.ID] = line
|
||||
if isTerminal {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
diff = 0
|
||||
} else {
|
||||
diff = len(ids) - line
|
||||
}
|
||||
if jm.ID != "" && isTerminal {
|
||||
// <ESC>[{diff}A = move cursor up diff rows
|
||||
fmt.Fprintf(out, "%c[%dA", 27, diff)
|
||||
}
|
||||
}
|
||||
err := jm.Display(out, isTerminal)
|
||||
if jm.ID != "" && isTerminal {
|
||||
// <ESC>[{diff}B = move cursor down diff rows
|
||||
fmt.Fprintf(out, "%c[%dB", 27, diff)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
38
Godeps/_workspace/src/github.com/dotcloud/docker/utils/jsonmessage_test.go
generated
vendored
38
Godeps/_workspace/src/github.com/dotcloud/docker/utils/jsonmessage_test.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
je := JSONError{404, "Not found"}
|
||||
if je.Error() != "Not found" {
|
||||
t.Fatalf("Expected 'Not found' got '%s'", je.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestProgress(t *testing.T) {
|
||||
jp := JSONProgress{}
|
||||
if jp.String() != "" {
|
||||
t.Fatalf("Expected empty string, got '%s'", jp.String())
|
||||
}
|
||||
|
||||
expected := " 1 B"
|
||||
jp2 := JSONProgress{Current: 1}
|
||||
if jp2.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp2.String())
|
||||
}
|
||||
|
||||
expected = "[=========================> ] 50 B/100 B"
|
||||
jp3 := JSONProgress{Current: 50, Total: 100}
|
||||
if jp3.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp3.String())
|
||||
}
|
||||
|
||||
// this number can't be negetive gh#7136
|
||||
expected = "[==============================================================>] 50 B/40 B"
|
||||
jp4 := JSONProgress{Current: 50, Total: 40}
|
||||
if jp4.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp4.String())
|
||||
}
|
||||
}
|
55
Godeps/_workspace/src/github.com/dotcloud/docker/utils/progressreader.go
generated
vendored
55
Godeps/_workspace/src/github.com/dotcloud/docker/utils/progressreader.go
generated
vendored
@ -1,55 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Reader with progress bar
|
||||
type progressReader struct {
|
||||
reader io.ReadCloser // Stream to read from
|
||||
output io.Writer // Where to send progress bar to
|
||||
progress JSONProgress
|
||||
lastUpdate int // How many bytes read at least update
|
||||
ID string
|
||||
action string
|
||||
sf *StreamFormatter
|
||||
newLine bool
|
||||
}
|
||||
|
||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
read, err := r.reader.Read(p)
|
||||
r.progress.Current += read
|
||||
updateEvery := 1024 * 512 //512kB
|
||||
if r.progress.Total > 0 {
|
||||
// Update progress for every 1% read if 1% < 512kB
|
||||
if increment := int(0.01 * float64(r.progress.Total)); increment < updateEvery {
|
||||
updateEvery = increment
|
||||
}
|
||||
}
|
||||
if r.progress.Current-r.lastUpdate > updateEvery || err != nil {
|
||||
r.output.Write(r.sf.FormatProgress(r.ID, r.action, &r.progress))
|
||||
r.lastUpdate = r.progress.Current
|
||||
}
|
||||
// Send newline when complete
|
||||
if r.newLine && err != nil && read == 0 {
|
||||
r.output.Write(r.sf.FormatStatus("", ""))
|
||||
}
|
||||
return read, err
|
||||
}
|
||||
func (r *progressReader) Close() error {
|
||||
r.progress.Current = r.progress.Total
|
||||
r.output.Write(r.sf.FormatProgress(r.ID, r.action, &r.progress))
|
||||
return r.reader.Close()
|
||||
}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, sf *StreamFormatter, newline bool, ID, action string) *progressReader {
|
||||
return &progressReader{
|
||||
reader: r,
|
||||
output: NewWriteFlusher(output),
|
||||
ID: ID,
|
||||
action: action,
|
||||
progress: JSONProgress{Total: size, Start: time.Now().UTC().Unix()},
|
||||
sf: sf,
|
||||
newLine: newline,
|
||||
}
|
||||
}
|
16
Godeps/_workspace/src/github.com/dotcloud/docker/utils/random.go
generated
vendored
16
Godeps/_workspace/src/github.com/dotcloud/docker/utils/random.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
)
|
||||
|
||||
func RandomString() string {
|
||||
id := make([]byte, 32)
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
panic(err) // This shouldn't happen
|
||||
}
|
||||
return hex.EncodeToString(id)
|
||||
}
|
112
Godeps/_workspace/src/github.com/dotcloud/docker/utils/streamformatter.go
generated
vendored
112
Godeps/_workspace/src/github.com/dotcloud/docker/utils/streamformatter.go
generated
vendored
@ -1,112 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type StreamFormatter struct {
|
||||
json bool
|
||||
}
|
||||
|
||||
func NewStreamFormatter(json bool) *StreamFormatter {
|
||||
return &StreamFormatter{json}
|
||||
}
|
||||
|
||||
const streamNewline = "\r\n"
|
||||
|
||||
var streamNewlineBytes = []byte(streamNewline)
|
||||
|
||||
func (sf *StreamFormatter) FormatStream(str string) []byte {
|
||||
if sf.json {
|
||||
b, err := json.Marshal(&JSONMessage{Stream: str})
|
||||
if err != nil {
|
||||
return sf.FormatError(err)
|
||||
}
|
||||
return append(b, streamNewlineBytes...)
|
||||
}
|
||||
return []byte(str + "\r")
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte {
|
||||
str := fmt.Sprintf(format, a...)
|
||||
if sf.json {
|
||||
b, err := json.Marshal(&JSONMessage{ID: id, Status: str})
|
||||
if err != nil {
|
||||
return sf.FormatError(err)
|
||||
}
|
||||
return append(b, streamNewlineBytes...)
|
||||
}
|
||||
return []byte(str + streamNewline)
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatError(err error) []byte {
|
||||
if sf.json {
|
||||
jsonError, ok := err.(*JSONError)
|
||||
if !ok {
|
||||
jsonError = &JSONError{Message: err.Error()}
|
||||
}
|
||||
if b, err := json.Marshal(&JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
|
||||
return append(b, streamNewlineBytes...)
|
||||
}
|
||||
return []byte("{\"error\":\"format error\"}" + streamNewline)
|
||||
}
|
||||
return []byte("Error: " + err.Error() + streamNewline)
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) FormatProgress(id, action string, progress *JSONProgress) []byte {
|
||||
if progress == nil {
|
||||
progress = &JSONProgress{}
|
||||
}
|
||||
if sf.json {
|
||||
|
||||
b, err := json.Marshal(&JSONMessage{
|
||||
Status: action,
|
||||
ProgressMessage: progress.String(),
|
||||
Progress: progress,
|
||||
ID: id,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
endl := "\r"
|
||||
if progress.String() == "" {
|
||||
endl += "\n"
|
||||
}
|
||||
return []byte(action + " " + progress.String() + endl)
|
||||
}
|
||||
|
||||
func (sf *StreamFormatter) Json() bool {
|
||||
return sf.json
|
||||
}
|
||||
|
||||
type StdoutFormater struct {
|
||||
io.Writer
|
||||
*StreamFormatter
|
||||
}
|
||||
|
||||
func (sf *StdoutFormater) Write(buf []byte) (int, error) {
|
||||
formattedBuf := sf.StreamFormatter.FormatStream(string(buf))
|
||||
n, err := sf.Writer.Write(formattedBuf)
|
||||
if n != len(formattedBuf) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
return len(buf), err
|
||||
}
|
||||
|
||||
type StderrFormater struct {
|
||||
io.Writer
|
||||
*StreamFormatter
|
||||
}
|
||||
|
||||
func (sf *StderrFormater) Write(buf []byte) (int, error) {
|
||||
formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
|
||||
n, err := sf.Writer.Write(formattedBuf)
|
||||
if n != len(formattedBuf) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
return len(buf), err
|
||||
}
|
67
Godeps/_workspace/src/github.com/dotcloud/docker/utils/streamformatter_test.go
generated
vendored
67
Godeps/_workspace/src/github.com/dotcloud/docker/utils/streamformatter_test.go
generated
vendored
@ -1,67 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatStream(t *testing.T) {
|
||||
sf := NewStreamFormatter(true)
|
||||
res := sf.FormatStream("stream")
|
||||
if string(res) != `{"stream":"stream"}`+"\r\n" {
|
||||
t.Fatalf("%q", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatStatus(t *testing.T) {
|
||||
sf := NewStreamFormatter(true)
|
||||
res := sf.FormatStatus("ID", "%s%d", "a", 1)
|
||||
if string(res) != `{"status":"a1","id":"ID"}`+"\r\n" {
|
||||
t.Fatalf("%q", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatSimpleError(t *testing.T) {
|
||||
sf := NewStreamFormatter(true)
|
||||
res := sf.FormatError(errors.New("Error for formatter"))
|
||||
if string(res) != `{"errorDetail":{"message":"Error for formatter"},"error":"Error for formatter"}`+"\r\n" {
|
||||
t.Fatalf("%q", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatJSONError(t *testing.T) {
|
||||
sf := NewStreamFormatter(true)
|
||||
err := &JSONError{Code: 50, Message: "Json error"}
|
||||
res := sf.FormatError(err)
|
||||
if string(res) != `{"errorDetail":{"code":50,"message":"Json error"},"error":"Json error"}`+"\r\n" {
|
||||
t.Fatalf("%q", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatProgress(t *testing.T) {
|
||||
sf := NewStreamFormatter(true)
|
||||
progress := &JSONProgress{
|
||||
Current: 15,
|
||||
Total: 30,
|
||||
Start: 1,
|
||||
}
|
||||
res := sf.FormatProgress("id", "action", progress)
|
||||
msg := &JSONMessage{}
|
||||
if err := json.Unmarshal(res, msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if msg.ID != "id" {
|
||||
t.Fatalf("ID must be 'id', got: %s", msg.ID)
|
||||
}
|
||||
if msg.Status != "action" {
|
||||
t.Fatalf("Status must be 'action', got: %s", msg.Status)
|
||||
}
|
||||
if msg.ProgressMessage != progress.String() {
|
||||
t.Fatalf("ProgressMessage must be %s, got: %s", progress.String(), msg.ProgressMessage)
|
||||
}
|
||||
if !reflect.DeepEqual(msg.Progress, progress) {
|
||||
t.Fatal("Original progress not equals progress from FormatProgress")
|
||||
}
|
||||
}
|
26
Godeps/_workspace/src/github.com/dotcloud/docker/utils/timeoutconn.go
generated
vendored
26
Godeps/_workspace/src/github.com/dotcloud/docker/utils/timeoutconn.go
generated
vendored
@ -1,26 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewTimeoutConn(conn net.Conn, timeout time.Duration) net.Conn {
|
||||
return &TimeoutConn{conn, timeout}
|
||||
}
|
||||
|
||||
// A net.Conn that sets a deadline for every Read or Write operation
|
||||
type TimeoutConn struct {
|
||||
net.Conn
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *TimeoutConn) Read(b []byte) (int, error) {
|
||||
if c.timeout > 0 {
|
||||
err := c.Conn.SetReadDeadline(time.Now().Add(c.timeout))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return c.Conn.Read(b)
|
||||
}
|
33
Godeps/_workspace/src/github.com/dotcloud/docker/utils/timeoutconn_test.go
generated
vendored
33
Godeps/_workspace/src/github.com/dotcloud/docker/utils/timeoutconn_test.go
generated
vendored
@ -1,33 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimeoutConnRead(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "hello")
|
||||
}))
|
||||
defer ts.Close()
|
||||
conn, err := net.Dial("tcp", ts.URL[7:])
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create connection to %q: %v", ts.URL, err)
|
||||
}
|
||||
tconn := NewTimeoutConn(conn, 1*time.Second)
|
||||
|
||||
if _, err = bufio.NewReader(tconn).ReadString('\n'); err == nil {
|
||||
t.Fatalf("expected timeout error, got none")
|
||||
}
|
||||
if _, err := fmt.Fprintf(tconn, "GET / HTTP/1.0\r\n\r\n"); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if _, err = bufio.NewReader(tconn).ReadString('\n'); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
12
Godeps/_workspace/src/github.com/dotcloud/docker/utils/tmpdir.go
generated
vendored
12
Godeps/_workspace/src/github.com/dotcloud/docker/utils/tmpdir.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// TempDir returns the default directory to use for temporary files.
|
||||
func TempDir(rootdir string) (string error) {
|
||||
return os.TempDir(), nil
|
||||
}
|
18
Godeps/_workspace/src/github.com/dotcloud/docker/utils/tmpdir_unix.go
generated
vendored
18
Godeps/_workspace/src/github.com/dotcloud/docker/utils/tmpdir_unix.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// TempDir returns the default directory to use for temporary files.
|
||||
func TempDir(rootDir string) (string, error) {
|
||||
var tmpDir string
|
||||
if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" {
|
||||
tmpDir = filepath.Join(rootDir, "tmp")
|
||||
}
|
||||
err := os.MkdirAll(tmpDir, 0700)
|
||||
return tmpDir, err
|
||||
}
|
593
Godeps/_workspace/src/github.com/dotcloud/docker/utils/utils.go
generated
vendored
593
Godeps/_workspace/src/github.com/dotcloud/docker/utils/utils.go
generated
vendored
@ -1,593 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/log"
|
||||
)
|
||||
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Go is a basic promise implementation: it wraps calls a function in a goroutine,
|
||||
// and returns a channel which will later return the function's return value.
|
||||
func Go(f func() error) chan error {
|
||||
ch := make(chan error, 1)
|
||||
go func() {
|
||||
ch <- f()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Request a given URL and return an io.Reader
|
||||
func Download(url string) (resp *http.Response, err error) {
|
||||
if resp, err = http.Get(url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("Got HTTP status code >= 400: %s", resp.Status)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func Trunc(s string, maxlen int) string {
|
||||
if len(s) <= maxlen {
|
||||
return s
|
||||
}
|
||||
return s[:maxlen]
|
||||
}
|
||||
|
||||
// Figure out the absolute path of our own binary (if it's still around).
|
||||
func SelfPath() string {
|
||||
path, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return ""
|
||||
}
|
||||
if execErr, ok := err.(*exec.Error); ok && os.IsNotExist(execErr.Err) {
|
||||
return ""
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return ""
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func dockerInitSha1(target string) string {
|
||||
f, err := os.Open(target)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha1.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func isValidDockerInitPath(target string, selfPath string) bool { // target and selfPath should be absolute (InitPath and SelfPath already do this)
|
||||
if target == "" {
|
||||
return false
|
||||
}
|
||||
if dockerversion.IAMSTATIC {
|
||||
if selfPath == "" {
|
||||
return false
|
||||
}
|
||||
if target == selfPath {
|
||||
return true
|
||||
}
|
||||
targetFileInfo, err := os.Lstat(target)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
selfPathFileInfo, err := os.Lstat(selfPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return os.SameFile(targetFileInfo, selfPathFileInfo)
|
||||
}
|
||||
return dockerversion.INITSHA1 != "" && dockerInitSha1(target) == dockerversion.INITSHA1
|
||||
}
|
||||
|
||||
// Figure out the path of our dockerinit (which may be SelfPath())
|
||||
func DockerInitPath(localCopy string) string {
|
||||
selfPath := SelfPath()
|
||||
if isValidDockerInitPath(selfPath, selfPath) {
|
||||
// if we're valid, don't bother checking anything else
|
||||
return selfPath
|
||||
}
|
||||
var possibleInits = []string{
|
||||
localCopy,
|
||||
dockerversion.INITPATH,
|
||||
filepath.Join(filepath.Dir(selfPath), "dockerinit"),
|
||||
|
||||
// FHS 3.0 Draft: "/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec."
|
||||
// http://www.linuxbase.org/betaspecs/fhs/fhs.html#usrlibexec
|
||||
"/usr/libexec/docker/dockerinit",
|
||||
"/usr/local/libexec/docker/dockerinit",
|
||||
|
||||
// FHS 2.3: "/usr/lib includes object files, libraries, and internal binaries that are not intended to be executed directly by users or shell scripts."
|
||||
// http://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA
|
||||
"/usr/lib/docker/dockerinit",
|
||||
"/usr/local/lib/docker/dockerinit",
|
||||
}
|
||||
for _, dockerInit := range possibleInits {
|
||||
if dockerInit == "" {
|
||||
continue
|
||||
}
|
||||
path, err := exec.LookPath(dockerInit)
|
||||
if err == nil {
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
// LookPath already validated that this file exists and is executable (following symlinks), so how could Abs fail?
|
||||
panic(err)
|
||||
}
|
||||
if isValidDockerInitPath(path, selfPath) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetTotalUsedFds() int {
|
||||
if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
|
||||
log.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
|
||||
} else {
|
||||
return len(fds)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// TruncateID returns a shorthand version of a string identifier for convenience.
|
||||
// A collision with other shorthands is very unlikely, but possible.
|
||||
// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
|
||||
// will need to use a langer prefix, or the full-length Id.
|
||||
func TruncateID(id string) string {
|
||||
shortLen := 12
|
||||
if len(id) < shortLen {
|
||||
shortLen = len(id)
|
||||
}
|
||||
return id[:shortLen]
|
||||
}
|
||||
|
||||
// GenerateRandomID returns an unique id
|
||||
func GenerateRandomID() string {
|
||||
for {
|
||||
id := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
panic(err) // This shouldn't happen
|
||||
}
|
||||
value := hex.EncodeToString(id)
|
||||
// if we try to parse the truncated for as an int and we don't have
|
||||
// an error then the value is all numberic and causes issues when
|
||||
// used as a hostname. ref #3869
|
||||
if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
|
||||
continue
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateID(id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("Id can't be empty")
|
||||
}
|
||||
if strings.Contains(id, ":") {
|
||||
return fmt.Errorf("Invalid character in id: ':'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Code c/c from io.Copy() modified to handle escape sequence
|
||||
func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) {
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
// ---- Docker addition
|
||||
// char 16 is C-p
|
||||
if nr == 1 && buf[0] == 16 {
|
||||
nr, er = src.Read(buf)
|
||||
// char 17 is C-q
|
||||
if nr == 1 && buf[0] == 17 {
|
||||
if err := src.Close(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
// ---- End of docker
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er == io.EOF {
|
||||
break
|
||||
}
|
||||
if er != nil {
|
||||
err = er
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
func HashData(src io.Reader) (string, error) {
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, src); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// FIXME: this is deprecated by CopyWithTar in archive.go
|
||||
func CopyDirectory(source, dest string) error {
|
||||
if output, err := exec.Command("cp", "-ra", source, dest).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("Error copy: %s (%s)", err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WriteFlusher struct {
|
||||
sync.Mutex
|
||||
w io.Writer
|
||||
flusher http.Flusher
|
||||
}
|
||||
|
||||
func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
|
||||
wf.Lock()
|
||||
defer wf.Unlock()
|
||||
n, err = wf.w.Write(b)
|
||||
wf.flusher.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Flush the stream immediately.
|
||||
func (wf *WriteFlusher) Flush() {
|
||||
wf.Lock()
|
||||
defer wf.Unlock()
|
||||
wf.flusher.Flush()
|
||||
}
|
||||
|
||||
func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
||||
var flusher http.Flusher
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
flusher = f
|
||||
} else {
|
||||
flusher = &ioutils.NopFlusher{}
|
||||
}
|
||||
return &WriteFlusher{w: w, flusher: flusher}
|
||||
}
|
||||
|
||||
func NewHTTPRequestError(msg string, res *http.Response) error {
|
||||
return &JSONError{
|
||||
Message: msg,
|
||||
Code: res.StatusCode,
|
||||
}
|
||||
}
|
||||
|
||||
func IsURL(str string) bool {
|
||||
return strings.HasPrefix(str, "http://") || strings.HasPrefix(str, "https://")
|
||||
}
|
||||
|
||||
func IsGIT(str string) bool {
|
||||
return strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "github.com/") || strings.HasPrefix(str, "git@github.com:") || (strings.HasSuffix(str, ".git") && IsURL(str))
|
||||
}
|
||||
|
||||
// CheckLocalDns looks into the /etc/resolv.conf,
|
||||
// it returns true if there is a local nameserver or if there is no nameserver.
|
||||
func CheckLocalDns(resolvConf []byte) bool {
|
||||
for _, line := range GetLines(resolvConf, []byte("#")) {
|
||||
if !bytes.Contains(line, []byte("nameserver")) {
|
||||
continue
|
||||
}
|
||||
for _, ip := range [][]byte{
|
||||
[]byte("127.0.0.1"),
|
||||
[]byte("127.0.1.1"),
|
||||
} {
|
||||
if bytes.Contains(line, ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetLines parses input into lines and strips away comments.
|
||||
func GetLines(input []byte, commentMarker []byte) [][]byte {
|
||||
lines := bytes.Split(input, []byte("\n"))
|
||||
var output [][]byte
|
||||
for _, currentLine := range lines {
|
||||
var commentIndex = bytes.Index(currentLine, commentMarker)
|
||||
if commentIndex == -1 {
|
||||
output = append(output, currentLine)
|
||||
} else {
|
||||
output = append(output, currentLine[:commentIndex])
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// An StatusError reports an unsuccessful exit by a command.
|
||||
type StatusError struct {
|
||||
Status string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
func (e *StatusError) Error() string {
|
||||
return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
|
||||
}
|
||||
|
||||
func quote(word string, buf *bytes.Buffer) {
|
||||
// Bail out early for "simple" strings
|
||||
if word != "" && !strings.ContainsAny(word, "\\'\"`${[|&;<>()~*?! \t\n") {
|
||||
buf.WriteString(word)
|
||||
return
|
||||
}
|
||||
|
||||
buf.WriteString("'")
|
||||
|
||||
for i := 0; i < len(word); i++ {
|
||||
b := word[i]
|
||||
if b == '\'' {
|
||||
// Replace literal ' with a close ', a \', and a open '
|
||||
buf.WriteString("'\\''")
|
||||
} else {
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("'")
|
||||
}
|
||||
|
||||
// Take a list of strings and escape them so they will be handled right
|
||||
// when passed as arguments to an program via a shell
|
||||
func ShellQuoteArguments(args []string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, arg := range args {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
quote(arg, &buf)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var globalTestID string
|
||||
|
||||
// TestDirectory creates a new temporary directory and returns its path.
|
||||
// The contents of directory at path `templateDir` is copied into the
|
||||
// new directory.
|
||||
func TestDirectory(templateDir string) (dir string, err error) {
|
||||
if globalTestID == "" {
|
||||
globalTestID = RandomString()[:4]
|
||||
}
|
||||
prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, GetCallerName(2))
|
||||
if prefix == "" {
|
||||
prefix = "docker-test-"
|
||||
}
|
||||
dir, err = ioutil.TempDir("", prefix)
|
||||
if err = os.Remove(dir); err != nil {
|
||||
return
|
||||
}
|
||||
if templateDir != "" {
|
||||
if err = CopyDirectory(templateDir, dir); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetCallerName introspects the call stack and returns the name of the
|
||||
// function `depth` levels down in the stack.
|
||||
func GetCallerName(depth int) string {
|
||||
// Use the caller function name as a prefix.
|
||||
// This helps trace temp directories back to their test.
|
||||
pc, _, _, _ := runtime.Caller(depth + 1)
|
||||
callerLongName := runtime.FuncForPC(pc).Name()
|
||||
parts := strings.Split(callerLongName, ".")
|
||||
callerShortName := parts[len(parts)-1]
|
||||
return callerShortName
|
||||
}
|
||||
|
||||
func CopyFile(src, dst string) (int64, error) {
|
||||
if src == dst {
|
||||
return 0, nil
|
||||
}
|
||||
sf, err := os.Open(src)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer sf.Close()
|
||||
if err := os.Remove(dst); err != nil && !os.IsNotExist(err) {
|
||||
return 0, err
|
||||
}
|
||||
df, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer df.Close()
|
||||
return io.Copy(df, sf)
|
||||
}
|
||||
|
||||
// ReplaceOrAppendValues returns the defaults with the overrides either
|
||||
// replaced by env key or appended to the list
|
||||
func ReplaceOrAppendEnvValues(defaults, overrides []string) []string {
|
||||
cache := make(map[string]int, len(defaults))
|
||||
for i, e := range defaults {
|
||||
parts := strings.SplitN(e, "=", 2)
|
||||
cache[parts[0]] = i
|
||||
}
|
||||
for _, value := range overrides {
|
||||
parts := strings.SplitN(value, "=", 2)
|
||||
if i, exists := cache[parts[0]]; exists {
|
||||
defaults[i] = value
|
||||
} else {
|
||||
defaults = append(defaults, value)
|
||||
}
|
||||
}
|
||||
return defaults
|
||||
}
|
||||
|
||||
// ReadSymlinkedDirectory returns the target directory of a symlink.
|
||||
// The target of the symbolic link may not be a file.
|
||||
func ReadSymlinkedDirectory(path string) (string, error) {
|
||||
var realPath string
|
||||
var err error
|
||||
if realPath, err = filepath.Abs(path); err != nil {
|
||||
return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
|
||||
}
|
||||
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
|
||||
return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
|
||||
}
|
||||
realPathInfo, err := os.Stat(realPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
|
||||
}
|
||||
if !realPathInfo.Mode().IsDir() {
|
||||
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
|
||||
}
|
||||
return realPath, nil
|
||||
}
|
||||
|
||||
// TreeSize walks a directory tree and returns its total size in bytes.
|
||||
func TreeSize(dir string) (size int64, err error) {
|
||||
data := make(map[uint64]struct{})
|
||||
err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
|
||||
// Ignore directory sizes
|
||||
if fileInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := fileInfo.Size()
|
||||
if fileInfo.IsDir() || s == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check inode to handle hard links correctly
|
||||
inode := fileInfo.Sys().(*syscall.Stat_t).Ino
|
||||
// inode is not a uint64 on all platforms. Cast it to avoid issues.
|
||||
if _, exists := data[uint64(inode)]; exists {
|
||||
return nil
|
||||
}
|
||||
// inode is not a uint64 on all platforms. Cast it to avoid issues.
|
||||
data[uint64(inode)] = struct{}{}
|
||||
|
||||
size += s
|
||||
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateContextDirectory checks if all the contents of the directory
|
||||
// can be read and returns an error if some files can't be read
|
||||
// symlinks which point to non-existing files don't trigger an error
|
||||
func ValidateContextDirectory(srcPath string, excludes []string) error {
|
||||
return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
|
||||
// skip this directory/file if it's not in the path, it won't get added to the context
|
||||
if relFilePath, err := filepath.Rel(srcPath, filePath); err != nil {
|
||||
return err
|
||||
} else if skip, err := Matches(relFilePath, excludes); err != nil {
|
||||
return err
|
||||
} else if skip {
|
||||
if f.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return fmt.Errorf("can't stat '%s'", filePath)
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// skip checking if symlinks point to non-existing files, such symlinks can be useful
|
||||
// also skip named pipes, because they hanging on open
|
||||
if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !f.IsDir() {
|
||||
currentFile, err := os.Open(filePath)
|
||||
if err != nil && os.IsPermission(err) {
|
||||
return fmt.Errorf("no permission to read from '%s'", filePath)
|
||||
}
|
||||
currentFile.Close()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func StringsContainsNoCase(slice []string, s string) bool {
|
||||
for _, ss := range slice {
|
||||
if strings.ToLower(s) == strings.ToLower(ss) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches returns true if relFilePath matches any of the patterns
|
||||
func Matches(relFilePath string, patterns []string) (bool, error) {
|
||||
for _, exclude := range patterns {
|
||||
matched, err := filepath.Match(exclude, relFilePath)
|
||||
if err != nil {
|
||||
log.Errorf("Error matching: %s (pattern: %s)", relFilePath, exclude)
|
||||
return false, err
|
||||
}
|
||||
if matched {
|
||||
if filepath.Clean(relFilePath) == "." {
|
||||
log.Errorf("Can't exclude whole path, excluding pattern: %s", exclude)
|
||||
continue
|
||||
}
|
||||
log.Debugf("Skipping excluded path: %s", relFilePath)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
128
Godeps/_workspace/src/github.com/dotcloud/docker/utils/utils_test.go
generated
vendored
128
Godeps/_workspace/src/github.com/dotcloud/docker/utils/utils_test.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckLocalDns(t *testing.T) {
|
||||
for resolv, result := range map[string]bool{`# Dynamic
|
||||
nameserver 10.0.2.3
|
||||
search docker.com`: false,
|
||||
`# Dynamic
|
||||
#nameserver 127.0.0.1
|
||||
nameserver 10.0.2.3
|
||||
search docker.com`: false,
|
||||
`# Dynamic
|
||||
nameserver 10.0.2.3 #not used 127.0.1.1
|
||||
search docker.com`: false,
|
||||
`# Dynamic
|
||||
#nameserver 10.0.2.3
|
||||
#search docker.com`: true,
|
||||
`# Dynamic
|
||||
nameserver 127.0.0.1
|
||||
search docker.com`: true,
|
||||
`# Dynamic
|
||||
nameserver 127.0.1.1
|
||||
search docker.com`: true,
|
||||
`# Dynamic
|
||||
`: true,
|
||||
``: true,
|
||||
} {
|
||||
if CheckLocalDns([]byte(resolv)) != result {
|
||||
t.Fatalf("Wrong local dns detection: {%s} should be %v", resolv, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestReplaceAndAppendEnvVars(t *testing.T) {
|
||||
var (
|
||||
d = []string{"HOME=/"}
|
||||
o = []string{"HOME=/root", "TERM=xterm"}
|
||||
)
|
||||
|
||||
env := ReplaceOrAppendEnvValues(d, o)
|
||||
if len(env) != 2 {
|
||||
t.Fatalf("expected len of 2 got %d", len(env))
|
||||
}
|
||||
if env[0] != "HOME=/root" {
|
||||
t.Fatalf("expected HOME=/root got '%s'", env[0])
|
||||
}
|
||||
if env[1] != "TERM=xterm" {
|
||||
t.Fatalf("expected TERM=xterm got '%s'", env[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Reading a symlink to a directory must return the directory
|
||||
func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) {
|
||||
var err error
|
||||
if err = os.Mkdir("/tmp/testReadSymlinkToExistingDirectory", 0777); err != nil {
|
||||
t.Errorf("failed to create directory: %s", err)
|
||||
}
|
||||
|
||||
if err = os.Symlink("/tmp/testReadSymlinkToExistingDirectory", "/tmp/dirLinkTest"); err != nil {
|
||||
t.Errorf("failed to create symlink: %s", err)
|
||||
}
|
||||
|
||||
var path string
|
||||
if path, err = ReadSymlinkedDirectory("/tmp/dirLinkTest"); err != nil {
|
||||
t.Fatalf("failed to read symlink to directory: %s", err)
|
||||
}
|
||||
|
||||
if path != "/tmp/testReadSymlinkToExistingDirectory" {
|
||||
t.Fatalf("symlink returned unexpected directory: %s", path)
|
||||
}
|
||||
|
||||
if err = os.Remove("/tmp/testReadSymlinkToExistingDirectory"); err != nil {
|
||||
t.Errorf("failed to remove temporary directory: %s", err)
|
||||
}
|
||||
|
||||
if err = os.Remove("/tmp/dirLinkTest"); err != nil {
|
||||
t.Errorf("failed to remove symlink: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reading a non-existing symlink must fail
|
||||
func TestReadSymlinkedDirectoryNonExistingSymlink(t *testing.T) {
|
||||
var path string
|
||||
var err error
|
||||
if path, err = ReadSymlinkedDirectory("/tmp/test/foo/Non/ExistingPath"); err == nil {
|
||||
t.Fatalf("error expected for non-existing symlink")
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
t.Fatalf("expected empty path, but '%s' was returned", path)
|
||||
}
|
||||
}
|
||||
|
||||
// Reading a symlink to a file must fail
|
||||
func TestReadSymlinkedDirectoryToFile(t *testing.T) {
|
||||
var err error
|
||||
var file *os.File
|
||||
|
||||
if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil {
|
||||
t.Fatalf("failed to create file: %s", err)
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
if err = os.Symlink("/tmp/testReadSymlinkToFile", "/tmp/fileLinkTest"); err != nil {
|
||||
t.Errorf("failed to create symlink: %s", err)
|
||||
}
|
||||
|
||||
var path string
|
||||
if path, err = ReadSymlinkedDirectory("/tmp/fileLinkTest"); err == nil {
|
||||
t.Fatalf("ReadSymlinkedDirectory on a symlink to a file should've failed")
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
t.Fatalf("path should've been empty: %s", path)
|
||||
}
|
||||
|
||||
if err = os.Remove("/tmp/testReadSymlinkToFile"); err != nil {
|
||||
t.Errorf("failed to remove file: %s", err)
|
||||
}
|
||||
|
||||
if err = os.Remove("/tmp/fileLinkTest"); err != nil {
|
||||
t.Errorf("failed to remove symlink: %s", err)
|
||||
}
|
||||
}
|
@ -13,9 +13,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker/pkg/stdcopy"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/pkg/parsers"
|
||||
"github.com/docker/docker/archive"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
)
|
||||
|
||||
type Images struct {
|
||||
|
Loading…
Reference in New Issue
Block a user