github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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/metadata"
    31  	"github.com/hyperledger/fabric/core/chaincode/platforms/car"
    32  	"github.com/hyperledger/fabric/core/chaincode/platforms/golang"
    33  	"github.com/hyperledger/fabric/core/chaincode/platforms/java"
    34  	cutil "github.com/hyperledger/fabric/core/container/util"
    35  	pb "github.com/hyperledger/fabric/protos/peer"
    36  	"github.com/op/go-logging"
    37  	"github.com/spf13/viper"
    38  )
    39  
    40  // Interface for validating the specification and and writing the package for
    41  // the given platform
    42  type Platform interface {
    43  	ValidateSpec(spec *pb.ChaincodeSpec) error
    44  	ValidateDeploymentSpec(spec *pb.ChaincodeDeploymentSpec) error
    45  	GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error)
    46  	GenerateDockerfile(spec *pb.ChaincodeDeploymentSpec) (string, error)
    47  	GenerateDockerBuild(spec *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error
    48  }
    49  
    50  var logger = logging.MustGetLogger("chaincode-platform")
    51  
    52  // Find returns the platform interface for the given platform type
    53  func Find(chaincodeType pb.ChaincodeSpec_Type) (Platform, error) {
    54  
    55  	switch chaincodeType {
    56  	case pb.ChaincodeSpec_GOLANG:
    57  		return &golang.Platform{}, nil
    58  	case pb.ChaincodeSpec_CAR:
    59  		return &car.Platform{}, nil
    60  	case pb.ChaincodeSpec_JAVA:
    61  		return &java.Platform{}, nil
    62  	default:
    63  		return nil, fmt.Errorf("Unknown chaincodeType: %s", chaincodeType)
    64  	}
    65  
    66  }
    67  
    68  func GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) {
    69  	platform, err := Find(spec.Type)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	return platform.GetDeploymentPayload(spec)
    75  }
    76  
    77  func getPeerTLSCert() ([]byte, error) {
    78  	path := viper.GetString("peer.tls.cert.file")
    79  	if _, err := os.Stat(path); err != nil {
    80  
    81  		if os.IsNotExist(err) && viper.GetBool("peer.tls.enabled") == false {
    82  			// It's not an error if the file doesn't exist but TLS is disabled anyway
    83  			return nil, nil
    84  		}
    85  
    86  		return nil, err
    87  	}
    88  
    89  	// FIXME: FAB-2037 - ensure we sanely resolve relative paths specified in the yaml
    90  	return ioutil.ReadFile(path)
    91  }
    92  
    93  func generateDockerfile(platform Platform, cds *pb.ChaincodeDeploymentSpec, tls bool) ([]byte, error) {
    94  
    95  	var buf []string
    96  
    97  	// ----------------------------------------------------------------------------------------------------
    98  	// Let the platform define the base Dockerfile
    99  	// ----------------------------------------------------------------------------------------------------
   100  	base, err := platform.GenerateDockerfile(cds)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("Failed to generate platform-specific Dockerfile: %s", err)
   103  	}
   104  
   105  	buf = append(buf, base)
   106  
   107  	// ----------------------------------------------------------------------------------------------------
   108  	// Add some handy labels
   109  	// ----------------------------------------------------------------------------------------------------
   110  	buf = append(buf, fmt.Sprintf("LABEL %s.chaincode.id.name=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.ChaincodeId.Name))
   111  	buf = append(buf, fmt.Sprintf("      %s.chaincode.id.version=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.ChaincodeId.Version))
   112  	buf = append(buf, fmt.Sprintf("      %s.chaincode.type=\"%s\" \\", metadata.BaseDockerLabel, cds.ChaincodeSpec.Type.String()))
   113  	buf = append(buf, fmt.Sprintf("      %s.version=\"%s\" \\", metadata.BaseDockerLabel, metadata.Version))
   114  	buf = append(buf, fmt.Sprintf("      %s.base.version=\"%s\"", metadata.BaseDockerLabel, metadata.BaseVersion))
   115  
   116  	// ----------------------------------------------------------------------------------------------------
   117  	// Then augment it with any general options
   118  	// ----------------------------------------------------------------------------------------------------
   119  	if tls {
   120  		const guestTLSPath = "/etc/hyperledger/fabric/peer.crt"
   121  
   122  		buf = append(buf, "ENV CORE_PEER_TLS_CERT_FILE="+guestTLSPath)
   123  		buf = append(buf, "COPY peer.crt "+guestTLSPath)
   124  	}
   125  
   126  	// ----------------------------------------------------------------------------------------------------
   127  	// Finalize it
   128  	// ----------------------------------------------------------------------------------------------------
   129  	contents := strings.Join(buf, "\n")
   130  	logger.Debugf("\n%s", contents)
   131  
   132  	return []byte(contents), nil
   133  }
   134  
   135  type InputFiles map[string][]byte
   136  
   137  func generateDockerBuild(platform Platform, cds *pb.ChaincodeDeploymentSpec, inputFiles InputFiles, tw *tar.Writer) error {
   138  
   139  	var err error
   140  
   141  	// ----------------------------------------------------------------------------------------------------
   142  	// First stream out our static inputFiles
   143  	// ----------------------------------------------------------------------------------------------------
   144  	for name, data := range inputFiles {
   145  		err = cutil.WriteBytesToPackage(name, data, tw)
   146  		if err != nil {
   147  			return fmt.Errorf("Failed to inject \"%s\": %s", name, err)
   148  		}
   149  	}
   150  
   151  	// ----------------------------------------------------------------------------------------------------
   152  	// Now give the platform an opportunity to contribute its own context to the build
   153  	// ----------------------------------------------------------------------------------------------------
   154  	err = platform.GenerateDockerBuild(cds, tw)
   155  	if err != nil {
   156  		return fmt.Errorf("Failed to generate platform-specific docker build: %s", err)
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  func GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec) (io.Reader, error) {
   163  
   164  	inputFiles := make(InputFiles)
   165  
   166  	// ----------------------------------------------------------------------------------------------------
   167  	// Determine our platform driver from the spec
   168  	// ----------------------------------------------------------------------------------------------------
   169  	platform, err := Find(cds.ChaincodeSpec.Type)
   170  	if err != nil {
   171  		return nil, fmt.Errorf("Failed to determine platform type: %s", err)
   172  	}
   173  
   174  	// ----------------------------------------------------------------------------------------------------
   175  	// Transfer the peer's TLS certificate to our list of input files, if applicable
   176  	// ----------------------------------------------------------------------------------------------------
   177  	// NOTE: We bake the peer TLS certificate in at the time we build the chaincode container if a cert is
   178  	// found, regardless of whether TLS is enabled or not.  The main implication is that if the administrator
   179  	// updates the peer cert, the chaincode containers will need to be invalidated and rebuilt.
   180  	// We will manage enabling or disabling TLS at container run time via CORE_PEER_TLS_ENABLED
   181  	cert, err := getPeerTLSCert()
   182  	if err != nil {
   183  		return nil, fmt.Errorf("Failed to read the TLS certificate: %s", err)
   184  	}
   185  
   186  	inputFiles["peer.crt"] = cert
   187  
   188  	// ----------------------------------------------------------------------------------------------------
   189  	// Generate the Dockerfile specific to our context
   190  	// ----------------------------------------------------------------------------------------------------
   191  	dockerFile, err := generateDockerfile(platform, cds, cert != nil)
   192  	if err != nil {
   193  		return nil, fmt.Errorf("Failed to generate a Dockerfile: %s", err)
   194  	}
   195  
   196  	inputFiles["Dockerfile"] = dockerFile
   197  
   198  	// ----------------------------------------------------------------------------------------------------
   199  	// Finally, launch an asynchronous process to stream all of the above into a docker build context
   200  	// ----------------------------------------------------------------------------------------------------
   201  	input, output := io.Pipe()
   202  
   203  	go func() {
   204  		gw := gzip.NewWriter(output)
   205  		tw := tar.NewWriter(gw)
   206  
   207  		err := generateDockerBuild(platform, cds, inputFiles, tw)
   208  		if err != nil {
   209  			logger.Error(err)
   210  		}
   211  
   212  		tw.Close()
   213  		gw.Close()
   214  		output.CloseWithError(err)
   215  	}()
   216  
   217  	return input, nil
   218  }