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