Skip to content

Commit 171df75

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 171df75

File tree

24 files changed

+2302
-199
lines changed

24 files changed

+2302
-199
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: 152 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -532,134 +532,188 @@ var _ = Describe("osnadmin", func() {
532532
})
533533
})
534534

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

553+
It("uses the channel participation API to join a channel", func() {
551554
args := []string{
552555
"channel",
553-
"join",
554-
"-o", ordererURL,
555-
"-c", channelID,
556-
"-b", blockPath,
556+
"update",
557+
"--orderer-address", ordererURL,
558+
"--channelID", channelID,
559+
"--config-update-envelope", envelopePath,
557560
"--ca-file", ordererCACert,
558561
"--client-cert", clientCert,
559562
"--client-key", clientKey,
560563
}
561564
output, exit, err := executeForArgs(args)
562565
expectedOutput := types.ChannelInfo{
563-
Name: "apple",
564-
URL: "/participation/v1/channels/apple",
565-
ConsensusRelation: "banana",
566-
Status: "orange",
567-
Height: 123,
566+
Name: "apple",
567+
URL: "/participation/v1/channels/apple",
568+
Height: 123,
568569
}
569570
checkStatusOutput(output, exit, err, 201, expectedOutput)
570571
})
571572

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'"))
573+
Context("when the envelope is empty", func() {
574+
BeforeEach(func() {
575+
envelopePath = createEnvelopeFile(tempDir, &cb.Envelope{})
576576
})
577577

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'"))
578+
It("returns with exit code 1 and prints the error", func() {
579+
args := []string{
580+
"channel",
581+
"update",
582+
"--orderer-address", ordererURL,
583+
"--channelID", channelID,
584+
"--config-update-envelope", envelopePath,
585+
"--ca-file", ordererCACert,
586+
"--client-cert", clientCert,
587+
"--client-key", clientKey,
588+
}
589+
output, exit, err := executeForArgs(args)
590+
591+
checkFlagError(output, exit, err, "failed to retrieve channel id - payload header is empty")
581592
})
582593
})
583594

584-
Context("when the ca cert cannot be read", func() {
595+
Context("when the --channelID does not match the channel ID in the envelope", func() {
585596
BeforeEach(func() {
586-
ordererCACert = "not-the-ca-cert-youre-looking-for"
597+
channelID = "not-the-channel-youre-looking-for"
587598
})
588599

589600
It("returns with exit code 1 and prints the error", func() {
590601
args := []string{
591602
"channel",
592-
"list",
603+
"update",
593604
"--orderer-address", ordererURL,
594605
"--channelID", channelID,
606+
"--config-update-envelope", envelopePath,
595607
"--ca-file", ordererCACert,
596608
"--client-cert", clientCert,
597609
"--client-key", clientKey,
598610
}
599611
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")
612+
613+
checkFlagError(output, exit, err, "specified --channelID not-the-channel-youre-looking-for does not match channel ID testing123 in config update envelope")
601614
})
602615
})
603616

604-
Context("when the ca-file contains a private key instead of certificate(s)", func() {
617+
Context("when the envelope isn't a valid config update", func() {
605618
BeforeEach(func() {
606-
ordererCACert = clientKey
619+
envelope := &cb.Envelope{
620+
Payload: protoutil.MarshalOrPanic(&cb.Payload{
621+
Header: &cb.Header{
622+
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
623+
Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
624+
ChannelId: channelID,
625+
}),
626+
},
627+
}),
628+
}
629+
envelopePath = createEnvelopeFile(tempDir, envelope)
607630
})
608631

609-
It("returns with exit code 1 and prints the error", func() {
632+
It("returns 405 bad request", func() {
610633
args := []string{
611634
"channel",
612-
"remove",
635+
"update",
613636
"--orderer-address", ordererURL,
614637
"--channelID", channelID,
638+
"--config-update-envelope", envelopePath,
615639
"--ca-file", ordererCACert,
616640
"--client-cert", clientCert,
617641
"--client-key", clientKey,
618642
}
619643
output, exit, err := executeForArgs(args)
620-
checkFlagError(output, exit, err, "failed to add ca-file PEM to cert pool")
644+
Expect(err).NotTo(HaveOccurred())
645+
Expect(exit).To(Equal(0))
646+
647+
expectedOutput := types.ErrorResponse{
648+
Error: "invalid config update envelope: bad type",
649+
}
650+
checkStatusOutput(output, exit, err, 400, expectedOutput)
621651
})
622652
})
623653

624-
Context("when the client cert/key pair fail to load", func() {
654+
Context("when updating the channel fails", func() {
625655
BeforeEach(func() {
626-
clientKey = "brussel-sprouts"
656+
mockChannelManagement.UpdateChannelReturns(types.ChannelInfo{}, types.ErrChannelNotExist)
627657
})
628658

629-
It("returns with exit code 1 and prints the error", func() {
659+
It("returns 405 not allowed", func() {
630660
args := []string{
631661
"channel",
632-
"list",
662+
"update",
633663
"--orderer-address", ordererURL,
664+
"--channelID", channelID,
665+
"--config-update-envelope", envelopePath,
634666
"--ca-file", ordererCACert,
635667
"--client-cert", clientCert,
636668
"--client-key", clientKey,
637669
}
638670
output, exit, err := executeForArgs(args)
639-
checkFlagError(output, exit, err, "loading client cert/key pair: open brussel-sprouts: no such file or directory")
671+
expectedOutput := types.ErrorResponse{
672+
Error: "cannot update: channel does not exist",
673+
}
674+
checkStatusOutput(output, exit, err, 405, expectedOutput)
640675
})
641-
})
642676

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

697+
Context("when TLS is disabled", func() {
646698
BeforeEach(func() {
647-
configBlockPath = "not-the-config-block-youre-looking-for"
699+
tlsConfig = nil
648700
})
649701

650-
It("returns with exit code 1 and prints the error", func() {
702+
It("uses the channel participation API to join a channel", func() {
651703
args := []string{
652704
"channel",
653-
"join",
705+
"update",
654706
"--orderer-address", ordererURL,
655707
"--channelID", channelID,
656-
"--ca-file", ordererCACert,
657-
"--client-cert", clientCert,
658-
"--client-key", clientKey,
659-
"--config-block", configBlockPath,
708+
"--config-update-envelope", envelopePath,
660709
}
661710
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")
711+
expectedOutput := types.ChannelInfo{
712+
Name: "apple",
713+
URL: "/participation/v1/channels/apple",
714+
Height: 123,
715+
}
716+
checkStatusOutput(output, exit, err, 201, expectedOutput)
663717
})
664718
})
665719
})
@@ -834,3 +888,51 @@ func createBlockFile(tempDir string, configBlock *cb.Block) string {
834888
Expect(err).NotTo(HaveOccurred())
835889
return blockPath
836890
}
891+
892+
func envelopeUpdateConfigWithGroups(groups map[string]*cb.ConfigGroup, channelID string) *cb.Envelope {
893+
data := &cb.Envelope{
894+
Payload: protoutil.MarshalOrPanic(&cb.Payload{
895+
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
896+
Config: &cb.Config{
897+
ChannelGroup: &cb.ConfigGroup{
898+
Groups: groups,
899+
Values: map[string]*cb.ConfigValue{
900+
"HashingAlgorithm": {
901+
Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{
902+
Name: bccsp.SHA256,
903+
}),
904+
},
905+
"BlockDataHashingStructure": {
906+
Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{
907+
Width: math.MaxUint32,
908+
}),
909+
},
910+
"OrdererAddresses": {
911+
Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{
912+
Addresses: []string{"localhost"},
913+
}),
914+
},
915+
},
916+
},
917+
},
918+
}),
919+
Header: &cb.Header{
920+
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
921+
Type: int32(cb.HeaderType_CONFIG_UPDATE),
922+
ChannelId: channelID,
923+
}),
924+
},
925+
}),
926+
}
927+
928+
return data
929+
}
930+
931+
func createEnvelopeFile(tempDir string, configEnvelope *cb.Envelope) string {
932+
envelopeBytes, err := proto.Marshal(configEnvelope)
933+
Expect(err).NotTo(HaveOccurred())
934+
envelopePath := filepath.Join(tempDir, "envelope.pb")
935+
err = os.WriteFile(envelopePath, envelopeBytes, 0o644)
936+
Expect(err).NotTo(HaveOccurred())
937+
return envelopePath
938+
}

0 commit comments

Comments
 (0)