代码拉取完成,页面将自动刷新
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package golang
import (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/core/chaincode/platforms/util"
"github.com/hyperledger/fabric/internal/ccmetadata"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
// Platform for chaincodes written in Go
type Platform struct{}
// Name returns the name of this platform.
func (p *Platform) Name() string {
return pb.ChaincodeSpec_GOLANG.String()
}
// ValidatePath is used to ensure that path provided points to something that
// looks like go chainccode.
//
// NOTE: this is only used at the _client_ side by the peer CLI.
func (p *Platform) ValidatePath(rawPath string) error {
_, err := DescribeCode(rawPath)
if err != nil {
return err
}
return nil
}
// NormalizePath is used to extract a relative module path from a module root.
// This should not impact legacy GOPATH chaincode.
//
// NOTE: this is only used at the _client_ side by the peer CLI.
func (p *Platform) NormalizePath(rawPath string) (string, error) {
modInfo, err := moduleInfo(rawPath)
if err != nil {
return "", err
}
// not a module
if modInfo == nil {
return rawPath, nil
}
return modInfo.ImportPath, nil
}
// ValidateCodePackage examines the chaincode archive to ensure it is valid.
//
// NOTE: this code is used in some transaction validation paths but can be changed
// post 2.0.
func (p *Platform) ValidateCodePackage(code []byte) error {
is := bytes.NewReader(code)
gr, err := gzip.NewReader(is)
if err != nil {
return fmt.Errorf("failure opening codepackage gzip stream: %s", err)
}
re := regexp.MustCompile(`^(src|META-INF)/`)
tr := tar.NewReader(gr)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
// maintain check for conforming paths for validation
if !re.MatchString(header.Name) {
return fmt.Errorf("illegal file name in payload: %s", header.Name)
}
// only files and directories; no links or special files
mode := header.FileInfo().Mode()
if mode&^(os.ModeDir|0777) != 0 {
return fmt.Errorf("illegal file mode in payload: %s", header.Name)
}
}
return nil
}
// Directory constant copied from tar package.
const c_ISDIR = 040000
// Default compression to use for production. Test packages disable compression.
var gzipCompressionLevel = gzip.DefaultCompression
// GetDeploymentPayload creates a gzip compressed tape archive that contains the
// required assets to build and run go chaincode.
//
// NOTE: this is only used at the _client_ side by the peer CLI.
func (p *Platform) GetDeploymentPayload(codepath string) ([]byte, error) {
codeDescriptor, err := DescribeCode(codepath)
if err != nil {
return nil, err
}
fileMap, err := findSource(codeDescriptor)
if err != nil {
return nil, err
}
var dependencyPackageInfo []PackageInfo
if !codeDescriptor.Module {
for _, dist := range distributions() {
pi, err := gopathDependencyPackageInfo(dist.goos, dist.goarch, codeDescriptor.Path)
if err != nil {
return nil, err
}
dependencyPackageInfo = append(dependencyPackageInfo, pi...)
}
}
for _, pkg := range dependencyPackageInfo {
for _, filename := range pkg.Files() {
sd := SourceDescriptor{
Name: path.Join("src", pkg.ImportPath, filename),
Path: filepath.Join(pkg.Dir, filename),
}
fileMap[sd.Name] = sd
}
}
payload := bytes.NewBuffer(nil)
gw, err := gzip.NewWriterLevel(payload, gzipCompressionLevel)
if err != nil {
return nil, err
}
tw := tar.NewWriter(gw)
// Create directories so they get sane ownership and permissions
for _, dirname := range fileMap.Directories() {
err := tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeDir,
Name: dirname + "/",
Mode: c_ISDIR | 0755,
Uid: 500,
Gid: 500,
})
if err != nil {
return nil, err
}
}
for _, file := range fileMap.Sources() {
err = util.WriteFileToPackage(file.Path, file.Name, tw)
if err != nil {
return nil, fmt.Errorf("Error writing %s to tar: %s", file.Name, err)
}
}
err = tw.Close()
if err == nil {
err = gw.Close()
}
if err != nil {
return nil, errors.Wrapf(err, "failed to create tar for chaincode")
}
return payload.Bytes(), nil
}
func (p *Platform) GenerateDockerfile() (string, error) {
var buf []string
buf = append(buf, "FROM "+util.GetDockerImageFromConfig("chaincode.golang.runtime"))
buf = append(buf, "ADD binpackage.tar /usr/local/bin")
return strings.Join(buf, "\n"), nil
}
const staticLDFlagsOpts = "-ldflags \"-linkmode external -extldflags '-static'\""
const dynamicLDFlagsOpts = ""
func getLDFlagsOpts() string {
if viper.GetBool("chaincode.golang.dynamicLink") {
return dynamicLDFlagsOpts
}
return staticLDFlagsOpts
}
var buildScript = `
set -e
if [ -f "/chaincode/input/src/go.mod" ] && [ -d "/chaincode/input/src/vendor" ]; then
cd /chaincode/input/src
GO111MODULE=on go build -v -mod=vendor %[1]s -o /chaincode/output/chaincode %[2]s
elif [ -f "/chaincode/input/src/go.mod" ]; then
cd /chaincode/input/src
GO111MODULE=on go build -v -mod=readonly %[1]s -o /chaincode/output/chaincode %[2]s
elif [ -f "/chaincode/input/src/%[2]s/go.mod" ] && [ -d "/chaincode/input/src/%[2]s/vendor" ]; then
cd /chaincode/input/src/%[2]s
GO111MODULE=on go build -v -mod=vendor %[1]s -o /chaincode/output/chaincode .
elif [ -f "/chaincode/input/src/%[2]s/go.mod" ]; then
cd /chaincode/input/src/%[2]s
GO111MODULE=on go build -v -mod=readonly %[1]s -o /chaincode/output/chaincode .
else
GOPATH=/chaincode/input:$GOPATH go build -v %[1]s -o /chaincode/output/chaincode %[2]s
fi
echo Done!
`
func (p *Platform) DockerBuildOptions(path string) (util.DockerBuildOptions, error) {
env := []string{}
for _, key := range []string{"GOPROXY", "GOSUMDB"} {
if val, ok := os.LookupEnv(key); ok {
env = append(env, fmt.Sprintf("%s=%s", key, val))
continue
}
if key == "GOPROXY" {
env = append(env, "GOPROXY=https://proxy.golang.org")
}
}
ldFlagOpts := getLDFlagsOpts()
return util.DockerBuildOptions{
Cmd: fmt.Sprintf(buildScript, ldFlagOpts, path),
Env: env,
}, nil
}
// CodeDescriptor describes the code we're packaging.
type CodeDescriptor struct {
Source string // absolute path of the source to package
MetadataRoot string // absolute path META-INF
Path string // import path of the package
Module bool // does this represent a go module
}
func (cd CodeDescriptor) isMetadata(path string) bool {
return strings.HasPrefix(
filepath.Clean(path),
filepath.Clean(cd.MetadataRoot),
)
}
// DescribeCode returns GOPATH and package information.
func DescribeCode(path string) (*CodeDescriptor, error) {
if path == "" {
return nil, errors.New("cannot collect files from empty chaincode path")
}
// Use the module root as the source path for go modules
modInfo, err := moduleInfo(path)
if err != nil {
return nil, err
}
if modInfo != nil {
// calculate where the metadata should be relative to module root
relImport, err := filepath.Rel(modInfo.ModulePath, modInfo.ImportPath)
if err != nil {
return nil, err
}
return &CodeDescriptor{
Module: true,
MetadataRoot: filepath.Join(modInfo.Dir, relImport, "META-INF"),
Path: modInfo.ImportPath,
Source: modInfo.Dir,
}, nil
}
return describeGopath(path)
}
func describeGopath(importPath string) (*CodeDescriptor, error) {
output, err := exec.Command("go", "list", "-f", "{{.Dir}}", importPath).Output()
if err != nil {
return nil, wrapExitErr(err, "'go list' failed")
}
sourcePath := filepath.Clean(strings.TrimSpace(string(output)))
return &CodeDescriptor{
Path: importPath,
MetadataRoot: filepath.Join(sourcePath, "META-INF"),
Source: sourcePath,
}, nil
}
func regularFileExists(path string) (bool, error) {
fi, err := os.Stat(path)
switch {
case os.IsNotExist(err):
return false, nil
case err != nil:
return false, err
default:
return fi.Mode().IsRegular(), nil
}
}
func moduleInfo(path string) (*ModuleInfo, error) {
entryWD, err := os.Getwd()
if err != nil {
return nil, errors.Wrap(err, "failed to get working directory")
}
// directory doesn't exist so unlikely to be a module
if err := os.Chdir(path); err != nil {
return nil, nil
}
defer func() {
if err := os.Chdir(entryWD); err != nil {
panic(fmt.Sprintf("failed to restore working directory: %s", err))
}
}()
// Using `go list -m -f '{{ if .Main }}{{.GoMod}}{{ end }}' all` may try to
// generate a go.mod when a vendor tool is in use. To avoid that behavior
// we use `go env GOMOD` followed by an existence check.
cmd := exec.Command("go", "env", "GOMOD")
cmd.Env = append(os.Environ(), "GO111MODULE=on")
output, err := cmd.Output()
if err != nil {
return nil, wrapExitErr(err, "failed to determine module root")
}
modExists, err := regularFileExists(strings.TrimSpace(string(output)))
if err != nil {
return nil, err
}
if !modExists {
return nil, nil
}
return listModuleInfo()
}
type SourceDescriptor struct {
Name string
Path string
}
type Sources []SourceDescriptor
func (s Sources) Len() int { return len(s) }
func (s Sources) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s Sources) Less(i, j int) bool { return s[i].Name < s[j].Name }
type SourceMap map[string]SourceDescriptor
func (s SourceMap) Sources() Sources {
var sources Sources
for _, src := range s {
sources = append(sources, src)
}
sort.Sort(sources)
return sources
}
func (s SourceMap) Directories() []string {
dirMap := map[string]bool{}
for entryName := range s {
dir := path.Dir(entryName)
for dir != "." && !dirMap[dir] {
dirMap[dir] = true
dir = path.Dir(dir)
}
}
var dirs []string
for dir := range dirMap {
dirs = append(dirs, dir)
}
sort.Strings(dirs)
return dirs
}
func findSource(cd *CodeDescriptor) (SourceMap, error) {
sources := SourceMap{}
walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
// Allow import of the top level chaincode directory into chaincode code package
if path == cd.Source {
return nil
}
// Allow import of META-INF metadata directories into chaincode code package tar.
// META-INF directories contain chaincode metadata artifacts such as statedb index definitions
if cd.isMetadata(path) {
return nil
}
// include everything except hidden dirs when we're not vendoring
if cd.Module && !strings.HasPrefix(info.Name(), ".") {
return nil
}
// Do not import any other directories into chaincode code package
return filepath.SkipDir
}
relativeRoot := cd.Source
if cd.isMetadata(path) {
relativeRoot = cd.MetadataRoot
}
name, err := filepath.Rel(relativeRoot, path)
if err != nil {
return errors.Wrapf(err, "failed to calculate relative path for %s", path)
}
switch {
case cd.isMetadata(path):
// Skip hidden files in metadata
if strings.HasPrefix(info.Name(), ".") {
return nil
}
name = filepath.Join("META-INF", name)
err := validateMetadata(name, path)
if err != nil {
return err
}
case cd.Module:
name = filepath.Join("src", name)
default:
// skip top level go.mod and go.sum when not in module mode
if name == "go.mod" || name == "go.sum" {
return nil
}
name = filepath.Join("src", cd.Path, name)
}
name = filepath.ToSlash(name)
sources[name] = SourceDescriptor{Name: name, Path: path}
return nil
}
if err := filepath.Walk(cd.Source, walkFn); err != nil {
return nil, errors.Wrap(err, "walk failed")
}
return sources, nil
}
func validateMetadata(name, path string) error {
contents, err := ioutil.ReadFile(path)
if err != nil {
return err
}
// Validate metadata file for inclusion in tar
// Validation is based on the passed filename with path
err = ccmetadata.ValidateMetadataFile(filepath.ToSlash(name), contents)
if err != nil {
return err
}
return nil
}
// dist holds go "distribution" information. The full list of distributions can
// be obtained with `go tool dist list.
type dist struct{ goos, goarch string }
// distributions returns the list of OS and ARCH combinations that we calcluate
// deps for.
func distributions() []dist {
// pre-populate linux architecutures
dists := map[dist]bool{
{goos: "linux", goarch: "amd64"}: true,
{goos: "linux", goarch: "s390x"}: true,
}
// add local OS and ARCH
dists[dist{goos: runtime.GOOS, goarch: runtime.GOARCH}] = true
var list []dist
for d := range dists {
list = append(list, d)
}
return list
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。