From 6e6c6bf9cc11b285abdb1134eea711e2de8eec8b Mon Sep 17 00:00:00 2001 From: Taku Shimosawa Date: Thu, 15 Jun 2017 17:53:15 -0700 Subject: [PATCH] [FAB-4819] Fix locating system packages in vendoring This patchset fixes some bugs addressed in [FAB-4819]. When the GOROOT environment variable is not set (i.e. Go is installed in the standard location), Go uses its default value for GOROOT. This patch introduces getGoEnv() to properly get GOROOT by using the "go env" command instead of directly reading an environment variable. This patch also fixes list() to prevent from obtaining empty string in error from the output of the "go list" command. Change-Id: Ie8007087aff40d5f8eab6021e4e184534d3074e4 Signed-off-by: Taku Shimosawa --- core/chaincode/platforms/golang/env.go | 24 ++++++++++++++- core/chaincode/platforms/golang/env_test.go | 11 +++++++ core/chaincode/platforms/golang/list.go | 34 ++++++++++++++------- core/chaincode/platforms/golang/platform.go | 14 ++++++--- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/core/chaincode/platforms/golang/env.go b/core/chaincode/platforms/golang/env.go index 2c7f210c68a..2d0eb134bf3 100644 --- a/core/chaincode/platforms/golang/env.go +++ b/core/chaincode/platforms/golang/env.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "strings" + "time" ) type Env map[string]string @@ -27,7 +28,7 @@ type Env map[string]string func getEnv() Env { env := make(Env) for _, entry := range os.Environ() { - tokens := strings.Split(entry, "=") + tokens := strings.SplitN(entry, "=", 2) if len(tokens) > 1 { env[tokens[0]] = tokens[1] } @@ -36,6 +37,27 @@ func getEnv() Env { return env } +func getGoEnv() (Env, error) { + env := getEnv() + + goenvbytes, err := runProgram(env, 10*time.Second, "go", "env") + if err != nil { + return nil, err + } + + goenv := make(Env) + + envout := strings.Split(string(goenvbytes), "\n") + for _, entry := range envout { + tokens := strings.SplitN(entry, "=", 2) + if len(tokens) > 1 { + goenv[tokens[0]] = strings.Trim(tokens[1], "\"") + } + } + + return goenv, nil +} + func flattenEnv(env Env) []string { result := make([]string, 0) for k, v := range env { diff --git a/core/chaincode/platforms/golang/env_test.go b/core/chaincode/platforms/golang/env_test.go index 92e23185d23..28116cc84ea 100644 --- a/core/chaincode/platforms/golang/env_test.go +++ b/core/chaincode/platforms/golang/env_test.go @@ -27,3 +27,14 @@ func Test_splitEnvPath(t *testing.T) { paths := splitEnvPaths("foo" + string(os.PathListSeparator) + "bar" + string(os.PathListSeparator) + "baz") assert.Equal(t, len(paths), 3) } + +func Test_getGoEnv(t *testing.T) { + goenv, err := getGoEnv() + assert.NilError(t, err) + + _, ok := goenv["GOPATH"] + assert.Equal(t, ok, true) + + _, ok = goenv["GOROOT"] + assert.Equal(t, ok, true) +} diff --git a/core/chaincode/platforms/golang/list.go b/core/chaincode/platforms/golang/list.go index 9eb345f2102..6b35f3112fd 100644 --- a/core/chaincode/platforms/golang/list.go +++ b/core/chaincode/platforms/golang/list.go @@ -25,17 +25,15 @@ import ( "time" ) -// Logic inspired by: https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife -func list(env Env, template, pkg string) ([]string, error) { - +//runProgram non-nil Env, timeout (typically secs or millisecs), program name and args +func runProgram(env Env, timeout time.Duration, pgm string, args ...string) ([]byte, error) { if env == nil { - env = getEnv() + return nil, fmt.Errorf("<%s, %v>: nil env provided", pgm, args) } - var stdOut bytes.Buffer var stdErr bytes.Buffer - cmd := exec.Command("go", "list", "-f", template, pkg) + cmd := exec.Command(pgm, args...) cmd.Env = flattenEnv(env) cmd.Stdout = &stdOut cmd.Stderr = &stdErr @@ -48,21 +46,35 @@ func list(env Env, template, pkg string) ([]string, error) { }() select { - case <-time.After(60 * time.Second): + case <-time.After(timeout): if err = cmd.Process.Kill(); err != nil { - return nil, fmt.Errorf("go list: failed to kill: %s", err) + return nil, fmt.Errorf("<%s, %v>: failed to kill: %s", pgm, args, err) } else { - return nil, errors.New("go list: timeout") + return nil, errors.New(fmt.Sprintf("<%s, %v>: timeout(%d msecs)", pgm, args, timeout/time.Millisecond)) } case err = <-done: if err != nil { - return nil, fmt.Errorf("go list: failed with error: \"%s\"\n%s", err, string(stdErr.Bytes())) + return nil, fmt.Errorf("<%s, %v>: failed with error: \"%s\"\n%s", pgm, args, err, string(stdErr.Bytes())) } - return strings.Split(string(stdOut.Bytes()), "\n"), nil + return stdOut.Bytes(), nil } } +// Logic inspired by: https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife +func list(env Env, template, pkg string) ([]string, error) { + if env == nil { + env = getEnv() + } + + lst, err := runProgram(env, 60*time.Second, "go", "list", "-f", template, pkg) + if err != nil { + return nil, err + } + + return strings.Split(strings.Trim(string(lst), "\n"), "\n"), nil +} + func listDeps(env Env, pkg string) ([]string, error) { return list(env, "{{ join .Deps \"\\n\"}}", pkg) } diff --git a/core/chaincode/platforms/golang/platform.go b/core/chaincode/platforms/golang/platform.go index f8e9be55526..5a9d78870b8 100644 --- a/core/chaincode/platforms/golang/platform.go +++ b/core/chaincode/platforms/golang/platform.go @@ -73,11 +73,14 @@ func decodeUrl(spec *pb.ChaincodeSpec) (string, error) { } func getGopath() (string, error) { - gopath := os.Getenv("GOPATH") + env, err := getGoEnv() + if err != nil { + return "", err + } // Only take the first element of GOPATH - splitGoPath := filepath.SplitList(gopath) + splitGoPath := filepath.SplitList(env["GOPATH"]) if len(splitGoPath) == 0 { - return "", fmt.Errorf("invalid GOPATH environment variable value:[%s]", gopath) + return "", fmt.Errorf("invalid GOPATH environment variable value:[%s]", env["GOPATH"]) } return splitGoPath[0], nil } @@ -194,7 +197,10 @@ func (goPlatform *Platform) GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte // -------------------------------------------------------------------------------------- // Update our environment for the purposes of executing go-list directives // -------------------------------------------------------------------------------------- - env := getEnv() + env, err := getGoEnv() + if err != nil { + return nil, err + } gopaths := splitEnvPaths(env["GOPATH"]) goroots := splitEnvPaths(env["GOROOT"]) gopaths[code.Gopath] = true