2013-05-14 22:37:35 +00:00
package utils
import (
2013-10-17 23:40:41 -06:00
"crypto/sha1"
2013-05-14 22:37:35 +00:00
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
2013-11-07 12:19:24 -08:00
"runtime"
2013-05-14 22:37:35 +00:00
"strings"
2014-05-05 22:51:32 +00:00
2015-07-21 13:30:32 -07:00
"github.com/docker/distribution/registry/api/errcode"
2015-11-09 19:32:46 +01:00
"github.com/docker/docker/dockerversion"
2014-11-01 12:23:08 -04:00
"github.com/docker/docker/pkg/archive"
2014-09-29 23:21:41 -07:00
"github.com/docker/docker/pkg/fileutils"
2015-04-01 07:21:07 -07:00
"github.com/docker/docker/pkg/stringid"
2013-05-14 22:37:35 +00:00
)
2015-07-22 05:20:12 +08:00
// SelfPath figures out the absolute path of our own binary (if it's still around).
2013-05-14 22:37:35 +00:00
func SelfPath ( ) string {
path , err := exec . LookPath ( os . Args [ 0 ] )
if err != nil {
2013-12-05 02:10:41 -07:00
if os . IsNotExist ( err ) {
return ""
}
if execErr , ok := err . ( * exec . Error ) ; ok && os . IsNotExist ( execErr . Err ) {
return ""
}
2013-05-14 22:37:35 +00:00
panic ( err )
}
path , err = filepath . Abs ( path )
if err != nil {
2013-12-05 02:10:41 -07:00
if os . IsNotExist ( err ) {
return ""
}
2013-05-14 22:37:35 +00:00
panic ( err )
}
return path
}
2013-10-17 23:40:41 -06: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 02:10:41 -07:00
if target == "" {
return false
}
2015-11-09 19:32:46 +01:00
if dockerversion . IAmStatic == "true" {
2013-12-05 02:10:41 -07:00
if selfPath == "" {
return false
}
2013-10-17 23:40:41 -06: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 )
}
2015-11-09 19:32:46 +01:00
return dockerversion . InitSHA1 != "" && dockerInitSha1 ( target ) == dockerversion . InitSHA1
2013-10-17 23:40:41 -06:00
}
2015-07-22 05:20:12 +08:00
// DockerInitPath figures out the path of our dockerinit (which may be SelfPath())
2013-11-25 14:42:22 -08:00
func DockerInitPath ( localCopy string ) string {
2013-10-17 23:40:41 -06: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 14:42:22 -08:00
localCopy ,
2015-11-09 19:32:46 +01:00
dockerversion . InitPath ,
2013-10-17 23:40:41 -06:00
filepath . Join ( filepath . Dir ( selfPath ) , "dockerinit" ) ,
2013-11-27 16:49:40 -07: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."
2015-04-11 13:31:34 -04:00
// https://www.linuxbase.org/betaspecs/fhs/fhs.html#usrlibexec
2013-10-17 23:40:41 -06:00
"/usr/libexec/docker/dockerinit" ,
"/usr/local/libexec/docker/dockerinit" ,
2013-11-27 16:49:40 -07: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."
2015-04-11 13:31:34 -04:00
// https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA
2013-11-27 16:49:40 -07:00
"/usr/lib/docker/dockerinit" ,
"/usr/local/lib/docker/dockerinit" ,
2013-10-17 23:40:41 -06:00
}
for _ , dockerInit := range possibleInits {
2013-12-05 02:10:41 -07:00
if dockerInit == "" {
continue
}
2013-10-17 23:40:41 -06: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-11-14 06:08:08 +00: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 == "" {
2015-07-28 17:19:17 -07:00
globalTestID = stringid . GenerateNonCryptoID ( ) [ : 4 ]
2013-11-14 06:08:08 +00:00
}
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 != "" {
2014-11-01 12:23:08 -04:00
if err = archive . CopyWithTar ( templateDir , dir ) ; err != nil {
2013-11-14 06:08:08 +00:00
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 14:42:22 -08:00
2015-07-22 05:20:12 +08:00
// ReplaceOrAppendEnvValues returns the defaults with the overrides either
2014-02-28 23:29:00 -08:00
// 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
}
2015-01-16 12:57:08 -08:00
2014-02-28 23:29:00 -08:00
for _ , value := range overrides {
2015-01-16 12:57:08 -08:00
// Values w/o = means they want this env to be removed/unset.
if ! strings . Contains ( value , "=" ) {
if i , exists := cache [ value ] ; exists {
defaults [ i ] = "" // Used to indicate it should be removed
}
continue
}
// Just do a normal set/update
2014-02-28 23:29:00 -08:00
parts := strings . SplitN ( value , "=" , 2 )
if i , exists := cache [ parts [ 0 ] ] ; exists {
defaults [ i ] = value
} else {
defaults = append ( defaults , value )
}
}
2015-01-16 12:57:08 -08:00
// Now remove all entries that we want to "unset"
for i := 0 ; i < len ( defaults ) ; i ++ {
if defaults [ i ] == "" {
defaults = append ( defaults [ : i ] , defaults [ i + 1 : ] ... )
i --
}
}
2014-02-28 23:29:00 -08:00
return defaults
}
2014-02-24 23:10:06 +02:00
2014-05-10 10:58:45 +03:00
// ValidateContextDirectory checks if all the contents of the directory
// can be read and returns an error if some files can't be read
// symlinks which point to non-existing files don't trigger an error
2014-07-07 14:23:07 +02:00
func ValidateContextDirectory ( srcPath string , excludes [ ] string ) error {
2015-08-24 14:07:22 -07:00
contextRoot , err := getContextRoot ( srcPath )
if err != nil {
return err
}
return filepath . Walk ( contextRoot , func ( filePath string , f os . FileInfo , err error ) error {
2014-05-10 10:58:45 +03:00
// skip this directory/file if it's not in the path, it won't get added to the context
2015-08-24 14:07:22 -07:00
if relFilePath , err := filepath . Rel ( contextRoot , filePath ) ; err != nil {
2014-09-14 18:13:40 +03:00
return err
2014-09-29 23:21:41 -07:00
} else if skip , err := fileutils . Matches ( relFilePath , excludes ) ; err != nil {
2014-09-14 18:13:40 +03:00
return err
} else if skip {
2014-07-07 14:23:07 +02:00
if f . IsDir ( ) {
return filepath . SkipDir
}
return nil
}
2014-09-14 18:13:40 +03:00
if err != nil {
if os . IsPermission ( err ) {
return fmt . Errorf ( "can't stat '%s'" , filePath )
}
if os . IsNotExist ( err ) {
return nil
}
2014-05-10 10:58:45 +03:00
return err
}
2014-09-14 18:13:40 +03:00
2014-05-10 10:58:45 +03:00
// skip checking if symlinks point to non-existing files, such symlinks can be useful
2014-08-22 18:37:37 +04:00
// also skip named pipes, because they hanging on open
2014-09-14 18:13:40 +03:00
if f . Mode ( ) & ( os . ModeSymlink | os . ModeNamedPipe ) != 0 {
2014-08-22 18:37:37 +04:00
return nil
2014-05-10 10:58:45 +03:00
}
if ! f . IsDir ( ) {
currentFile , err := os . Open ( filePath )
if err != nil && os . IsPermission ( err ) {
2014-09-14 18:13:40 +03:00
return fmt . Errorf ( "no permission to read from '%s'" , filePath )
2014-05-10 10:58:45 +03:00
}
2014-08-29 15:21:28 +04:00
currentFile . Close ( )
2014-05-10 10:58:45 +03:00
}
return nil
} )
}
2014-07-10 18:41:11 +00:00
2015-07-21 13:30:32 -07:00
// GetErrorMessage returns the human readable message associated with
// the passed-in error. In some cases the default Error() func returns
// something that is less than useful so based on its types this func
// will go and get a better piece of text.
func GetErrorMessage ( err error ) string {
switch err . ( type ) {
case errcode . Error :
e , _ := err . ( errcode . Error )
return e . Message
case errcode . ErrorCode :
ec , _ := err . ( errcode . ErrorCode )
return ec . Message ( )
default :
return err . Error ( )
}
}