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  }