github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/cmd/osnadmin/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  	"bytes"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"encoding/json"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"net/http"
    18  	"os"
    19  
    20  	"github.com/golang/protobuf/proto"
    21  	"github.com/hechain20/hechain/internal/osnadmin"
    22  	"github.com/hechain20/hechain/protoutil"
    23  	"github.com/hyperledger/fabric-protos-go/common"
    24  	"gopkg.in/alecthomas/kingpin.v2"
    25  )
    26  
    27  func main() {
    28  	kingpin.Version("0.0.1")
    29  
    30  	output, exit, err := executeForArgs(os.Args[1:])
    31  	if err != nil {
    32  		kingpin.Fatalf("parsing arguments: %s. Try --help", err)
    33  	}
    34  	fmt.Println(output)
    35  	os.Exit(exit)
    36  }
    37  
    38  func executeForArgs(args []string) (output string, exit int, err error) {
    39  	//
    40  	// command line flags
    41  	//
    42  	app := kingpin.New("osnadmin", "Orderer Service Node (OSN) administration")
    43  	orderer := app.Flag("orderer-address", "Admin endpoint of the OSN").Short('o').Required().String()
    44  	caFile := app.Flag("ca-file", "Path to file containing PEM-encoded TLS CA certificate(s) for the OSN").String()
    45  	clientCert := app.Flag("client-cert", "Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the OSN").String()
    46  	clientKey := app.Flag("client-key", "Path to file containing PEM-encoded private key to use for mutual TLS communication with the OSN").String()
    47  	noStatus := app.Flag("no-status", "Remove the HTTP status message from the command output").Default("false").Bool()
    48  
    49  	channel := app.Command("channel", "Channel actions")
    50  
    51  	join := channel.Command("join", "Join an Ordering Service Node (OSN) to a channel. If the channel does not yet exist, it will be created.")
    52  	joinChannelID := join.Flag("channelID", "Channel ID").Short('c').Required().String()
    53  	configBlockPath := join.Flag("config-block", "Path to the file containing an up-to-date config block for the channel").Short('b').Required().String()
    54  
    55  	list := channel.Command("list", "List channel information for an Ordering Service Node (OSN). If the channelID flag is set, more detailed information will be provided for that channel.")
    56  	listChannelID := list.Flag("channelID", "Channel ID").Short('c').String()
    57  
    58  	remove := channel.Command("remove", "Remove an Ordering Service Node (OSN) from a channel.")
    59  	removeChannelID := remove.Flag("channelID", "Channel ID").Short('c').Required().String()
    60  
    61  	command, err := app.Parse(args)
    62  	if err != nil {
    63  		return "", 1, err
    64  	}
    65  
    66  	//
    67  	// flag validation
    68  	//
    69  	var (
    70  		osnURL        string
    71  		caCertPool    *x509.CertPool
    72  		tlsClientCert tls.Certificate
    73  	)
    74  	// TLS enabled
    75  	if *caFile != "" {
    76  		osnURL = fmt.Sprintf("https://%s", *orderer)
    77  		var err error
    78  		caCertPool = x509.NewCertPool()
    79  		caFilePEM, err := ioutil.ReadFile(*caFile)
    80  		if err != nil {
    81  			return "", 1, fmt.Errorf("reading orderer CA certificate: %s", err)
    82  		}
    83  		if !caCertPool.AppendCertsFromPEM(caFilePEM) {
    84  			return "", 1, fmt.Errorf("failed to add ca-file PEM to cert pool")
    85  		}
    86  
    87  		tlsClientCert, err = tls.LoadX509KeyPair(*clientCert, *clientKey)
    88  		if err != nil {
    89  			return "", 1, fmt.Errorf("loading client cert/key pair: %s", err)
    90  		}
    91  	} else { // TLS disabled
    92  		osnURL = fmt.Sprintf("http://%s", *orderer)
    93  	}
    94  
    95  	var marshaledConfigBlock []byte
    96  	if *configBlockPath != "" {
    97  		marshaledConfigBlock, err = ioutil.ReadFile(*configBlockPath)
    98  		if err != nil {
    99  			return "", 1, fmt.Errorf("reading config block: %s", err)
   100  		}
   101  
   102  		err = validateBlockChannelID(marshaledConfigBlock, *joinChannelID)
   103  		if err != nil {
   104  			return "", 1, err
   105  		}
   106  	}
   107  
   108  	//
   109  	// call the underlying implementations
   110  	//
   111  	var resp *http.Response
   112  
   113  	switch command {
   114  	case join.FullCommand():
   115  		resp, err = osnadmin.Join(osnURL, marshaledConfigBlock, caCertPool, tlsClientCert)
   116  	case list.FullCommand():
   117  		if *listChannelID != "" {
   118  			resp, err = osnadmin.ListSingleChannel(osnURL, *listChannelID, caCertPool, tlsClientCert)
   119  			break
   120  		}
   121  		resp, err = osnadmin.ListAllChannels(osnURL, caCertPool, tlsClientCert)
   122  	case remove.FullCommand():
   123  		resp, err = osnadmin.Remove(osnURL, *removeChannelID, caCertPool, tlsClientCert)
   124  	}
   125  	if err != nil {
   126  		return errorOutput(err), 1, nil
   127  	}
   128  
   129  	bodyBytes, err := readBodyBytes(resp.Body)
   130  	if err != nil {
   131  		return errorOutput(err), 1, nil
   132  	}
   133  
   134  	output, err = responseOutput(!*noStatus, resp.StatusCode, bodyBytes)
   135  	if err != nil {
   136  		return errorOutput(err), 1, nil
   137  	}
   138  
   139  	return output, 0, nil
   140  }
   141  
   142  func responseOutput(showStatus bool, statusCode int, responseBody []byte) (string, error) {
   143  	var buffer bytes.Buffer
   144  	if showStatus {
   145  		fmt.Fprintf(&buffer, "Status: %d\n", statusCode)
   146  	}
   147  	if len(responseBody) != 0 {
   148  		if err := json.Indent(&buffer, responseBody, "", "\t"); err != nil {
   149  			return "", err
   150  		}
   151  	}
   152  	return buffer.String(), nil
   153  }
   154  
   155  func readBodyBytes(body io.ReadCloser) ([]byte, error) {
   156  	bodyBytes, err := ioutil.ReadAll(body)
   157  	if err != nil {
   158  		return nil, fmt.Errorf("reading http response body: %s", err)
   159  	}
   160  	body.Close()
   161  
   162  	return bodyBytes, nil
   163  }
   164  
   165  func errorOutput(err error) string {
   166  	return fmt.Sprintf("Error: %s\n", err)
   167  }
   168  
   169  func validateBlockChannelID(blockBytes []byte, channelID string) error {
   170  	block := &common.Block{}
   171  	err := proto.Unmarshal(blockBytes, block)
   172  	if err != nil {
   173  		return fmt.Errorf("unmarshalling block: %s", err)
   174  	}
   175  
   176  	blockChannelID, err := protoutil.GetChannelIDFromBlock(block)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	// quick sanity check that the orderer admin is joining
   182  	// the channel they think they're joining.
   183  	if channelID != blockChannelID {
   184  		return fmt.Errorf("specified --channelID %s does not match channel ID %s in config block", channelID, blockChannelID)
   185  	}
   186  
   187  	return nil
   188  }