github.com/zhouxv/fabric@v2.1.1+incompatible/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 Registry struct { 53 Platforms map[string]Platform 54 PackageWriter PackageWriter 55 } 56 57 var logger = flogging.MustGetLogger("chaincode.platform") 58 59 func NewRegistry(platformTypes ...Platform) *Registry { 60 platforms := make(map[string]Platform) 61 for _, platform := range platformTypes { 62 if _, ok := platforms[platform.Name()]; ok { 63 logger.Panicf("Multiple platforms of the same name specified: %s", platform.Name()) 64 } 65 platforms[platform.Name()] = platform 66 } 67 return &Registry{ 68 Platforms: platforms, 69 PackageWriter: PackageWriterWrapper(writeBytesToPackage), 70 } 71 } 72 73 func (r *Registry) GenerateDockerfile(ccType string) (string, error) { 74 platform, ok := r.Platforms[ccType] 75 if !ok { 76 return "", fmt.Errorf("Unknown chaincodeType: %s", ccType) 77 } 78 79 var buf []string 80 81 // ---------------------------------------------------------------------------------------------------- 82 // Let the platform define the base Dockerfile 83 // ---------------------------------------------------------------------------------------------------- 84 base, err := platform.GenerateDockerfile() 85 if err != nil { 86 return "", fmt.Errorf("Failed to generate platform-specific Dockerfile: %s", err) 87 } 88 buf = append(buf, base) 89 buf = append(buf, fmt.Sprintf(`LABEL %s.chaincode.type="%s" \`, metadata.BaseDockerLabel, ccType)) 90 buf = append(buf, fmt.Sprintf(` %s.version="%s"`, metadata.BaseDockerLabel, metadata.Version)) 91 // ---------------------------------------------------------------------------------------------------- 92 // Then augment it with any general options 93 // ---------------------------------------------------------------------------------------------------- 94 //append version so chaincode build version can be compared against peer build version 95 buf = append(buf, fmt.Sprintf("ENV CORE_CHAINCODE_BUILDLEVEL=%s", metadata.Version)) 96 97 // ---------------------------------------------------------------------------------------------------- 98 // Finalize it 99 // ---------------------------------------------------------------------------------------------------- 100 contents := strings.Join(buf, "\n") 101 logger.Debugf("\n%s", contents) 102 103 return contents, nil 104 } 105 106 func (r *Registry) StreamDockerBuild(ccType, path string, codePackage io.Reader, inputFiles map[string][]byte, tw *tar.Writer, client *docker.Client) error { 107 var err error 108 109 // ---------------------------------------------------------------------------------------------------- 110 // Determine our platform driver from the spec 111 // ---------------------------------------------------------------------------------------------------- 112 platform, ok := r.Platforms[ccType] 113 if !ok { 114 return fmt.Errorf("could not find platform of type: %s", ccType) 115 } 116 117 // ---------------------------------------------------------------------------------------------------- 118 // First stream out our static inputFiles 119 // ---------------------------------------------------------------------------------------------------- 120 for name, data := range inputFiles { 121 err = r.PackageWriter.Write(name, data, tw) 122 if err != nil { 123 return fmt.Errorf(`Failed to inject "%s": %s`, name, err) 124 } 125 } 126 127 buildOptions, err := platform.DockerBuildOptions(path) 128 if err != nil { 129 return errors.Wrap(err, "platform failed to create docker build options") 130 } 131 132 output := &bytes.Buffer{} 133 buildOptions.InputStream = codePackage 134 buildOptions.OutputStream = output 135 136 err = util.DockerBuild(buildOptions, client) 137 if err != nil { 138 return errors.Wrap(err, "docker build failed") 139 } 140 141 return writeBytesToPackage("binpackage.tar", output.Bytes(), tw) 142 } 143 144 func writeBytesToPackage(name string, payload []byte, tw *tar.Writer) error { 145 err := tw.WriteHeader(&tar.Header{ 146 Name: name, 147 Size: int64(len(payload)), 148 Mode: 0100644, 149 }) 150 if err != nil { 151 return err 152 } 153 154 _, err = tw.Write(payload) 155 if err != nil { 156 return err 157 } 158 159 return nil 160 } 161 162 func (r *Registry) GenerateDockerBuild(ccType, path string, codePackage io.Reader, client *docker.Client) (io.Reader, error) { 163 inputFiles := make(map[string][]byte) 164 165 // ---------------------------------------------------------------------------------------------------- 166 // Generate the Dockerfile specific to our context 167 // ---------------------------------------------------------------------------------------------------- 168 dockerFile, err := r.GenerateDockerfile(ccType) 169 if err != nil { 170 return nil, fmt.Errorf("Failed to generate a Dockerfile: %s", err) 171 } 172 173 inputFiles["Dockerfile"] = []byte(dockerFile) 174 175 // ---------------------------------------------------------------------------------------------------- 176 // Finally, launch an asynchronous process to stream all of the above into a docker build context 177 // ---------------------------------------------------------------------------------------------------- 178 input, output := io.Pipe() 179 180 go func() { 181 gw := gzip.NewWriter(output) 182 tw := tar.NewWriter(gw) 183 err := r.StreamDockerBuild(ccType, path, codePackage, inputFiles, tw, client) 184 if err != nil { 185 logger.Error(err) 186 } 187 188 tw.Close() 189 gw.Close() 190 output.CloseWithError(err) 191 }() 192 193 return input, nil 194 }