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 }