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