diff --git a/cmd/osnadmin/main.go b/cmd/osnadmin/main.go index 464293ae515..b63834223d6 100644 --- a/cmd/osnadmin/main.go +++ b/cmd/osnadmin/main.go @@ -52,7 +52,8 @@ func executeForArgs(args []string) (output string, exit int, err error) { 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() + info := channel.Command("info", "Get detailed information about a specific channel.") + infoChannelID := info.Flag("channelID", "Channel ID").Short('c').Required().String() remove := channel.Command("remove", "Remove a channel from an Ordering Service Node (OSN).") removeChannelID := remove.Flag("channelID", "Channel ID").Short('c').Required().String() @@ -113,11 +114,13 @@ func executeForArgs(args []string) (output string, exit int, err error) { 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 - } + // if *listChannelID != "" { + // resp, err = osnadmin.ListSingleChannel(osnURL, *listChannelID, caCertPool, tlsClientCert) + // break + // } resp, err = osnadmin.ListAllChannels(osnURL, caCertPool, tlsClientCert) + case info.FullCommand(): + resp, err = osnadmin.ListSingleChannel(osnURL, *infoChannelID, caCertPool, tlsClientCert) case remove.FullCommand(): resp, err = osnadmin.Remove(osnURL, *removeChannelID, caCertPool, tlsClientCert) } diff --git a/cmd/osnadmin/main_test.go b/cmd/osnadmin/main_test.go index 325341fdcc3..4005dba2604 100644 --- a/cmd/osnadmin/main_test.go +++ b/cmd/osnadmin/main_test.go @@ -118,13 +118,6 @@ var _ = Describe("osnadmin", func() { Name: "fight-the-system", }, }) - - mockChannelManagement.ChannelInfoReturns(types.ChannelInfo{ - Name: "asparagus", - ConsensusRelation: "broccoli", - Status: "carrot", - Height: 987, - }, nil) }) It("uses the channel participation API to list all application channels and the system channel (when it exists)", func() { @@ -156,10 +149,70 @@ var _ = Describe("osnadmin", func() { checkStatusOutput(output, exit, err, 200, expectedOutput) }) + Context("when TLS is disabled", func() { + BeforeEach(func() { + tlsConfig = nil + }) + + It("uses the channel participation API to list all channels", func() { + args := []string{ + "channel", + "list", + "--orderer-address", ordererURL, + } + output, exit, err := executeForArgs(args) + Expect(err).NotTo(HaveOccurred()) + Expect(exit).To(Equal(0)) + + expectedOutput := types.ChannelList{ + Channels: []types.ChannelInfoShort{ + { + Name: "participation-trophy", + URL: "/participation/v1/channels/participation-trophy", + }, + { + Name: "another-participation-trophy", + URL: "/participation/v1/channels/another-participation-trophy", + }, + }, + SystemChannel: &types.ChannelInfoShort{ + Name: "fight-the-system", + URL: "/participation/v1/channels/fight-the-system", + }, + } + checkStatusOutput(output, exit, err, 200, expectedOutput) + }) + }) + }) + + Describe("Info", func() { + BeforeEach(func() { + mockChannelManagement.ChannelListReturns(types.ChannelList{ + Channels: []types.ChannelInfoShort{ + { + Name: "participation-trophy", + }, + { + Name: "another-participation-trophy", + }, + }, + SystemChannel: &types.ChannelInfoShort{ + Name: "fight-the-system", + }, + }) + + mockChannelManagement.ChannelInfoReturns(types.ChannelInfo{ + Name: "asparagus", + ConsensusRelation: "broccoli", + Status: "carrot", + Height: 987, + }, nil) + }) + It("uses the channel participation API to list the details of a single channel", func() { args := []string{ "channel", - "list", + "info", "--orderer-address", ordererURL, "--channelID", "tell-me-your-secrets", "--ca-file", ordererCACert, @@ -185,7 +238,7 @@ var _ = Describe("osnadmin", func() { It("returns 404 not found", func() { args := []string{ "channel", - "list", + "info", "--orderer-address", ordererURL, "--channelID", "tell-me-your-secrets", "--ca-file", ordererCACert, @@ -205,39 +258,10 @@ var _ = Describe("osnadmin", func() { tlsConfig = nil }) - It("uses the channel participation API to list all channels", func() { - args := []string{ - "channel", - "list", - "--orderer-address", ordererURL, - } - output, exit, err := executeForArgs(args) - Expect(err).NotTo(HaveOccurred()) - Expect(exit).To(Equal(0)) - - expectedOutput := types.ChannelList{ - Channels: []types.ChannelInfoShort{ - { - Name: "participation-trophy", - URL: "/participation/v1/channels/participation-trophy", - }, - { - Name: "another-participation-trophy", - URL: "/participation/v1/channels/another-participation-trophy", - }, - }, - SystemChannel: &types.ChannelInfoShort{ - Name: "fight-the-system", - URL: "/participation/v1/channels/fight-the-system", - }, - } - checkStatusOutput(output, exit, err, 200, expectedOutput) - }) - It("uses the channel participation API to list the details of a single channel", func() { args := []string{ "channel", - "list", + "info", "--orderer-address", ordererURL, "--channelID", "tell-me-your-secrets", } @@ -254,6 +278,25 @@ var _ = Describe("osnadmin", func() { } checkStatusOutput(output, exit, err, 200, expectedOutput) }) + Context("when the channel does not exist", func() { + BeforeEach(func() { + mockChannelManagement.ChannelInfoReturns(types.ChannelInfo{}, errors.New("eat-your-peas")) + }) + + It("returns 404 not found with the info subcommand", func() { + args := []string{ + "channel", + "info", + "--orderer-address", ordererURL, + "--channelID", "tell-me-your-secrets", + } + output, exit, err := executeForArgs(args) + expectedOutput := types.ErrorResponse{ + Error: "eat-your-peas", + } + checkStatusOutput(output, exit, err, 404, expectedOutput) + }) + }) }) }) @@ -581,6 +624,18 @@ var _ = Describe("osnadmin", func() { }) }) + Context("when an unknown flag is used with the info subcommand", func() { + It("returns an error for long flags", func() { + _, _, err := executeForArgs([]string{"channel", "info", "--bad-flag"}) + Expect(err).To(MatchError("unknown long flag '--bad-flag'")) + }) + + It("returns an error for short flags", func() { + _, _, err := executeForArgs([]string{"channel", "info", "-z"}) + Expect(err).To(MatchError("unknown short flag '-z'")) + }) + }) + Context("when the ca cert cannot be read", func() { BeforeEach(func() { ordererCACert = "not-the-ca-cert-youre-looking-for" @@ -591,6 +646,25 @@ var _ = Describe("osnadmin", func() { "channel", "list", "--orderer-address", ordererURL, + "--ca-file", ordererCACert, + "--client-cert", clientCert, + "--client-key", clientKey, + } + output, exit, err := executeForArgs(args) + checkFlagError(output, exit, err, "reading orderer CA certificate: open not-the-ca-cert-youre-looking-for: no such file or directory") + }) + }) + + Context("when the ca cert cannot be read with the info subcommand", func() { + BeforeEach(func() { + ordererCACert = "not-the-ca-cert-youre-looking-for" + }) + + It("returns with exit code 1 and prints the error with the info subcommand", func() { + args := []string{ + "channel", + "info", + "--orderer-address", ordererURL, "--channelID", channelID, "--ca-file", ordererCACert, "--client-cert", clientCert, @@ -640,6 +714,26 @@ var _ = Describe("osnadmin", func() { }) }) + Context("when the client cert/key pair fail to load with the info subcommand", func() { + BeforeEach(func() { + clientKey = "brussel-sprouts" + }) + + It("returns with exit code 1 and prints the error with the info subcommand", func() { + args := []string{ + "channel", + "info", + "--orderer-address", ordererURL, + "--channelID", channelID, + "--ca-file", ordererCACert, + "--client-cert", clientCert, + "--client-key", clientKey, + } + output, exit, err := executeForArgs(args) + checkFlagError(output, exit, err, "loading client cert/key pair: open brussel-sprouts: no such file or directory") + }) + }) + Context("when the config block cannot be read", func() { var configBlockPath string