github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/cmd/configtxlator/main.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package main 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "net" 13 "net/http" 14 "os" 15 "reflect" 16 17 "github.com/golang/protobuf/proto" 18 "github.com/hechain20/hechain/common/flogging" 19 "github.com/hechain20/hechain/internal/configtxlator/metadata" 20 "github.com/hechain20/hechain/internal/configtxlator/rest" 21 "github.com/hechain20/hechain/internal/configtxlator/update" 22 "github.com/hyperledger/fabric-config/protolator" 23 cb "github.com/hyperledger/fabric-protos-go/common" 24 _ "github.com/hyperledger/fabric-protos-go/msp" 25 _ "github.com/hyperledger/fabric-protos-go/orderer" 26 _ "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 27 _ "github.com/hyperledger/fabric-protos-go/peer" 28 29 "github.com/gorilla/handlers" 30 "github.com/pkg/errors" 31 "gopkg.in/alecthomas/kingpin.v2" 32 ) 33 34 // command line flags 35 var ( 36 app = kingpin.New("configtxlator", "Utility for generating Hechain channel configurations") 37 38 start = app.Command("start", "Start the configtxlator REST server") 39 hostname = start.Flag("hostname", "The hostname or IP on which the REST server will listen").Default("0.0.0.0").String() 40 port = start.Flag("port", "The port on which the REST server will listen").Default("7059").Int() 41 cors = start.Flag("CORS", "Allowable CORS domains, e.g. '*' or 'www.example.com' (may be repeated).").Strings() 42 43 protoEncode = app.Command("proto_encode", "Converts a JSON document to protobuf.") 44 protoEncodeType = protoEncode.Flag("type", "The type of protobuf structure to encode to. For example, 'common.Config'.").Required().String() 45 protoEncodeSource = protoEncode.Flag("input", "A file containing the JSON document.").Default(os.Stdin.Name()).File() 46 protoEncodeDest = protoEncode.Flag("output", "A file to write the output to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) 47 48 protoDecode = app.Command("proto_decode", "Converts a proto message to JSON.") 49 protoDecodeType = protoDecode.Flag("type", "The type of protobuf structure to decode from. For example, 'common.Config'.").Required().String() 50 protoDecodeSource = protoDecode.Flag("input", "A file containing the proto message.").Default(os.Stdin.Name()).File() 51 protoDecodeDest = protoDecode.Flag("output", "A file to write the JSON document to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) 52 53 computeUpdate = app.Command("compute_update", "Takes two marshaled common.Config messages and computes the config update which transitions between the two.") 54 computeUpdateOriginal = computeUpdate.Flag("original", "The original config message.").File() 55 computeUpdateUpdated = computeUpdate.Flag("updated", "The updated config message.").File() 56 computeUpdateChannelID = computeUpdate.Flag("channel_id", "The name of the channel for this update.").Required().String() 57 computeUpdateDest = computeUpdate.Flag("output", "A file to write the JSON document to.").Default(os.Stdout.Name()).OpenFile(os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600) 58 59 version = app.Command("version", "Show version information") 60 ) 61 62 var logger = flogging.MustGetLogger("configtxlator") 63 64 func main() { 65 kingpin.Version("0.0.1") 66 switch kingpin.MustParse(app.Parse(os.Args[1:])) { 67 // "start" command 68 case start.FullCommand(): 69 startServer(fmt.Sprintf("%s:%d", *hostname, *port), *cors) 70 // "proto_encode" command 71 case protoEncode.FullCommand(): 72 defer (*protoEncodeSource).Close() 73 defer (*protoEncodeDest).Close() 74 err := encodeProto(*protoEncodeType, *protoEncodeSource, *protoEncodeDest) 75 if err != nil { 76 app.Fatalf("Error decoding: %s", err) 77 } 78 case protoDecode.FullCommand(): 79 defer (*protoDecodeSource).Close() 80 defer (*protoDecodeDest).Close() 81 err := decodeProto(*protoDecodeType, *protoDecodeSource, *protoDecodeDest) 82 if err != nil { 83 app.Fatalf("Error decoding: %s", err) 84 } 85 case computeUpdate.FullCommand(): 86 defer (*computeUpdateOriginal).Close() 87 defer (*computeUpdateUpdated).Close() 88 defer (*computeUpdateDest).Close() 89 err := computeUpdt(*computeUpdateOriginal, *computeUpdateUpdated, *computeUpdateDest, *computeUpdateChannelID) 90 if err != nil { 91 app.Fatalf("Error computing update: %s", err) 92 } 93 // "version" command 94 case version.FullCommand(): 95 printVersion() 96 } 97 } 98 99 func startServer(address string, cors []string) { 100 var err error 101 102 listener, err := net.Listen("tcp", address) 103 if err != nil { 104 app.Fatalf("Could not bind to address '%s': %s", address, err) 105 } 106 107 if len(cors) > 0 { 108 origins := handlers.AllowedOrigins(cors) 109 // Note, configtxlator only exposes POST APIs for the time being, this 110 // list will need to be expanded if new non-POST APIs are added 111 methods := handlers.AllowedMethods([]string{http.MethodPost}) 112 headers := handlers.AllowedHeaders([]string{"Content-Type"}) 113 logger.Infof("Serving HTTP requests on %s with CORS %v", listener.Addr(), cors) 114 err = http.Serve(listener, handlers.CORS(origins, methods, headers)(rest.NewRouter())) 115 } else { 116 logger.Infof("Serving HTTP requests on %s", listener.Addr()) 117 err = http.Serve(listener, rest.NewRouter()) 118 } 119 120 app.Fatalf("Error starting server:[%s]\n", err) 121 } 122 123 func printVersion() { 124 fmt.Println(metadata.GetVersionInfo()) 125 } 126 127 func encodeProto(msgName string, input, output *os.File) error { 128 msgType := proto.MessageType(msgName) 129 if msgType == nil { 130 return errors.Errorf("message of type %s unknown", msgType) 131 } 132 msg := reflect.New(msgType.Elem()).Interface().(proto.Message) 133 134 err := protolator.DeepUnmarshalJSON(input, msg) 135 if err != nil { 136 return errors.Wrapf(err, "error decoding input") 137 } 138 139 out, err := proto.Marshal(msg) 140 if err != nil { 141 return errors.Wrapf(err, "error marshaling") 142 } 143 144 _, err = output.Write(out) 145 if err != nil { 146 return errors.Wrapf(err, "error writing output") 147 } 148 149 return nil 150 } 151 152 func decodeProto(msgName string, input, output *os.File) error { 153 msgType := proto.MessageType(msgName) 154 if msgType == nil { 155 return errors.Errorf("message of type %s unknown", msgType) 156 } 157 msg := reflect.New(msgType.Elem()).Interface().(proto.Message) 158 159 in, err := ioutil.ReadAll(input) 160 if err != nil { 161 return errors.Wrapf(err, "error reading input") 162 } 163 164 err = proto.Unmarshal(in, msg) 165 if err != nil { 166 return errors.Wrapf(err, "error unmarshalling") 167 } 168 169 err = protolator.DeepMarshalJSON(output, msg) 170 if err != nil { 171 return errors.Wrapf(err, "error encoding output") 172 } 173 174 return nil 175 } 176 177 func computeUpdt(original, updated, output *os.File, channelID string) error { 178 origIn, err := ioutil.ReadAll(original) 179 if err != nil { 180 return errors.Wrapf(err, "error reading original config") 181 } 182 183 origConf := &cb.Config{} 184 err = proto.Unmarshal(origIn, origConf) 185 if err != nil { 186 return errors.Wrapf(err, "error unmarshalling original config") 187 } 188 189 updtIn, err := ioutil.ReadAll(updated) 190 if err != nil { 191 return errors.Wrapf(err, "error reading updated config") 192 } 193 194 updtConf := &cb.Config{} 195 err = proto.Unmarshal(updtIn, updtConf) 196 if err != nil { 197 return errors.Wrapf(err, "error unmarshalling updated config") 198 } 199 200 cu, err := update.Compute(origConf, updtConf) 201 if err != nil { 202 return errors.Wrapf(err, "error computing config update") 203 } 204 205 cu.ChannelId = channelID 206 207 outBytes, err := proto.Marshal(cu) 208 if err != nil { 209 return errors.Wrapf(err, "error marshaling computed config update") 210 } 211 212 _, err = output.Write(outBytes) 213 if err != nil { 214 return errors.Wrapf(err, "error writing config update to output") 215 } 216 217 return nil 218 }