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