github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/chaincode/platforms/platforms.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package platforms
    18  
    19  import (
    20  	"archive/tar"
    21  	"compress/gzip"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  
    26  	"strings"
    27  
    28  	"io/ioutil"
    29  
    30  	"github.com/hyperledger/fabric/common/flogging"
    31  	"github.com/hyperledger/fabric/common/metadata"
    32  	"github.com/hyperledger/fabric/core/chaincode/platforms/car"
    33  	"github.com/hyperledger/fabric/core/chaincode/platforms/golang"
    34  	"github.com/hyperledger/fabric/core/chaincode/platforms/java"
    35  	"github.com/hyperledger/fabric/core/config"
    36  	cutil "github.com/hyperledger/fabric/core/container/util"
    37  	pb "github.com/hyperledger/fabric/protos/peer"
    38  	"github.com/spf13/viper"
    39  )
    40  
    41  // Interface for validating the specification and and writing the package for
    42  // the given platform
    43  type Platform interface {
    44  	ValidateSpec(spec *pb.ChaincodeSpec) error
    45  	ValidateDeploymentSpec(spec *pb.ChaincodeDeploymentSpec) error
    46  	GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error)
    47  	GenerateDockerfile(spec *pb.ChaincodeDeploymentSpec) (string, error)
    48  	GenerateDockerBuild(spec *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error
    49  }
    50  
    51  var logger = flogging.MustGetLogger("chaincode-platform")
    52  
    53  // Find returns the platform interface for the given platform type
    54  func Find(chaincodeType pb.ChaincodeSpec_Type) (Platform, error) {
    55  
    56  	switch chaincodeType {
    57  	case pb.ChaincodeSpec_GOLANG:
    58  		return &golang.Platform{}, nil
    59  	case pb.ChaincodeSpec_CAR:
    60  		return &car.Platform{}, nil
    61  	case pb.ChaincodeSpec_JAVA:
    62  		return &java.Platform{}, nil
    63  	default:
    64  		return nil, fmt.Errorf("Unknown chaincodeType: %s", chaincodeType)
    65  	}
    66  
    67  }
    68  
    69  func GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) {
    70  	platform, err := Find(spec.Type)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return platform.GetDeploymentPayload(spec)
    76  }
    77  
    78  func getPeerTLSCert() ([]byte, error) {
    79  
    80  	if viper.GetBool("peer.tls.enabled") == false {
    81  		// no need for certificates if TLS is not enabled
    82  		return nil, nil
    83  	}
    84  	var path string
    85  	// first we check for the rootcert
    86  	path = config.GetPath("peer.tls.rootcert.file")
    87  	if path == "" {
    88  		// check for tls cert
    89  		path = config.GetPath("peer.tls.cert.file")
    90  	}
    91  	// this should not happen if the peer is running with TLS enabled
    92  	if _, err := os.Stat(path); err != nil {
    93  		return nil, err
    94  	}
    95  	// FIXME: FAB-2037 - ensure we sanely resolve relative paths specified in the yaml
    96  	return ioutil.ReadFile(path)
    97  }
    98  
    99  func generateDockerfile(platform Platform, cds *pb.ChaincodeDeploymentSpec, tls bool) ([]byte, error) {
   100  
   101  	var buf []string
   102  
   103  	// ----------------------------------------------------------------------------------------------------
   104  	// Let the platform define the base Dockerfile
   105  	// ----------------------------------------------------------------------------------------------------
   106  	base, err := platform.GenerateDockerfile(cds)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("Failed to generate platform-specific Dockerfile: %s", err)
   109  	}
   110  
   111  	buf = append(buf, base)
   112  
   113  	// ----------------------------------------------------------------------------------------------------
   114  	// Add some handy labels
   115  	// ----------------------------------------------------------------------------------------------------
   116  	buf = append(buf, fmt.Sprintf("LABEL %s.chaincode.id.name=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.ChaincodeId.Name))
   117  	buf = append(buf, fmt.Sprintf("      %s.chaincode.id.version=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.ChaincodeId.Version))
   118  	buf = append(buf, fmt.Sprintf("      %s.chaincode.type=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.Type.String()))
   119  	buf = append(buf, fmt.Sprintf("      %s.version=\"%s\" \\", metadata.BaseDockerLabel, metadata.Version))
   120  	buf = append(buf, fmt.Sprintf("      %s.base.version=\"%s\"", metadata.BaseDockerLabel, metadata.BaseVersion))
   121  
   122  	// ----------------------------------------------------------------------------------------------------
   123  	// Then augment it with any general options
   124  	// ----------------------------------------------------------------------------------------------------
   125  	//append version so chaincode build version can be campared against peer build version
   126  	buf = append(buf, fmt.Sprintf("ENV CORE_CHAINCODE_BUILDLEVEL=%s", metadata.Version))
   127  
   128  	if tls {
   129  		const guestTLSPath = "/etc/hyperledger/fabric/peer.crt"
   130  
   131  		buf = append(buf, "ENV CORE_PEER_TLS_ROOTCERT_FILE="+guestTLSPath)
   132  		buf = append(buf, "COPY peer.crt "+guestTLSPath)
   133  	}
   134  
   135  	// ----------------------------------------------------------------------------------------------------
   136  	// Finalize it
   137  	// ----------------------------------------------------------------------------------------------------
   138  	contents := strings.Join(buf, "\n")
   139  	logger.Debugf("\n%s", contents)
   140  
   141  	return []byte(contents), nil
   142  }
   143  
   144  type InputFiles map[string][]byte
   145  
   146  func generateDockerBuild(platform Platform, cds *pb.ChaincodeDeploymentSpec, inputFiles InputFiles, tw *tar.Writer) error {
   147  
   148  	var err error
   149  
   150  	// ----------------------------------------------------------------------------------------------------
   151  	// First stream out our static inputFiles
   152  	// ----------------------------------------------------------------------------------------------------
   153  	for name, data := range inputFiles {
   154  		err = cutil.WriteBytesToPackage(name, data, tw)
   155  		if err != nil {
   156  			return fmt.Errorf("Failed to inject \"%s\": %s", name, err)
   157  		}
   158  	}
   159  
   160  	// ----------------------------------------------------------------------------------------------------
   161  	// Now give the platform an opportunity to contribute its own context to the build
   162  	// ----------------------------------------------------------------------------------------------------
   163  	err = platform.GenerateDockerBuild(cds, tw)
   164  	if err != nil {
   165  		return fmt.Errorf("Failed to generate platform-specific docker build: %s", err)
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec) (io.Reader, error) {
   172  
   173  	inputFiles := make(InputFiles)
   174  
   175  	// ----------------------------------------------------------------------------------------------------
   176  	// Determine our platform driver from the spec
   177  	// ----------------------------------------------------------------------------------------------------
   178  	platform, err := Find(cds.ChaincodeSpec.Type)
   179  	if err != nil {
   180  		return nil, fmt.Errorf("Failed to determine platform type: %s", err)
   181  	}
   182  
   183  	// ----------------------------------------------------------------------------------------------------
   184  	// Transfer the peer's TLS certificate to our list of input files, if applicable
   185  	// ----------------------------------------------------------------------------------------------------
   186  	// NOTE: We bake the peer TLS certificate in at the time we build the chaincode container if a cert is
   187  	// found, regardless of whether TLS is enabled or not.  The main implication is that if the administrator
   188  	// updates the peer cert, the chaincode containers will need to be invalidated and rebuilt.
   189  	// We will manage enabling or disabling TLS at container run time via CORE_PEER_TLS_ENABLED
   190  	cert, err := getPeerTLSCert()
   191  	if err != nil {
   192  		return nil, fmt.Errorf("Failed to read the TLS certificate: %s", err)
   193  	}
   194  
   195  	inputFiles["peer.crt"] = cert
   196  
   197  	// ----------------------------------------------------------------------------------------------------
   198  	// Generate the Dockerfile specific to our context
   199  	// ----------------------------------------------------------------------------------------------------
   200  	dockerFile, err := generateDockerfile(platform, cds, cert != nil)
   201  	if err != nil {
   202  		return nil, fmt.Errorf("Failed to generate a Dockerfile: %s", err)
   203  	}
   204  
   205  	inputFiles["Dockerfile"] = dockerFile
   206  
   207  	// ----------------------------------------------------------------------------------------------------
   208  	// Finally, launch an asynchronous process to stream all of the above into a docker build context
   209  	// ----------------------------------------------------------------------------------------------------
   210  	input, output := io.Pipe()
   211  
   212  	go func() {
   213  		gw := gzip.NewWriter(output)
   214  		tw := tar.NewWriter(gw)
   215  
   216  		err := generateDockerBuild(platform, cds, inputFiles, tw)
   217  		if err != nil {
   218  			logger.Error(err)
   219  		}
   220  
   221  		tw.Close()
   222  		gw.Close()
   223  		output.CloseWithError(err)
   224  	}()
   225  
   226  	return input, nil
   227  }