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 }