github.com/vincentwoo/docker@v0.7.3-0.20160116130405-82401a4b13c0/utils/utils.go (about)

     1  package utils
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  
    15  	"github.com/docker/distribution/registry/api/errcode"
    16  	"github.com/docker/docker/dockerversion"
    17  	"github.com/docker/docker/pkg/archive"
    18  	"github.com/docker/docker/pkg/stringid"
    19  )
    20  
    21  // SelfPath figures out the absolute path of our own binary (if it's still around).
    22  func SelfPath() string {
    23  	path, err := exec.LookPath(os.Args[0])
    24  	if err != nil {
    25  		if os.IsNotExist(err) {
    26  			return ""
    27  		}
    28  		if execErr, ok := err.(*exec.Error); ok && os.IsNotExist(execErr.Err) {
    29  			return ""
    30  		}
    31  		panic(err)
    32  	}
    33  	path, err = filepath.Abs(path)
    34  	if err != nil {
    35  		if os.IsNotExist(err) {
    36  			return ""
    37  		}
    38  		panic(err)
    39  	}
    40  	return path
    41  }
    42  
    43  func dockerInitSha1(target string) string {
    44  	f, err := os.Open(target)
    45  	if err != nil {
    46  		return ""
    47  	}
    48  	defer f.Close()
    49  	h := sha1.New()
    50  	_, err = io.Copy(h, f)
    51  	if err != nil {
    52  		return ""
    53  	}
    54  	return hex.EncodeToString(h.Sum(nil))
    55  }
    56  
    57  func isValidDockerInitPath(target string, selfPath string) bool { // target and selfPath should be absolute (InitPath and SelfPath already do this)
    58  	if target == "" {
    59  		return false
    60  	}
    61  	if dockerversion.IAmStatic == "true" {
    62  		if selfPath == "" {
    63  			return false
    64  		}
    65  		if target == selfPath {
    66  			return true
    67  		}
    68  		targetFileInfo, err := os.Lstat(target)
    69  		if err != nil {
    70  			return false
    71  		}
    72  		selfPathFileInfo, err := os.Lstat(selfPath)
    73  		if err != nil {
    74  			return false
    75  		}
    76  		return os.SameFile(targetFileInfo, selfPathFileInfo)
    77  	}
    78  	return dockerversion.InitSHA1 != "" && dockerInitSha1(target) == dockerversion.InitSHA1
    79  }
    80  
    81  // DockerInitPath figures out the path of our dockerinit (which may be SelfPath())
    82  func DockerInitPath(localCopy string) string {
    83  	selfPath := SelfPath()
    84  	if isValidDockerInitPath(selfPath, selfPath) {
    85  		// if we're valid, don't bother checking anything else
    86  		return selfPath
    87  	}
    88  	var possibleInits = []string{
    89  		localCopy,
    90  		dockerversion.InitPath,
    91  		filepath.Join(filepath.Dir(selfPath), "dockerinit"),
    92  
    93  		// 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."
    94  		// https://www.linuxbase.org/betaspecs/fhs/fhs.html#usrlibexec
    95  		"/usr/libexec/docker/dockerinit",
    96  		"/usr/local/libexec/docker/dockerinit",
    97  
    98  		// 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."
    99  		// https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA
   100  		"/usr/lib/docker/dockerinit",
   101  		"/usr/local/lib/docker/dockerinit",
   102  	}
   103  	for _, dockerInit := range possibleInits {
   104  		if dockerInit == "" {
   105  			continue
   106  		}
   107  		path, err := exec.LookPath(dockerInit)
   108  		if err == nil {
   109  			path, err = filepath.Abs(path)
   110  			if err != nil {
   111  				// LookPath already validated that this file exists and is executable (following symlinks), so how could Abs fail?
   112  				panic(err)
   113  			}
   114  			if isValidDockerInitPath(path, selfPath) {
   115  				return path
   116  			}
   117  		}
   118  	}
   119  	return ""
   120  }
   121  
   122  var globalTestID string
   123  
   124  // TestDirectory creates a new temporary directory and returns its path.
   125  // The contents of directory at path `templateDir` is copied into the
   126  // new directory.
   127  func TestDirectory(templateDir string) (dir string, err error) {
   128  	if globalTestID == "" {
   129  		globalTestID = stringid.GenerateNonCryptoID()[:4]
   130  	}
   131  	prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, GetCallerName(2))
   132  	if prefix == "" {
   133  		prefix = "docker-test-"
   134  	}
   135  	dir, err = ioutil.TempDir("", prefix)
   136  	if err = os.Remove(dir); err != nil {
   137  		return
   138  	}
   139  	if templateDir != "" {
   140  		if err = archive.CopyWithTar(templateDir, dir); err != nil {
   141  			return
   142  		}
   143  	}
   144  	return
   145  }
   146  
   147  // GetCallerName introspects the call stack and returns the name of the
   148  // function `depth` levels down in the stack.
   149  func GetCallerName(depth int) string {
   150  	// Use the caller function name as a prefix.
   151  	// This helps trace temp directories back to their test.
   152  	pc, _, _, _ := runtime.Caller(depth + 1)
   153  	callerLongName := runtime.FuncForPC(pc).Name()
   154  	parts := strings.Split(callerLongName, ".")
   155  	callerShortName := parts[len(parts)-1]
   156  	return callerShortName
   157  }
   158  
   159  // ReplaceOrAppendEnvValues returns the defaults with the overrides either
   160  // replaced by env key or appended to the list
   161  func ReplaceOrAppendEnvValues(defaults, overrides []string) []string {
   162  	cache := make(map[string]int, len(defaults))
   163  	for i, e := range defaults {
   164  		parts := strings.SplitN(e, "=", 2)
   165  		cache[parts[0]] = i
   166  	}
   167  
   168  	for _, value := range overrides {
   169  		// Values w/o = means they want this env to be removed/unset.
   170  		if !strings.Contains(value, "=") {
   171  			if i, exists := cache[value]; exists {
   172  				defaults[i] = "" // Used to indicate it should be removed
   173  			}
   174  			continue
   175  		}
   176  
   177  		// Just do a normal set/update
   178  		parts := strings.SplitN(value, "=", 2)
   179  		if i, exists := cache[parts[0]]; exists {
   180  			defaults[i] = value
   181  		} else {
   182  			defaults = append(defaults, value)
   183  		}
   184  	}
   185  
   186  	// Now remove all entries that we want to "unset"
   187  	for i := 0; i < len(defaults); i++ {
   188  		if defaults[i] == "" {
   189  			defaults = append(defaults[:i], defaults[i+1:]...)
   190  			i--
   191  		}
   192  	}
   193  
   194  	return defaults
   195  }
   196  
   197  // GetErrorMessage returns the human readable message associated with
   198  // the passed-in error. In some cases the default Error() func returns
   199  // something that is less than useful so based on its types this func
   200  // will go and get a better piece of text.
   201  func GetErrorMessage(err error) string {
   202  	switch err.(type) {
   203  	case errcode.Error:
   204  		e, _ := err.(errcode.Error)
   205  		return e.Message
   206  
   207  	case errcode.ErrorCode:
   208  		ec, _ := err.(errcode.ErrorCode)
   209  		return ec.Message()
   210  
   211  	default:
   212  		return err.Error()
   213  	}
   214  }