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 }