2013-05-14 18:37:35 -04:00
package utils
import (
"bytes"
2014-03-07 20:36:47 -05:00
"crypto/rand"
2013-10-18 01:40:41 -04:00
"crypto/sha1"
2013-05-14 18:37:35 -04:00
"crypto/sha256"
"encoding/hex"
2013-05-25 11:51:26 -04:00
"encoding/json"
2014-01-20 22:20:33 -05:00
"errors"
2013-05-14 18:37:35 -04:00
"fmt"
"index/suffixarray"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
2013-08-17 23:49:33 -04:00
"regexp"
2013-11-07 15:19:24 -05:00
"runtime"
2013-05-15 20:40:47 -04:00
"strconv"
2013-05-14 18:37:35 -04:00
"strings"
"sync"
"time"
2014-05-05 18:51:32 -04:00
"github.com/dotcloud/docker/dockerversion"
2013-05-14 18:37:35 -04:00
)
2014-03-13 12:03:09 -04:00
type KeyValuePair struct {
Key string
Value string
}
2013-10-27 03:13:45 -04:00
// A common interface to access the Fatal method of
// both testing.B and testing.T.
type Fataler interface {
Fatal ( args ... interface { } )
}
2013-05-14 18:37:35 -04:00
// Go is a basic promise implementation: it wraps calls a function in a goroutine,
// and returns a channel which will later return the function's return value.
func Go ( f func ( ) error ) chan error {
2014-02-28 19:35:43 -05:00
ch := make ( chan error , 1 )
2013-05-14 18:37:35 -04:00
go func ( ) {
ch <- f ( )
} ( )
return ch
}
// Request a given URL and return an io.Reader
2014-01-08 17:20:30 -05:00
func Download ( url string ) ( resp * http . Response , err error ) {
2013-05-14 18:37:35 -04:00
if resp , err = http . Get ( url ) ; err != nil {
return nil , err
}
if resp . StatusCode >= 400 {
2014-01-08 17:20:30 -05:00
return nil , fmt . Errorf ( "Got HTTP status code >= 400: %s" , resp . Status )
2013-05-14 18:37:35 -04:00
}
return resp , nil
}
2013-10-08 03:54:47 -04:00
func logf ( level string , format string , a ... interface { } ) {
// Retrieve the stack infos
_ , file , line , ok := runtime . Caller ( 2 )
if ! ok {
file = "<unknown>"
line = - 1
} else {
file = file [ strings . LastIndex ( file , "/" ) + 1 : ]
}
fmt . Fprintf ( os . Stderr , fmt . Sprintf ( "[%s] %s:%d %s\n" , level , file , line , format ) , a ... )
}
2013-05-14 18:37:35 -04:00
// Debug function, if the debug flag is set, then display. Do nothing otherwise
// If Docker is in damon mode, also send the debug info on the socket
func Debugf ( format string , a ... interface { } ) {
if os . Getenv ( "DEBUG" ) != "" {
2013-10-08 03:54:47 -04:00
logf ( "debug" , format , a ... )
2013-05-14 18:37:35 -04:00
}
}
2013-10-08 03:54:47 -04:00
func Errorf ( format string , a ... interface { } ) {
logf ( "error" , format , a ... )
}
2013-05-14 18:37:35 -04:00
// HumanDuration returns a human-readable approximation of a duration
// (eg. "About a minute", "4 hours ago", etc.)
func HumanDuration ( d time . Duration ) string {
if seconds := int ( d . Seconds ( ) ) ; seconds < 1 {
return "Less than a second"
} else if seconds < 60 {
return fmt . Sprintf ( "%d seconds" , seconds )
} else if minutes := int ( d . Minutes ( ) ) ; minutes == 1 {
return "About a minute"
} else if minutes < 60 {
return fmt . Sprintf ( "%d minutes" , minutes )
} else if hours := int ( d . Hours ( ) ) ; hours == 1 {
return "About an hour"
} else if hours < 48 {
return fmt . Sprintf ( "%d hours" , hours )
} else if hours < 24 * 7 * 2 {
return fmt . Sprintf ( "%d days" , hours / 24 )
} else if hours < 24 * 30 * 3 {
return fmt . Sprintf ( "%d weeks" , hours / 24 / 7 )
} else if hours < 24 * 365 * 2 {
return fmt . Sprintf ( "%d months" , hours / 24 / 30 )
}
2013-06-27 00:40:13 -04:00
return fmt . Sprintf ( "%f years" , d . Hours ( ) / 24 / 365 )
2013-05-14 18:37:35 -04:00
}
2013-05-23 05:35:20 -04:00
// HumanSize returns a human-readable approximation of a size
2013-05-23 06:29:09 -04:00
// using SI standard (eg. "44kB", "17MB")
2013-05-22 09:41:29 -04:00
func HumanSize ( size int64 ) string {
i := 0
var sizef float64
sizef = float64 ( size )
units := [ ] string { "B" , "kB" , "MB" , "GB" , "TB" , "PB" , "EB" , "ZB" , "YB" }
2013-05-23 06:29:09 -04:00
for sizef >= 1000.0 {
sizef = sizef / 1000.0
2013-05-22 09:41:29 -04:00
i ++
}
2013-07-12 14:08:45 -04:00
return fmt . Sprintf ( "%.4g %s" , sizef , units [ i ] )
2013-05-22 09:41:29 -04:00
}
2013-10-03 14:23:29 -04:00
// Parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes or gibibytes, and returns the
// number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes ( size string ) ( bytes int64 , err error ) {
re , error := regexp . Compile ( "^(\\d+)([kKmMgG])?[bB]?$" )
2013-11-11 15:28:20 -05:00
if error != nil {
return - 1 , error
}
2013-10-03 14:23:29 -04:00
matches := re . FindStringSubmatch ( size )
if len ( matches ) != 3 {
return - 1 , fmt . Errorf ( "Invalid size: '%s'" , size )
}
memLimit , error := strconv . ParseInt ( matches [ 1 ] , 10 , 0 )
2013-11-11 15:28:20 -05:00
if error != nil {
return - 1 , error
}
2013-10-03 14:23:29 -04:00
unit := strings . ToLower ( matches [ 2 ] )
if unit == "k" {
memLimit *= 1024
} else if unit == "m" {
2013-11-11 15:28:20 -05:00
memLimit *= 1024 * 1024
2013-10-03 14:23:29 -04:00
} else if unit == "g" {
2013-11-11 15:28:20 -05:00
memLimit *= 1024 * 1024 * 1024
2013-10-03 14:23:29 -04:00
}
return memLimit , nil
}
2013-05-14 18:37:35 -04:00
func Trunc ( s string , maxlen int ) string {
if len ( s ) <= maxlen {
return s
}
return s [ : maxlen ]
}
2013-12-05 04:10:41 -05:00
// Figure out the absolute path of our own binary (if it's still around).
2013-05-14 18:37:35 -04:00
func SelfPath ( ) string {
path , err := exec . LookPath ( os . Args [ 0 ] )
if err != nil {
2013-12-05 04:10:41 -05:00
if os . IsNotExist ( err ) {
return ""
}
if execErr , ok := err . ( * exec . Error ) ; ok && os . IsNotExist ( execErr . Err ) {
return ""
}
2013-05-14 18:37:35 -04:00
panic ( err )
}
path , err = filepath . Abs ( path )
if err != nil {
2013-12-05 04:10:41 -05:00
if os . IsNotExist ( err ) {
return ""
}
2013-05-14 18:37:35 -04:00
panic ( err )
}
return path
}
2013-10-18 01:40:41 -04:00
func dockerInitSha1 ( target string ) string {
f , err := os . Open ( target )
if err != nil {
return ""
}
defer f . Close ( )
h := sha1 . New ( )
_ , err = io . Copy ( h , f )
if err != nil {
return ""
}
return hex . EncodeToString ( h . Sum ( nil ) )
}
func isValidDockerInitPath ( target string , selfPath string ) bool { // target and selfPath should be absolute (InitPath and SelfPath already do this)
2013-12-05 04:10:41 -05:00
if target == "" {
return false
}
2014-02-11 19:26:54 -05:00
if dockerversion . IAMSTATIC {
2013-12-05 04:10:41 -05:00
if selfPath == "" {
return false
}
2013-10-18 01:40:41 -04:00
if target == selfPath {
return true
}
targetFileInfo , err := os . Lstat ( target )
if err != nil {
return false
}
selfPathFileInfo , err := os . Lstat ( selfPath )
if err != nil {
return false
}
return os . SameFile ( targetFileInfo , selfPathFileInfo )
}
2014-02-11 19:26:54 -05:00
return dockerversion . INITSHA1 != "" && dockerInitSha1 ( target ) == dockerversion . INITSHA1
2013-10-18 01:40:41 -04:00
}
// Figure out the path of our dockerinit (which may be SelfPath())
2013-11-25 17:42:22 -05:00
func DockerInitPath ( localCopy string ) string {
2013-10-18 01:40:41 -04:00
selfPath := SelfPath ( )
if isValidDockerInitPath ( selfPath , selfPath ) {
// if we're valid, don't bother checking anything else
return selfPath
}
var possibleInits = [ ] string {
2013-11-25 17:42:22 -05:00
localCopy ,
2014-02-11 19:26:54 -05:00
dockerversion . INITPATH ,
2013-10-18 01:40:41 -04:00
filepath . Join ( filepath . Dir ( selfPath ) , "dockerinit" ) ,
2013-11-27 18:49:40 -05:00
// FHS 3.0 Draft: "/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec."
// http://www.linuxbase.org/betaspecs/fhs/fhs.html#usrlibexec
2013-10-18 01:40:41 -04:00
"/usr/libexec/docker/dockerinit" ,
"/usr/local/libexec/docker/dockerinit" ,
2013-11-27 18:49:40 -05:00
// FHS 2.3: "/usr/lib includes object files, libraries, and internal binaries that are not intended to be executed directly by users or shell scripts."
// http://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA
"/usr/lib/docker/dockerinit" ,
"/usr/local/lib/docker/dockerinit" ,
2013-10-18 01:40:41 -04:00
}
for _ , dockerInit := range possibleInits {
2013-12-05 04:10:41 -05:00
if dockerInit == "" {
continue
}
2013-10-18 01:40:41 -04:00
path , err := exec . LookPath ( dockerInit )
if err == nil {
path , err = filepath . Abs ( path )
if err != nil {
// LookPath already validated that this file exists and is executable (following symlinks), so how could Abs fail?
panic ( err )
}
if isValidDockerInitPath ( path , selfPath ) {
return path
}
}
}
return ""
}
2013-07-02 17:55:20 -04:00
type NopWriter struct { }
2013-05-14 18:37:35 -04:00
2013-07-02 17:55:20 -04:00
func ( * NopWriter ) Write ( buf [ ] byte ) ( int , error ) {
2013-05-14 18:37:35 -04:00
return len ( buf ) , nil
}
type nopWriteCloser struct {
io . Writer
}
func ( w * nopWriteCloser ) Close ( ) error { return nil }
func NopWriteCloser ( w io . Writer ) io . WriteCloser {
return & nopWriteCloser { w }
}
type bufReader struct {
2013-07-02 18:46:32 -04:00
sync . Mutex
2013-05-14 18:37:35 -04:00
buf * bytes . Buffer
reader io . Reader
err error
wait sync . Cond
}
func NewBufReader ( r io . Reader ) * bufReader {
reader := & bufReader {
buf : & bytes . Buffer { } ,
reader : r ,
}
2013-07-02 18:46:32 -04:00
reader . wait . L = & reader . Mutex
2013-05-14 18:37:35 -04:00
go reader . drain ( )
return reader
}
func ( r * bufReader ) drain ( ) {
buf := make ( [ ] byte , 1024 )
for {
n , err := r . reader . Read ( buf )
2013-07-02 18:46:32 -04:00
r . Lock ( )
2013-05-14 18:37:35 -04:00
if err != nil {
r . err = err
} else {
r . buf . Write ( buf [ 0 : n ] )
}
r . wait . Signal ( )
2013-07-02 18:46:32 -04:00
r . Unlock ( )
2013-05-14 18:37:35 -04:00
if err != nil {
break
}
}
}
func ( r * bufReader ) Read ( p [ ] byte ) ( n int , err error ) {
2013-07-02 18:46:32 -04:00
r . Lock ( )
defer r . Unlock ( )
2013-05-14 18:37:35 -04:00
for {
n , err = r . buf . Read ( p )
if n > 0 {
return n , err
}
if r . err != nil {
return 0 , r . err
}
r . wait . Wait ( )
}
}
func ( r * bufReader ) Close ( ) error {
closer , ok := r . reader . ( io . ReadCloser )
if ! ok {
return nil
}
return closer . Close ( )
}
type WriteBroadcaster struct {
2013-07-02 18:46:32 -04:00
sync . Mutex
2013-07-15 12:17:58 -04:00
buf * bytes . Buffer
2014-04-02 15:26:06 -04:00
streams map [ string ] ( map [ io . WriteCloser ] struct { } )
2013-05-14 18:37:35 -04:00
}
2013-07-11 13:18:28 -04:00
func ( w * WriteBroadcaster ) AddWriter ( writer io . WriteCloser , stream string ) {
2013-07-02 18:46:32 -04:00
w . Lock ( )
2014-04-02 15:26:06 -04:00
if _ , ok := w . streams [ stream ] ; ! ok {
w . streams [ stream ] = make ( map [ io . WriteCloser ] struct { } )
}
w . streams [ stream ] [ writer ] = struct { } { }
2013-07-02 18:46:32 -04:00
w . Unlock ( )
2013-05-14 18:37:35 -04:00
}
2013-07-11 13:18:28 -04:00
type JSONLog struct {
2013-07-15 12:17:58 -04:00
Log string ` json:"log,omitempty" `
Stream string ` json:"stream,omitempty" `
2013-07-11 13:18:28 -04:00
Created time . Time ` json:"time" `
}
2014-04-02 15:26:06 -04:00
func ( jl * JSONLog ) Format ( format string ) ( string , error ) {
if format == "" {
return jl . Log , nil
}
if format == "json" {
m , err := json . Marshal ( jl )
return string ( m ) , err
}
return fmt . Sprintf ( "[%s] %s" , jl . Created . Format ( format ) , jl . Log ) , nil
}
func WriteLog ( src io . Reader , dst io . WriteCloser , format string ) error {
dec := json . NewDecoder ( src )
for {
l := & JSONLog { }
if err := dec . Decode ( l ) ; err == io . EOF {
return nil
} else if err != nil {
Errorf ( "Error streaming logs: %s" , err )
return err
}
line , err := l . Format ( format )
if err != nil {
return err
}
fmt . Fprintf ( dst , "%s" , line )
}
}
type LogFormatter struct {
wc io . WriteCloser
timeFormat string
}
2013-05-14 18:37:35 -04:00
func ( w * WriteBroadcaster ) Write ( p [ ] byte ) ( n int , err error ) {
2014-04-02 15:26:06 -04:00
created := time . Now ( ) . UTC ( )
2013-07-02 18:46:32 -04:00
w . Lock ( )
defer w . Unlock ( )
2014-04-02 15:26:06 -04:00
if writers , ok := w . streams [ "" ] ; ok {
for sw := range writers {
if n , err := sw . Write ( p ) ; err != nil || n != len ( p ) {
// On error, evict the writer
delete ( writers , sw )
}
}
}
2013-07-15 12:17:58 -04:00
w . buf . Write ( p )
2014-04-02 15:26:06 -04:00
lines := [ ] string { }
for {
line , err := w . buf . ReadString ( '\n' )
if err != nil {
w . buf . Write ( [ ] byte ( line ) )
break
}
lines = append ( lines , line )
}
if len ( lines ) != 0 {
for stream , writers := range w . streams {
if stream == "" {
continue
}
var lp [ ] byte
for _ , line := range lines {
b , err := json . Marshal ( & JSONLog { Log : line , Stream : stream , Created : created } )
2013-07-11 13:18:28 -04:00
if err != nil {
2014-04-02 15:26:06 -04:00
Errorf ( "Error making JSON log line: %s" , err )
2013-07-11 13:18:28 -04:00
}
2013-07-15 12:17:58 -04:00
lp = append ( lp , b ... )
2013-08-27 15:09:26 -04:00
lp = append ( lp , '\n' )
2013-07-11 13:18:28 -04:00
}
2014-04-02 15:26:06 -04:00
for sw := range writers {
if _ , err := sw . Write ( lp ) ; err != nil {
delete ( writers , sw )
}
}
2013-05-14 18:37:35 -04:00
}
}
return len ( p ) , nil
}
func ( w * WriteBroadcaster ) CloseWriters ( ) error {
2013-07-02 18:46:32 -04:00
w . Lock ( )
defer w . Unlock ( )
2014-04-02 15:26:06 -04:00
for _ , writers := range w . streams {
for w := range writers {
w . Close ( )
}
2013-05-14 18:37:35 -04:00
}
2014-04-02 15:26:06 -04:00
w . streams = make ( map [ string ] ( map [ io . WriteCloser ] struct { } ) )
2013-05-14 18:37:35 -04:00
return nil
}
func NewWriteBroadcaster ( ) * WriteBroadcaster {
2014-04-02 15:26:06 -04:00
return & WriteBroadcaster {
streams : make ( map [ string ] ( map [ io . WriteCloser ] struct { } ) ) ,
buf : bytes . NewBuffer ( nil ) ,
}
2013-05-14 18:37:35 -04:00
}
func GetTotalUsedFds ( ) int {
if fds , err := ioutil . ReadDir ( fmt . Sprintf ( "/proc/%d/fd" , os . Getpid ( ) ) ) ; err != nil {
2013-10-08 03:54:47 -04:00
Errorf ( "Error opening /proc/%d/fd: %s" , os . Getpid ( ) , err )
2013-05-14 18:37:35 -04:00
} else {
return len ( fds )
}
return - 1
}
// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
type TruncIndex struct {
2014-01-15 12:29:46 -05:00
sync . RWMutex
2013-05-14 18:37:35 -04:00
index * suffixarray . Index
ids map [ string ] bool
bytes [ ] byte
}
2014-04-11 16:39:58 -04:00
func NewTruncIndex ( ids [ ] string ) ( idx * TruncIndex ) {
idx = & TruncIndex {
2013-05-14 18:37:35 -04:00
ids : make ( map [ string ] bool ) ,
bytes : [ ] byte { ' ' } ,
}
2014-04-11 16:39:58 -04:00
for _ , id := range ids {
idx . ids [ id ] = true
idx . bytes = append ( idx . bytes , [ ] byte ( id + " " ) ... )
}
idx . index = suffixarray . New ( idx . bytes )
return
2013-05-14 18:37:35 -04:00
}
func ( idx * TruncIndex ) Add ( id string ) error {
2014-01-15 12:29:46 -05:00
idx . Lock ( )
defer idx . Unlock ( )
2013-05-14 18:37:35 -04:00
if strings . Contains ( id , " " ) {
return fmt . Errorf ( "Illegal character: ' '" )
}
if _ , exists := idx . ids [ id ] ; exists {
return fmt . Errorf ( "Id already exists: %s" , id )
}
idx . ids [ id ] = true
idx . bytes = append ( idx . bytes , [ ] byte ( id + " " ) ... )
idx . index = suffixarray . New ( idx . bytes )
return nil
}
func ( idx * TruncIndex ) Delete ( id string ) error {
2014-01-15 12:29:46 -05:00
idx . Lock ( )
defer idx . Unlock ( )
2013-05-14 18:37:35 -04:00
if _ , exists := idx . ids [ id ] ; ! exists {
return fmt . Errorf ( "No such id: %s" , id )
}
before , after , err := idx . lookup ( id )
if err != nil {
return err
}
delete ( idx . ids , id )
idx . bytes = append ( idx . bytes [ : before ] , idx . bytes [ after : ] ... )
idx . index = suffixarray . New ( idx . bytes )
return nil
}
func ( idx * TruncIndex ) lookup ( s string ) ( int , int , error ) {
offsets := idx . index . Lookup ( [ ] byte ( " " + s ) , - 1 )
//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
if offsets == nil || len ( offsets ) == 0 || len ( offsets ) > 1 {
return - 1 , - 1 , fmt . Errorf ( "No such id: %s" , s )
}
offsetBefore := offsets [ 0 ] + 1
offsetAfter := offsetBefore + strings . Index ( string ( idx . bytes [ offsetBefore : ] ) , " " )
return offsetBefore , offsetAfter , nil
}
func ( idx * TruncIndex ) Get ( s string ) ( string , error ) {
2014-01-15 12:29:46 -05:00
idx . RLock ( )
defer idx . RUnlock ( )
2013-05-14 18:37:35 -04:00
before , after , err := idx . lookup ( s )
//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
if err != nil {
return "" , err
}
return string ( idx . bytes [ before : after ] ) , err
}
2013-06-04 14:00:22 -04:00
// TruncateID returns a shorthand version of a string identifier for convenience.
2013-05-14 18:37:35 -04:00
// A collision with other shorthands is very unlikely, but possible.
// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
// will need to use a langer prefix, or the full-length Id.
2013-06-04 14:00:22 -04:00
func TruncateID ( id string ) string {
2013-05-14 18:37:35 -04:00
shortLen := 12
if len ( id ) < shortLen {
shortLen = len ( id )
}
return id [ : shortLen ]
}
2014-03-07 20:36:47 -05:00
// GenerateRandomID returns an unique id
func GenerateRandomID ( ) string {
for {
id := make ( [ ] byte , 32 )
if _ , err := io . ReadFull ( rand . Reader , id ) ; err != nil {
panic ( err ) // This shouldn't happen
}
value := hex . EncodeToString ( id )
// if we try to parse the truncated for as an int and we don't have
// an error then the value is all numberic and causes issues when
// used as a hostname. ref #3869
if _ , err := strconv . Atoi ( TruncateID ( value ) ) ; err == nil {
continue
}
return value
}
}
func ValidateID ( id string ) error {
if id == "" {
return fmt . Errorf ( "Id can't be empty" )
}
if strings . Contains ( id , ":" ) {
return fmt . Errorf ( "Invalid character in id: ':'" )
}
return nil
}
2013-05-14 18:37:35 -04:00
// Code c/c from io.Copy() modified to handle escape sequence
func CopyEscapable ( dst io . Writer , src io . ReadCloser ) ( written int64 , err error ) {
buf := make ( [ ] byte , 32 * 1024 )
for {
nr , er := src . Read ( buf )
if nr > 0 {
// ---- Docker addition
// char 16 is C-p
if nr == 1 && buf [ 0 ] == 16 {
nr , er = src . Read ( buf )
// char 17 is C-q
if nr == 1 && buf [ 0 ] == 17 {
if err := src . Close ( ) ; err != nil {
return 0 , err
}
2013-11-28 19:57:51 -05:00
return 0 , nil
2013-05-14 18:37:35 -04:00
}
}
// ---- End of docker
nw , ew := dst . Write ( buf [ 0 : nr ] )
if nw > 0 {
written += int64 ( nw )
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io . ErrShortWrite
break
}
}
if er == io . EOF {
break
}
if er != nil {
err = er
break
}
}
return written , err
}
func HashData ( src io . Reader ) ( string , error ) {
h := sha256 . New ( )
if _ , err := io . Copy ( h , src ) ; err != nil {
return "" , err
}
return "sha256:" + hex . EncodeToString ( h . Sum ( nil ) ) , nil
}
type KernelVersionInfo struct {
Kernel int
Major int
Minor int
2014-01-21 00:03:09 -05:00
Flavor string
2013-05-14 18:37:35 -04:00
}
func ( k * KernelVersionInfo ) String ( ) string {
2014-01-21 00:03:09 -05:00
return fmt . Sprintf ( "%d.%d.%d%s" , k . Kernel , k . Major , k . Minor , k . Flavor )
2013-05-14 18:37:35 -04:00
}
// Compare two KernelVersionInfo struct.
2014-01-20 22:21:25 -05:00
// Returns -1 if a < b, 0 if a == b, 1 it a > b
2013-05-14 18:37:35 -04:00
func CompareKernelVersion ( a , b * KernelVersionInfo ) int {
if a . Kernel < b . Kernel {
return - 1
} else if a . Kernel > b . Kernel {
return 1
}
if a . Major < b . Major {
return - 1
} else if a . Major > b . Major {
return 1
}
if a . Minor < b . Minor {
return - 1
} else if a . Minor > b . Minor {
return 1
}
return 0
}
2013-05-15 20:40:47 -04:00
func GetKernelVersion ( ) ( * KernelVersionInfo , error ) {
var (
2013-08-24 03:24:40 -04:00
err error
2013-05-15 20:40:47 -04:00
)
uts , err := uname ( )
if err != nil {
return nil , err
}
release := make ( [ ] byte , len ( uts . Release ) )
i := 0
for _ , c := range uts . Release {
release [ i ] = byte ( c )
i ++
}
// Remove the \x00 from the release for Atoi to parse correctly
release = release [ : bytes . IndexByte ( release , 0 ) ]
2013-08-24 03:24:40 -04:00
return ParseRelease ( string ( release ) )
}
func ParseRelease ( release string ) ( * KernelVersionInfo , error ) {
var (
2014-01-21 00:02:37 -05:00
kernel , major , minor , parsed int
2014-02-24 15:38:58 -05:00
flavor , partial string
2013-08-24 03:24:40 -04:00
)
2014-01-22 12:32:50 -05:00
// Ignore error from Sscanf to allow an empty flavor. Instead, just
// make sure we got all the version numbers.
2014-02-24 15:38:58 -05:00
parsed , _ = fmt . Sscanf ( release , "%d.%d%s" , & kernel , & major , & partial )
if parsed < 2 {
2014-01-21 00:02:37 -05:00
return nil , errors . New ( "Can't parse kernel version " + release )
2013-05-15 20:40:47 -04:00
}
2014-02-24 15:38:58 -05:00
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
parsed , _ = fmt . Sscanf ( partial , ".%d%s" , & minor , & flavor )
if parsed < 1 {
flavor = partial
}
2013-05-15 20:40:47 -04:00
return & KernelVersionInfo {
2014-01-21 00:02:37 -05:00
Kernel : kernel ,
Major : major ,
Minor : minor ,
2014-01-21 00:03:09 -05:00
Flavor : flavor ,
2013-05-15 20:40:47 -04:00
} , nil
}
2013-05-18 10:03:53 -04:00
2013-06-14 19:43:39 -04:00
// FIXME: this is deprecated by CopyWithTar in archive.go
2013-05-28 16:38:26 -04:00
func CopyDirectory ( source , dest string ) error {
2013-05-28 18:22:01 -04:00
if output , err := exec . Command ( "cp" , "-ra" , source , dest ) . CombinedOutput ( ) ; err != nil {
return fmt . Errorf ( "Error copy: %s (%s)" , err , output )
2013-05-28 16:38:26 -04:00
}
return nil
}
2013-05-20 13:58:35 -04:00
type NopFlusher struct { }
func ( f * NopFlusher ) Flush ( ) { }
2013-05-20 13:22:50 -04:00
type WriteFlusher struct {
2013-07-24 11:41:34 -04:00
sync . Mutex
2013-05-20 13:58:35 -04:00
w io . Writer
flusher http . Flusher
2013-05-20 13:22:50 -04:00
}
2013-05-18 10:03:53 -04:00
2013-05-20 13:22:50 -04:00
func ( wf * WriteFlusher ) Write ( b [ ] byte ) ( n int , err error ) {
2013-07-24 11:41:34 -04:00
wf . Lock ( )
defer wf . Unlock ( )
2013-05-20 13:58:35 -04:00
n , err = wf . w . Write ( b )
wf . flusher . Flush ( )
2013-05-18 10:03:53 -04:00
return n , err
2013-05-20 13:22:50 -04:00
}
2013-05-20 13:58:35 -04:00
2013-11-01 13:11:21 -04:00
// Flush the stream immediately.
func ( wf * WriteFlusher ) Flush ( ) {
wf . Lock ( )
defer wf . Unlock ( )
wf . flusher . Flush ( )
}
2013-05-20 13:58:35 -04:00
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 }
}
2013-05-24 10:43:52 -04:00
2013-07-30 18:48:20 -04:00
func NewHTTPRequestError ( msg string , res * http . Response ) error {
return & JSONError {
Message : msg ,
Code : res . StatusCode ,
}
2013-05-25 11:51:26 -04:00
}
2013-06-06 18:50:09 -04:00
func IsURL ( str string ) bool {
return strings . HasPrefix ( str , "http://" ) || strings . HasPrefix ( str , "https://" )
}
2013-06-06 19:09:46 -04:00
func IsGIT ( str string ) bool {
2014-02-26 18:20:58 -05:00
return strings . HasPrefix ( str , "git://" ) || strings . HasPrefix ( str , "github.com/" ) || strings . HasPrefix ( str , "git@github.com:" ) || ( strings . HasSuffix ( str , ".git" ) && IsURL ( str ) )
2013-06-06 19:09:46 -04:00
}
2013-06-14 20:08:39 -04:00
2013-08-02 18:23:36 -04:00
// CheckLocalDns looks into the /etc/resolv.conf,
// it returns true if there is a local nameserver or if there is no nameserver.
func CheckLocalDns ( resolvConf [ ] byte ) bool {
2014-02-07 11:48:14 -05:00
for _ , line := range GetLines ( resolvConf , [ ] byte ( "#" ) ) {
if ! bytes . Contains ( line , [ ] byte ( "nameserver" ) ) {
continue
}
for _ , ip := range [ ] [ ] byte {
[ ] byte ( "127.0.0.1" ) ,
[ ] byte ( "127.0.1.1" ) ,
} {
if bytes . Contains ( line , ip ) {
return true
}
2013-06-06 14:01:09 -04:00
}
2014-02-07 11:48:14 -05:00
return false
2013-06-06 14:01:09 -04:00
}
2014-02-07 11:48:14 -05:00
return true
2013-06-06 14:01:09 -04:00
}
2013-06-18 14:59:56 -04:00
2014-02-07 11:48:14 -05:00
// GetLines parses input into lines and strips away comments.
func GetLines ( input [ ] byte , commentMarker [ ] byte ) [ ] [ ] byte {
2013-08-21 09:48:39 -04:00
lines := bytes . Split ( input , [ ] byte ( "\n" ) )
2014-02-07 11:48:14 -05:00
var output [ ] [ ] byte
2013-08-21 09:23:12 -04:00
for _ , currentLine := range lines {
2013-08-21 09:48:39 -04:00
var commentIndex = bytes . Index ( currentLine , commentMarker )
2013-09-06 19:00:21 -04:00
if commentIndex == - 1 {
2014-02-07 11:48:14 -05:00
output = append ( output , currentLine )
2013-08-21 09:23:12 -04:00
} else {
2014-02-07 11:48:14 -05:00
output = append ( output , currentLine [ : commentIndex ] )
2013-08-21 09:23:12 -04:00
}
}
2013-08-21 09:48:39 -04:00
return output
2013-08-21 09:23:12 -04:00
}
2013-12-16 19:30:23 -05:00
// FIXME: Change this not to receive default value as parameter
2014-02-12 20:26:35 -05:00
func ParseHost ( defaultHost string , defaultUnix , addr string ) ( string , error ) {
2013-12-16 19:30:23 -05:00
var (
proto string
host string
port int
)
2014-01-07 00:14:35 -05:00
addr = strings . TrimSpace ( addr )
2013-10-23 06:29:35 -04:00
switch {
2014-02-12 20:26:35 -05:00
case addr == "tcp://" :
return "" , fmt . Errorf ( "Invalid bind address format: %s" , addr )
2013-10-23 06:29:35 -04:00
case strings . HasPrefix ( addr , "unix://" ) :
2013-12-16 19:30:23 -05:00
proto = "unix"
addr = strings . TrimPrefix ( addr , "unix://" )
if addr == "" {
addr = defaultUnix
}
2013-10-23 06:29:35 -04:00
case strings . HasPrefix ( addr , "tcp://" ) :
proto = "tcp"
2013-06-19 08:31:54 -04:00
addr = strings . TrimPrefix ( addr , "tcp://" )
2013-12-07 00:19:30 -05:00
case strings . HasPrefix ( addr , "fd://" ) :
return addr , nil
2013-12-28 16:16:03 -05:00
case addr == "" :
proto = "unix"
addr = defaultUnix
2013-10-23 06:29:35 -04:00
default :
if strings . Contains ( addr , "://" ) {
2013-10-24 01:23:02 -04:00
return "" , fmt . Errorf ( "Invalid bind address protocol: %s" , addr )
2013-10-23 06:29:35 -04:00
}
proto = "tcp"
2013-06-19 08:31:54 -04:00
}
2013-10-23 06:29:35 -04:00
2013-12-16 19:30:23 -05:00
if proto != "unix" && strings . Contains ( addr , ":" ) {
2013-06-19 08:31:54 -04:00
hostParts := strings . Split ( addr , ":" )
if len ( hostParts ) != 2 {
2013-10-24 01:23:02 -04:00
return "" , fmt . Errorf ( "Invalid bind address format: %s" , addr )
2013-06-19 08:31:54 -04:00
}
if hostParts [ 0 ] != "" {
host = hostParts [ 0 ]
2013-12-16 19:30:23 -05:00
} else {
host = defaultHost
2013-06-19 08:31:54 -04:00
}
2013-12-16 19:30:23 -05:00
if p , err := strconv . Atoi ( hostParts [ 1 ] ) ; err == nil && p != 0 {
2013-06-19 08:31:54 -04:00
port = p
2013-12-16 19:30:23 -05:00
} else {
2014-02-12 20:26:35 -05:00
return "" , fmt . Errorf ( "Invalid bind address format: %s" , addr )
2013-06-19 08:31:54 -04:00
}
2013-12-16 19:30:23 -05:00
2014-02-12 20:26:35 -05:00
} else if proto == "tcp" && ! strings . Contains ( addr , ":" ) {
return "" , fmt . Errorf ( "Invalid bind address format: %s" , addr )
2013-06-19 08:31:54 -04:00
} else {
host = addr
2013-12-16 19:30:23 -05:00
}
if proto == "unix" {
return fmt . Sprintf ( "%s://%s" , proto , host ) , nil
2013-06-19 08:31:54 -04:00
}
2013-10-24 01:23:02 -04:00
return fmt . Sprintf ( "%s://%s:%d" , proto , host , port ) , nil
2013-06-19 08:31:54 -04:00
}
2013-07-09 11:06:10 -04:00
2013-07-03 13:11:00 -04:00
func GetReleaseVersion ( ) string {
2014-01-13 15:28:30 -05:00
resp , err := http . Get ( "https://get.docker.io/latest" )
2013-07-03 13:11:00 -04:00
if err != nil {
return ""
}
defer resp . Body . Close ( )
2013-07-30 12:39:35 -04:00
if resp . ContentLength > 24 || resp . StatusCode != 200 {
return ""
}
2013-07-03 13:11:00 -04:00
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
return ""
}
2013-07-12 07:45:40 -04:00
return strings . TrimSpace ( string ( body ) )
}
2013-07-09 11:06:10 -04:00
// Get a repos name and returns the right reposName + tag
// The tag can be confusing because of a port in a repository name.
// Ex: localhost.localdomain:5000/samalba/hipache:latest
func ParseRepositoryTag ( repos string ) ( string , string ) {
n := strings . LastIndex ( repos , ":" )
if n < 0 {
return repos , ""
}
if tag := repos [ n + 1 : ] ; ! strings . Contains ( tag , "/" ) {
return repos [ : n ] , tag
}
return repos , ""
}
2013-07-01 19:48:59 -04:00
2013-09-09 17:26:35 -04:00
// An StatusError reports an unsuccessful exit by a command.
type StatusError struct {
2013-10-12 08:14:52 -04:00
Status string
StatusCode int
2013-09-09 17:26:35 -04:00
}
func ( e * StatusError ) Error ( ) string {
2013-10-12 08:14:52 -04:00
return fmt . Sprintf ( "Status: %s, Code: %d" , e . Status , e . StatusCode )
2013-09-09 17:26:35 -04:00
}
2013-10-16 16:40:52 -04:00
2013-09-12 09:17:39 -04:00
func quote ( word string , buf * bytes . Buffer ) {
// Bail out early for "simple" strings
if word != "" && ! strings . ContainsAny ( word , "\\'\"`${[|&;<>()~*?! \t\n" ) {
buf . WriteString ( word )
return
}
buf . WriteString ( "'" )
for i := 0 ; i < len ( word ) ; i ++ {
b := word [ i ]
if b == '\'' {
// Replace literal ' with a close ', a \', and a open '
buf . WriteString ( "'\\''" )
} else {
buf . WriteByte ( b )
}
}
buf . WriteString ( "'" )
}
// Take a list of strings and escape them so they will be handled right
// when passed as arguments to an program via a shell
func ShellQuoteArguments ( args [ ] string ) string {
var buf bytes . Buffer
for i , arg := range args {
if i != 0 {
buf . WriteByte ( ' ' )
}
quote ( arg , & buf )
}
return buf . String ( )
}
2013-10-04 22:25:15 -04:00
func PartParser ( template , data string ) ( map [ string ] string , error ) {
// ip:public:private
2013-11-22 13:12:27 -05:00
var (
templateParts = strings . Split ( template , ":" )
parts = strings . Split ( data , ":" )
out = make ( map [ string ] string , len ( templateParts ) )
)
2013-10-04 22:25:15 -04:00
if len ( parts ) != len ( templateParts ) {
return nil , fmt . Errorf ( "Invalid format to parse. %s should match template %s" , data , template )
}
for i , t := range templateParts {
value := ""
if len ( parts ) > i {
value = parts [ i ]
}
out [ t ] = value
}
return out , nil
}
2013-11-14 01:08:08 -05:00
var globalTestID string
// TestDirectory creates a new temporary directory and returns its path.
// The contents of directory at path `templateDir` is copied into the
// new directory.
func TestDirectory ( templateDir string ) ( dir string , err error ) {
if globalTestID == "" {
globalTestID = RandomString ( ) [ : 4 ]
}
prefix := fmt . Sprintf ( "docker-test%s-%s-" , globalTestID , GetCallerName ( 2 ) )
if prefix == "" {
prefix = "docker-test-"
}
dir , err = ioutil . TempDir ( "" , prefix )
if err = os . Remove ( dir ) ; err != nil {
return
}
if templateDir != "" {
if err = CopyDirectory ( templateDir , dir ) ; err != nil {
return
}
}
return
}
// GetCallerName introspects the call stack and returns the name of the
// function `depth` levels down in the stack.
func GetCallerName ( depth int ) string {
// Use the caller function name as a prefix.
// This helps trace temp directories back to their test.
pc , _ , _ , _ := runtime . Caller ( depth + 1 )
callerLongName := runtime . FuncForPC ( pc ) . Name ( )
parts := strings . Split ( callerLongName , "." )
callerShortName := parts [ len ( parts ) - 1 ]
return callerShortName
}
2013-11-25 17:42:22 -05:00
func CopyFile ( src , dst string ) ( int64 , error ) {
if src == dst {
return 0 , nil
}
sf , err := os . Open ( src )
if err != nil {
return 0 , err
}
defer sf . Close ( )
if err := os . Remove ( dst ) ; err != nil && ! os . IsNotExist ( err ) {
return 0 , err
}
df , err := os . Create ( dst )
if err != nil {
return 0 , err
}
defer df . Close ( )
return io . Copy ( df , sf )
}
2014-01-16 14:02:51 -05:00
type readCloserWrapper struct {
io . Reader
closer func ( ) error
}
func ( r * readCloserWrapper ) Close ( ) error {
return r . closer ( )
}
func NewReadCloserWrapper ( r io . Reader , closer func ( ) error ) io . ReadCloser {
return & readCloserWrapper {
Reader : r ,
closer : closer ,
}
}
2014-03-01 02:29:00 -05:00
// ReplaceOrAppendValues returns the defaults with the overrides either
// replaced by env key or appended to the list
func ReplaceOrAppendEnvValues ( defaults , overrides [ ] string ) [ ] string {
cache := make ( map [ string ] int , len ( defaults ) )
for i , e := range defaults {
parts := strings . SplitN ( e , "=" , 2 )
cache [ parts [ 0 ] ] = i
}
for _ , value := range overrides {
parts := strings . SplitN ( value , "=" , 2 )
if i , exists := cache [ parts [ 0 ] ] ; exists {
defaults [ i ] = value
} else {
defaults = append ( defaults , value )
}
}
return defaults
}
2014-02-24 16:10:06 -05:00
// ReadSymlinkedDirectory returns the target directory of a symlink.
// The target of the symbolic link may not be a file.
func ReadSymlinkedDirectory ( path string ) ( string , error ) {
var realPath string
var err error
if realPath , err = filepath . Abs ( path ) ; err != nil {
return "" , fmt . Errorf ( "unable to get absolute path for %s: %s" , path , err )
}
if realPath , err = filepath . EvalSymlinks ( realPath ) ; err != nil {
return "" , fmt . Errorf ( "failed to canonicalise path for %s: %s" , path , err )
}
realPathInfo , err := os . Stat ( realPath )
if err != nil {
return "" , fmt . Errorf ( "failed to stat target '%s' of '%s': %s" , realPath , path , err )
}
if ! realPathInfo . Mode ( ) . IsDir ( ) {
return "" , fmt . Errorf ( "canonical path points to a file '%s'" , realPath )
}
return realPath , nil
}
2014-03-13 12:03:09 -04:00
func ParseKeyValueOpt ( opt string ) ( string , string , error ) {
parts := strings . SplitN ( opt , "=" , 2 )
if len ( parts ) != 2 {
return "" , "" , fmt . Errorf ( "Unable to parse key/value option: %s" , opt )
}
return strings . TrimSpace ( parts [ 0 ] ) , strings . TrimSpace ( parts [ 1 ] ) , nil
}