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  }