Skip to content

Commit f09cef5

Browse files
committed
added to OSN new handler for update config of channel
Signed-off-by: Fedor Partanskiy <fredprtnsk@gmail.com>
1 parent e06da3c commit f09cef5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2487
-561
lines changed

cmd/osnadmin/main.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ func executeForArgs(args []string) (output string, exit int, err error) {
5858
remove := channel.Command("remove", "Remove a channel from an Ordering Service Node (OSN).")
5959
removeChannelID := remove.Flag("channelID", "Channel ID").Short('c').Required().String()
6060

61+
update := channel.Command("update", "Update an Ordering Service Node (OSN) to a channel.")
62+
updateChannelID := update.Flag("channelID", "Channel ID").Short('c').Required().String()
63+
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()
64+
6165
command, err := app.Parse(args)
6266
if err != nil {
6367
return "", 1, err
@@ -105,6 +109,19 @@ func executeForArgs(args []string) (output string, exit int, err error) {
105109
}
106110
}
107111

112+
var marshaledConfigEnvelope []byte
113+
if *configUpdateEnvelopePath != "" {
114+
marshaledConfigEnvelope, err = os.ReadFile(*configUpdateEnvelopePath)
115+
if err != nil {
116+
return "", 1, fmt.Errorf("reading config updte envelope: %s", err)
117+
}
118+
119+
err = validateEnvelopeChannelID(marshaledConfigEnvelope, *updateChannelID)
120+
if err != nil {
121+
return "", 1, err
122+
}
123+
}
124+
108125
//
109126
// call the underlying implementations
110127
//
@@ -121,6 +138,8 @@ func executeForArgs(args []string) (output string, exit int, err error) {
121138
resp, err = osnadmin.ListAllChannels(osnURL, caCertPool, tlsClientCert)
122139
case remove.FullCommand():
123140
resp, err = osnadmin.Remove(osnURL, *removeChannelID, caCertPool, tlsClientCert)
141+
case update.FullCommand():
142+
resp, err = osnadmin.Update(osnURL, marshaledConfigEnvelope, caCertPool, tlsClientCert)
124143
}
125144
if err != nil {
126145
return errorOutput(err), 1, nil
@@ -186,3 +205,24 @@ func validateBlockChannelID(blockBytes []byte, channelID string) error {
186205

187206
return nil
188207
}
208+
209+
func validateEnvelopeChannelID(envelopeBytes []byte, channelID string) error {
210+
envelope := &common.Envelope{}
211+
err := proto.Unmarshal(envelopeBytes, envelope)
212+
if err != nil {
213+
return fmt.Errorf("unmarshalling envelope: %s", err)
214+
}
215+
216+
envelopeChannelID, err := protoutil.GetChannelIDFromEnvelope(envelope)
217+
if err != nil {
218+
return err
219+
}
220+
221+
// quick sanity check that the orderer admin is joining
222+
// the channel they think they're joining.
223+
if channelID != envelopeChannelID {
224+
return fmt.Errorf("specified --channelID %s does not match channel ID %s in config update envelope", channelID, envelopeChannelID)
225+
}
226+
227+
return nil
228+
}

cmd/osnadmin/main_test.go

Lines changed: 136 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ 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/orderer/common/channelparticipation"
2625
"github.com/hyperledger/fabric/orderer/common/localconfig"
2726
"github.com/hyperledger/fabric/orderer/common/types"
2827
"github.com/hyperledger/fabric/protoutil"
@@ -63,7 +62,7 @@ var _ = Describe("osnadmin", func() {
6362
}
6463
mockChannelManagement = &mocks.ChannelManagement{}
6564

66-
h := channelparticipation.NewHTTPHandler(config, mockChannelManagement)
65+
h := nwo.NewHTTPHandler(config, mockChannelManagement)
6766
Expect(h).NotTo(BeNil())
6867
testServer = httptest.NewUnstartedServer(h)
6968

@@ -532,134 +531,183 @@ var _ = Describe("osnadmin", func() {
532531
})
533532
})
534533

535-
Describe("Flags", func() {
536-
It("accepts short versions of the --orderer-address, --channelID, and --config-block flags", func() {
537-
configBlock := blockWithGroups(
538-
map[string]*cb.ConfigGroup{
539-
"Application": {},
540-
},
541-
"testing123",
542-
)
543-
blockPath := createBlockFile(tempDir, configBlock)
544-
mockChannelManagement.JoinChannelReturns(types.ChannelInfo{
545-
Name: "apple",
546-
ConsensusRelation: "banana",
547-
Status: "orange",
548-
Height: 123,
534+
Describe("Update", func() {
535+
var envelopePath string
536+
537+
BeforeEach(func() {
538+
configEnvelope := envelopeUpdateConfigWithGroups("testing123")
539+
envelopePath = createEnvelopeFile(tempDir, configEnvelope)
540+
541+
mockChannelManagement.UpdateChannelReturns(types.ChannelInfo{
542+
Name: "apple",
543+
Height: 123,
549544
}, nil)
545+
})
550546

547+
It("uses the channel participation API to update a channel", func() {
551548
args := []string{
552549
"channel",
553-
"join",
554-
"-o", ordererURL,
555-
"-c", channelID,
556-
"-b", blockPath,
550+
"update",
551+
"--orderer-address", ordererURL,
552+
"--channelID", channelID,
553+
"--config-update-envelope", envelopePath,
557554
"--ca-file", ordererCACert,
558555
"--client-cert", clientCert,
559556
"--client-key", clientKey,
560557
}
561558
output, exit, err := executeForArgs(args)
562559
expectedOutput := types.ChannelInfo{
563-
Name: "apple",
564-
URL: "/participation/v1/channels/apple",
565-
ConsensusRelation: "banana",
566-
Status: "orange",
567-
Height: 123,
560+
Name: "apple",
561+
URL: "/participation/v1/channels/apple",
562+
Height: 123,
568563
}
569564
checkStatusOutput(output, exit, err, 201, expectedOutput)
570565
})
571566

572-
Context("when an unknown flag is used", func() {
573-
It("returns an error for long flags", func() {
574-
_, _, err := executeForArgs([]string{"channel", "list", "--bad-flag"})
575-
Expect(err).To(MatchError("unknown long flag '--bad-flag'"))
567+
Context("when the envelope is empty", func() {
568+
BeforeEach(func() {
569+
envelopePath = createEnvelopeFile(tempDir, &cb.Envelope{})
576570
})
577571

578-
It("returns an error for short flags", func() {
579-
_, _, err := executeForArgs([]string{"channel", "list", "-z"})
580-
Expect(err).To(MatchError("unknown short flag '-z'"))
572+
It("returns with exit code 1 and prints the error", func() {
573+
args := []string{
574+
"channel",
575+
"update",
576+
"--orderer-address", ordererURL,
577+
"--channelID", channelID,
578+
"--config-update-envelope", envelopePath,
579+
"--ca-file", ordererCACert,
580+
"--client-cert", clientCert,
581+
"--client-key", clientKey,
582+
}
583+
output, exit, err := executeForArgs(args)
584+
585+
checkFlagError(output, exit, err, "failed to retrieve channel id - payload header is empty")
581586
})
582587
})
583588

584-
Context("when the ca cert cannot be read", func() {
589+
Context("when the --channelID does not match the channel ID in the envelope", func() {
585590
BeforeEach(func() {
586-
ordererCACert = "not-the-ca-cert-youre-looking-for"
591+
channelID = "not-the-channel-youre-looking-for"
587592
})
588593

589594
It("returns with exit code 1 and prints the error", func() {
590595
args := []string{
591596
"channel",
592-
"list",
597+
"update",
593598
"--orderer-address", ordererURL,
594599
"--channelID", channelID,
600+
"--config-update-envelope", envelopePath,
595601
"--ca-file", ordererCACert,
596602
"--client-cert", clientCert,
597603
"--client-key", clientKey,
598604
}
599605
output, exit, err := executeForArgs(args)
600-
checkFlagError(output, exit, err, "reading orderer CA certificate: open not-the-ca-cert-youre-looking-for: no such file or directory")
606+
607+
checkFlagError(output, exit, err, "specified --channelID not-the-channel-youre-looking-for does not match channel ID testing123 in config update envelope")
601608
})
602609
})
603610

604-
Context("when the ca-file contains a private key instead of certificate(s)", func() {
611+
Context("when the envelope isn't a valid config update", func() {
605612
BeforeEach(func() {
606-
ordererCACert = clientKey
613+
envelope := &cb.Envelope{
614+
Payload: protoutil.MarshalOrPanic(&cb.Payload{
615+
Header: &cb.Header{
616+
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
617+
Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
618+
ChannelId: channelID,
619+
}),
620+
},
621+
}),
622+
}
623+
envelopePath = createEnvelopeFile(tempDir, envelope)
607624
})
608625

609-
It("returns with exit code 1 and prints the error", func() {
626+
It("returns 405 bad request", func() {
610627
args := []string{
611628
"channel",
612-
"remove",
629+
"update",
613630
"--orderer-address", ordererURL,
614631
"--channelID", channelID,
632+
"--config-update-envelope", envelopePath,
615633
"--ca-file", ordererCACert,
616634
"--client-cert", clientCert,
617635
"--client-key", clientKey,
618636
}
619637
output, exit, err := executeForArgs(args)
620-
checkFlagError(output, exit, err, "failed to add ca-file PEM to cert pool")
638+
Expect(err).NotTo(HaveOccurred())
639+
Expect(exit).To(Equal(0))
640+
641+
expectedOutput := types.ErrorResponse{
642+
Error: "invalid config update envelope: bad type",
643+
}
644+
checkStatusOutput(output, exit, err, 400, expectedOutput)
621645
})
622646
})
623647

624-
Context("when the client cert/key pair fail to load", func() {
648+
Context("when updating the channel fails", func() {
625649
BeforeEach(func() {
626-
clientKey = "brussel-sprouts"
650+
mockChannelManagement.UpdateChannelReturns(types.ChannelInfo{}, types.ErrChannelNotExist)
627651
})
628652

629-
It("returns with exit code 1 and prints the error", func() {
653+
It("returns 405 not allowed", func() {
630654
args := []string{
631655
"channel",
632-
"list",
656+
"update",
633657
"--orderer-address", ordererURL,
658+
"--channelID", channelID,
659+
"--config-update-envelope", envelopePath,
634660
"--ca-file", ordererCACert,
635661
"--client-cert", clientCert,
636662
"--client-key", clientKey,
637663
}
638664
output, exit, err := executeForArgs(args)
639-
checkFlagError(output, exit, err, "loading client cert/key pair: open brussel-sprouts: no such file or directory")
665+
expectedOutput := types.ErrorResponse{
666+
Error: "cannot update: channel does not exist",
667+
}
668+
checkStatusOutput(output, exit, err, 405, expectedOutput)
640669
})
641-
})
642670

643-
Context("when the config block cannot be read", func() {
644-
var configBlockPath string
671+
It("returns 405 not allowed (without status)", func() {
672+
args := []string{
673+
"channel",
674+
"update",
675+
"--orderer-address", ordererURL,
676+
"--channelID", channelID,
677+
"--config-update-envelope", envelopePath,
678+
"--ca-file", ordererCACert,
679+
"--client-cert", clientCert,
680+
"--client-key", clientKey,
681+
"--no-status",
682+
}
683+
output, exit, err := executeForArgs(args)
684+
expectedOutput := types.ErrorResponse{
685+
Error: "cannot update: channel does not exist",
686+
}
687+
checkOutput(output, exit, err, expectedOutput)
688+
})
689+
})
645690

691+
Context("when TLS is disabled", func() {
646692
BeforeEach(func() {
647-
configBlockPath = "not-the-config-block-youre-looking-for"
693+
tlsConfig = nil
648694
})
649695

650-
It("returns with exit code 1 and prints the error", func() {
696+
It("uses the channel participation API to join a channel", func() {
651697
args := []string{
652698
"channel",
653-
"join",
699+
"update",
654700
"--orderer-address", ordererURL,
655701
"--channelID", channelID,
656-
"--ca-file", ordererCACert,
657-
"--client-cert", clientCert,
658-
"--client-key", clientKey,
659-
"--config-block", configBlockPath,
702+
"--config-update-envelope", envelopePath,
660703
}
661704
output, exit, err := executeForArgs(args)
662-
checkFlagError(output, exit, err, "reading config block: open not-the-config-block-youre-looking-for: no such file or directory")
705+
expectedOutput := types.ChannelInfo{
706+
Name: "apple",
707+
URL: "/participation/v1/channels/apple",
708+
Height: 123,
709+
}
710+
checkStatusOutput(output, exit, err, 201, expectedOutput)
663711
})
664712
})
665713
})
@@ -834,3 +882,34 @@ func createBlockFile(tempDir string, configBlock *cb.Block) string {
834882
Expect(err).NotTo(HaveOccurred())
835883
return blockPath
836884
}
885+
886+
func envelopeUpdateConfigWithGroups(channelID string) *cb.Envelope {
887+
data := &cb.Envelope{
888+
Payload: protoutil.MarshalOrPanic(&cb.Payload{
889+
Data: protoutil.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
890+
ConfigUpdate: protoutil.MarshalOrPanic(&cb.ConfigUpdate{
891+
ChannelId: channelID,
892+
ReadSet: &cb.ConfigGroup{},
893+
WriteSet: &cb.ConfigGroup{},
894+
}),
895+
}),
896+
Header: &cb.Header{
897+
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
898+
Type: int32(cb.HeaderType_CONFIG_UPDATE),
899+
ChannelId: channelID,
900+
}),
901+
},
902+
}),
903+
}
904+
905+
return data
906+
}
907+
908+
func createEnvelopeFile(tempDir string, configEnvelope *cb.Envelope) string {
909+
envelopeBytes, err := proto.Marshal(configEnvelope)
910+
Expect(err).NotTo(HaveOccurred())
911+
envelopePath := filepath.Join(tempDir, "envelope.pb")
912+
err = os.WriteFile(envelopePath, envelopeBytes, 0o644)
913+
Expect(err).NotTo(HaveOccurred())
914+
return envelopePath
915+
}

0 commit comments

Comments
 (0)