mirror of https://github.com/docker/docs.git
Merge pull request #1515 from vieux/refresh_docker_godeps
update docker godeps
This commit is contained in:
commit
5b28d35d31
|
|
@ -32,32 +32,37 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
||||||
"Comment": "v1.4.1-3245-g443437f",
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
"Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96"
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
|
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
|
||||||
"Rev": "c7a04fda2ad804601385f054c19b69cf43fcfe46"
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
|
},
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/random",
|
||||||
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
||||||
"Comment": "v1.4.1-3245-g443437f",
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
"Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96"
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
||||||
"Comment": "v1.4.1-3245-g443437f",
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
"Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96"
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/units",
|
"ImportPath": "github.com/docker/docker/pkg/units",
|
||||||
"Comment": "v1.4.1-3245-g443437f",
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
"Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96"
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/version",
|
"ImportPath": "github.com/docker/docker/pkg/version",
|
||||||
"Comment": "v1.4.1-3245-g443437f",
|
"Comment": "v1.4.1-7381-g270e8cf",
|
||||||
"Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96"
|
"Rev": "270e8cf64dee586240968900fb1cf8e36ed641a5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/libkv",
|
"ImportPath": "github.com/docker/libkv",
|
||||||
|
|
|
||||||
89
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
const maxCap = 1e6
|
||||||
|
|
||||||
|
// BytesPipe is io.ReadWriter which works similarly to pipe(queue).
|
||||||
|
// All written data could be read only once. Also BytesPipe is allocating
|
||||||
|
// and releasing new byte slices to adjust to current needs, so there won't be
|
||||||
|
// overgrown buffer after high load peak.
|
||||||
|
// BytesPipe isn't goroutine-safe, caller must synchronize it if needed.
|
||||||
|
type BytesPipe struct {
|
||||||
|
buf [][]byte // slice of byte-slices of buffered data
|
||||||
|
lastRead int // index in the first slice to a read point
|
||||||
|
bufLen int // length of data buffered over the slices
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBytesPipe creates new BytesPipe, initialized by specified slice.
|
||||||
|
// If buf is nil, then it will be initialized with slice which cap is 64.
|
||||||
|
// buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf).
|
||||||
|
func NewBytesPipe(buf []byte) *BytesPipe {
|
||||||
|
if cap(buf) == 0 {
|
||||||
|
buf = make([]byte, 0, 64)
|
||||||
|
}
|
||||||
|
return &BytesPipe{
|
||||||
|
buf: [][]byte{buf[:0]},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes p to BytesPipe.
|
||||||
|
// It can allocate new []byte slices in a process of writing.
|
||||||
|
func (bp *BytesPipe) Write(p []byte) (n int, err error) {
|
||||||
|
for {
|
||||||
|
// write data to the last buffer
|
||||||
|
b := bp.buf[len(bp.buf)-1]
|
||||||
|
// copy data to the current empty allocated area
|
||||||
|
n := copy(b[len(b):cap(b)], p)
|
||||||
|
// increment buffered data length
|
||||||
|
bp.bufLen += n
|
||||||
|
// include written data in last buffer
|
||||||
|
bp.buf[len(bp.buf)-1] = b[:len(b)+n]
|
||||||
|
|
||||||
|
// if there was enough room to write all then break
|
||||||
|
if len(p) == n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// more data: write to the next slice
|
||||||
|
p = p[n:]
|
||||||
|
// allocate slice that has twice the size of the last unless maximum reached
|
||||||
|
nextCap := 2 * cap(bp.buf[len(bp.buf)-1])
|
||||||
|
if maxCap < nextCap {
|
||||||
|
nextCap = maxCap
|
||||||
|
}
|
||||||
|
// add new byte slice to the buffers slice and continue writing
|
||||||
|
bp.buf = append(bp.buf, make([]byte, 0, nextCap))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bp *BytesPipe) len() int {
|
||||||
|
return bp.bufLen - bp.lastRead
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes from BytesPipe.
|
||||||
|
// Data could be read only once.
|
||||||
|
func (bp *BytesPipe) Read(p []byte) (n int, err error) {
|
||||||
|
for {
|
||||||
|
read := copy(p, bp.buf[0][bp.lastRead:])
|
||||||
|
n += read
|
||||||
|
bp.lastRead += read
|
||||||
|
if bp.len() == 0 {
|
||||||
|
// we have read everything. reset to the beginning.
|
||||||
|
bp.lastRead = 0
|
||||||
|
bp.bufLen -= len(bp.buf[0])
|
||||||
|
bp.buf[0] = bp.buf[0][:0]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// break if everything was read
|
||||||
|
if len(p) == read {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// more buffered data and more asked. read from next slice.
|
||||||
|
p = p[read:]
|
||||||
|
bp.lastRead = 0
|
||||||
|
bp.bufLen -= len(bp.buf[0])
|
||||||
|
bp.buf[0] = nil // throw away old slice
|
||||||
|
bp.buf = bp.buf[1:] // switch to next
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FprintfIfNotEmpty prints the string value if it's not empty
|
||||||
|
func FprintfIfNotEmpty(w io.Writer, format, value string) (int, error) {
|
||||||
|
if value != "" {
|
||||||
|
return fmt.Fprintf(w, format, value)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintfIfTrue prints the boolean value if it's true
|
||||||
|
func FprintfIfTrue(w io.Writer, format string, ok bool) (int, error) {
|
||||||
|
if ok {
|
||||||
|
return fmt.Fprintf(w, format, ok)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
226
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go
generated
vendored
Normal file
226
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pos struct {
|
||||||
|
idx int
|
||||||
|
offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type multiReadSeeker struct {
|
||||||
|
readers []io.ReadSeeker
|
||||||
|
pos *pos
|
||||||
|
posIdx map[io.ReadSeeker]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
var tmpOffset int64
|
||||||
|
switch whence {
|
||||||
|
case os.SEEK_SET:
|
||||||
|
for i, rdr := range r.readers {
|
||||||
|
// get size of the current reader
|
||||||
|
s, err := rdr.Seek(0, os.SEEK_END)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset > tmpOffset+s {
|
||||||
|
if i == len(r.readers)-1 {
|
||||||
|
rdrOffset := s + (offset - tmpOffset)
|
||||||
|
if _, err := rdr.Seek(rdrOffset, os.SEEK_SET); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
r.pos = &pos{i, rdrOffset}
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpOffset += s
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rdrOffset := offset - tmpOffset
|
||||||
|
idx := i
|
||||||
|
|
||||||
|
rdr.Seek(rdrOffset, os.SEEK_SET)
|
||||||
|
// make sure all following readers are at 0
|
||||||
|
for _, rdr := range r.readers[i+1:] {
|
||||||
|
rdr.Seek(0, os.SEEK_SET)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rdrOffset == s && i != len(r.readers)-1 {
|
||||||
|
idx++
|
||||||
|
rdrOffset = 0
|
||||||
|
}
|
||||||
|
r.pos = &pos{idx, rdrOffset}
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
case os.SEEK_END:
|
||||||
|
for _, rdr := range r.readers {
|
||||||
|
s, err := rdr.Seek(0, os.SEEK_END)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
tmpOffset += s
|
||||||
|
}
|
||||||
|
r.Seek(tmpOffset+offset, os.SEEK_SET)
|
||||||
|
return tmpOffset + offset, nil
|
||||||
|
case os.SEEK_CUR:
|
||||||
|
if r.pos == nil {
|
||||||
|
return r.Seek(offset, os.SEEK_SET)
|
||||||
|
}
|
||||||
|
// Just return the current offset
|
||||||
|
if offset == 0 {
|
||||||
|
return r.getCurOffset()
|
||||||
|
}
|
||||||
|
|
||||||
|
curOffset, err := r.getCurOffset()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
rdr, rdrOffset, err := r.getReaderForOffset(curOffset + offset)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.pos = &pos{r.posIdx[rdr], rdrOffset}
|
||||||
|
return curOffset + offset, nil
|
||||||
|
default:
|
||||||
|
return -1, fmt.Errorf("Invalid whence: %d", whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1, fmt.Errorf("Error seeking for whence: %d, offset: %d", whence, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) {
|
||||||
|
var rdr io.ReadSeeker
|
||||||
|
var rdrOffset int64
|
||||||
|
|
||||||
|
for i, rdr := range r.readers {
|
||||||
|
offsetTo, err := r.getOffsetToReader(rdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, -1, err
|
||||||
|
}
|
||||||
|
if offsetTo > offset {
|
||||||
|
rdr = r.readers[i-1]
|
||||||
|
rdrOffset = offsetTo - offset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if rdr == r.readers[len(r.readers)-1] {
|
||||||
|
rdrOffset = offsetTo + offset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rdr, rdrOffset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) getCurOffset() (int64, error) {
|
||||||
|
var totalSize int64
|
||||||
|
for _, rdr := range r.readers[:r.pos.idx+1] {
|
||||||
|
if r.posIdx[rdr] == r.pos.idx {
|
||||||
|
totalSize += r.pos.offset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := getReadSeekerSize(rdr)
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("error getting seeker size: %v", err)
|
||||||
|
}
|
||||||
|
totalSize += size
|
||||||
|
}
|
||||||
|
return totalSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) {
|
||||||
|
var offset int64
|
||||||
|
for _, r := range r.readers {
|
||||||
|
if r == rdr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
size, err := getReadSeekerSize(rdr)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
offset += size
|
||||||
|
}
|
||||||
|
return offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multiReadSeeker) Read(b []byte) (int, error) {
|
||||||
|
if r.pos == nil {
|
||||||
|
r.pos = &pos{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
bCap := int64(cap(b))
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
var rdr io.ReadSeeker
|
||||||
|
|
||||||
|
for _, rdr = range r.readers[r.pos.idx:] {
|
||||||
|
readBytes, err := io.CopyN(buf, rdr, bCap)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
bCap -= readBytes
|
||||||
|
|
||||||
|
if bCap == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rdrPos, err := rdr.Seek(0, os.SEEK_CUR)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
r.pos = &pos{r.posIdx[rdr], rdrPos}
|
||||||
|
return buf.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) {
|
||||||
|
// save the current position
|
||||||
|
pos, err := rdr.Seek(0, os.SEEK_CUR)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the size
|
||||||
|
size, err := rdr.Seek(0, os.SEEK_END)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the position
|
||||||
|
if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided
|
||||||
|
// input readseekers. After calling this method the initial position is set to the
|
||||||
|
// beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances
|
||||||
|
// to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker.
|
||||||
|
// Seek can be used over the sum of lengths of all readseekers.
|
||||||
|
//
|
||||||
|
// When a MultiReadSeeker is used, no Read and Seek operations should be made on
|
||||||
|
// its ReadSeeker components. Also, users should make no assumption on the state
|
||||||
|
// of individual readseekers while the MultiReadSeeker is used.
|
||||||
|
func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
|
||||||
|
if len(readers) == 1 {
|
||||||
|
return readers[0]
|
||||||
|
}
|
||||||
|
idx := make(map[io.ReadSeeker]int)
|
||||||
|
for i, rdr := range readers {
|
||||||
|
idx[rdr] = i
|
||||||
|
}
|
||||||
|
return &multiReadSeeker{
|
||||||
|
readers: readers,
|
||||||
|
posIdx: idx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
package ioutils
|
package ioutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type readCloserWrapper struct {
|
type readCloserWrapper struct {
|
||||||
|
|
@ -20,6 +16,7 @@ func (r *readCloserWrapper) Close() error {
|
||||||
return r.closer()
|
return r.closer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewReadCloserWrapper returns a new io.ReadCloser.
|
||||||
func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
|
func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
|
||||||
return &readCloserWrapper{
|
return &readCloserWrapper{
|
||||||
Reader: r,
|
Reader: r,
|
||||||
|
|
@ -40,6 +37,7 @@ func (r *readerErrWrapper) Read(p []byte) (int, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewReaderErrWrapper returns a new io.Reader.
|
||||||
func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
|
func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
|
||||||
return &readerErrWrapper{
|
return &readerErrWrapper{
|
||||||
reader: r,
|
reader: r,
|
||||||
|
|
@ -53,41 +51,27 @@ func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
|
||||||
// expanding buffer.
|
// expanding buffer.
|
||||||
type bufReader struct {
|
type bufReader struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
buf *bytes.Buffer
|
buf io.ReadWriter
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
err error
|
err error
|
||||||
wait sync.Cond
|
wait sync.Cond
|
||||||
drainBuf []byte
|
drainBuf []byte
|
||||||
reuseBuf []byte
|
|
||||||
maxReuse int64
|
|
||||||
resetTimeout time.Duration
|
|
||||||
bufLenResetThreshold int64
|
|
||||||
maxReadDataReset int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBufReader(r io.Reader) *bufReader {
|
// NewBufReader returns a new bufReader.
|
||||||
var timeout int
|
func NewBufReader(r io.Reader) io.ReadCloser {
|
||||||
if randVal, err := rand.Int(rand.Reader, big.NewInt(120)); err == nil {
|
|
||||||
timeout = int(randVal.Int64()) + 180
|
|
||||||
} else {
|
|
||||||
timeout = 300
|
|
||||||
}
|
|
||||||
reader := &bufReader{
|
reader := &bufReader{
|
||||||
buf: &bytes.Buffer{},
|
buf: NewBytesPipe(nil),
|
||||||
drainBuf: make([]byte, 1024),
|
reader: r,
|
||||||
reuseBuf: make([]byte, 4096),
|
drainBuf: make([]byte, 1024),
|
||||||
maxReuse: 1000,
|
|
||||||
resetTimeout: time.Second * time.Duration(timeout),
|
|
||||||
bufLenResetThreshold: 100 * 1024,
|
|
||||||
maxReadDataReset: 10 * 1024 * 1024,
|
|
||||||
reader: r,
|
|
||||||
}
|
}
|
||||||
reader.wait.L = &reader.Mutex
|
reader.wait.L = &reader.Mutex
|
||||||
go reader.drain()
|
go reader.drain()
|
||||||
return reader
|
return reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBufReaderWithDrainbufAndBuffer(r io.Reader, drainBuffer []byte, buffer *bytes.Buffer) *bufReader {
|
// NewBufReaderWithDrainbufAndBuffer returns a BufReader with drainBuffer and buffer.
|
||||||
|
func NewBufReaderWithDrainbufAndBuffer(r io.Reader, drainBuffer []byte, buffer io.ReadWriter) io.ReadCloser {
|
||||||
reader := &bufReader{
|
reader := &bufReader{
|
||||||
buf: buffer,
|
buf: buffer,
|
||||||
drainBuf: drainBuffer,
|
drainBuf: drainBuffer,
|
||||||
|
|
@ -99,94 +83,22 @@ func NewBufReaderWithDrainbufAndBuffer(r io.Reader, drainBuffer []byte, buffer *
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *bufReader) drain() {
|
func (r *bufReader) drain() {
|
||||||
var (
|
|
||||||
duration time.Duration
|
|
||||||
lastReset time.Time
|
|
||||||
now time.Time
|
|
||||||
reset bool
|
|
||||||
bufLen int64
|
|
||||||
dataSinceReset int64
|
|
||||||
maxBufLen int64
|
|
||||||
reuseBufLen int64
|
|
||||||
reuseCount int64
|
|
||||||
)
|
|
||||||
reuseBufLen = int64(len(r.reuseBuf))
|
|
||||||
lastReset = time.Now()
|
|
||||||
for {
|
for {
|
||||||
|
//Call to scheduler is made to yield from this goroutine.
|
||||||
|
//This avoids goroutine looping here when n=0,err=nil, fixes code hangs when run with GCC Go.
|
||||||
|
callSchedulerIfNecessary()
|
||||||
n, err := r.reader.Read(r.drainBuf)
|
n, err := r.reader.Read(r.drainBuf)
|
||||||
dataSinceReset += int64(n)
|
|
||||||
r.Lock()
|
r.Lock()
|
||||||
bufLen = int64(r.buf.Len())
|
|
||||||
if bufLen > maxBufLen {
|
|
||||||
maxBufLen = bufLen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid unbounded growth of the buffer over time.
|
|
||||||
// This has been discovered to be the only non-intrusive
|
|
||||||
// solution to the unbounded growth of the buffer.
|
|
||||||
// Alternative solutions such as compression, multiple
|
|
||||||
// buffers, channels and other similar pieces of code
|
|
||||||
// were reducing throughput, overall Docker performance
|
|
||||||
// or simply crashed Docker.
|
|
||||||
// This solution releases the buffer when specific
|
|
||||||
// conditions are met to avoid the continuous resizing
|
|
||||||
// of the buffer for long lived containers.
|
|
||||||
//
|
|
||||||
// Move data to the front of the buffer if it's
|
|
||||||
// smaller than what reuseBuf can store
|
|
||||||
if bufLen > 0 && reuseBufLen >= bufLen {
|
|
||||||
n, _ := r.buf.Read(r.reuseBuf)
|
|
||||||
r.buf.Write(r.reuseBuf[0:n])
|
|
||||||
// Take action if the buffer has been reused too many
|
|
||||||
// times and if there's data in the buffer.
|
|
||||||
// The timeout is also used as means to avoid doing
|
|
||||||
// these operations more often or less often than
|
|
||||||
// required.
|
|
||||||
// The various conditions try to detect heavy activity
|
|
||||||
// in the buffer which might be indicators of heavy
|
|
||||||
// growth of the buffer.
|
|
||||||
} else if reuseCount >= r.maxReuse && bufLen > 0 {
|
|
||||||
now = time.Now()
|
|
||||||
duration = now.Sub(lastReset)
|
|
||||||
timeoutReached := duration >= r.resetTimeout
|
|
||||||
|
|
||||||
// The timeout has been reached and the
|
|
||||||
// buffered data couldn't be moved to the front
|
|
||||||
// of the buffer, so the buffer gets reset.
|
|
||||||
if timeoutReached && bufLen > reuseBufLen {
|
|
||||||
reset = true
|
|
||||||
}
|
|
||||||
// The amount of buffered data is too high now,
|
|
||||||
// reset the buffer.
|
|
||||||
if timeoutReached && maxBufLen >= r.bufLenResetThreshold {
|
|
||||||
reset = true
|
|
||||||
}
|
|
||||||
// Reset the buffer if a certain amount of
|
|
||||||
// data has gone through the buffer since the
|
|
||||||
// last reset.
|
|
||||||
if timeoutReached && dataSinceReset >= r.maxReadDataReset {
|
|
||||||
reset = true
|
|
||||||
}
|
|
||||||
// The buffered data is moved to a fresh buffer,
|
|
||||||
// swap the old buffer with the new one and
|
|
||||||
// reset all counters.
|
|
||||||
if reset {
|
|
||||||
newbuf := &bytes.Buffer{}
|
|
||||||
newbuf.ReadFrom(r.buf)
|
|
||||||
r.buf = newbuf
|
|
||||||
lastReset = now
|
|
||||||
reset = false
|
|
||||||
dataSinceReset = 0
|
|
||||||
maxBufLen = 0
|
|
||||||
reuseCount = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = err
|
r.err = err
|
||||||
} else {
|
} else {
|
||||||
r.buf.Write(r.drainBuf[0:n])
|
if n == 0 {
|
||||||
|
// nothing written, no need to signal
|
||||||
|
r.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.buf.Write(r.drainBuf[:n])
|
||||||
}
|
}
|
||||||
reuseCount++
|
|
||||||
r.wait.Signal()
|
r.wait.Signal()
|
||||||
r.Unlock()
|
r.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -210,6 +122,7 @@ func (r *bufReader) Read(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the bufReader
|
||||||
func (r *bufReader) Close() error {
|
func (r *bufReader) Close() error {
|
||||||
closer, ok := r.reader.(io.ReadCloser)
|
closer, ok := r.reader.(io.ReadCloser)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -218,6 +131,7 @@ func (r *bufReader) Close() error {
|
||||||
return closer.Close()
|
return closer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashData returns the sha256 sum of src.
|
||||||
func HashData(src io.Reader) (string, error) {
|
func HashData(src io.Reader) (string, error) {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
if _, err := io.Copy(h, src); err != nil {
|
if _, err := io.Copy(h, src); err != nil {
|
||||||
|
|
@ -225,3 +139,32 @@ func HashData(src io.Reader) (string, error) {
|
||||||
}
|
}
|
||||||
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
|
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnEOFReader wraps a io.ReadCloser and a function
|
||||||
|
// the function will run at the end of file or close the file.
|
||||||
|
type OnEOFReader struct {
|
||||||
|
Rc io.ReadCloser
|
||||||
|
Fn func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OnEOFReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = r.Rc.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
r.runFunc()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the file and run the function.
|
||||||
|
func (r *OnEOFReader) Close() error {
|
||||||
|
err := r.Rc.Close()
|
||||||
|
r.runFunc()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OnEOFReader) runFunc() {
|
||||||
|
if fn := r.Fn; fn != nil {
|
||||||
|
fn()
|
||||||
|
r.Fn = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,217 +0,0 @@
|
||||||
package ioutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Implement io.Reader
|
|
||||||
type errorReader struct{}
|
|
||||||
|
|
||||||
func (r *errorReader) Read(p []byte) (int, error) {
|
|
||||||
return 0, fmt.Errorf("Error reader always fail.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadCloserWrapperClose(t *testing.T) {
|
|
||||||
reader := strings.NewReader("A string reader")
|
|
||||||
wrapper := NewReadCloserWrapper(reader, func() error {
|
|
||||||
return fmt.Errorf("This will be called when closing")
|
|
||||||
})
|
|
||||||
err := wrapper.Close()
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "This will be called when closing") {
|
|
||||||
t.Fatalf("readCloserWrapper should have call the anonymous func and thus, fail.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderErrWrapperReadOnError(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
reader := &errorReader{}
|
|
||||||
wrapper := NewReaderErrWrapper(reader, func() {
|
|
||||||
called = true
|
|
||||||
})
|
|
||||||
_, err := wrapper.Read([]byte{})
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "Error reader always fail.") {
|
|
||||||
t.Fatalf("readErrWrapper should returned an error")
|
|
||||||
}
|
|
||||||
if !called {
|
|
||||||
t.Fatalf("readErrWrapper should have call the anonymous function on failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaderErrWrapperRead(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
reader := strings.NewReader("a string reader.")
|
|
||||||
wrapper := NewReaderErrWrapper(reader, func() {
|
|
||||||
called = true // Should not be called
|
|
||||||
})
|
|
||||||
// Read 20 byte (should be ok with the string above)
|
|
||||||
num, err := wrapper.Read(make([]byte, 20))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if num != 16 {
|
|
||||||
t.Fatalf("readerErrWrapper should have read 16 byte, but read %d", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewBufReaderWithDrainbufAndBuffer(t *testing.T) {
|
|
||||||
reader, writer := io.Pipe()
|
|
||||||
|
|
||||||
drainBuffer := make([]byte, 1024)
|
|
||||||
buffer := bytes.Buffer{}
|
|
||||||
bufreader := NewBufReaderWithDrainbufAndBuffer(reader, drainBuffer, &buffer)
|
|
||||||
|
|
||||||
// Write everything down to a Pipe
|
|
||||||
// Usually, a pipe should block but because of the buffered reader,
|
|
||||||
// the writes will go through
|
|
||||||
done := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
writer.Write([]byte("hello world"))
|
|
||||||
writer.Close()
|
|
||||||
done <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Drain the reader *after* everything has been written, just to verify
|
|
||||||
// it is indeed buffering
|
|
||||||
<-done
|
|
||||||
|
|
||||||
output, err := ioutil.ReadAll(bufreader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(output, []byte("hello world")) {
|
|
||||||
t.Error(string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBufReader(t *testing.T) {
|
|
||||||
reader, writer := io.Pipe()
|
|
||||||
bufreader := NewBufReader(reader)
|
|
||||||
|
|
||||||
// Write everything down to a Pipe
|
|
||||||
// Usually, a pipe should block but because of the buffered reader,
|
|
||||||
// the writes will go through
|
|
||||||
done := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
writer.Write([]byte("hello world"))
|
|
||||||
writer.Close()
|
|
||||||
done <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Drain the reader *after* everything has been written, just to verify
|
|
||||||
// it is indeed buffering
|
|
||||||
<-done
|
|
||||||
output, err := ioutil.ReadAll(bufreader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(output, []byte("hello world")) {
|
|
||||||
t.Error(string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBufReaderCloseWithNonReaderCloser(t *testing.T) {
|
|
||||||
reader := strings.NewReader("buffer")
|
|
||||||
bufreader := NewBufReader(reader)
|
|
||||||
|
|
||||||
if err := bufreader.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// implements io.ReadCloser
|
|
||||||
type simpleReaderCloser struct{}
|
|
||||||
|
|
||||||
func (r *simpleReaderCloser) Read(p []byte) (n int, err error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *simpleReaderCloser) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBufReaderCloseWithReaderCloser(t *testing.T) {
|
|
||||||
reader := &simpleReaderCloser{}
|
|
||||||
bufreader := NewBufReader(reader)
|
|
||||||
|
|
||||||
err := bufreader.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashData(t *testing.T) {
|
|
||||||
reader := strings.NewReader("hash-me")
|
|
||||||
actual, err := HashData(reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa"
|
|
||||||
if actual != expected {
|
|
||||||
t.Fatalf("Expecting %s, got %s", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type repeatedReader struct {
|
|
||||||
readCount int
|
|
||||||
maxReads int
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRepeatedReader(max int, data []byte) *repeatedReader {
|
|
||||||
return &repeatedReader{0, max, data}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repeatedReader) Read(p []byte) (int, error) {
|
|
||||||
if r.readCount >= r.maxReads {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
r.readCount++
|
|
||||||
n := copy(p, r.data)
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func testWithData(data []byte, reads int) {
|
|
||||||
reader := newRepeatedReader(reads, data)
|
|
||||||
bufReader := NewBufReader(reader)
|
|
||||||
io.Copy(ioutil.Discard, bufReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark1M10BytesReads(b *testing.B) {
|
|
||||||
reads := 1000000
|
|
||||||
readSize := int64(10)
|
|
||||||
data := make([]byte, readSize)
|
|
||||||
b.SetBytes(readSize * int64(reads))
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
testWithData(data, reads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark1M1024BytesReads(b *testing.B) {
|
|
||||||
reads := 1000000
|
|
||||||
readSize := int64(1024)
|
|
||||||
data := make([]byte, readSize)
|
|
||||||
b.SetBytes(readSize * int64(reads))
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
testWithData(data, reads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark10k32KBytesReads(b *testing.B) {
|
|
||||||
reads := 10000
|
|
||||||
readSize := int64(32 * 1024)
|
|
||||||
data := make([]byte, readSize)
|
|
||||||
b.SetBytes(readSize * int64(reads))
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
testWithData(data, reads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler.go
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
func callSchedulerIfNecessary() {
|
||||||
|
}
|
||||||
13
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler_gccgo.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/scheduler_gccgo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build gccgo
|
||||||
|
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func callSchedulerIfNecessary() {
|
||||||
|
//allow or force Go scheduler to switch context, without explicitly
|
||||||
|
//forcing this will make it hang when using gccgo implementation
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
10
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_unix.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_unix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import "io/ioutil"
|
||||||
|
|
||||||
|
// TempDir on Unix systems is equivalent to ioutil.TempDir.
|
||||||
|
func TempDir(dir, prefix string) (string, error) {
|
||||||
|
return ioutil.TempDir(dir, prefix)
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_windows.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/temp_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/longpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TempDir is the equivalent of ioutil.TempDir, except that the result is in Windows longpath format.
|
||||||
|
func TempDir(dir, prefix string) (string, error) {
|
||||||
|
tempDir, err := ioutil.TempDir(dir, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return longpath.AddPrefix(tempDir), nil
|
||||||
|
}
|
||||||
51
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writeflusher.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/writeflusher.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package ioutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteFlusher wraps the Write and Flush operation.
|
||||||
|
type WriteFlusher struct {
|
||||||
|
sync.Mutex
|
||||||
|
w io.Writer
|
||||||
|
flusher http.Flusher
|
||||||
|
flushed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
|
||||||
|
wf.Lock()
|
||||||
|
defer wf.Unlock()
|
||||||
|
n, err = wf.w.Write(b)
|
||||||
|
wf.flushed = true
|
||||||
|
wf.flusher.Flush()
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush the stream immediately.
|
||||||
|
func (wf *WriteFlusher) Flush() {
|
||||||
|
wf.Lock()
|
||||||
|
defer wf.Unlock()
|
||||||
|
wf.flushed = true
|
||||||
|
wf.flusher.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flushed returns the state of flushed.
|
||||||
|
// If it's flushed, return true, or else it return false.
|
||||||
|
func (wf *WriteFlusher) Flushed() bool {
|
||||||
|
wf.Lock()
|
||||||
|
defer wf.Unlock()
|
||||||
|
return wf.flushed
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriteFlusher returns a new WriteFlusher.
|
||||||
|
func NewWriteFlusher(w io.Writer) *WriteFlusher {
|
||||||
|
var flusher http.Flusher
|
||||||
|
if f, ok := w.(http.Flusher); ok {
|
||||||
|
flusher = f
|
||||||
|
} else {
|
||||||
|
flusher = &NopFlusher{}
|
||||||
|
}
|
||||||
|
return &WriteFlusher{w: w, flusher: flusher}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package ioutils
|
||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
|
// NopWriter represents a type which write operation is nop.
|
||||||
type NopWriter struct{}
|
type NopWriter struct{}
|
||||||
|
|
||||||
func (*NopWriter) Write(buf []byte) (int, error) {
|
func (*NopWriter) Write(buf []byte) (int, error) {
|
||||||
|
|
@ -14,12 +15,15 @@ type nopWriteCloser struct {
|
||||||
|
|
||||||
func (w *nopWriteCloser) Close() error { return nil }
|
func (w *nopWriteCloser) Close() error { return nil }
|
||||||
|
|
||||||
|
// NopWriteCloser returns a nopWriteCloser.
|
||||||
func NopWriteCloser(w io.Writer) io.WriteCloser {
|
func NopWriteCloser(w io.Writer) io.WriteCloser {
|
||||||
return &nopWriteCloser{w}
|
return &nopWriteCloser{w}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NopFlusher represents a type which flush opetatin is nop.
|
||||||
type NopFlusher struct{}
|
type NopFlusher struct{}
|
||||||
|
|
||||||
|
// Flush is a nop operation.
|
||||||
func (f *NopFlusher) Flush() {}
|
func (f *NopFlusher) Flush() {}
|
||||||
|
|
||||||
type writeCloserWrapper struct {
|
type writeCloserWrapper struct {
|
||||||
|
|
@ -31,6 +35,7 @@ func (r *writeCloserWrapper) Close() error {
|
||||||
return r.closer()
|
return r.closer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWriteCloserWrapper returns a new io.WriteCloser.
|
||||||
func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser {
|
func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser {
|
||||||
return &writeCloserWrapper{
|
return &writeCloserWrapper{
|
||||||
Writer: r,
|
Writer: r,
|
||||||
|
|
@ -38,7 +43,7 @@ func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap a concrete io.Writer and hold a count of the number
|
// WriteCounter wraps a concrete io.Writer and hold a count of the number
|
||||||
// of bytes written to the writer during a "session".
|
// of bytes written to the writer during a "session".
|
||||||
// This can be convenient when write return is masked
|
// This can be convenient when write return is masked
|
||||||
// (e.g., json.Encoder.Encode())
|
// (e.g., json.Encoder.Encode())
|
||||||
|
|
@ -47,6 +52,7 @@ type WriteCounter struct {
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWriteCounter returns a new WriteCounter.
|
||||||
func NewWriteCounter(w io.Writer) *WriteCounter {
|
func NewWriteCounter(w io.Writer) *WriteCounter {
|
||||||
return &WriteCounter{
|
return &WriteCounter{
|
||||||
Writer: w,
|
Writer: w,
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
package ioutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWriteCloserWrapperClose(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
writer := bytes.NewBuffer([]byte{})
|
|
||||||
wrapper := NewWriteCloserWrapper(writer, func() error {
|
|
||||||
called = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err := wrapper.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !called {
|
|
||||||
t.Fatalf("writeCloserWrapper should have call the anonymous function.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNopWriteCloser(t *testing.T) {
|
|
||||||
writer := bytes.NewBuffer([]byte{})
|
|
||||||
wrapper := NopWriteCloser(writer)
|
|
||||||
if err := wrapper.Close(); err != nil {
|
|
||||||
t.Fatal("NopWriteCloser always return nil on Close.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNopWriter(t *testing.T) {
|
|
||||||
nw := &NopWriter{}
|
|
||||||
l, err := nw.Write([]byte{'c'})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if l != 1 {
|
|
||||||
t.Fatalf("Expected 1 got %d", l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWriteCounter(t *testing.T) {
|
|
||||||
dummy1 := "This is a dummy string."
|
|
||||||
dummy2 := "This is another dummy string."
|
|
||||||
totalLength := int64(len(dummy1) + len(dummy2))
|
|
||||||
|
|
||||||
reader1 := strings.NewReader(dummy1)
|
|
||||||
reader2 := strings.NewReader(dummy2)
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
wc := NewWriteCounter(&buffer)
|
|
||||||
|
|
||||||
reader1.WriteTo(wc)
|
|
||||||
reader2.WriteTo(wc)
|
|
||||||
|
|
||||||
if wc.Count != totalLength {
|
|
||||||
t.Errorf("Wrong count: %d vs. %d", wc.Count, totalLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
if buffer.String() != dummy1+dummy2 {
|
|
||||||
t.Error("Wrong message written")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Package filters provides helper function to parse and handle command line
|
||||||
|
// filter, used for example in docker ps or docker images commands.
|
||||||
package filters
|
package filters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -7,16 +9,22 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Args stores filter arguments as map key:{array of values}.
|
||||||
|
// It contains a aggregation of the list of arguments (which are in the form
|
||||||
|
// of -f 'key=value') based on the key, and store values for the same key
|
||||||
|
// in an slice.
|
||||||
|
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
||||||
|
// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
|
||||||
type Args map[string][]string
|
type Args map[string][]string
|
||||||
|
|
||||||
// Parse the argument to the filter flag. Like
|
// ParseFlag parses the argument to the filter flag. Like
|
||||||
//
|
//
|
||||||
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
||||||
//
|
//
|
||||||
// If prev map is provided, then it is appended to, and returned. By default a new
|
// If prev map is provided, then it is appended to, and returned. By default a new
|
||||||
// map is created.
|
// map is created.
|
||||||
func ParseFlag(arg string, prev Args) (Args, error) {
|
func ParseFlag(arg string, prev Args) (Args, error) {
|
||||||
var filters Args = prev
|
filters := prev
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
filters = Args{}
|
filters = Args{}
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +33,7 @@ func ParseFlag(arg string, prev Args) (Args, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(arg, "=") {
|
if !strings.Contains(arg, "=") {
|
||||||
return filters, ErrorBadFormat
|
return filters, ErrBadFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
f := strings.SplitN(arg, "=", 2)
|
f := strings.SplitN(arg, "=", 2)
|
||||||
|
|
@ -36,9 +44,10 @@ func ParseFlag(arg string, prev Args) (Args, error) {
|
||||||
return filters, nil
|
return filters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrorBadFormat = errors.New("bad format of filter (expected name=value)")
|
// ErrBadFormat is an error returned in case of bad format for a filter.
|
||||||
|
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||||
|
|
||||||
// packs the Args into an string for easy transport from client to server
|
// ToParam packs the Args into an string for easy transport from client to server.
|
||||||
func ToParam(a Args) (string, error) {
|
func ToParam(a Args) (string, error) {
|
||||||
// this way we don't URL encode {}, just empty space
|
// this way we don't URL encode {}, just empty space
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
|
|
@ -52,7 +61,7 @@ func ToParam(a Args) (string, error) {
|
||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpacks the filter Args
|
// FromParam unpacks the filter Args.
|
||||||
func FromParam(p string) (Args, error) {
|
func FromParam(p string) (Args, error) {
|
||||||
args := Args{}
|
args := Args{}
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
|
|
@ -64,6 +73,11 @@ func FromParam(p string) (Args, error) {
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchKVList returns true if the values for the specified field maches the ones
|
||||||
|
// from the sources.
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'label' and sources are {'label':{'label1=1','label2=2','label3=3'}}
|
||||||
|
// it returns true.
|
||||||
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||||
fieldValues := filters[field]
|
fieldValues := filters[field]
|
||||||
|
|
||||||
|
|
@ -96,6 +110,10 @@ outer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match returns true if the values for the specified field matches the source string
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'image.name' and source is 'ubuntu'
|
||||||
|
// it returns true.
|
||||||
func (filters Args) Match(field, source string) bool {
|
func (filters Args) Match(field, source string) bool {
|
||||||
fieldValues := filters[field]
|
fieldValues := filters[field]
|
||||||
|
|
||||||
|
|
|
||||||
78
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go
generated
vendored
78
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go
generated
vendored
|
|
@ -1,78 +0,0 @@
|
||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseArgs(t *testing.T) {
|
|
||||||
// equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'`
|
|
||||||
flagArgs := []string{
|
|
||||||
"created=today",
|
|
||||||
"image.name=ubuntu*",
|
|
||||||
"image.name=*untu",
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
args = Args{}
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for i := range flagArgs {
|
|
||||||
args, err = ParseFlag(flagArgs[i], args)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to parse %s: %s", flagArgs[i], err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(args["created"]) != 1 {
|
|
||||||
t.Errorf("failed to set this arg")
|
|
||||||
}
|
|
||||||
if len(args["image.name"]) != 2 {
|
|
||||||
t.Errorf("the args should have collapsed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParam(t *testing.T) {
|
|
||||||
a := Args{
|
|
||||||
"created": []string{"today"},
|
|
||||||
"image.name": []string{"ubuntu*", "*untu"},
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := ToParam(a)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to marshal the filters: %s", err)
|
|
||||||
}
|
|
||||||
v1, err := FromParam(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
for key, vals := range v1 {
|
|
||||||
if _, ok := a[key]; !ok {
|
|
||||||
t.Errorf("could not find key %s in original set", key)
|
|
||||||
}
|
|
||||||
sort.Strings(vals)
|
|
||||||
sort.Strings(a[key])
|
|
||||||
if len(vals) != len(a[key]) {
|
|
||||||
t.Errorf("value lengths ought to match")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i := range vals {
|
|
||||||
if vals[i] != a[key][i] {
|
|
||||||
t.Errorf("expected %s, but got %s", a[key][i], vals[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
|
||||||
a := Args{}
|
|
||||||
v, err := ToParam(a)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to marshal the filters: %s", err)
|
|
||||||
}
|
|
||||||
v1, err := FromParam(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
if len(a) != len(v1) {
|
|
||||||
t.Errorf("these should both be empty sets")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
// Package kernel provides helper function to get, parse and compare kernel
|
||||||
|
// versions for different platforms.
|
||||||
package kernel
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -6,20 +10,21 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KernelVersionInfo struct {
|
// VersionInfo holds information about the kernel.
|
||||||
Kernel int
|
type VersionInfo struct {
|
||||||
Major int
|
Kernel int // Version of the kernel (e.g. 4.1.2-generic -> 4)
|
||||||
Minor int
|
Major int // Major part of the kernel version (e.g. 4.1.2-generic -> 1)
|
||||||
Flavor string
|
Minor int // Minor part of the kernel version (e.g. 4.1.2-generic -> 2)
|
||||||
|
Flavor string // Flavor of the kernel version (e.g. 4.1.2-generic -> generic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KernelVersionInfo) String() string {
|
func (k *VersionInfo) String() string {
|
||||||
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare two KernelVersionInfo struct.
|
// CompareKernelVersion compares two kernel.VersionInfo structs.
|
||||||
// Returns -1 if a < b, 0 if a == b, 1 it a > b
|
// Returns -1 if a < b, 0 if a == b, 1 it a > b
|
||||||
func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
func CompareKernelVersion(a, b VersionInfo) int {
|
||||||
if a.Kernel < b.Kernel {
|
if a.Kernel < b.Kernel {
|
||||||
return -1
|
return -1
|
||||||
} else if a.Kernel > b.Kernel {
|
} else if a.Kernel > b.Kernel {
|
||||||
|
|
@ -41,7 +46,8 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
// GetKernelVersion gets the current kernel version.
|
||||||
|
func GetKernelVersion() (*VersionInfo, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
@ -65,7 +71,8 @@ func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||||
return ParseRelease(string(release))
|
return ParseRelease(string(release))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseRelease(release string) (*KernelVersionInfo, error) {
|
// ParseRelease parses a string and creates a VersionInfo based on it.
|
||||||
|
func ParseRelease(release string) (*VersionInfo, error) {
|
||||||
var (
|
var (
|
||||||
kernel, major, minor, parsed int
|
kernel, major, minor, parsed int
|
||||||
flavor, partial string
|
flavor, partial string
|
||||||
|
|
@ -84,7 +91,7 @@ func ParseRelease(release string) (*KernelVersionInfo, error) {
|
||||||
flavor = partial
|
flavor = partial
|
||||||
}
|
}
|
||||||
|
|
||||||
return &KernelVersionInfo{
|
return &VersionInfo{
|
||||||
Kernel: kernel,
|
Kernel: kernel,
|
||||||
Major: major,
|
Major: major,
|
||||||
Minor: minor,
|
Minor: minor,
|
||||||
|
|
|
||||||
61
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
61
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
|
|
@ -1,61 +0,0 @@
|
||||||
package kernel
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
|
|
||||||
var (
|
|
||||||
a *KernelVersionInfo
|
|
||||||
)
|
|
||||||
a, _ = ParseRelease(release)
|
|
||||||
|
|
||||||
if r := CompareKernelVersion(a, b); r != result {
|
|
||||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
|
||||||
}
|
|
||||||
if a.Flavor != b.Flavor {
|
|
||||||
t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseRelease(t *testing.T) {
|
|
||||||
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
|
|
||||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
|
||||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
|
||||||
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
|
|
||||||
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
|
|
||||||
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
|
||||||
if r := CompareKernelVersion(a, b); r != result {
|
|
||||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareKernelVersion(t *testing.T) {
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
0)
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
-1)
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
|
||||||
1)
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
0)
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
1)
|
|
||||||
assertKernelVersion(t,
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
|
|
||||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
|
||||||
-1)
|
|
||||||
}
|
|
||||||
67
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go
generated
vendored
Normal file
67
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VersionInfo holds information about the kernel.
|
||||||
|
type VersionInfo struct {
|
||||||
|
kvi string // Version of the kernel (e.g. 6.1.7601.17592 -> 6)
|
||||||
|
major int // Major part of the kernel version (e.g. 6.1.7601.17592 -> 1)
|
||||||
|
minor int // Minor part of the kernel version (e.g. 6.1.7601.17592 -> 7601)
|
||||||
|
build int // Build number of the kernel version (e.g. 6.1.7601.17592 -> 17592)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *VersionInfo) String() string {
|
||||||
|
return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKernelVersion gets the current kernel version.
|
||||||
|
func GetKernelVersion() (*VersionInfo, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
h syscall.Handle
|
||||||
|
dwVersion uint32
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
KVI := &VersionInfo{"Unknown", 0, 0, 0}
|
||||||
|
|
||||||
|
if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
|
||||||
|
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
|
||||||
|
0,
|
||||||
|
syscall.KEY_READ,
|
||||||
|
&h); err != nil {
|
||||||
|
return KVI, err
|
||||||
|
}
|
||||||
|
defer syscall.RegCloseKey(h)
|
||||||
|
|
||||||
|
var buf [1 << 10]uint16
|
||||||
|
var typ uint32
|
||||||
|
n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
|
||||||
|
|
||||||
|
if err = syscall.RegQueryValueEx(h,
|
||||||
|
syscall.StringToUTF16Ptr("BuildLabEx"),
|
||||||
|
nil,
|
||||||
|
&typ,
|
||||||
|
(*byte)(unsafe.Pointer(&buf[0])),
|
||||||
|
&n); err != nil {
|
||||||
|
return KVI, err
|
||||||
|
}
|
||||||
|
|
||||||
|
KVI.kvi = syscall.UTF16ToString(buf[:])
|
||||||
|
|
||||||
|
// Important - docker.exe MUST be manifested for this API to return
|
||||||
|
// the correct information.
|
||||||
|
if dwVersion, err = syscall.GetVersion(); err != nil {
|
||||||
|
return KVI, err
|
||||||
|
}
|
||||||
|
|
||||||
|
KVI.major = int(dwVersion & 0xFF)
|
||||||
|
KVI.minor = int((dwVersion & 0XFF00) >> 8)
|
||||||
|
KVI.build = int((dwVersion & 0xFFFF0000) >> 16)
|
||||||
|
|
||||||
|
return KVI, nil
|
||||||
|
}
|
||||||
3
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
3
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Utsname represents the system name structure.
|
||||||
|
// It is passthgrouh for syscall.Utsname in order to make it portable with
|
||||||
|
// other platforms where it is not available.
|
||||||
type Utsname syscall.Utsname
|
type Utsname syscall.Utsname
|
||||||
|
|
||||||
func uname() (*syscall.Utsname, error) {
|
func uname() (*syscall.Utsname, error) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Utsname represents the system name structure.
|
||||||
|
// It is defined here to make it portable as it is available on linux but not
|
||||||
|
// on windows.
|
||||||
type Utsname struct {
|
type Utsname struct {
|
||||||
Release [65]byte
|
Release [65]byte
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_freebsd.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_freebsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package operatingsystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetOperatingSystem gets the name of the current operating system.
|
||||||
|
func GetOperatingSystem() (string, error) {
|
||||||
|
// TODO: Implement OS detection
|
||||||
|
return "", errors.New("Cannot detect OS version")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainerized returns true if we are running inside a container.
|
||||||
|
// No-op on FreeBSD, always returns false.
|
||||||
|
func IsContainerized() (bool, error) {
|
||||||
|
// TODO: Implement jail detection
|
||||||
|
return false, errors.New("Cannot detect if we are in container")
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Package operatingsystem provides helper function to get the operating system
|
||||||
|
// name for different platforms.
|
||||||
package operatingsystem
|
package operatingsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -14,6 +16,7 @@ var (
|
||||||
etcOsRelease = "/etc/os-release"
|
etcOsRelease = "/etc/os-release"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetOperatingSystem gets the name of the current operating system.
|
||||||
func GetOperatingSystem() (string, error) {
|
func GetOperatingSystem() (string, error) {
|
||||||
b, err := ioutil.ReadFile(etcOsRelease)
|
b, err := ioutil.ReadFile(etcOsRelease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -26,6 +29,7 @@ func GetOperatingSystem() (string, error) {
|
||||||
return "", errors.New("PRETTY_NAME not found")
|
return "", errors.New("PRETTY_NAME not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsContainerized returns true if we are running inside a container.
|
||||||
func IsContainerized() (bool, error) {
|
func IsContainerized() (bool, error) {
|
||||||
b, err := ioutil.ReadFile(proc1Cgroup)
|
b, err := ioutil.ReadFile(proc1Cgroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
package operatingsystem
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetOperatingSystem(t *testing.T) {
|
|
||||||
var (
|
|
||||||
backup = etcOsRelease
|
|
||||||
ubuntuTrusty = []byte(`NAME="Ubuntu"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
PRETTY_NAME="Ubuntu 14.04 LTS"
|
|
||||||
VERSION_ID="14.04"
|
|
||||||
HOME_URL="http://www.ubuntu.com/"
|
|
||||||
SUPPORT_URL="http://help.ubuntu.com/"
|
|
||||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
|
|
||||||
gentoo = []byte(`NAME=Gentoo
|
|
||||||
ID=gentoo
|
|
||||||
PRETTY_NAME="Gentoo/Linux"
|
|
||||||
ANSI_COLOR="1;32"
|
|
||||||
HOME_URL="http://www.gentoo.org/"
|
|
||||||
SUPPORT_URL="http://www.gentoo.org/main/en/support.xml"
|
|
||||||
BUG_REPORT_URL="https://bugs.gentoo.org/"
|
|
||||||
`)
|
|
||||||
noPrettyName = []byte(`NAME="Ubuntu"
|
|
||||||
VERSION="14.04, Trusty Tahr"
|
|
||||||
ID=ubuntu
|
|
||||||
ID_LIKE=debian
|
|
||||||
VERSION_ID="14.04"
|
|
||||||
HOME_URL="http://www.ubuntu.com/"
|
|
||||||
SUPPORT_URL="http://help.ubuntu.com/"
|
|
||||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
|
|
||||||
)
|
|
||||||
|
|
||||||
dir := os.TempDir()
|
|
||||||
etcOsRelease = filepath.Join(dir, "etcOsRelease")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.Remove(etcOsRelease)
|
|
||||||
etcOsRelease = backup
|
|
||||||
}()
|
|
||||||
|
|
||||||
for expect, osRelease := range map[string][]byte{
|
|
||||||
"Ubuntu 14.04 LTS": ubuntuTrusty,
|
|
||||||
"Gentoo/Linux": gentoo,
|
|
||||||
"": noPrettyName,
|
|
||||||
} {
|
|
||||||
if err := ioutil.WriteFile(etcOsRelease, osRelease, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", etcOsRelease, err)
|
|
||||||
}
|
|
||||||
s, err := GetOperatingSystem()
|
|
||||||
if s != expect {
|
|
||||||
if expect == "" {
|
|
||||||
t.Fatalf("Expected error 'PRETTY_NAME not found', but got %v", err)
|
|
||||||
} else {
|
|
||||||
t.Fatalf("Expected '%s', but got '%s'. Err=%v", expect, s, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsContainerized(t *testing.T) {
|
|
||||||
var (
|
|
||||||
backup = proc1Cgroup
|
|
||||||
nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/
|
|
||||||
13:hugetlb:/
|
|
||||||
12:net_prio:/
|
|
||||||
11:perf_event:/
|
|
||||||
10:bfqio:/
|
|
||||||
9:blkio:/
|
|
||||||
8:net_cls:/
|
|
||||||
7:freezer:/
|
|
||||||
6:devices:/
|
|
||||||
5:memory:/
|
|
||||||
4:cpuacct:/
|
|
||||||
3:cpu:/
|
|
||||||
2:cpuset:/
|
|
||||||
`)
|
|
||||||
containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
7:net_cls:/
|
|
||||||
6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
|
||||||
1:cpuset:/`)
|
|
||||||
)
|
|
||||||
|
|
||||||
dir := os.TempDir()
|
|
||||||
proc1Cgroup = filepath.Join(dir, "proc1Cgroup")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.Remove(proc1Cgroup)
|
|
||||||
proc1Cgroup = backup
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
|
||||||
}
|
|
||||||
inContainer, err := IsContainerized()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if inContainer {
|
|
||||||
t.Fatal("Wrongly assuming containerized")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil {
|
|
||||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
|
||||||
}
|
|
||||||
inContainer, err = IsContainerized()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !inContainer {
|
|
||||||
t.Fatal("Wrongly assuming non-containerized")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
49
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package operatingsystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c
|
||||||
|
// for a similar sample
|
||||||
|
|
||||||
|
// GetOperatingSystem gets the name of the current operating system.
|
||||||
|
func GetOperatingSystem() (string, error) {
|
||||||
|
|
||||||
|
var h syscall.Handle
|
||||||
|
|
||||||
|
// Default return value
|
||||||
|
ret := "Unknown Operating System"
|
||||||
|
|
||||||
|
if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
|
||||||
|
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
|
||||||
|
0,
|
||||||
|
syscall.KEY_READ,
|
||||||
|
&h); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
defer syscall.RegCloseKey(h)
|
||||||
|
|
||||||
|
var buf [1 << 10]uint16
|
||||||
|
var typ uint32
|
||||||
|
n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
|
||||||
|
|
||||||
|
if err := syscall.RegQueryValueEx(h,
|
||||||
|
syscall.StringToUTF16Ptr("ProductName"),
|
||||||
|
nil,
|
||||||
|
&typ,
|
||||||
|
(*byte)(unsafe.Pointer(&buf[0])),
|
||||||
|
&n); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
ret = syscall.UTF16ToString(buf[:])
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainerized returns true if we are running inside a container.
|
||||||
|
// No-op on Windows, always returns false.
|
||||||
|
func IsContainerized() (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,32 @@
|
||||||
|
// Package parsers provides helper functions to parse and validate different type
|
||||||
|
// of string. It can be hosts, unix addresses, tcp addresses, filters, kernel
|
||||||
|
// operating system versions.
|
||||||
package parsers
|
package parsers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME: Change this not to receive default value as parameter
|
// ParseDockerDaemonHost parses the specified address and returns an address that will be used as the host.
|
||||||
func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
|
// Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr
|
||||||
|
// defaultUnixAddr must be a absolute file path (no `unix://` prefix)
|
||||||
|
// defaultTCPAddr must be the full `tcp://host:port` form
|
||||||
|
func ParseDockerDaemonHost(defaultTCPAddr, defaultTLSHost, defaultUnixAddr, defaultAddr, addr string) (string, error) {
|
||||||
addr = strings.TrimSpace(addr)
|
addr = strings.TrimSpace(addr)
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
addr = fmt.Sprintf("unix://%s", defaultUnixAddr)
|
if defaultAddr == defaultTLSHost {
|
||||||
|
return defaultTLSHost, nil
|
||||||
|
}
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return fmt.Sprintf("unix://%s", defaultUnixAddr), nil
|
||||||
|
}
|
||||||
|
return defaultTCPAddr, nil
|
||||||
}
|
}
|
||||||
addrParts := strings.Split(addr, "://")
|
addrParts := strings.Split(addr, "://")
|
||||||
if len(addrParts) == 1 {
|
if len(addrParts) == 1 {
|
||||||
|
|
@ -29,6 +45,10 @@ func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseUnixAddr parses and validates that the specified address is a valid UNIX
|
||||||
|
// socket address. It returns a formatted UNIX socket address, either using the
|
||||||
|
// address parsed from addr, or the contents of defaultAddr if addr is a blank
|
||||||
|
// string.
|
||||||
func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
|
func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
|
||||||
addr = strings.TrimPrefix(addr, "unix://")
|
addr = strings.TrimPrefix(addr, "unix://")
|
||||||
if strings.Contains(addr, "://") {
|
if strings.Contains(addr, "://") {
|
||||||
|
|
@ -40,29 +60,61 @@ func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
|
||||||
return fmt.Sprintf("unix://%s", addr), nil
|
return fmt.Sprintf("unix://%s", addr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseTCPAddr(addr string, defaultAddr string) (string, error) {
|
// ParseTCPAddr parses and validates that the specified address is a valid TCP
|
||||||
addr = strings.TrimPrefix(addr, "tcp://")
|
// address. It returns a formatted TCP address, either using the address parsed
|
||||||
|
// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
|
||||||
|
// tryAddr is expected to have already been Trim()'d
|
||||||
|
// defaultAddr must be in the full `tcp://host:port` form
|
||||||
|
func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
|
||||||
|
if tryAddr == "" || tryAddr == "tcp://" {
|
||||||
|
return defaultAddr, nil
|
||||||
|
}
|
||||||
|
addr := strings.TrimPrefix(tryAddr, "tcp://")
|
||||||
if strings.Contains(addr, "://") || addr == "" {
|
if strings.Contains(addr, "://") || addr == "" {
|
||||||
return "", fmt.Errorf("Invalid proto, expected tcp: %s", addr)
|
return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostParts := strings.Split(addr, ":")
|
defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
|
||||||
if len(hostParts) != 2 {
|
defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
|
||||||
return "", fmt.Errorf("Invalid bind address format: %s", addr)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
host := hostParts[0]
|
// url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but
|
||||||
|
// not 1.4. See https://github.com/golang/go/issues/12200 and
|
||||||
|
// https://github.com/golang/go/issues/6530.
|
||||||
|
if strings.HasSuffix(addr, "]:") {
|
||||||
|
addr += defaultPort
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse("tcp://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(u.Host)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||||
|
}
|
||||||
|
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = defaultAddr
|
host = defaultHost
|
||||||
|
}
|
||||||
|
if port == "" {
|
||||||
|
port = defaultPort
|
||||||
|
}
|
||||||
|
p, err := strconv.Atoi(port)
|
||||||
|
if err != nil && p == 0 {
|
||||||
|
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := strconv.Atoi(hostParts[1])
|
if net.ParseIP(host).To4() == nil && strings.Contains(host, ":") {
|
||||||
if err != nil && p == 0 {
|
// This is either an ipv6 address
|
||||||
return "", fmt.Errorf("Invalid bind address format: %s", addr)
|
host = "[" + host + "]"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("tcp://%s:%d", host, p), nil
|
return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a repos name and returns the right reposName + tag|digest
|
// ParseRepositoryTag gets a repos name and returns the right reposName + tag|digest
|
||||||
// The tag can be confusing because of a port in a repository name.
|
// The tag can be confusing because of a port in a repository name.
|
||||||
// Ex: localhost.localdomain:5000/samalba/hipache:latest
|
// Ex: localhost.localdomain:5000/samalba/hipache:latest
|
||||||
// Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
|
// Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
|
||||||
|
|
@ -82,6 +134,8 @@ func ParseRepositoryTag(repos string) (string, string) {
|
||||||
return repos, ""
|
return repos, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PartParser parses and validates the specified string (data) using the specified template
|
||||||
|
// e.g. ip:public:private -> 192.168.0.1:80:8000
|
||||||
func PartParser(template, data string) (map[string]string, error) {
|
func PartParser(template, data string) (map[string]string, error) {
|
||||||
// ip:public:private
|
// ip:public:private
|
||||||
var (
|
var (
|
||||||
|
|
@ -90,7 +144,7 @@ func PartParser(template, data string) (map[string]string, error) {
|
||||||
out = make(map[string]string, len(templateParts))
|
out = make(map[string]string, len(templateParts))
|
||||||
)
|
)
|
||||||
if len(parts) != len(templateParts) {
|
if len(parts) != len(templateParts) {
|
||||||
return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template)
|
return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, t := range templateParts {
|
for i, t := range templateParts {
|
||||||
|
|
@ -103,6 +157,7 @@ func PartParser(template, data string) (map[string]string, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
|
||||||
func ParseKeyValueOpt(opt string) (string, string, error) {
|
func ParseKeyValueOpt(opt string) (string, string, error) {
|
||||||
parts := strings.SplitN(opt, "=", 2)
|
parts := strings.SplitN(opt, "=", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
|
|
@ -111,6 +166,7 @@ func ParseKeyValueOpt(opt string) (string, string, error) {
|
||||||
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
|
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
|
||||||
func ParsePortRange(ports string) (uint64, uint64, error) {
|
func ParsePortRange(ports string) (uint64, uint64, error) {
|
||||||
if ports == "" {
|
if ports == "" {
|
||||||
return 0, 0, fmt.Errorf("Empty string specified for ports.")
|
return 0, 0, fmt.Errorf("Empty string specified for ports.")
|
||||||
|
|
@ -135,3 +191,75 @@ func ParsePortRange(ports string) (uint64, uint64, error) {
|
||||||
}
|
}
|
||||||
return start, end, nil
|
return start, end, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseLink parses and validates the specified string as a link format (name:alias)
|
||||||
|
func ParseLink(val string) (string, string, error) {
|
||||||
|
if val == "" {
|
||||||
|
return "", "", fmt.Errorf("empty string specified for links")
|
||||||
|
}
|
||||||
|
arr := strings.Split(val, ":")
|
||||||
|
if len(arr) > 2 {
|
||||||
|
return "", "", fmt.Errorf("bad format for links: %s", val)
|
||||||
|
}
|
||||||
|
if len(arr) == 1 {
|
||||||
|
return val, val, nil
|
||||||
|
}
|
||||||
|
// This is kept because we can actually get an HostConfig with links
|
||||||
|
// from an already created container and the format is not `foo:bar`
|
||||||
|
// but `/foo:/c1/bar`
|
||||||
|
if strings.HasPrefix(arr[0], "/") {
|
||||||
|
_, alias := path.Split(arr[1])
|
||||||
|
return arr[0][1:], alias, nil
|
||||||
|
}
|
||||||
|
return arr[0], arr[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUintList parses and validates the specified string as the value
|
||||||
|
// found in some cgroup file (e.g. `cpuset.cpus`, `cpuset.mems`), which could be
|
||||||
|
// one of the formats below. Note that duplicates are actually allowed in the
|
||||||
|
// input string. It returns a `map[int]bool` with available elements from `val`
|
||||||
|
// set to `true`.
|
||||||
|
// Supported formats:
|
||||||
|
// 7
|
||||||
|
// 1-6
|
||||||
|
// 0,3-4,7,8-10
|
||||||
|
// 0-0,0,1-7
|
||||||
|
// 03,1-3 <- this is gonna get parsed as [1,2,3]
|
||||||
|
// 3,2,1
|
||||||
|
// 0-2,3,1
|
||||||
|
func ParseUintList(val string) (map[int]bool, error) {
|
||||||
|
if val == "" {
|
||||||
|
return map[int]bool{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
availableInts := make(map[int]bool)
|
||||||
|
split := strings.Split(val, ",")
|
||||||
|
errInvalidFormat := fmt.Errorf("invalid format: %s", val)
|
||||||
|
|
||||||
|
for _, r := range split {
|
||||||
|
if !strings.Contains(r, "-") {
|
||||||
|
v, err := strconv.Atoi(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errInvalidFormat
|
||||||
|
}
|
||||||
|
availableInts[v] = true
|
||||||
|
} else {
|
||||||
|
split := strings.SplitN(r, "-", 2)
|
||||||
|
min, err := strconv.Atoi(split[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errInvalidFormat
|
||||||
|
}
|
||||||
|
max, err := strconv.Atoi(split[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errInvalidFormat
|
||||||
|
}
|
||||||
|
if max < min {
|
||||||
|
return nil, errInvalidFormat
|
||||||
|
}
|
||||||
|
for i := min; i <= max; i++ {
|
||||||
|
availableInts[i] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableInts, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
package parsers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseHost(t *testing.T) {
|
|
||||||
var (
|
|
||||||
defaultHttpHost = "127.0.0.1"
|
|
||||||
defaultUnix = "/var/run/docker.sock"
|
|
||||||
)
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.0"); err == nil {
|
|
||||||
t.Errorf("tcp 0.0.0.0 address expected error return, but err == nil, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://"); err == nil {
|
|
||||||
t.Errorf("default tcp:// address expected error return, but err == nil, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.1:5555"); err != nil || addr != "tcp://0.0.0.1:5555" {
|
|
||||||
t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, ":6666"); err != nil || addr != "tcp://127.0.0.1:6666" {
|
|
||||||
t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" {
|
|
||||||
t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" {
|
|
||||||
t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" {
|
|
||||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix://"); err != nil || addr != "unix:///var/run/docker.sock" {
|
|
||||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1"); err == nil {
|
|
||||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
|
||||||
}
|
|
||||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1:2375"); err == nil {
|
|
||||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseRepositoryTag(t *testing.T) {
|
|
||||||
if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, digest := ParseRepositoryTag("root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "root" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
|
||||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "root", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
|
||||||
}
|
|
||||||
if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, digest := ParseRepositoryTag("user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "user/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
|
||||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "user/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
|
||||||
}
|
|
||||||
if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" {
|
|
||||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag)
|
|
||||||
}
|
|
||||||
if repo, digest := ParseRepositoryTag("url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "url:5000/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
|
||||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "url:5000/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortMapping(t *testing.T) {
|
|
||||||
data, err := PartParser("ip:public:private", "192.168.1.1:80:8080")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) != 3 {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if data["ip"] != "192.168.1.1" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if data["public"] != "80" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if data["private"] != "8080" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortRange(t *testing.T) {
|
|
||||||
if start, end, err := ParsePortRange("8000-8080"); err != nil || start != 8000 || end != 8080 {
|
|
||||||
t.Fatalf("Error: %s or Expecting {start,end} values {8000,8080} but found {%d,%d}.", err, start, end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortRangeIncorrectRange(t *testing.T) {
|
|
||||||
if _, _, err := ParsePortRange("9000-8080"); err == nil || !strings.Contains(err.Error(), "Invalid range specified for the Port") {
|
|
||||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortRangeIncorrectEndRange(t *testing.T) {
|
|
||||||
if _, _, err := ParsePortRange("8000-a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
|
||||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, _, err := ParsePortRange("8000-30a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
|
||||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePortRangeIncorrectStartRange(t *testing.T) {
|
|
||||||
if _, _, err := ParsePortRange("a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
|
||||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, _, err := ParsePortRange("30a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
|
||||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
71
Godeps/_workspace/src/github.com/docker/docker/pkg/random/random.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/docker/docker/pkg/random/random.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package random
|
||||||
|
|
||||||
|
import (
|
||||||
|
cryptorand "crypto/rand"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rand is a global *rand.Rand instance, which initilized with NewSource() source.
|
||||||
|
var Rand = rand.New(NewSource())
|
||||||
|
|
||||||
|
// Reader is a global, shared instance of a pseudorandom bytes generator.
|
||||||
|
// It doesn't consume entropy.
|
||||||
|
var Reader io.Reader = &reader{rnd: Rand}
|
||||||
|
|
||||||
|
// copypaste from standard math/rand
|
||||||
|
type lockedSource struct {
|
||||||
|
lk sync.Mutex
|
||||||
|
src rand.Source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lockedSource) Int63() (n int64) {
|
||||||
|
r.lk.Lock()
|
||||||
|
n = r.src.Int63()
|
||||||
|
r.lk.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *lockedSource) Seed(seed int64) {
|
||||||
|
r.lk.Lock()
|
||||||
|
r.src.Seed(seed)
|
||||||
|
r.lk.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSource returns math/rand.Source safe for concurrent use and initialized
|
||||||
|
// with current unix-nano timestamp
|
||||||
|
func NewSource() rand.Source {
|
||||||
|
var seed int64
|
||||||
|
if cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)); err != nil {
|
||||||
|
// This should not happen, but worst-case fallback to time-based seed.
|
||||||
|
seed = time.Now().UnixNano()
|
||||||
|
} else {
|
||||||
|
seed = cryptoseed.Int64()
|
||||||
|
}
|
||||||
|
return &lockedSource{
|
||||||
|
src: rand.NewSource(seed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
rnd *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) Read(b []byte) (int, error) {
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
val := r.rnd.Int63()
|
||||||
|
for val > 0 {
|
||||||
|
b[i] = byte(val)
|
||||||
|
i++
|
||||||
|
if i == len(b) {
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
val >>= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,67 @@
|
||||||
|
// Package stringid provides helper functions for dealing with string identifiers
|
||||||
package stringid
|
package stringid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/random"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const shortLen = 12
|
||||||
|
|
||||||
|
var validShortID = regexp.MustCompile("^[a-z0-9]{12}$")
|
||||||
|
|
||||||
|
// IsShortID determines if an arbitrary string *looks like* a short ID.
|
||||||
|
func IsShortID(id string) bool {
|
||||||
|
return validShortID.MatchString(id)
|
||||||
|
}
|
||||||
|
|
||||||
// TruncateID returns a shorthand version of a string identifier for convenience.
|
// TruncateID returns a shorthand version of a string identifier for convenience.
|
||||||
// A collision with other shorthands is very unlikely, but possible.
|
// 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
|
// 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.
|
// will need to use a langer prefix, or the full-length Id.
|
||||||
func TruncateID(id string) string {
|
func TruncateID(id string) string {
|
||||||
shortLen := 12
|
trimTo := shortLen
|
||||||
if len(id) < shortLen {
|
if len(id) < shortLen {
|
||||||
shortLen = len(id)
|
trimTo = len(id)
|
||||||
}
|
}
|
||||||
return id[:shortLen]
|
return id[:trimTo]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRandomID returns an unique id
|
func generateID(crypto bool) string {
|
||||||
func GenerateRandomID() string {
|
b := make([]byte, 32)
|
||||||
|
r := random.Reader
|
||||||
|
if crypto {
|
||||||
|
r = rand.Reader
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
id := make([]byte, 32)
|
if _, err := io.ReadFull(r, b); err != nil {
|
||||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
|
||||||
panic(err) // This shouldn't happen
|
panic(err) // This shouldn't happen
|
||||||
}
|
}
|
||||||
value := hex.EncodeToString(id)
|
id := hex.EncodeToString(b)
|
||||||
// if we try to parse the truncated for as an int and we don't have
|
// 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
|
// an error then the value is all numberic and causes issues when
|
||||||
// used as a hostname. ref #3869
|
// used as a hostname. ref #3869
|
||||||
if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
|
if _, err := strconv.ParseInt(TruncateID(id), 10, 64); err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return value
|
return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateRandomID returns an unique id.
|
||||||
|
func GenerateRandomID() string {
|
||||||
|
return generateID(true)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateNonCryptoID generates unique id without using cryptographically
|
||||||
|
// secure sources of random.
|
||||||
|
// It helps you to save entropy.
|
||||||
|
func GenerateNonCryptoID() string {
|
||||||
|
return generateID(false)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
package stringid
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestGenerateRandomID(t *testing.T) {
|
|
||||||
id := GenerateRandomID()
|
|
||||||
|
|
||||||
if len(id) != 64 {
|
|
||||||
t.Fatalf("Id returned is incorrect: %s", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortenId(t *testing.T) {
|
|
||||||
id := GenerateRandomID()
|
|
||||||
truncID := TruncateID(id)
|
|
||||||
if len(truncID) != 12 {
|
|
||||||
t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortenIdEmpty(t *testing.T) {
|
|
||||||
id := ""
|
|
||||||
truncID := TruncateID(id)
|
|
||||||
if len(truncID) > len(id) {
|
|
||||||
t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortenIdInvalid(t *testing.T) {
|
|
||||||
id := "1234"
|
|
||||||
truncID := TruncateID(id)
|
|
||||||
if len(truncID) != len(id) {
|
|
||||||
t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -47,8 +47,9 @@ var clientCipherSuites = []uint16{
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For use by code which already has a crypto/tls options struct but wants to
|
// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
|
||||||
// use a commonly accepted set of TLS cipher suites, with known weak algorithms removed
|
// options struct but wants to use a commonly accepted set of TLS cipher suites, with
|
||||||
|
// known weak algorithms removed.
|
||||||
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
|
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)
|
||||||
|
|
||||||
// ServerDefault is a secure-enough TLS configuration for the server TLS configuration.
|
// ServerDefault is a secure-enough TLS configuration for the server TLS configuration.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Package units provides helper function to parse and print size and time units
|
||||||
|
// in human-readable format.
|
||||||
package units
|
package units
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -6,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// HumanDuration returns a human-readable approximation of a duration
|
// HumanDuration returns a human-readable approximation of a duration
|
||||||
// (eg. "About a minute", "4 hours ago", etc.)
|
// (eg. "About a minute", "4 hours ago", etc.).
|
||||||
func HumanDuration(d time.Duration) string {
|
func HumanDuration(d time.Duration) string {
|
||||||
if seconds := int(d.Seconds()); seconds < 1 {
|
if seconds := int(d.Seconds()); seconds < 1 {
|
||||||
return "Less than a second"
|
return "Less than a second"
|
||||||
|
|
@ -26,7 +28,6 @@ func HumanDuration(d time.Duration) string {
|
||||||
return fmt.Sprintf("%d weeks", hours/24/7)
|
return fmt.Sprintf("%d weeks", hours/24/7)
|
||||||
} else if hours < 24*365*2 {
|
} else if hours < 24*365*2 {
|
||||||
return fmt.Sprintf("%d months", hours/24/30)
|
return fmt.Sprintf("%d months", hours/24/30)
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("%d years", hours/24/365)
|
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHumanDuration(t *testing.T) {
|
|
||||||
// Useful duration abstractions
|
|
||||||
day := 24 * time.Hour
|
|
||||||
week := 7 * day
|
|
||||||
month := 30 * day
|
|
||||||
year := 365 * day
|
|
||||||
|
|
||||||
assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond))
|
|
||||||
assertEquals(t, "47 seconds", HumanDuration(47*time.Second))
|
|
||||||
assertEquals(t, "About a minute", HumanDuration(1*time.Minute))
|
|
||||||
assertEquals(t, "3 minutes", HumanDuration(3*time.Minute))
|
|
||||||
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute))
|
|
||||||
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second))
|
|
||||||
assertEquals(t, "About an hour", HumanDuration(1*time.Hour))
|
|
||||||
assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute))
|
|
||||||
assertEquals(t, "3 hours", HumanDuration(3*time.Hour))
|
|
||||||
assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute))
|
|
||||||
assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute))
|
|
||||||
assertEquals(t, "24 hours", HumanDuration(24*time.Hour))
|
|
||||||
assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour))
|
|
||||||
assertEquals(t, "2 days", HumanDuration(2*day))
|
|
||||||
assertEquals(t, "7 days", HumanDuration(7*day))
|
|
||||||
assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour))
|
|
||||||
assertEquals(t, "2 weeks", HumanDuration(2*week))
|
|
||||||
assertEquals(t, "2 weeks", HumanDuration(2*week+4*day))
|
|
||||||
assertEquals(t, "3 weeks", HumanDuration(3*week))
|
|
||||||
assertEquals(t, "4 weeks", HumanDuration(4*week))
|
|
||||||
assertEquals(t, "4 weeks", HumanDuration(4*week+3*day))
|
|
||||||
assertEquals(t, "4 weeks", HumanDuration(1*month))
|
|
||||||
assertEquals(t, "6 weeks", HumanDuration(1*month+2*week))
|
|
||||||
assertEquals(t, "8 weeks", HumanDuration(2*month))
|
|
||||||
assertEquals(t, "3 months", HumanDuration(3*month+1*week))
|
|
||||||
assertEquals(t, "5 months", HumanDuration(5*month+2*week))
|
|
||||||
assertEquals(t, "13 months", HumanDuration(13*month))
|
|
||||||
assertEquals(t, "23 months", HumanDuration(23*month))
|
|
||||||
assertEquals(t, "24 months", HumanDuration(24*month))
|
|
||||||
assertEquals(t, "2 years", HumanDuration(24*month+2*week))
|
|
||||||
assertEquals(t, "3 years", HumanDuration(3*year+2*month))
|
|
||||||
}
|
|
||||||
|
|
@ -38,7 +38,7 @@ var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||||
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
||||||
|
|
||||||
// CustomSize returns a human-readable approximation of a size
|
// CustomSize returns a human-readable approximation of a size
|
||||||
// using custom format
|
// using custom format.
|
||||||
func CustomSize(format string, size float64, base float64, _map []string) string {
|
func CustomSize(format string, size float64, base float64, _map []string) string {
|
||||||
i := 0
|
i := 0
|
||||||
for size >= base {
|
for size >= base {
|
||||||
|
|
@ -49,17 +49,19 @@ func CustomSize(format string, size float64, base float64, _map []string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HumanSize returns a human-readable approximation of a size
|
// HumanSize returns a human-readable approximation of a size
|
||||||
// using SI standard (eg. "44kB", "17MB")
|
// capped at 4 valid numbers (eg. "2.746 MB", "796 KB").
|
||||||
func HumanSize(size float64) string {
|
func HumanSize(size float64) string {
|
||||||
return CustomSize("%.4g %s", float64(size), 1000.0, decimapAbbrs)
|
return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BytesSize returns a human-readable size in bytes, kibibytes,
|
||||||
|
// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
|
||||||
func BytesSize(size float64) string {
|
func BytesSize(size float64) string {
|
||||||
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
|
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromHumanSize returns an integer from a human-readable specification of a
|
// FromHumanSize returns an integer from a human-readable specification of a
|
||||||
// size using SI standard (eg. "44kB", "17MB")
|
// size using SI standard (eg. "44kB", "17MB").
|
||||||
func FromHumanSize(size string) (int64, error) {
|
func FromHumanSize(size string) (int64, error) {
|
||||||
return parseSize(size, decimalMap)
|
return parseSize(size, decimalMap)
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +74,7 @@ func RAMInBytes(size string) (int64, error) {
|
||||||
return parseSize(size, binaryMap)
|
return parseSize(size, binaryMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the human-readable size string into the amount it represents
|
// Parses the human-readable size string into the amount it represents.
|
||||||
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
|
||||||
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
matches := sizeRegex.FindStringSubmatch(sizeStr)
|
||||||
if len(matches) != 3 {
|
if len(matches) != 3 {
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBytesSize(t *testing.T) {
|
|
||||||
assertEquals(t, "1 KiB", BytesSize(1024))
|
|
||||||
assertEquals(t, "1 MiB", BytesSize(1024*1024))
|
|
||||||
assertEquals(t, "1 MiB", BytesSize(1048576))
|
|
||||||
assertEquals(t, "2 MiB", BytesSize(2*MiB))
|
|
||||||
assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB))
|
|
||||||
assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB))
|
|
||||||
assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHumanSize(t *testing.T) {
|
|
||||||
assertEquals(t, "1 kB", HumanSize(1000))
|
|
||||||
assertEquals(t, "1.024 kB", HumanSize(1024))
|
|
||||||
assertEquals(t, "1 MB", HumanSize(1000000))
|
|
||||||
assertEquals(t, "1.049 MB", HumanSize(1048576))
|
|
||||||
assertEquals(t, "2 MB", HumanSize(2*MB))
|
|
||||||
assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB)))
|
|
||||||
assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB)))
|
|
||||||
assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromHumanSize(t *testing.T) {
|
|
||||||
assertSuccessEquals(t, 32, FromHumanSize, "32")
|
|
||||||
assertSuccessEquals(t, 32, FromHumanSize, "32b")
|
|
||||||
assertSuccessEquals(t, 32, FromHumanSize, "32B")
|
|
||||||
assertSuccessEquals(t, 32*KB, FromHumanSize, "32k")
|
|
||||||
assertSuccessEquals(t, 32*KB, FromHumanSize, "32K")
|
|
||||||
assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb")
|
|
||||||
assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb")
|
|
||||||
assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb")
|
|
||||||
assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb")
|
|
||||||
assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb")
|
|
||||||
assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb")
|
|
||||||
|
|
||||||
assertError(t, FromHumanSize, "")
|
|
||||||
assertError(t, FromHumanSize, "hello")
|
|
||||||
assertError(t, FromHumanSize, "-32")
|
|
||||||
assertError(t, FromHumanSize, "32.3")
|
|
||||||
assertError(t, FromHumanSize, " 32 ")
|
|
||||||
assertError(t, FromHumanSize, "32.3Kb")
|
|
||||||
assertError(t, FromHumanSize, "32 mb")
|
|
||||||
assertError(t, FromHumanSize, "32m b")
|
|
||||||
assertError(t, FromHumanSize, "32bm")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRAMInBytes(t *testing.T) {
|
|
||||||
assertSuccessEquals(t, 32, RAMInBytes, "32")
|
|
||||||
assertSuccessEquals(t, 32, RAMInBytes, "32b")
|
|
||||||
assertSuccessEquals(t, 32, RAMInBytes, "32B")
|
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k")
|
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K")
|
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb")
|
|
||||||
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb")
|
|
||||||
assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb")
|
|
||||||
assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb")
|
|
||||||
assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb")
|
|
||||||
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb")
|
|
||||||
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB")
|
|
||||||
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P")
|
|
||||||
|
|
||||||
assertError(t, RAMInBytes, "")
|
|
||||||
assertError(t, RAMInBytes, "hello")
|
|
||||||
assertError(t, RAMInBytes, "-32")
|
|
||||||
assertError(t, RAMInBytes, "32.3")
|
|
||||||
assertError(t, RAMInBytes, " 32 ")
|
|
||||||
assertError(t, RAMInBytes, "32.3Kb")
|
|
||||||
assertError(t, RAMInBytes, "32 mb")
|
|
||||||
assertError(t, RAMInBytes, "32m b")
|
|
||||||
assertError(t, RAMInBytes, "32bm")
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertEquals(t *testing.T, expected, actual interface{}) {
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected '%v' but got '%v'", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func that maps to the parse function signatures as testing abstraction
|
|
||||||
type parseFn func(string) (int64, error)
|
|
||||||
|
|
||||||
// Define 'String()' for pretty-print
|
|
||||||
func (fn parseFn) String() string {
|
|
||||||
fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
|
||||||
return fnName[strings.LastIndex(fnName, ".")+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) {
|
|
||||||
res, err := fn(arg)
|
|
||||||
if err != nil || res != expected {
|
|
||||||
t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertError(t *testing.T, fn parseFn, arg string) {
|
|
||||||
res, err := fn(arg)
|
|
||||||
if err == nil && res != -1 {
|
|
||||||
t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func assertVersion(t *testing.T, a, b string, result int) {
|
|
||||||
if r := Version(a).compareTo(Version(b)); r != result {
|
|
||||||
t.Fatalf("Unexpected version comparison result. Found %d, expected %d", r, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareVersion(t *testing.T) {
|
|
||||||
assertVersion(t, "1.12", "1.12", 0)
|
|
||||||
assertVersion(t, "1.0.0", "1", 0)
|
|
||||||
assertVersion(t, "1", "1.0.0", 0)
|
|
||||||
assertVersion(t, "1.05.00.0156", "1.0.221.9289", 1)
|
|
||||||
assertVersion(t, "1", "1.0.1", -1)
|
|
||||||
assertVersion(t, "1.0.1", "1", 1)
|
|
||||||
assertVersion(t, "1.0.1", "1.0.2", -1)
|
|
||||||
assertVersion(t, "1.0.2", "1.0.3", -1)
|
|
||||||
assertVersion(t, "1.0.3", "1.1", -1)
|
|
||||||
assertVersion(t, "1.1", "1.1.1", -1)
|
|
||||||
assertVersion(t, "1.1.1", "1.1.2", -1)
|
|
||||||
assertVersion(t, "1.1.2", "1.2", -1)
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue