github.com/Hnampk/my-fabric@v0.0.0-20201028083322-75069da399c0/core/chaincode/platforms/platforms.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package platforms
     8  
     9  import (
    10  	"archive/tar"
    11  	"bytes"
    12  	"compress/gzip"
    13  	"fmt"
    14  	"io"
    15  	"strings"
    16  
    17  	docker "github.com/fsouza/go-dockerclient"
    18  	"github.com/hyperledger/fabric/common/flogging"
    19  	"github.com/hyperledger/fabric/common/metadata"
    20  	"github.com/hyperledger/fabric/core/chaincode/platforms/golang"
    21  	"github.com/hyperledger/fabric/core/chaincode/platforms/java"
    22  	"github.com/hyperledger/fabric/core/chaincode/platforms/node"
    23  	"github.com/hyperledger/fabric/core/chaincode/platforms/util"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  // SupportedPlatforms is the canonical list of platforms Fabric supports
    28  var SupportedPlatforms = []Platform{
    29  	&java.Platform{},
    30  	&golang.Platform{},
    31  	&node.Platform{},
    32  }
    33  
    34  // Interface for validating the specification and writing the package for
    35  // the given platform
    36  type Platform interface {
    37  	Name() string
    38  	GenerateDockerfile() (string, error)
    39  	DockerBuildOptions(path string) (util.DockerBuildOptions, error)
    40  }
    41  
    42  type PackageWriter interface {
    43  	Write(name string, payload []byte, tw *tar.Writer) error
    44  }
    45  
    46  type PackageWriterWrapper func(name string, payload []byte, tw *tar.Writer) error
    47  
    48  func (pw PackageWriterWrapper) Write(name string, payload []byte, tw *tar.Writer) error {
    49  	return pw(name, payload, tw)
    50  }
    51  
    52  type BuildFunc func(util.DockerBuildOptions, *docker.Client) error
    53  
    54  type Registry struct {
    55  	Platforms     map[string]Platform
    56  	PackageWriter PackageWriter
    57  	DockerBuild   BuildFunc
    58  }
    59  
    60  var logger = flogging.MustGetLogger("chaincode.platform")
    61  
    62  func NewRegistry(platformTypes ...Platform) *Registry {
    63  	platforms := make(map[string]Platform)
    64  	for _, platform := range platformTypes {
    65  		if _, ok := platforms[platform.Name()]; ok {
    66  			logger.Panicf("Multiple platforms of the same name specified: %s", platform.Name())
    67  		}
    68  		platforms[platform.Name()] = platform
    69  	}
    70  	return &Registry{
    71  		Platforms:     platforms,
    72  		PackageWriter: PackageWriterWrapper(writeBytesToPackage),
    73  		DockerBuild:   util.DockerBuild,
    74  	}
    75  }
    76  
    77  func (r *Registry) GenerateDockerfile(ccType string) (string, error) {
    78  	platform, ok := r.Platforms[ccType]
    79  	if !ok {
    80  		return "", fmt.Errorf("Unknown chaincodeType: %s", ccType)
    81  	}
    82  
    83  	var buf []string
    84  
    85  	// ----------------------------------------------------------------------------------------------------
    86  	// Let the platform define the base Dockerfile
    87  	// ----------------------------------------------------------------------------------------------------
    88  	base, err := platform.GenerateDockerfile()
    89  	if err != nil {
    90  		return "", fmt.Errorf("Failed to generate platform-specific Dockerfile: %s", err)
    91  	}
    92  	buf = append(buf, base)
    93  	buf = append(buf, fmt.Sprintf(`LABEL %s.chaincode.type="%s" \`, metadata.BaseDockerLabel, ccType))
    94  	buf = append(buf, fmt.Sprintf(`      %s.version="%s"`, metadata.BaseDockerLabel, metadata.Version))
    95  	// ----------------------------------------------------------------------------------------------------
    96  	// Then augment it with any general options
    97  	// ----------------------------------------------------------------------------------------------------
    98  	//append version so chaincode build version can be compared against peer build version
    99  	buf = append(buf, fmt.Sprintf("ENV CORE_CHAINCODE_BUILDLEVEL=%s", metadata.Version))
   100  
   101  	// ----------------------------------------------------------------------------------------------------
   102  	// Finalize it
   103  	// ----------------------------------------------------------------------------------------------------
   104  	contents := strings.Join(buf, "\n")
   105  	logger.Debugf("\n%s", contents)
   106  
   107  	return contents, nil
   108  }
   109  
   110  func (r *Registry) StreamDockerBuild(ccType, path string, codePackage io.Reader, inputFiles map[string][]byte, tw *tar.Writer, client *docker.Client) error {
   111  	var err error
   112  
   113  	// ----------------------------------------------------------------------------------------------------
   114  	// Determine our platform driver from the spec
   115  	// ----------------------------------------------------------------------------------------------------
   116  	platform, ok := r.Platforms[ccType]
   117  	if !ok {
   118  		return fmt.Errorf("could not find platform of type: %s", ccType)
   119  	}
   120  
   121  	// ----------------------------------------------------------------------------------------------------
   122  	// First stream out our static inputFiles
   123  	// ----------------------------------------------------------------------------------------------------
   124  	for name, data := range inputFiles {
   125  		err = r.PackageWriter.Write(name, data, tw)
   126  		if err != nil {
   127  			return fmt.Errorf(`Failed to inject "%s": %s`, name, err)
   128  		}
   129  	}
   130  
   131  	buildOptions, err := platform.DockerBuildOptions(path)
   132  	if err != nil {
   133  		return errors.Wrap(err, "platform failed to create docker build options")
   134  	}
   135  
   136  	output := &bytes.Buffer{}
   137  	buildOptions.InputStream = codePackage
   138  	buildOptions.OutputStream = output
   139  
   140  	err = r.DockerBuild(buildOptions, client)
   141  	if err != nil {
   142  		return errors.Wrap(err, "docker build failed")
   143  	}
   144  
   145  	return writeBytesToPackage("binpackage.tar", output.Bytes(), tw)
   146  }
   147  
   148  func writeBytesToPackage(name string, payload []byte, tw *tar.Writer) error {
   149  	err := tw.WriteHeader(&tar.Header{
   150  		Name: name,
   151  		Size: int64(len(payload)),
   152  		Mode: 0100644,
   153  	})
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	_, err = tw.Write(payload)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (r *Registry) GenerateDockerBuild(ccType, path string, codePackage io.Reader, client *docker.Client) (io.Reader, error) {
   167  	inputFiles := make(map[string][]byte)
   168  
   169  	// ----------------------------------------------------------------------------------------------------
   170  	// Generate the Dockerfile specific to our context
   171  	// ----------------------------------------------------------------------------------------------------
   172  	dockerFile, err := r.GenerateDockerfile(ccType)
   173  	if err != nil {
   174  		return nil, fmt.Errorf("Failed to generate a Dockerfile: %s", err)
   175  	}
   176  
   177  	inputFiles["Dockerfile"] = []byte(dockerFile)
   178  
   179  	// ----------------------------------------------------------------------------------------------------
   180  	// Finally, launch an asynchronous process to stream all of the above into a docker build context
   181  	// ----------------------------------------------------------------------------------------------------
   182  	input, output := io.Pipe()
   183  
   184  	go func() {
   185  		gw := gzip.NewWriter(output)
   186  		tw := tar.NewWriter(gw)
   187  		err := r.StreamDockerBuild(ccType, path, codePackage, inputFiles, tw, client)
   188  		if err != nil {
   189  			logger.Error(err)
   190  		}
   191  
   192  		tw.Close()
   193  		gw.Close()
   194  		output.CloseWithError(err)
   195  	}()
   196  
   197  	return input, nil
   198  }