Skip to content

Commit ea81d21

Browse files
stephyeewlahti
andauthored
osnadmin CLI (hyperledger#1907)
Adds CLI commands for the list, join, and remove using the new channel participation API. FAB-18196 #done Signed-off-by: Will Lahti <[email protected]> Signed-off-by: Tiffany Harris <[email protected]> Co-authored-by: Will Lahti <[email protected]>
1 parent d7b1ed1 commit ea81d21

File tree

8 files changed

+1350
-0
lines changed

8 files changed

+1350
-0
lines changed

cmd/osnadmin/main.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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+
"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/hyperledger/fabric-protos-go/common"
22+
"github.com/hyperledger/fabric/internal/osnadmin"
23+
"github.com/hyperledger/fabric/internal/pkg/comm"
24+
"github.com/hyperledger/fabric/protoutil"
25+
"gopkg.in/alecthomas/kingpin.v2"
26+
)
27+
28+
func main() {
29+
kingpin.Version("0.0.1")
30+
31+
output, exit, err := executeForArgs(os.Args[1:])
32+
if err != nil {
33+
kingpin.Fatalf("parsing arguments: %s. Try --help", err)
34+
}
35+
fmt.Println(output)
36+
os.Exit(exit)
37+
}
38+
39+
func executeForArgs(args []string) (output string, exit int, err error) {
40+
//
41+
// command line flags
42+
//
43+
app := kingpin.New("osnadmin", "Orderer Service Node (OSN) administration")
44+
orderer := app.Flag("orderer-address", "Endpoint of the OSN").Short('o').Required().String()
45+
caFile := app.Flag("ca-file", "Path to file containing PEM-encoded trusted certificate(s) for the OSN").Required().String()
46+
clientCert := app.Flag("client-cert", "Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the OSN").Required().String()
47+
clientKey := app.Flag("client-key", "Path to file containing PEM-encoded private key to use for mutual TLS communication with the OSN").Required().String()
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("channel-id", "Channel ID").Short('c').Required().String()
53+
configBlockPath := join.Flag("config-block", "Path to the file containing the config block").Short('b').Required().String()
54+
55+
list := channel.Command("list", "List channel information for an Ordering Service Node (OSN). If the channel-id flag is set, more detailed information will be provided for that channel.")
56+
listChannelID := list.Flag("channel-id", "Channel ID").Short('c').String()
57+
58+
remove := channel.Command("remove", "Remove an Ordering Service Node (OSN) from a channel.")
59+
removeChannelID := remove.Flag("channel-id", "Channel ID").Short('c').Required().String()
60+
61+
command := kingpin.MustParse(app.Parse(args))
62+
63+
//
64+
// flag validation
65+
//
66+
osnURL := fmt.Sprintf("https://%s", *orderer)
67+
68+
caCertPool := x509.NewCertPool()
69+
caFilePEM, err := ioutil.ReadFile(*caFile)
70+
if err != nil {
71+
return "", 1, fmt.Errorf("reading orderer CA certificate: %s", err)
72+
}
73+
err = comm.AddPemToCertPool(caFilePEM, caCertPool)
74+
if err != nil {
75+
return "", 1, fmt.Errorf("adding ca-file PEM to cert pool: %s", err)
76+
}
77+
78+
tlsClientCert, err := tls.LoadX509KeyPair(*clientCert, *clientKey)
79+
if err != nil {
80+
return "", 1, fmt.Errorf("loading client cert/key pair: %s", err)
81+
}
82+
83+
var marshaledConfigBlock []byte
84+
if *configBlockPath != "" {
85+
marshaledConfigBlock, err = ioutil.ReadFile(*configBlockPath)
86+
if err != nil {
87+
return "", 1, fmt.Errorf("reading config block: %s", err)
88+
}
89+
90+
err = validateBlockChannelID(marshaledConfigBlock, *joinChannelID)
91+
if err != nil {
92+
return "", 1, err
93+
}
94+
}
95+
96+
//
97+
// call the underlying implementations
98+
//
99+
var resp *http.Response
100+
101+
switch command {
102+
case join.FullCommand():
103+
resp, err = osnadmin.Join(osnURL, marshaledConfigBlock, caCertPool, tlsClientCert)
104+
case list.FullCommand():
105+
if *listChannelID != "" {
106+
resp, err = osnadmin.ListSingleChannel(osnURL, *listChannelID, caCertPool, tlsClientCert)
107+
break
108+
}
109+
resp, err = osnadmin.ListAllChannels(osnURL, caCertPool, tlsClientCert)
110+
case remove.FullCommand():
111+
resp, err = osnadmin.Remove(osnURL, *removeChannelID, caCertPool, tlsClientCert)
112+
}
113+
if err != nil {
114+
return errorOutput(err), 1, nil
115+
}
116+
117+
bodyBytes, err := readBodyBytes(resp.Body)
118+
if err != nil {
119+
return errorOutput(err), 1, nil
120+
}
121+
122+
return responseOutput(resp.StatusCode, bodyBytes), 0, nil
123+
}
124+
125+
func responseOutput(statusCode int, responseBody []byte) string {
126+
status := fmt.Sprintf("Status: %d", statusCode)
127+
128+
var buffer bytes.Buffer
129+
json.Indent(&buffer, responseBody, "", "\t")
130+
response := fmt.Sprintf("%s", buffer.Bytes())
131+
132+
output := fmt.Sprintf("%s\n%s", status, response)
133+
134+
return output
135+
}
136+
137+
func readBodyBytes(body io.ReadCloser) ([]byte, error) {
138+
bodyBytes, err := ioutil.ReadAll(body)
139+
if err != nil {
140+
return nil, fmt.Errorf("reading http response body: %s", err)
141+
}
142+
body.Close()
143+
144+
return bodyBytes, nil
145+
}
146+
147+
func errorOutput(err error) string {
148+
return fmt.Sprintf("Error: %s\n", err)
149+
}
150+
151+
func validateBlockChannelID(blockBytes []byte, channelID string) error {
152+
block := &common.Block{}
153+
err := proto.Unmarshal(blockBytes, block)
154+
if err != nil {
155+
return fmt.Errorf("unmarshaling block: %s", err)
156+
}
157+
158+
blockChannelID, err := protoutil.GetChannelIDFromBlock(block)
159+
if err != nil {
160+
return err
161+
}
162+
163+
// quick sanity check that the orderer admin is joining
164+
// the channel they think they're joining.
165+
if channelID != blockChannelID {
166+
return fmt.Errorf("specified --channel-id %s does not match channel ID %s in config block", channelID, blockChannelID)
167+
}
168+
169+
return nil
170+
}

0 commit comments

Comments
 (0)