63 Star 183 Fork 3

Gitee 极速下载/hyperledger-fabric

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/hyperledger/fabric
Clone or Download
platform.go 15.26 KB
Copy Edit Raw Blame History
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package golang
import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"sort"
"github.com/hyperledger/fabric/core/chaincode/platforms/util"
cutil "github.com/hyperledger/fabric/core/container/util"
pb "github.com/hyperledger/fabric/protos/peer"
)
// Platform for chaincodes written in Go
type Platform struct {
}
// Returns whether the given file or directory exists or not
func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}
func decodeUrl(spec *pb.ChaincodeSpec) (string, error) {
var urlLocation string
if strings.HasPrefix(spec.ChaincodeId.Path, "http://") {
urlLocation = spec.ChaincodeId.Path[7:]
} else if strings.HasPrefix(spec.ChaincodeId.Path, "https://") {
urlLocation = spec.ChaincodeId.Path[8:]
} else {
urlLocation = spec.ChaincodeId.Path
}
if len(urlLocation) < 2 {
return "", errors.New("ChaincodeSpec's path/URL invalid")
}
if strings.LastIndex(urlLocation, "/") == len(urlLocation)-1 {
urlLocation = urlLocation[:len(urlLocation)-1]
}
return urlLocation, nil
}
func getGopath() (string, error) {
env, err := getGoEnv()
if err != nil {
return "", err
}
// Only take the first element of GOPATH
splitGoPath := filepath.SplitList(env["GOPATH"])
if len(splitGoPath) == 0 {
return "", fmt.Errorf("invalid GOPATH environment variable value:[%s]", env["GOPATH"])
}
return splitGoPath[0], nil
}
func filter(vs []string, f func(string) bool) []string {
vsf := make([]string, 0)
for _, v := range vs {
if f(v) {
vsf = append(vsf, v)
}
}
return vsf
}
// ValidateSpec validates Go chaincodes
func (goPlatform *Platform) ValidateSpec(spec *pb.ChaincodeSpec) error {
path, err := url.Parse(spec.ChaincodeId.Path)
if err != nil || path == nil {
return fmt.Errorf("invalid path: %s", err)
}
//we have no real good way of checking existence of remote urls except by downloading and testin
//which we do later anyway. But we *can* - and *should* - test for existence of local paths.
//Treat empty scheme as a local filesystem path
if path.Scheme == "" {
gopath, err := getGopath()
if err != nil {
return err
}
pathToCheck := filepath.Join(gopath, "src", spec.ChaincodeId.Path)
exists, err := pathExists(pathToCheck)
if err != nil {
return fmt.Errorf("error validating chaincode path: %s", err)
}
if !exists {
return fmt.Errorf("path to chaincode does not exist: %s", spec.ChaincodeId.Path)
}
}
return nil
}
func (goPlatform *Platform) ValidateDeploymentSpec(cds *pb.ChaincodeDeploymentSpec) error {
if cds.CodePackage == nil || len(cds.CodePackage) == 0 {
// Nothing to validate if no CodePackage was included
return nil
}
// FAB-2122: Scan the provided tarball to ensure it only contains source-code under
// /src/$packagename. We do not want to allow something like ./pkg/shady.a to be installed under
// $GOPATH within the container. Note, we do not look deeper than the path at this time
// with the knowledge that only the go/cgo compiler will execute for now. We will remove the source
// from the system after the compilation as an extra layer of protection.
//
// It should be noted that we cannot catch every threat with these techniques. Therefore,
// the container itself needs to be the last line of defense and be configured to be
// resilient in enforcing constraints. However, we should still do our best to keep as much
// garbage out of the system as possible.
re := regexp.MustCompile(`(/)?src/.*`)
is := bytes.NewReader(cds.CodePackage)
gr, err := gzip.NewReader(is)
if err != nil {
return fmt.Errorf("failure opening codepackage gzip stream: %s", err)
}
tr := tar.NewReader(gr)
for {
header, err := tr.Next()
if err != nil {
// We only get here if there are no more entries to scan
break
}
// --------------------------------------------------------------------------------------
// Check name for conforming path
// --------------------------------------------------------------------------------------
if !re.MatchString(header.Name) {
return fmt.Errorf("illegal file detected in payload: \"%s\"", header.Name)
}
// --------------------------------------------------------------------------------------
// Check that file mode makes sense
// --------------------------------------------------------------------------------------
// Acceptable flags:
// ISREG == 0100000
// -rw-rw-rw- == 0666
//
// Anything else is suspect in this context and will be rejected
// --------------------------------------------------------------------------------------
if header.Mode&^0100666 != 0 {
return fmt.Errorf("illegal file mode detected for file %s: %o", header.Name, header.Mode)
}
}
return nil
}
// Vendor any packages that are not already within our chaincode's primary package
// or vendored by it. We take the name of the primary package and a list of files
// that have been previously determined to comprise the package's dependencies.
// For anything that needs to be vendored, we simply update its path specification.
// Everything else, we pass through untouched.
func vendorDependencies(pkg string, files Sources) {
exclusions := make([]string, 0)
elements := strings.Split(pkg, "/")
// --------------------------------------------------------------------------------------
// First, add anything already vendored somewhere within our primary package to the
// "exclusions". For a package "foo/bar/baz", we want to ensure we don't auto-vendor
// any of the following:
//
// [ "foo/vendor", "foo/bar/vendor", "foo/bar/baz/vendor"]
//
// and we therefore employ a recursive path building process to form this list
// --------------------------------------------------------------------------------------
prev := filepath.Join("src")
for _, element := range elements {
curr := filepath.Join(prev, element)
vendor := filepath.Join(curr, "vendor")
exclusions = append(exclusions, vendor)
prev = curr
}
// --------------------------------------------------------------------------------------
// Next add our primary package to the list of "exclusions"
// --------------------------------------------------------------------------------------
exclusions = append(exclusions, filepath.Join("src", pkg))
count := len(files)
sem := make(chan bool, count)
// --------------------------------------------------------------------------------------
// Now start a parallel process which checks each file in files to see if it matches
// any of the excluded patterns. Any that match are renamed such that they are vendored
// under src/$pkg/vendor.
// --------------------------------------------------------------------------------------
vendorPath := filepath.Join("src", pkg, "vendor")
for i, file := range files {
go func(i int, file SourceDescriptor) {
excluded := false
for _, exclusion := range exclusions {
if strings.HasPrefix(file.Name, exclusion) == true {
excluded = true
break
}
}
if excluded == false {
origName := file.Name
file.Name = strings.Replace(origName, "src", vendorPath, 1)
logger.Debugf("vendoring %s -> %s", origName, file.Name)
}
files[i] = file
sem <- true
}(i, file)
}
for i := 0; i < count; i++ {
<-sem
}
}
// Generates a deployment payload for GOLANG as a series of src/$pkg entries in .tar.gz format
func (goPlatform *Platform) GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) {
var err error
// --------------------------------------------------------------------------------------
// retrieve a CodeDescriptor from either HTTP or the filesystem
// --------------------------------------------------------------------------------------
code, err := getCode(spec)
if err != nil {
return nil, err
}
if code.Cleanup != nil {
defer code.Cleanup()
}
// --------------------------------------------------------------------------------------
// Update our environment for the purposes of executing go-list directives
// --------------------------------------------------------------------------------------
env, err := getGoEnv()
if err != nil {
return nil, err
}
gopaths := splitEnvPaths(env["GOPATH"])
goroots := splitEnvPaths(env["GOROOT"])
gopaths[code.Gopath] = true
env["GOPATH"] = flattenEnvPaths(gopaths)
// --------------------------------------------------------------------------------------
// Retrieve the list of first-order imports referenced by the chaincode
// --------------------------------------------------------------------------------------
imports, err := listImports(env, code.Pkg)
if err != nil {
return nil, fmt.Errorf("Error obtaining imports: %s", err)
}
// --------------------------------------------------------------------------------------
// Remove any imports that are provided by the ccenv or system
// --------------------------------------------------------------------------------------
var provided = map[string]bool{
"github.com/hyperledger/fabric/core/chaincode/shim": true,
"github.com/hyperledger/fabric/protos/peer": true,
}
imports = filter(imports, func(pkg string) bool {
// Drop if provided by CCENV
if _, ok := provided[pkg]; ok == true {
logger.Debugf("Discarding provided package %s", pkg)
return false
}
// Drop if provided by GOROOT
for goroot := range goroots {
fqp := filepath.Join(goroot, "src", pkg)
exists, err := pathExists(fqp)
if err == nil && exists {
logger.Debugf("Discarding GOROOT package %s", pkg)
return false
}
}
// Else, we keep it
logger.Debugf("Accepting import: %s", pkg)
return true
})
// --------------------------------------------------------------------------------------
// Assemble the fully resolved list of direct and transitive dependencies based on the
// imports that remain after filtering
// --------------------------------------------------------------------------------------
deps := make(map[string]bool)
for _, pkg := range imports {
// ------------------------------------------------------------------------------
// Resolve direct import's transitives
// ------------------------------------------------------------------------------
transitives, err := listDeps(env, pkg)
if err != nil {
return nil, fmt.Errorf("Error obtaining dependencies for %s: %s", pkg, err)
}
// ------------------------------------------------------------------------------
// Merge all results with our top list
// ------------------------------------------------------------------------------
// Merge direct dependency...
deps[pkg] = true
// .. and then all transitives
for _, dep := range transitives {
deps[dep] = true
}
}
// cull "" if it exists
delete(deps, "")
// --------------------------------------------------------------------------------------
// Find the source from our first-order code package ...
// --------------------------------------------------------------------------------------
fileMap, err := findSource(code.Gopath, code.Pkg)
if err != nil {
return nil, err
}
// --------------------------------------------------------------------------------------
// ... followed by the source for any non-system dependencies that our code-package has
// from the filtered list
// --------------------------------------------------------------------------------------
for dep := range deps {
logger.Debugf("processing dep: %s", dep)
// Each dependency should either be in our GOPATH or GOROOT. We are not interested in packaging
// any of the system packages. However, the official way (go-list) to make this determination
// is too expensive to run for every dep. Therefore, we cheat. We assume that any packages that
// cannot be found must be system packages and silently skip them
for gopath := range gopaths {
fqp := filepath.Join(gopath, "src", dep)
exists, err := pathExists(fqp)
logger.Debugf("checking: %s exists: %v", fqp, exists)
if err == nil && exists {
// We only get here when we found it, so go ahead and load its code
files, err := findSource(gopath, dep)
if err != nil {
return nil, err
}
// Merge the map manually
for _, file := range files {
fileMap[file.Name] = file
}
}
}
}
logger.Debugf("done")
// --------------------------------------------------------------------------------------
// Reprocess into a list for easier handling going forward
// --------------------------------------------------------------------------------------
files := make(Sources, 0)
for _, file := range fileMap {
files = append(files, file)
}
// --------------------------------------------------------------------------------------
// Remap non-package dependencies to package/vendor
// --------------------------------------------------------------------------------------
vendorDependencies(code.Pkg, files)
// --------------------------------------------------------------------------------------
// Sort on the filename so the tarball at least looks sane in terms of package grouping
// --------------------------------------------------------------------------------------
sort.Sort(files)
// --------------------------------------------------------------------------------------
// Write out our tar package
// --------------------------------------------------------------------------------------
payload := bytes.NewBuffer(nil)
gw := gzip.NewWriter(payload)
tw := tar.NewWriter(gw)
for _, file := range files {
err = cutil.WriteFileToPackage(file.Path, file.Name, tw)
if err != nil {
return nil, fmt.Errorf("Error writing %s to tar: %s", file.Name, err)
}
}
tw.Close()
gw.Close()
return payload.Bytes(), nil
}
func (goPlatform *Platform) GenerateDockerfile(cds *pb.ChaincodeDeploymentSpec) (string, error) {
var buf []string
buf = append(buf, "FROM "+cutil.GetDockerfileFromConfig("chaincode.golang.runtime"))
buf = append(buf, "ADD binpackage.tar /usr/local/bin")
dockerFileContents := strings.Join(buf, "\n")
return dockerFileContents, nil
}
func (goPlatform *Platform) GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error {
spec := cds.ChaincodeSpec
pkgname, err := decodeUrl(spec)
if err != nil {
return fmt.Errorf("could not decode url: %s", err)
}
const ldflags = "-linkmode external -extldflags '-static'"
codepackage := bytes.NewReader(cds.CodePackage)
binpackage := bytes.NewBuffer(nil)
err = util.DockerBuild(util.DockerBuildOptions{
Cmd: fmt.Sprintf("GOPATH=/chaincode/input:$GOPATH go build -ldflags \"%s\" -o /chaincode/output/chaincode %s", ldflags, pkgname),
InputStream: codepackage,
OutputStream: binpackage,
})
if err != nil {
return err
}
return cutil.WriteBytesToPackage("binpackage.tar", binpackage.Bytes(), tw)
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mirrors/hyperledger-fabric.git
git@gitee.com:mirrors/hyperledger-fabric.git
mirrors
hyperledger-fabric
hyperledger-fabric
v1.0.0

Search