Skip to content

Commit 9a8fec3

Browse files
committed
added to OSN new handler for fetch a block of channel
Signed-off-by: Fedor Partanskiy <fredprtnsk@gmail.com>
1 parent 824eb5a commit 9a8fec3

File tree

31 files changed

+1372
-375
lines changed

31 files changed

+1372
-375
lines changed

cmd/osnadmin/main.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"io"
1717
"net/http"
1818
"os"
19+
"strconv"
1920

2021
"github.com/hyperledger/fabric-protos-go-apiv2/common"
2122
"github.com/hyperledger/fabric/internal/osnadmin"
@@ -63,6 +64,12 @@ func executeForArgs(args []string) (output string, exit int, err error) {
6364
configUpdateEnvelopePath := update.Flag("config-update-envelope", "Path to the file containing an up-to-date config update envelope for the channel").Short('e').Required().String()
6465
tlsHandshakeTimeShift := update.Flag("tlsHandshakeTimeShift", "The amount of time to shift backwards for certificate expiration checks during TLS handshakes with the orderer endpoint").Short('t').Default("0").Duration()
6566

67+
fetch := channel.Command("fetch", "Fetch a specified block, writing it to a file.")
68+
fetchChannelID := fetch.Flag("channelID", "Channel ID").Short('c').Required().String()
69+
fetchBlockID := fetch.Flag("blockID", "Block ID - <newest|oldest|config|(number)>").Short('b').Required().String()
70+
fetchOutputFile := fetch.Flag("outputfile", "Puth to a file.").Short('f').Required().String()
71+
tlsHandshakeTimeShift1 := fetch.Flag("tlsHandshakeTimeShift", "The amount of time to shift backwards for certificate expiration checks during TLS handshakes with the orderer endpoint").Short('t').Default("0").Duration()
72+
6673
command, err := app.Parse(args)
6774
if err != nil {
6875
return "", 1, err
@@ -141,6 +148,14 @@ func executeForArgs(args []string) (output string, exit int, err error) {
141148
resp, err = osnadmin.Remove(osnURL, *removeChannelID, caCertPool, tlsClientCert)
142149
case update.FullCommand():
143150
resp, err = osnadmin.Update(osnURL, marshaledConfigEnvelope, caCertPool, tlsClientCert, *tlsHandshakeTimeShift)
151+
case fetch.FullCommand():
152+
if *fetchBlockID != "newest" && *fetchBlockID != "oldest" && *fetchBlockID != "config" {
153+
_, err = strconv.Atoi(*fetchBlockID)
154+
if err != nil {
155+
return "", 1, fmt.Errorf("'%s' not equal <newest|oldest|config|(number)>", *fetchBlockID)
156+
}
157+
}
158+
resp, err = osnadmin.Fetch(osnURL, *fetchChannelID, *fetchBlockID, caCertPool, tlsClientCert, *tlsHandshakeTimeShift1)
144159
}
145160
if err != nil {
146161
return errorOutput(err), 1, nil
@@ -151,22 +166,28 @@ func executeForArgs(args []string) (output string, exit int, err error) {
151166
return errorOutput(err), 1, nil
152167
}
153168

154-
output, err = responseOutput(!*noStatus, resp.StatusCode, bodyBytes)
169+
output, err = responseOutput(!*noStatus, resp.StatusCode, bodyBytes, *fetchOutputFile)
155170
if err != nil {
156171
return errorOutput(err), 1, nil
157172
}
158173

159174
return output, 0, nil
160175
}
161176

162-
func responseOutput(showStatus bool, statusCode int, responseBody []byte) (string, error) {
177+
func responseOutput(showStatus bool, statusCode int, responseBody []byte, outputFile string) (string, error) {
163178
var buffer bytes.Buffer
164179
if showStatus {
165180
fmt.Fprintf(&buffer, "Status: %d\n", statusCode)
166181
}
167182
if len(responseBody) != 0 {
168-
if err := json.Indent(&buffer, responseBody, "", "\t"); err != nil {
169-
return "", err
183+
if statusCode == http.StatusOK && outputFile != "" {
184+
if err := os.WriteFile(outputFile, responseBody, 0o644); err != nil {
185+
return "", err
186+
}
187+
} else {
188+
if err := json.Indent(&buffer, responseBody, "", "\t"); err != nil {
189+
return "", err
190+
}
170191
}
171192
}
172193
return buffer.String(), nil

cmd/osnadmin/main_test.go

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
2323
"github.com/hyperledger/fabric/cmd/osnadmin/mocks"
2424
"github.com/hyperledger/fabric/common/crypto/tlsgen"
25+
. "github.com/hyperledger/fabric/internal/test"
2526
"github.com/hyperledger/fabric/orderer/common/channelparticipation"
2627
"github.com/hyperledger/fabric/orderer/common/localconfig"
2728
"github.com/hyperledger/fabric/orderer/common/types"
@@ -694,7 +695,7 @@ var _ = Describe("osnadmin", func() {
694695
tlsConfig = nil
695696
})
696697

697-
It("uses the channel participation API to join a channel", func() {
698+
It("uses the channel participation API to update a channel", func() {
698699
args := []string{
699700
"channel",
700701
"update",
@@ -713,6 +714,137 @@ var _ = Describe("osnadmin", func() {
713714
})
714715
})
715716

717+
Describe("Fetch", func() {
718+
var (
719+
blockPath string
720+
block *cb.Block
721+
)
722+
723+
BeforeEach(func() {
724+
blockPath = filepath.Join(tempDir, "block.pb")
725+
block = blockWithGroups(
726+
map[string]*cb.ConfigGroup{
727+
"Application": {},
728+
},
729+
"testing123",
730+
)
731+
mockChannelManagement.FetchBlockReturns(block, nil)
732+
})
733+
734+
AfterEach(func() {
735+
_ = os.Remove(blockPath)
736+
})
737+
738+
It("uses the channel participation API to fetch a block", func() {
739+
args := []string{
740+
"channel",
741+
"fetch",
742+
"--outputfile", blockPath,
743+
"--channelID", channelID,
744+
"--blockID", "100",
745+
"--orderer-address", ordererURL,
746+
"--ca-file", ordererCACert,
747+
"--client-cert", clientCert,
748+
"--client-key", clientKey,
749+
}
750+
output, exit, err := executeForArgs(args)
751+
Expect(err).NotTo(HaveOccurred())
752+
Expect(exit).To(Equal(0))
753+
Expect(output).To(Equal(fmt.Sprintf("Status: %d\n", 200)))
754+
755+
blockBytes, err := os.ReadFile(blockPath)
756+
Expect(err).NotTo(HaveOccurred())
757+
758+
b := &cb.Block{}
759+
err = proto.Unmarshal(blockBytes, b)
760+
Expect(err).NotTo(HaveOccurred())
761+
Expect(b).To(ProtoEqual(block))
762+
})
763+
764+
Context("when the block is empty", func() {
765+
BeforeEach(func() {
766+
block = &cb.Block{}
767+
mockChannelManagement.FetchBlockReturns(block, nil)
768+
})
769+
770+
It("returns with exit code 1 and prints the error", func() {
771+
args := []string{
772+
"channel",
773+
"fetch",
774+
"--outputfile", blockPath,
775+
"--channelID", channelID,
776+
"--blockID", "100",
777+
"--orderer-address", ordererURL,
778+
"--ca-file", ordererCACert,
779+
"--client-cert", clientCert,
780+
"--client-key", clientKey,
781+
}
782+
output, exit, err := executeForArgs(args)
783+
Expect(err).NotTo(HaveOccurred())
784+
Expect(exit).To(Equal(0))
785+
Expect(output).To(Equal(fmt.Sprintf("Status: %d\n", 200)))
786+
787+
blockBytes, err := os.ReadFile(blockPath)
788+
Expect(err).To(HaveOccurred())
789+
Expect(blockBytes).To(BeNil())
790+
})
791+
})
792+
793+
Context("when fetch the channel fails", func() {
794+
BeforeEach(func() {
795+
mockChannelManagement.FetchBlockReturns(nil, types.ErrChannelNotExist)
796+
})
797+
798+
It("returns 404 does not exist", func() {
799+
args := []string{
800+
"channel",
801+
"fetch",
802+
"--outputfile", blockPath,
803+
"--channelID", channelID,
804+
"--blockID", "100",
805+
"--orderer-address", ordererURL,
806+
"--ca-file", ordererCACert,
807+
"--client-cert", clientCert,
808+
"--client-key", clientKey,
809+
}
810+
output, exit, err := executeForArgs(args)
811+
expectedOutput := types.ErrorResponse{
812+
Error: "channel does not exist",
813+
}
814+
checkStatusOutput(output, exit, err, 404, expectedOutput)
815+
})
816+
})
817+
818+
Context("when TLS is disabled", func() {
819+
BeforeEach(func() {
820+
tlsConfig = nil
821+
})
822+
823+
It("uses the channel participation API to fetch a block", func() {
824+
args := []string{
825+
"channel",
826+
"fetch",
827+
"--outputfile", blockPath,
828+
"--channelID", channelID,
829+
"--blockID", "100",
830+
"--orderer-address", ordererURL,
831+
}
832+
output, exit, err := executeForArgs(args)
833+
Expect(err).NotTo(HaveOccurred())
834+
Expect(exit).To(Equal(0))
835+
Expect(output).To(Equal(fmt.Sprintf("Status: %d\n", 200)))
836+
837+
blockBytes, err := os.ReadFile(blockPath)
838+
Expect(err).NotTo(HaveOccurred())
839+
840+
b := &cb.Block{}
841+
err = proto.Unmarshal(blockBytes, b)
842+
Expect(err).NotTo(HaveOccurred())
843+
Expect(b).To(ProtoEqual(block))
844+
})
845+
})
846+
})
847+
716848
Describe("Flags", func() {
717849
It("accepts short versions of the --orderer-address, --channelID, and --config-block flags", func() {
718850
configBlock := blockWithGroups(
@@ -750,6 +882,41 @@ var _ = Describe("osnadmin", func() {
750882
checkStatusOutput(output, exit, err, 201, expectedOutput)
751883
})
752884

885+
It("accepts short versions of the --channelID, --blockID, and --outputfile flags", func() {
886+
blockPath := filepath.Join(tempDir, "block.pb")
887+
block := blockWithGroups(
888+
map[string]*cb.ConfigGroup{
889+
"Application": {},
890+
},
891+
"testing123",
892+
)
893+
mockChannelManagement.FetchBlockReturns(block, nil)
894+
895+
args := []string{
896+
"channel",
897+
"fetch",
898+
"-f", blockPath,
899+
"-c", channelID,
900+
"-b", "oldest",
901+
"-o", ordererURL,
902+
"--ca-file", ordererCACert,
903+
"--client-cert", clientCert,
904+
"--client-key", clientKey,
905+
}
906+
output, exit, err := executeForArgs(args)
907+
Expect(err).NotTo(HaveOccurred())
908+
Expect(exit).To(Equal(0))
909+
Expect(output).To(Equal(fmt.Sprintf("Status: %d\n", 200)))
910+
911+
blockBytes, err := os.ReadFile(blockPath)
912+
Expect(err).NotTo(HaveOccurred())
913+
914+
b := &cb.Block{}
915+
err = proto.Unmarshal(blockBytes, b)
916+
Expect(err).NotTo(HaveOccurred())
917+
Expect(b).To(ProtoEqual(block))
918+
})
919+
753920
Context("when an unknown flag is used", func() {
754921
It("returns an error for long flags", func() {
755922
_, _, err := executeForArgs([]string{"channel", "list", "--bad-flag"})

cmd/osnadmin/mocks/channel_management.go

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/osnadmin/osnadmin_suite_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type channelManagement interface {
2323
JoinChannel(channelID string, configBlock *cb.Block) (types.ChannelInfo, error)
2424
UpdateChannel(channelID string, configUpdateEnvelope *cb.Envelope) (types.ChannelInfo, error)
2525
RemoveChannel(channelID string) error
26+
FetchBlock(channelID string, blockID string) (*cb.Block, error)
2627
}
2728

2829
func TestOsnadmin(t *testing.T) {

0 commit comments

Comments
 (0)