From b714858f6b4dcc88538105c4bc2ce9b51bfb8155 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Thu, 14 Nov 2019 15:45:06 +0800 Subject: [PATCH 1/2] [BK-SUPPORT-PART-4] Support bookkeeper autorecovery commands --- Master Issue: #127 *Motivation* Support bookkeeper autorecovery commands in Pulsarctl. *Modifications* Add bookkeeper autorecovery commands. --- pkg/bkctl/autorecovery/autorecovery.go | 46 +++++++ pkg/bkctl/autorecovery/decommission.go | 69 ++++++++++ .../get_lost_bookie_recovery_delay.go | 64 +++++++++ .../list_under_replicated_ledger.go | 96 ++++++++++++++ pkg/bkctl/autorecovery/recover_bookie.go | 78 +++++++++++ .../set_lost_bookie_recovery_delay.go | 78 +++++++++++ pkg/bkctl/autorecovery/trigger_audit.go | 64 +++++++++ pkg/bkctl/autorecovery/who_is_auditor.go | 66 ++++++++++ pkg/bkctl/bk.go | 5 +- pkg/bookkeeper/admin.go | 2 + pkg/bookkeeper/autorecovery.go | 124 ++++++++++++++++++ pkg/bookkeeper/bkdata/autorecovery_data.go | 31 +++++ 12 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 pkg/bkctl/autorecovery/autorecovery.go create mode 100644 pkg/bkctl/autorecovery/decommission.go create mode 100644 pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go create mode 100644 pkg/bkctl/autorecovery/list_under_replicated_ledger.go create mode 100644 pkg/bkctl/autorecovery/recover_bookie.go create mode 100644 pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go create mode 100644 pkg/bkctl/autorecovery/trigger_audit.go create mode 100644 pkg/bkctl/autorecovery/who_is_auditor.go create mode 100644 pkg/bookkeeper/autorecovery.go create mode 100644 pkg/bookkeeper/bkdata/autorecovery_data.go diff --git a/pkg/bkctl/autorecovery/autorecovery.go b/pkg/bkctl/autorecovery/autorecovery.go new file mode 100644 index 00000000..7fce86e5 --- /dev/null +++ b/pkg/bkctl/autorecovery/autorecovery.go @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/spf13/cobra" +) + +func Command(flagGrouping *cmdutils.FlagGrouping) *cobra.Command { + resourceCmd := cmdutils.NewResourceCmd( + "autorecovery", + "Operations about ledger", + "", + "") + + commands := []func(*cmdutils.VerbCmd){ + recoverBookieCmd, + listUnderReplicatedLedgerCmd, + whoIsAuditorCmd, + triggerAuditCmd, + setLostBookieRecoveryDelayCmd, + getLostBookieRecoveryDelayCmd, + decommissionCmd, + } + + cmdutils.AddVerbCmds(flagGrouping, resourceCmd, commands...) + + return resourceCmd +} diff --git a/pkg/bkctl/autorecovery/decommission.go b/pkg/bkctl/autorecovery/decommission.go new file mode 100644 index 00000000..bed469dc --- /dev/null +++ b/pkg/bkctl/autorecovery/decommission.go @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" +) + +func decommissionCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for decommission a bookie." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + c := cmdutils.Example{ + Desc: "Decommission a bookie", + Command: "pulsarctl bookkeeper autorecovery (bk-ip:bk-port)", + } + examples = append(examples, c) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: "Successfully decommission the bookie (bookie-ip:bookie-port)", + } + + argError := cmdutils.Output{ + Desc: "the bookie address is not specified or the bookie address is specified more than one", + Out: "[✖] the bookie address is not specified or the bookie address is specified more than one", + } + out = append(out, successOut, argError) + desc.CommandOutput = out + + vc.SetDescription( + "decommission", + "Decommission a bookie", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFuncWithNameArg(func() error { + return doDecommission(vc) + }, "the bookie address is not specified or the bookie address is specified more than one") +} + +func doDecommission(vc *cmdutils.VerbCmd) error { + admin := cmdutils.NewBookieClient() + err := admin.AutoRecovery().Decommission(vc.NameArg) + if err == nil { + vc.Command.Printf("Successfully decommission the bookie %s\n", vc.NameArg) + } + + return err +} diff --git a/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go b/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go new file mode 100644 index 00000000..94f37484 --- /dev/null +++ b/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" +) + +func getLostBookieRecoveryDelayCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for getting the lost bookie recovery delay in second of a bookie." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + get := cmdutils.Example{ + Desc: "Get the lost Bookie Recovery Delay of a bookie", + Command: "pulsarctl bookkeeeper autorecovery getdelay", + } + examples = append(examples, get) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: "lostBookieRecoveryDelay value: (delay)", + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "getdelay", + "Get the lost bookie recovery delay of a bookie", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFunc(func() error { + return doGetLostBookieRecoveryDelay(vc) + }) +} + +func doGetLostBookieRecoveryDelay(vc *cmdutils.VerbCmd) error { + admin := cmdutils.NewBookieClient() + out, err := admin.AutoRecovery().GetLostBookieRecoveryDelay() + if err == nil { + vc.Command.Println(out) + } + + return err +} diff --git a/pkg/bkctl/autorecovery/list_under_replicated_ledger.go b/pkg/bkctl/autorecovery/list_under_replicated_ledger.go new file mode 100644 index 00000000..715bcec1 --- /dev/null +++ b/pkg/bkctl/autorecovery/list_under_replicated_ledger.go @@ -0,0 +1,96 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/spf13/pflag" +) + +func listUnderReplicatedLedgerCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for listing all the underreplicated ledgers which have been marked " + + "for rereplication." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + list := cmdutils.Example{ + Desc: "List all the underreplicated ledgers which have been marked for rereplication", + Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger", + } + + li := cmdutils.Example{ + Desc: "List all the underreplicated ledgers of a bookie which have been marked for rereplication", + Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger --include (bookie-ip:bookie-port)", + } + + le := cmdutils.Example{ + Desc: "List all the underreplicated ledgers except a bookie which have been marked for rereplication", + Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger --exclude (bookie-ip:bookie-port)", + } + examples = append(examples, list, li, le) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: `{ + [ledgerId1, ledgerId2...] +}`, + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "listunderreplicatedledger", + "List all the underreplicated ledgers which have been marked for rereplication", + desc.ToString(), + desc.ExampleToString()) + + var include string + var exclude string + var show bool + + vc.SetRunFunc(func() error { + return doListUnderReplicatedLedger(vc, include, exclude, show) + }) + + vc.FlagSetGroup.InFlagSet("List Under Replicated Ledger", func(set *pflag.FlagSet) { + set.StringVar(&include, "include", "", "show the underreplicated ledger of the bookie") + set.StringVar(&exclude, "exclude", "", "show the underreplicated ledger exclude the bookie") + set.BoolVar(&show, "show", false, "show the ledgers replica list") + }) +} + +func doListUnderReplicatedLedger(vc *cmdutils.VerbCmd, include, exclude string, print bool) error { + admin := cmdutils.NewBookieClient() + var l interface{} + var err error + if print { + l, err = admin.AutoRecovery().PrintListUnderReplicatedLedger(include, exclude) + } else { + l, err = admin.AutoRecovery().ListUnderReplicatedLedger(include, exclude) + } + + if err == nil { + cmdutils.PrintJSON(vc.Command.OutOrStdout(), l) + } + + return err +} diff --git a/pkg/bkctl/autorecovery/recover_bookie.go b/pkg/bkctl/autorecovery/recover_bookie.go new file mode 100644 index 00000000..e47e5739 --- /dev/null +++ b/pkg/bkctl/autorecovery/recover_bookie.go @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/spf13/pflag" +) + +func recoverBookieCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for recovering the ledger data of a failed bookie." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + rb := cmdutils.Example{ + Desc: "Recover the ledger data of a failed bookie", + Command: "pulsarctl bookkeeper autorecovery recoverbookie (bookie-1) (bookie-2)", + } + examples = append(examples, rb) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: "Successfully recover the bookies (bookie-1) (bookie-2)", + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "recoverbookie", + "Recover the ledger data of a failed bookie", + desc.ToString(), + desc.ExampleToString()) + + var deleteCookie bool + + vc.SetRunFuncWithMultiNameArgs(func() error { + return doRecoverBookie(vc, deleteCookie) + }, func(args []string) error { + return nil + }) + + vc.FlagSetGroup.InFlagSet("Recover Bookie", func(set *pflag.FlagSet) { + set.BoolVar(&deleteCookie, "delelte-cookie", false, "delete cookie") + }) +} + +func doRecoverBookie(vc *cmdutils.VerbCmd, deleteCookie bool) error { + admin := cmdutils.NewBookieClient() + err := admin.AutoRecovery().RecoverBookie(vc.NameArgs, deleteCookie) + if err == nil { + if deleteCookie { + vc.Command.Printf("Successfully recover the bookies %v and delete the cookie\n", vc.NameArgs) + } else { + vc.Command.Printf("Successfully recover the bookie %v\n", vc.NameArgs) + } + } + + return err +} diff --git a/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go new file mode 100644 index 00000000..6415f3ca --- /dev/null +++ b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "strconv" + + "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/pkg/errors" +) + +func setLostBookieRecoveryDelayCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for setting the lost bookie recovery delay in second." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + set := cmdutils.Example{ + Desc: "Set the lost Bookie Recovery Delay", + Command: "pulsarctl bookkeeper autorecovery setdelay (delay)", + } + examples = append(examples, set) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: "Successfully set the lost bookie recovery delay to (delay)(second)", + } + + argError := cmdutils.Output{ + Desc: "the specified delay time is not specified or the delay time is specified more than one", + Out: "[✖] the specified delay time is not specified or the delay time is specified more than one", + } + out = append(out, successOut, argError) + desc.CommandOutput = out + + vc.SetDescription( + "setdelay", + "Set the lost bookie recovery delay", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFuncWithNameArg(func() error { + return doLostBookieRecoveryDelay(vc) + }, "the delay time is not specified or the delay time is specified more than one") +} + +func doLostBookieRecoveryDelay(vc *cmdutils.VerbCmd) error { + delay, err := strconv.Atoi(vc.NameArg) + if err != nil { + return errors.Errorf("invalid delay times %s", vc.NameArg) + } + + admin := cmdutils.NewBookieClient() + err = admin.AutoRecovery().SetLostBookieRecoveryDelay(delay) + if err == nil { + vc.Command.Printf("Successfully set the lost bookie recovery delay to %d(second)\n", delay) + } + + return err +} diff --git a/pkg/bkctl/autorecovery/trigger_audit.go b/pkg/bkctl/autorecovery/trigger_audit.go new file mode 100644 index 00000000..f8610730 --- /dev/null +++ b/pkg/bkctl/autorecovery/trigger_audit.go @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" +) + +func triggerAuditCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for triggering audit by resetting the lostBookieRecoveryDelay." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + trigger := cmdutils.Example{ + Desc: "Trigger audit by resetting the lostBookieRecoveryDelay", + Command: "pulsarctl bookkeeper autorecovery triggeraudit", + } + examples = append(examples, trigger) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: "Successfully trigger audit by resetting the lostBookieRecoveryDelay", + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "triggeraudit", + "Trigger audit by resetting the lostBookieRecoveryDelay", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFunc(func() error { + return doTriggerAudit(vc) + }) +} + +func doTriggerAudit(vc *cmdutils.VerbCmd) error { + admin := cmdutils.NewBookieClient() + err := admin.AutoRecovery().TriggerAudit() + if err == nil { + vc.Command.Println("Successfully trigger audit by resetting the lostBookieRecoveryDelay") + } + + return err +} diff --git a/pkg/bkctl/autorecovery/who_is_auditor.go b/pkg/bkctl/autorecovery/who_is_auditor.go new file mode 100644 index 00000000..365b2388 --- /dev/null +++ b/pkg/bkctl/autorecovery/who_is_auditor.go @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "github.com/streamnative/pulsarctl/pkg/cmdutils" +) + +func whoIsAuditorCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for getting who is the auditor." + desc.CommandPermission = "none" + + var examples []cmdutils.Example + get := cmdutils.Example{ + Desc: "Get who is the auditor", + Command: "pulsarctl bookkeeper autorecovery whoisauditor", + } + examples = append(examples, get) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "normal output", + Out: `{ + "Auditor": "hostname/hostAddress:Port" +}`, + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "whoisauditor", + "Get who is the auditor", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFunc(func() error { + return doWhoIsAuditor(vc) + }) +} + +func doWhoIsAuditor(vc *cmdutils.VerbCmd) error { + admin := cmdutils.NewBookieClient() + auditor, err := admin.AutoRecovery().WhoIsAuditor() + if err == nil { + cmdutils.PrintJSON(vc.Command.OutOrStdout(), auditor) + } + + return err +} diff --git a/pkg/bkctl/bk.go b/pkg/bkctl/bk.go index 982aba8a..c2224ff2 100644 --- a/pkg/bkctl/bk.go +++ b/pkg/bkctl/bk.go @@ -18,9 +18,11 @@ package bkctl import ( - "github.com/spf13/cobra" + "github.com/streamnative/pulsarctl/pkg/bkctl/autorecovery" "github.com/streamnative/pulsarctl/pkg/bkctl/ledger" "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/spf13/cobra" ) func Command(flagGrouping *cmdutils.FlagGrouping) *cobra.Command { @@ -32,6 +34,7 @@ func Command(flagGrouping *cmdutils.FlagGrouping) *cobra.Command { ) resourceCmd.AddCommand(ledger.Command(flagGrouping)) + resourceCmd.AddCommand(autorecovery.Command(flagGrouping)) return resourceCmd } diff --git a/pkg/bookkeeper/admin.go b/pkg/bookkeeper/admin.go index 05836236..a7ca8107 100644 --- a/pkg/bookkeeper/admin.go +++ b/pkg/bookkeeper/admin.go @@ -29,6 +29,8 @@ import ( type Client interface { // Ledger related commands Ledger() Ledger + // AutoRecovery related commands + AutoRecovery() AutoRecovery } type bookieClient struct { diff --git a/pkg/bookkeeper/autorecovery.go b/pkg/bookkeeper/autorecovery.go new file mode 100644 index 00000000..f1d2e5f1 --- /dev/null +++ b/pkg/bookkeeper/autorecovery.go @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package bookkeeper + +import "github.com/streamnative/pulsarctl/pkg/bookkeeper/bkdata" + +type AutoRecovery interface { + // RecoverBookie is used to recovering ledger data for a failed bookie + RecoverBookie([]string, bool) error + + // ListUnderReplicatedLedger is used to listing all the underreplicated ledgers + // which have been marked for rereplication + ListUnderReplicatedLedger(string, string) ([]int64, error) + + // PrintListUnderReplicatedLedger is used to printing the replicate list of the replicated ledgers + PrintListUnderReplicatedLedger(string, string) (map[int64][]string, error) + + // WhoIsAuditor is used to getting which bookie is the auditor + WhoIsAuditor() (map[string]string, error) + + // TriggerAudit is used to triggering audit by resetting the lostBookieRecoveryDelay + TriggerAudit() error + + // GetLostBookieRecoveryDelay is used to getting the lostBookieRecoveryDelay of a bookie + GetLostBookieRecoveryDelay() (string, error) + + // SetLostBookieRecoveryDelay is used to setting the lastBookieRecoverDelay of a bookie + SetLostBookieRecoveryDelay(int) error + + // Decommission is used to decommissioning a bookie + Decommission(string) error +} + +type autoRecovery struct { + bk *bookieClient + basePath string + params map[string]string +} + +func (c *bookieClient) AutoRecovery() AutoRecovery { + return &autoRecovery{ + bk: c, + basePath: "/autorecovery", + params: make(map[string]string), + } +} + +func (a *autoRecovery) RecoverBookie(src []string, deleteCookie bool) error { + endpoint := a.bk.endpoint(a.basePath, "/bookie") + request := bkdata.RecoveryRequest{ + BookieSrc: src, + DeleteCookie: deleteCookie, + } + return a.bk.Client.Put(endpoint, &request) +} + +func (a *autoRecovery) ListUnderReplicatedLedger(missingReplica, excludingMissingReplica string) ([]int64, error) { + endpoint := a.bk.endpoint(a.basePath, "/list_under_replicated_ledger") + a.params["missingreplica"] = missingReplica + a.params["excludingmissingreplica"] = excludingMissingReplica + resp := make([]int64, 0) + _, err := a.bk.Client.GetWithQueryParams(endpoint, &resp, a.params, true) + return resp, err +} + +func (a *autoRecovery) PrintListUnderReplicatedLedger(missingReplica, + excludingMissingReplica string) (map[int64][]string, error) { + + endpoint := a.bk.endpoint(a.basePath, "/list_under_replicated_ledger") + a.params["missingreplica"] = missingReplica + a.params["excludingmissingreplica"] = excludingMissingReplica + a.params["printmissingreplica"] = "true" + resp := make(map[int64][]string) + _, err := a.bk.Client.GetWithQueryParams(endpoint, &resp, a.params, true) + return resp, err +} + +func (a *autoRecovery) WhoIsAuditor() (map[string]string, error) { + endpoint := a.bk.endpoint(a.basePath, "/who_is_auditor") + resp := make(map[string]string) + return resp, a.bk.Client.Get(endpoint, &resp) +} + +func (a *autoRecovery) TriggerAudit() error { + endpoint := a.bk.endpoint(a.basePath, "/trigger_audit") + return a.bk.Client.Put(endpoint, nil) +} + +func (a *autoRecovery) GetLostBookieRecoveryDelay() (string, error) { + endpoint := a.bk.endpoint(a.basePath, "/lost_bookie_recovery_delay") + resp, err := a.bk.Client.GetWithQueryParams(endpoint, nil, nil, false) + return string(resp), err +} + +func (a *autoRecovery) SetLostBookieRecoveryDelay(delay int) error { + endpoint := a.bk.endpoint(a.basePath, "/lost_bookie_recovery_delay") + req := bkdata.LostBookieRecoverDelayRequest{ + DelaySeconds: delay, + } + return a.bk.Client.Put(endpoint, &req) +} + +func (a *autoRecovery) Decommission(src string) error { + endpoint := a.bk.endpoint(a.basePath, "/decommission") + req := bkdata.DecommissionRequest{ + BookieSrc: src, + } + return a.bk.Client.Put(endpoint, &req) +} diff --git a/pkg/bookkeeper/bkdata/autorecovery_data.go b/pkg/bookkeeper/bkdata/autorecovery_data.go new file mode 100644 index 00000000..c838153e --- /dev/null +++ b/pkg/bookkeeper/bkdata/autorecovery_data.go @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package bkdata + +type RecoveryRequest struct { + BookieSrc []string `json:"bookie_src"` + DeleteCookie bool `json:"delete_cookie"` +} + +type LostBookieRecoverDelayRequest struct { + DelaySeconds int `json:"delay_seconds"` +} + +type DecommissionRequest struct { + BookieSrc string `json:"bookie_src"` +} From 6ca514d23883e5d5174e3c8f96825970214fe2cb Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Wed, 8 Jan 2020 15:44:36 +0800 Subject: [PATCH 2/2] Address comments --- pkg/bkctl/autorecovery/autorecovery.go | 4 +- pkg/bkctl/autorecovery/decommission.go | 16 ++-- pkg/bkctl/autorecovery/decommission_test.go | 48 ++++++++++++ .../get_lost_bookie_recovery_delay.go | 12 +-- .../list_under_replicated_ledger.go | 36 ++++----- pkg/bkctl/autorecovery/recover_bookie.go | 35 ++++++--- pkg/bkctl/autorecovery/recover_bookie_test.go | 35 +++++++++ .../set_lost_bookie_recovery_delay.go | 18 ++--- .../set_lost_bookie_recovery_delay_test.go | 75 +++++++++++++++++++ pkg/bkctl/autorecovery/test_help.go | 58 ++++++++++++++ pkg/bkctl/autorecovery/trigger_audit.go | 18 ++--- pkg/bkctl/autorecovery/who_is_auditor.go | 10 +-- 12 files changed, 296 insertions(+), 69 deletions(-) create mode 100644 pkg/bkctl/autorecovery/decommission_test.go create mode 100644 pkg/bkctl/autorecovery/recover_bookie_test.go create mode 100644 pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay_test.go create mode 100644 pkg/bkctl/autorecovery/test_help.go diff --git a/pkg/bkctl/autorecovery/autorecovery.go b/pkg/bkctl/autorecovery/autorecovery.go index 7fce86e5..10da5a46 100644 --- a/pkg/bkctl/autorecovery/autorecovery.go +++ b/pkg/bkctl/autorecovery/autorecovery.go @@ -25,8 +25,8 @@ import ( func Command(flagGrouping *cmdutils.FlagGrouping) *cobra.Command { resourceCmd := cmdutils.NewResourceCmd( - "autorecovery", - "Operations about ledger", + "auto-recovery", + "Operations about auto recovering", "", "") diff --git a/pkg/bkctl/autorecovery/decommission.go b/pkg/bkctl/autorecovery/decommission.go index bed469dc..e1b33dd4 100644 --- a/pkg/bkctl/autorecovery/decommission.go +++ b/pkg/bkctl/autorecovery/decommission.go @@ -23,25 +23,25 @@ import ( func decommissionCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription - desc.CommandUsedFor = "This command is used for decommission a bookie." - desc.CommandPermission = "none" + desc.CommandUsedFor = "This command is used for decommissioning a bookie." + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example c := cmdutils.Example{ - Desc: "Decommission a bookie", - Command: "pulsarctl bookkeeper autorecovery (bk-ip:bk-port)", + Desc: "Decommission a bookie.", + Command: "pulsarctl bookkeeper auto-recovery (bk-ip:bk-port)", } examples = append(examples, c) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", + Desc: "Successfully decommission a bookie.", Out: "Successfully decommission the bookie (bookie-ip:bookie-port)", } argError := cmdutils.Output{ - Desc: "the bookie address is not specified or the bookie address is specified more than one", + Desc: "The bookie address is not specified or the bookie address is specified more than one.", Out: "[✖] the bookie address is not specified or the bookie address is specified more than one", } out = append(out, successOut, argError) @@ -49,7 +49,7 @@ func decommissionCmd(vc *cmdutils.VerbCmd) { vc.SetDescription( "decommission", - "Decommission a bookie", + "Decommission a bookie.", desc.ToString(), desc.ExampleToString()) @@ -62,7 +62,7 @@ func doDecommission(vc *cmdutils.VerbCmd) error { admin := cmdutils.NewBookieClient() err := admin.AutoRecovery().Decommission(vc.NameArg) if err == nil { - vc.Command.Printf("Successfully decommission the bookie %s\n", vc.NameArg) + vc.Command.Printf("Successfully decommission the bookie %s.\n", vc.NameArg) } return err diff --git a/pkg/bkctl/autorecovery/decommission_test.go b/pkg/bkctl/autorecovery/decommission_test.go new file mode 100644 index 00000000..51ff60c6 --- /dev/null +++ b/pkg/bkctl/autorecovery/decommission_test.go @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecommissionArgsErr(t *testing.T) { + // no args specified + args := []string{"decommission"} + _, _, nameErr, err := testAutoRecoveryCommands(decommissionCmd, args) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, nameErr) + assert.Equal(t, "the bookie address is not specified or the bookie address is specified more than one", + nameErr.Error()) + + // more than one args specified + args = []string{"decommission", "bookie-1:3181", "bookie-2:3181"} + _, _, nameErr, err = testAutoRecoveryCommands(decommissionCmd, args) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, nameErr) + assert.Equal(t, "the bookie address is not specified or the bookie address is specified more than one", + nameErr.Error()) +} diff --git a/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go b/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go index 94f37484..855818f1 100644 --- a/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go +++ b/pkg/bkctl/autorecovery/get_lost_bookie_recovery_delay.go @@ -24,27 +24,27 @@ import ( func getLostBookieRecoveryDelayCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription desc.CommandUsedFor = "This command is used for getting the lost bookie recovery delay in second of a bookie." - desc.CommandPermission = "none" + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example get := cmdutils.Example{ - Desc: "Get the lost Bookie Recovery Delay of a bookie", - Command: "pulsarctl bookkeeeper autorecovery getdelay", + Desc: "Get the lost Bookie Recovery Delay of a bookie.", + Command: "pulsarctl bookkeeper auto-recovery get-lost-bookie-recovery-delay", } examples = append(examples, get) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", + Desc: "Get the lost bookie recovery delay of a bookie. ", Out: "lostBookieRecoveryDelay value: (delay)", } out = append(out, successOut) desc.CommandOutput = out vc.SetDescription( - "getdelay", - "Get the lost bookie recovery delay of a bookie", + "get-lost-bookie-recovery-delay", + "Get the lost bookie recovery delay of a bookie.", desc.ToString(), desc.ExampleToString()) diff --git a/pkg/bkctl/autorecovery/list_under_replicated_ledger.go b/pkg/bkctl/autorecovery/list_under_replicated_ledger.go index 715bcec1..dcdac48f 100644 --- a/pkg/bkctl/autorecovery/list_under_replicated_ledger.go +++ b/pkg/bkctl/autorecovery/list_under_replicated_ledger.go @@ -25,31 +25,31 @@ import ( func listUnderReplicatedLedgerCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription - desc.CommandUsedFor = "This command is used for listing all the underreplicated ledgers which have been marked " + - "for rereplication." - desc.CommandPermission = "none" + desc.CommandUsedFor = "This command is used for getting all the under-replicated ledgers which have been marked " + + "for re-replication." + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example list := cmdutils.Example{ - Desc: "List all the underreplicated ledgers which have been marked for rereplication", - Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger", + Desc: "Get all the under-replicated ledgers which have been marked for re-replication.", + Command: "pulsarctl bookkeeper auto-recovery list-under-replicated-ledger", } li := cmdutils.Example{ - Desc: "List all the underreplicated ledgers of a bookie which have been marked for rereplication", - Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger --include (bookie-ip:bookie-port)", + Desc: "Get all the under-replicated ledgers of a bookie which have been marked for re-replication.", + Command: "pulsarctl bookkeeper auto-recovery list-under-replicated-ledger --include (bookie-ip:bookie-port)", } le := cmdutils.Example{ - Desc: "List all the underreplicated ledgers except a bookie which have been marked for rereplication", - Command: "pulsarctl bookkeeper autorecovery listunderreplicatedledger --exclude (bookie-ip:bookie-port)", + Desc: "Get all the under-replicated ledgers except a bookie which have been marked for re-replication.", + Command: "pulsarctl bookkeeper auto-recovery list-under-replicated-ledger --exclude (bookie-ip:bookie-port)", } examples = append(examples, list, li, le) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", + Desc: "Get the under-replicated ledgers successfully.", Out: `{ [ledgerId1, ledgerId2...] }`, @@ -58,8 +58,8 @@ func listUnderReplicatedLedgerCmd(vc *cmdutils.VerbCmd) { desc.CommandOutput = out vc.SetDescription( - "listunderreplicatedledger", - "List all the underreplicated ledgers which have been marked for rereplication", + "list-under-replicated-ledger", + "Get all the under-replicated ledgers which have been marked for re-replication.", desc.ToString(), desc.ExampleToString()) @@ -71,18 +71,18 @@ func listUnderReplicatedLedgerCmd(vc *cmdutils.VerbCmd) { return doListUnderReplicatedLedger(vc, include, exclude, show) }) - vc.FlagSetGroup.InFlagSet("List Under Replicated Ledger", func(set *pflag.FlagSet) { - set.StringVar(&include, "include", "", "show the underreplicated ledger of the bookie") - set.StringVar(&exclude, "exclude", "", "show the underreplicated ledger exclude the bookie") - set.BoolVar(&show, "show", false, "show the ledgers replica list") + vc.FlagSetGroup.InFlagSet("List under replicated ledgers", func(set *pflag.FlagSet) { + set.StringVar(&include, "include", "", "Show the under-replicated ledger of the bookie.") + set.StringVar(&exclude, "exclude", "", "Show the under-replicated ledger exclude the bookie.") + set.BoolVar(&show, "show", false, "Show the replicate ledger list.") }) } -func doListUnderReplicatedLedger(vc *cmdutils.VerbCmd, include, exclude string, print bool) error { +func doListUnderReplicatedLedger(vc *cmdutils.VerbCmd, include, exclude string, show bool) error { admin := cmdutils.NewBookieClient() var l interface{} var err error - if print { + if show { l, err = admin.AutoRecovery().PrintListUnderReplicatedLedger(include, exclude) } else { l, err = admin.AutoRecovery().ListUnderReplicatedLedger(include, exclude) diff --git a/pkg/bkctl/autorecovery/recover_bookie.go b/pkg/bkctl/autorecovery/recover_bookie.go index e47e5739..cc644d07 100644 --- a/pkg/bkctl/autorecovery/recover_bookie.go +++ b/pkg/bkctl/autorecovery/recover_bookie.go @@ -18,6 +18,8 @@ package autorecovery import ( + "errors" + "github.com/streamnative/pulsarctl/pkg/cmdutils" "github.com/spf13/pflag" @@ -26,27 +28,32 @@ import ( func recoverBookieCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription desc.CommandUsedFor = "This command is used for recovering the ledger data of a failed bookie." - desc.CommandPermission = "none" + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example rb := cmdutils.Example{ - Desc: "Recover the ledger data of a failed bookie", - Command: "pulsarctl bookkeeper autorecovery recoverbookie (bookie-1) (bookie-2)", + Desc: "Recover the ledger data of a failed bookie.", + Command: "pulsarctl bookkeeper auto-recovery recover-bookie (bookie-1) (bookie-2)", } examples = append(examples, rb) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", - Out: "Successfully recover the bookies (bookie-1) (bookie-2)", + Desc: "Recover the bookies successfully.", + Out: "Successfully recover the bookies (bookie-1) (bookie-2).", + } + + IDNotSpecified := cmdutils.Output{ + Desc: "The recover bookie id is not specified.", + Out: "[✖] you need to specify the recover bookies id", } - out = append(out, successOut) + out = append(out, successOut, IDNotSpecified) desc.CommandOutput = out vc.SetDescription( - "recoverbookie", - "Recover the ledger data of a failed bookie", + "recover-bookie", + "Recover the ledger data of a failed bookie.", desc.ToString(), desc.ExampleToString()) @@ -55,11 +62,15 @@ func recoverBookieCmd(vc *cmdutils.VerbCmd) { vc.SetRunFuncWithMultiNameArgs(func() error { return doRecoverBookie(vc, deleteCookie) }, func(args []string) error { + if len(args) == 0 { + return errors.New("you need to specify the recover bookies id") + } return nil }) - vc.FlagSetGroup.InFlagSet("Recover Bookie", func(set *pflag.FlagSet) { - set.BoolVar(&deleteCookie, "delelte-cookie", false, "delete cookie") + vc.FlagSetGroup.InFlagSet("Recover bookie", func(set *pflag.FlagSet) { + set.BoolVar(&deleteCookie, "delete-cookie", false, + "Delete cookie when recovering the failed bookies.") }) } @@ -68,9 +79,9 @@ func doRecoverBookie(vc *cmdutils.VerbCmd, deleteCookie bool) error { err := admin.AutoRecovery().RecoverBookie(vc.NameArgs, deleteCookie) if err == nil { if deleteCookie { - vc.Command.Printf("Successfully recover the bookies %v and delete the cookie\n", vc.NameArgs) + vc.Command.Printf("Successfully recover the bookies %v and delete the cookie.\n", vc.NameArgs) } else { - vc.Command.Printf("Successfully recover the bookie %v\n", vc.NameArgs) + vc.Command.Printf("Successfully recover the bookie %v.\n", vc.NameArgs) } } diff --git a/pkg/bkctl/autorecovery/recover_bookie_test.go b/pkg/bkctl/autorecovery/recover_bookie_test.go new file mode 100644 index 00000000..d95038fa --- /dev/null +++ b/pkg/bkctl/autorecovery/recover_bookie_test.go @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRecoverBookieArgsErr(t *testing.T) { + args := []string{"recover-bookie"} + _, _, nameErr, err := testAutoRecoveryCommands(recoverBookieCmd, args) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, nameErr) + assert.Equal(t, "you need to specify the recover bookies id", nameErr.Error()) +} diff --git a/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go index 6415f3ca..5c3cd7f9 100644 --- a/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go +++ b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay.go @@ -28,32 +28,32 @@ import ( func setLostBookieRecoveryDelayCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription desc.CommandUsedFor = "This command is used for setting the lost bookie recovery delay in second." - desc.CommandPermission = "none" + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example set := cmdutils.Example{ - Desc: "Set the lost Bookie Recovery Delay", - Command: "pulsarctl bookkeeper autorecovery setdelay (delay)", + Desc: "Set the lost bookie recovery delay.", + Command: "pulsarctl bookkeeper auto-recovery set-lost-bookie-recovery-delay (delay)", } examples = append(examples, set) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", + Desc: "Set the lost bookie recovery delay to the new delay successfully.", Out: "Successfully set the lost bookie recovery delay to (delay)(second)", } argError := cmdutils.Output{ - Desc: "the specified delay time is not specified or the delay time is specified more than one", + Desc: "The specified delay time is not specified or the delay time is specified more than one.", Out: "[✖] the specified delay time is not specified or the delay time is specified more than one", } out = append(out, successOut, argError) desc.CommandOutput = out vc.SetDescription( - "setdelay", - "Set the lost bookie recovery delay", + "set-lost-bookie-recovery-delay", + "Set the lost bookie recovery delay.", desc.ToString(), desc.ExampleToString()) @@ -64,14 +64,14 @@ func setLostBookieRecoveryDelayCmd(vc *cmdutils.VerbCmd) { func doLostBookieRecoveryDelay(vc *cmdutils.VerbCmd) error { delay, err := strconv.Atoi(vc.NameArg) - if err != nil { + if err != nil || delay < 0 { return errors.Errorf("invalid delay times %s", vc.NameArg) } admin := cmdutils.NewBookieClient() err = admin.AutoRecovery().SetLostBookieRecoveryDelay(delay) if err == nil { - vc.Command.Printf("Successfully set the lost bookie recovery delay to %d(second)\n", delay) + vc.Command.Printf("Successfully set the lost bookie recovery delay to %d(second).\n", delay) } return err diff --git a/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay_test.go b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay_test.go new file mode 100644 index 00000000..5ee61285 --- /dev/null +++ b/pkg/bkctl/autorecovery/set_lost_bookie_recovery_delay_test.go @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetLostBookieRecoveryDelayArgsErr(t *testing.T) { + // no args specified + args := []string{"set-lost-bookie-recovery-delay"} + _, _, nameErr, err := testAutoRecoveryCommands(setLostBookieRecoveryDelayCmd, args) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, nameErr) + assert.Equal(t, "the delay time is not specified or the delay time is specified more than one", + nameErr.Error()) + + // specify more than one args + args = []string{"set-lost-bookie-recovery-delay", "1", "2"} + _, _, nameErr, err = testAutoRecoveryCommands(setLostBookieRecoveryDelayCmd, args) + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, nameErr) + assert.Equal(t, "the delay time is not specified or the delay time is specified more than one", + nameErr.Error()) + + // specify invalid args + args = []string{"set-lost-bookie-recovery-delay", "a"} + _, execErr, nameErr, err := testAutoRecoveryCommands(setLostBookieRecoveryDelayCmd, args) + if err != nil { + t.Fatal(err) + } + + if nameErr != nil { + t.Fatal(nameErr) + } + + assert.NotNil(t, execErr) + assert.Equal(t, "invalid delay times a", execErr.Error()) + + args = []string{"set-lost-bookie-recovery-delay", "--", "-1"} + _, execErr, nameErr, err = testAutoRecoveryCommands(setLostBookieRecoveryDelayCmd, args) + if err != nil { + t.Fatal(err) + } + + if nameErr != nil { + t.Fatal(nameErr) + } + + assert.NotNil(t, execErr) + assert.Equal(t, "invalid delay times -1", execErr.Error()) +} diff --git a/pkg/bkctl/autorecovery/test_help.go b/pkg/bkctl/autorecovery/test_help.go new file mode 100644 index 00000000..900286b5 --- /dev/null +++ b/pkg/bkctl/autorecovery/test_help.go @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package autorecovery + +import ( + "bytes" + + "github.com/streamnative/pulsarctl/pkg/cmdutils" + + "github.com/spf13/cobra" +) + +func testAutoRecoveryCommands(newVerb func(cmd *cmdutils.VerbCmd), args []string) (out *bytes.Buffer, + execErr, nameErr, err error) { + + var execError error + cmdutils.ExecErrorHandler = func(err error) { + execError = err + } + + var nameError error + cmdutils.CheckNameArgError = func(err error) { + nameError = err + } + + var rootCmd = &cobra.Command{} + + buf := new(bytes.Buffer) + rootCmd.SetOut(buf) + rootCmd.SetArgs(append([]string{"auto-recovery"}, args...)) + + resourceCmd := cmdutils.NewResourceCmd( + "auto-recovery", + "Operations about auto recovering", + "", + "") + flagGrouping := cmdutils.NewGrouping() + cmdutils.AddVerbCmd(flagGrouping, resourceCmd, newVerb) + rootCmd.AddCommand(resourceCmd) + err = rootCmd.Execute() + + return buf, execError, nameError, err +} diff --git a/pkg/bkctl/autorecovery/trigger_audit.go b/pkg/bkctl/autorecovery/trigger_audit.go index f8610730..e054a04e 100644 --- a/pkg/bkctl/autorecovery/trigger_audit.go +++ b/pkg/bkctl/autorecovery/trigger_audit.go @@ -23,28 +23,28 @@ import ( func triggerAuditCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription - desc.CommandUsedFor = "This command is used for triggering audit by resetting the lostBookieRecoveryDelay." - desc.CommandPermission = "none" + desc.CommandUsedFor = "This command is used for triggering audit by resetting the lost bookie recovery delay." + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example trigger := cmdutils.Example{ - Desc: "Trigger audit by resetting the lostBookieRecoveryDelay", - Command: "pulsarctl bookkeeper autorecovery triggeraudit", + Desc: "Trigger audit by resetting the lost bookie recovery delay", + Command: "pulsarctl bookkeeper auto-recovery trigger-audit", } examples = append(examples, trigger) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", - Out: "Successfully trigger audit by resetting the lostBookieRecoveryDelay", + Desc: "Trigger audit by resetting the lost bookie recovery delay successfully.", + Out: "Successfully trigger audit by resetting the lost bookie recovery delay.", } out = append(out, successOut) desc.CommandOutput = out vc.SetDescription( - "triggeraudit", - "Trigger audit by resetting the lostBookieRecoveryDelay", + "trigger-audit", + "Trigger audit by resetting the lost bookie recovery delay.", desc.ToString(), desc.ExampleToString()) @@ -57,7 +57,7 @@ func doTriggerAudit(vc *cmdutils.VerbCmd) error { admin := cmdutils.NewBookieClient() err := admin.AutoRecovery().TriggerAudit() if err == nil { - vc.Command.Println("Successfully trigger audit by resetting the lostBookieRecoveryDelay") + vc.Command.Println("Successfully trigger audit by resetting the lost bookie recovery delay.") } return err diff --git a/pkg/bkctl/autorecovery/who_is_auditor.go b/pkg/bkctl/autorecovery/who_is_auditor.go index 365b2388..0693bf14 100644 --- a/pkg/bkctl/autorecovery/who_is_auditor.go +++ b/pkg/bkctl/autorecovery/who_is_auditor.go @@ -24,19 +24,19 @@ import ( func whoIsAuditorCmd(vc *cmdutils.VerbCmd) { var desc cmdutils.LongDescription desc.CommandUsedFor = "This command is used for getting who is the auditor." - desc.CommandPermission = "none" + desc.CommandPermission = "This command does not need any permission." var examples []cmdutils.Example get := cmdutils.Example{ Desc: "Get who is the auditor", - Command: "pulsarctl bookkeeper autorecovery whoisauditor", + Command: "pulsarctl bookkeeper auto-recovery who-is-auditor", } examples = append(examples, get) desc.CommandExamples = examples var out []cmdutils.Output successOut := cmdutils.Output{ - Desc: "normal output", + Desc: "Get the auditor successfully.", Out: `{ "Auditor": "hostname/hostAddress:Port" }`, @@ -45,8 +45,8 @@ func whoIsAuditorCmd(vc *cmdutils.VerbCmd) { desc.CommandOutput = out vc.SetDescription( - "whoisauditor", - "Get who is the auditor", + "who-is-auditor", + "Get the auditor of the bookie.", desc.ToString(), desc.ExampleToString())