Skip to content

Commit

Permalink
Use go list -json to build go cc dependencies
Browse files Browse the repository at this point in the history
Prior to these changes, custom format templates were used to format
dependency information as JSON for go chaincode. This did not work on
Windows because the backlash path separators were not properly escaped.

Using the -json option (instead of the template) results in properly
escaped filenames in the structure so we abandon the custom format and
use the JSON output to generate our model.

Later, when we create the tar that's embedded in the chaincode, we
transform the path separators to slashes.

FAB-17288

Change-Id: If3e04f5fa4deacb734e94323c9872406f3f6406e
Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
  • Loading branch information
sykesm committed Jan 8, 2020
1 parent 571842f commit 42606a5
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 50 deletions.
77 changes: 37 additions & 40 deletions core/chaincode/platforms/golang/list.go
Expand Up @@ -21,31 +21,19 @@ import (

const listTimeout = 3 * time.Minute

const packageListFormat = `
{{- if eq .Goroot false -}}
{
"import_path": "{{ .ImportPath }}",
"incomplete": {{ .Incomplete }},
"dir": "{{ .Dir }}",
"go_files" : [{{ range $i, $file := .GoFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}],
"c_files": [{{ range $i, $file := .CFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}],
"cgo_files": [{{ range $i, $file := .CgoFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}],
"h_files": [{{ range $i, $file := .HFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}],
"s_files": [{{ range $i, $file := .SFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}],
"ignored_go_files": [{{ range $i, $file := .IgnoredGoFiles }}{{ if $i }}, {{ end }}"{{ $file }}"{{end}}]
}
{{- end -}}`

// PackageInfo is the subset of data from `go list -deps -json` that's
// necessary to calculate chaincode package dependencies.
type PackageInfo struct {
ImportPath string `json:"import_path,omitempty"`
Dir string `json:"dir,omitempty"`
GoFiles []string `json:"go_files,omitempty"`
CFiles []string `json:"c_files,omitempty"`
CgoFiles []string `json:"cgo_files,omitempty"`
HFiles []string `json:"h_files,omitempty"`
SFiles []string `json:"s_files,omitempty"`
IgnoredGoFiles []string `json:"ignored_go_files,omitempty"`
Incomplete bool `json:"incomplete,omitempty"`
ImportPath string
Dir string
GoFiles []string
Goroot bool
CFiles []string
CgoFiles []string
HFiles []string
SFiles []string
IgnoredGoFiles []string
Incomplete bool
}

func (p PackageInfo) Files() []string {
Expand All @@ -65,7 +53,7 @@ func gopathDependencyPackageInfo(goos, goarch, pkg string) ([]PackageInfo, error
ctx, cancel := context.WithTimeout(context.Background(), listTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, "go", "list", "-deps", "-f", packageListFormat, pkg)
cmd := exec.CommandContext(ctx, "go", "list", "-deps", "-json", pkg)
cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)

stdout, err := cmd.StdoutPipe()
Expand All @@ -92,6 +80,9 @@ func gopathDependencyPackageInfo(goos, goarch, pkg string) ([]PackageInfo, error
if packageInfo.Incomplete {
return nil, fmt.Errorf("failed to calculate dependencies: incomplete package: %s", packageInfo.ImportPath)
}
if packageInfo.Goroot {
continue
}

list = append(list, packageInfo)
}
Expand All @@ -111,26 +102,19 @@ func wrapExitErr(err error, message string) error {
return errors.Wrap(err, message)
}

const moduleListFormat = `{
"dir": "{{ .Module.Dir }}",
"gomod": "{{ .Module.GoMod }}",
"import_path": "{{ .ImportPath }}",
"module_path": "{{ .Module.Path }}"
}`

type ModuleInfo struct {
Dir string `json:"dir,omitempty"`
ImportPath string `json:"import_path,omitempty"`
ModulePath string `json:"module_path,omitempty"`
GoMod string `json:"gomod,omitempty"`
Dir string
GoMod string
ImportPath string
ModulePath string
}

// listModuleInfo extracts module information for the curent working directory.
func listModuleInfo(extraEnv ...string) (*ModuleInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), listTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, "go", "list", "-f", moduleListFormat, ".")
cmd := exec.CommandContext(ctx, "go", "list", "-json", ".")
cmd.Env = append(os.Environ(), "GO111MODULE=on")
cmd.Env = append(cmd.Env, extraEnv...)

Expand All @@ -139,10 +123,23 @@ func listModuleInfo(extraEnv ...string) (*ModuleInfo, error) {
return nil, wrapExitErr(err, "'go list' failed")
}

var moduleInfo ModuleInfo
if err := json.Unmarshal(output, &moduleInfo); err != nil {
var moduleData struct {
ImportPath string
Module struct {
Dir string
Path string
GoMod string
}
}

if err := json.Unmarshal(output, &moduleData); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal output from 'go list'")
}

return &moduleInfo, nil
return &ModuleInfo{
Dir: moduleData.Module.Dir,
GoMod: moduleData.Module.GoMod,
ImportPath: moduleData.ImportPath,
ModulePath: moduleData.Module.Path,
}, nil
}
9 changes: 5 additions & 4 deletions core/chaincode/platforms/golang/platform.go
Expand Up @@ -369,11 +369,11 @@ func (s SourceMap) Sources() Sources {

func (s SourceMap) Directories() []string {
dirMap := map[string]bool{}
for filename := range s {
dir := filepath.Dir(filename)
for entryName := range s {
dir := path.Dir(entryName)
for dir != "." && !dirMap[dir] {
dirMap[dir] = true
dir = filepath.Dir(dir)
dir = path.Dir(dir)
}
}

Expand Down Expand Up @@ -442,6 +442,7 @@ func findSource(cd *CodeDescriptor) (SourceMap, error) {
name = filepath.Join("src", cd.Path, name)
}

name = filepath.ToSlash(name)
sources[name] = SourceDescriptor{Name: name, Path: path}
return nil
}
Expand All @@ -461,7 +462,7 @@ func validateMetadata(name, path string) error {

// Validate metadata file for inclusion in tar
// Validation is based on the passed filename with path
err = ccmetadata.ValidateMetadataFile(name, contents)
err = ccmetadata.ValidateMetadataFile(filepath.ToSlash(name), contents)
if err != nil {
return err
}
Expand Down
13 changes: 7 additions & 6 deletions core/chaincode/platforms/golang/platform_test.go
Expand Up @@ -23,6 +23,7 @@ import (
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/core/chaincode/platforms/util"
"github.com/hyperledger/fabric/core/config/configtest"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -171,8 +172,8 @@ func getGopath() (string, error) {
func Test_findSource(t *testing.T) {
t.Run("Gopath", func(t *testing.T) {
source, err := findSource(&CodeDescriptor{
Source: filepath.Join("testdata/src/chaincodes/noop"),
MetadataRoot: filepath.Join("testdata/src/chaincodes/noop/META-INF"),
Source: filepath.FromSlash("testdata/src/chaincodes/noop"),
MetadataRoot: filepath.FromSlash("testdata/src/chaincodes/noop/META-INF"),
Path: "chaincodes/noop",
})
require.NoError(t, err, "failed to find source")
Expand All @@ -184,8 +185,8 @@ func Test_findSource(t *testing.T) {
t.Run("Module", func(t *testing.T) {
source, err := findSource(&CodeDescriptor{
Module: true,
Source: filepath.Join("testdata/ccmodule"),
MetadataRoot: filepath.Join("testdata/ccmodule/META-INF"),
Source: filepath.FromSlash("testdata/ccmodule"),
MetadataRoot: filepath.FromSlash("testdata/ccmodule/META-INF"),
Path: "ccmodule",
})
require.NoError(t, err, "failed to find source")
Expand All @@ -202,7 +203,7 @@ func Test_findSource(t *testing.T) {
t.Run("NonExistent", func(t *testing.T) {
_, err := findSource(&CodeDescriptor{Path: "acme.com/this/should/not/exist"})
assert.Error(t, err)
assert.Contains(t, err.Error(), "no such file or directory")
assert.True(t, os.IsNotExist(errors.Cause(err)))
})
}

Expand Down Expand Up @@ -427,7 +428,7 @@ echo Done!
}

func TestDescribeCode(t *testing.T) {
abs, err := filepath.Abs("testdata/ccmodule")
abs, err := filepath.Abs(filepath.FromSlash("testdata/ccmodule"))
assert.NoError(t, err)

t.Run("TopLevelModulePackage", func(t *testing.T) {
Expand Down

0 comments on commit 42606a5

Please sign in to comment.