From ed2b71643cb340dac6bf458af03ed2820431eb1a Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 14:32:36 +0800 Subject: [PATCH 1/7] readonly --- pkg/bookkeeper/bkdata/bookie_data.go | 4 ++++ pkg/bookkeeper/bookie.go | 11 +++++++++++ pkg/cmdutils/cmdutils.go | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/pkg/bookkeeper/bkdata/bookie_data.go b/pkg/bookkeeper/bkdata/bookie_data.go index b8f8c8bd..40032abc 100644 --- a/pkg/bookkeeper/bkdata/bookie_data.go +++ b/pkg/bookkeeper/bkdata/bookie_data.go @@ -31,6 +31,10 @@ type GCStatus struct { MinorCompactionCounter int64 `json:"minorCompactionCounter"` } +type ReadonlyState struct { + ReadOnly bool `json:"readOnly"` +} + type State struct { Running bool `json:"running"` ReadOnly bool `json:"readOnly"` diff --git a/pkg/bookkeeper/bookie.go b/pkg/bookkeeper/bookie.go index 618898ba..1569a95a 100644 --- a/pkg/bookkeeper/bookie.go +++ b/pkg/bookkeeper/bookie.go @@ -42,6 +42,9 @@ type Bookie interface { // State gets the state of a bookie State() (*bkdata.State, error) + + // SetReadonlyState sets the readonly state of a bookie + SetReadonlyState(bool) error } type bookie struct { @@ -58,6 +61,14 @@ func (c *bookieClient) Bookie() Bookie { } } +func (b *bookie) SetReadonlyState(readonly bool) error { + endpoint := b.bk.endpoint(b.basePath, "/state/readonly") + request := bkdata.ReadonlyState{ + ReadOnly: readonly, + } + return b.bk.Client.Put(endpoint, &request) +} + func (b *bookie) LastLogMark() (map[string]string, error) { endpoint := b.bk.endpoint(b.basePath, "/last_log_mark") marker := make(map[string]string) diff --git a/pkg/cmdutils/cmdutils.go b/pkg/cmdutils/cmdutils.go index 00f8bb6b..9ccebbb5 100644 --- a/pkg/cmdutils/cmdutils.go +++ b/pkg/cmdutils/cmdutils.go @@ -170,6 +170,10 @@ func NewBookieClient() bookkeeper.Client { return PulsarCtlConfig.BookieClient() } +func NewBookieClientFromConfig(config *ClusterConfig) bookkeeper.Client { + return config.BookieClient() +} + func PrintJSON(w io.Writer, obj interface{}) { b, err := json.MarshalIndent(obj, "", " ") if err != nil { From 5323f72019aa77238b35c64c43e66a081c2ba138 Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 15:08:55 +0800 Subject: [PATCH 2/7] add cmd --- pkg/bkctl/bookie/bookie.go | 1 + pkg/bkctl/bookie/set_readonly_state.go | 70 +++++++++++++++++++++ pkg/bkctl/bookie/set_readonly_state_test.go | 33 ++++++++++ 3 files changed, 104 insertions(+) create mode 100644 pkg/bkctl/bookie/set_readonly_state.go create mode 100644 pkg/bkctl/bookie/set_readonly_state_test.go diff --git a/pkg/bkctl/bookie/bookie.go b/pkg/bkctl/bookie/bookie.go index 1e53e2e4..9a974717 100644 --- a/pkg/bkctl/bookie/bookie.go +++ b/pkg/bkctl/bookie/bookie.go @@ -37,6 +37,7 @@ func Command(flagGrouping *cmdutils.FlagGrouping) *cobra.Command { gcStatusCmd, gcDetailsCmd, stateCmd, + setReadonlyStateCmd, } cmdutils.AddVerbCmds(flagGrouping, resourceCmd, commands...) diff --git a/pkg/bkctl/bookie/set_readonly_state.go b/pkg/bkctl/bookie/set_readonly_state.go new file mode 100644 index 00000000..1180d7b4 --- /dev/null +++ b/pkg/bkctl/bookie/set_readonly_state.go @@ -0,0 +1,70 @@ +// 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 bookie + +import ( + "strconv" + + "github.com/streamnative/pulsarctl/pkg/cmdutils" +) + +func setReadonlyStateCmd(vc *cmdutils.VerbCmd) { + var desc cmdutils.LongDescription + desc.CommandUsedFor = "This command is used for setting the readonly state of a bookie." + desc.CommandPermission = "This command does not need any permission." + + var examples []cmdutils.Example + get := cmdutils.Example{ + Desc: "Set the readonly state of the bookie.", + Command: "pulsarctl bookkeeper bookie set-readonly ", + } + examples = append(examples, get) + desc.CommandExamples = examples + + var out []cmdutils.Output + successOut := cmdutils.Output{ + Desc: "Successfully set the readonly state of a bookie.", + Out: "Successfully set the readonly state of a bookie", + } + out = append(out, successOut) + desc.CommandOutput = out + + vc.SetDescription( + "set-readonly", + "Set the readonly state of a bookie.", + desc.ToString(), + desc.ExampleToString()) + + vc.SetRunFuncWithNameArg(func() error { + return doSetReadonlyState(vc) + }, "the readonly state is boolean") +} + +func doSetReadonlyState(vc *cmdutils.VerbCmd) error { + admin := cmdutils.NewBookieClient() + readonly, err := strconv.ParseBool(vc.NameArg) + if err != nil { + return err + } + err = admin.Bookie().SetReadonlyState(readonly) + if err == nil { + cmdutils.PrintJSON(vc.Command.OutOrStdout(), "Successfully set the readonly state of a bookie") + } + + return err +} diff --git a/pkg/bkctl/bookie/set_readonly_state_test.go b/pkg/bkctl/bookie/set_readonly_state_test.go new file mode 100644 index 00000000..2e54f709 --- /dev/null +++ b/pkg/bkctl/bookie/set_readonly_state_test.go @@ -0,0 +1,33 @@ +// 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 bookie + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetReadonlyStateCmd(t *testing.T) { + args := []string{"set-readonly", "true"} + out, execErr, nameErr, err := testBookieCommands(setReadonlyStateCmd, args) + assert.Nil(t, err) + assert.Nil(t, nameErr) + assert.Nil(t, execErr) + assert.Equal(t, "Successfully set the readonly state of a bookie\n", out.String()) +} From 0467750980b973d85508f495f2ee270bf0f34f0c Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 15:18:09 +0800 Subject: [PATCH 3/7] fix test --- pkg/bkctl/bookie/set_readonly_state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/bkctl/bookie/set_readonly_state_test.go b/pkg/bkctl/bookie/set_readonly_state_test.go index 2e54f709..01c2aa93 100644 --- a/pkg/bkctl/bookie/set_readonly_state_test.go +++ b/pkg/bkctl/bookie/set_readonly_state_test.go @@ -29,5 +29,5 @@ func TestSetReadonlyStateCmd(t *testing.T) { assert.Nil(t, err) assert.Nil(t, nameErr) assert.Nil(t, execErr) - assert.Equal(t, "Successfully set the readonly state of a bookie\n", out.String()) + assert.Equal(t, "\"Successfully set the readonly state of a bookie\"\n", out.String()) } From ab7f7072af6476b9adb628b7e375cecb2cf46bb9 Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 15:31:03 +0800 Subject: [PATCH 4/7] upgrage image --- test/docker/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/docker/docker-compose.yml b/test/docker/docker-compose.yml index b1b4520a..c6f48cfc 100644 --- a/test/docker/docker-compose.yml +++ b/test/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: zookeeper: container_name: bk-zookeeper - image: "apache/bookkeeper:4.10.0" + image: "apache/bookkeeper:4.16.3" hostname: zookeeper entrypoint: - /bin/bash @@ -13,7 +13,7 @@ services: depends_on: - zookeeper - bookie-init - image: "apache/bookkeeper:4.10.0" + image: "apache/bookkeeper:4.16.3" hostname: bookie links: - zookeeper @@ -32,7 +32,7 @@ services: /opt/bookkeeper/bin/bookkeeper bookie restart: on-failure bookie-init: - image: "apache/bookkeeper:4.10.0" + image: "apache/bookkeeper:4.16.3" hostname: bookie-client links: - zookeeper From b390b8e66e6aea5730b2234116ec77c7066bff12 Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 15:37:59 +0800 Subject: [PATCH 5/7] remove function --- pkg/cmdutils/cmdutils.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/cmdutils/cmdutils.go b/pkg/cmdutils/cmdutils.go index 9ccebbb5..00f8bb6b 100644 --- a/pkg/cmdutils/cmdutils.go +++ b/pkg/cmdutils/cmdutils.go @@ -170,10 +170,6 @@ func NewBookieClient() bookkeeper.Client { return PulsarCtlConfig.BookieClient() } -func NewBookieClientFromConfig(config *ClusterConfig) bookkeeper.Client { - return config.BookieClient() -} - func PrintJSON(w io.Writer, obj interface{}) { b, err := json.MarshalIndent(obj, "", " ") if err != nil { From 14a3f76a63e4ef8a7cce840ff39724681202f3ee Mon Sep 17 00:00:00 2001 From: labuladong Date: Wed, 8 Nov 2023 15:45:52 +0800 Subject: [PATCH 6/7] improve test --- pkg/bkctl/bookie/set_readonly_state_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/bkctl/bookie/set_readonly_state_test.go b/pkg/bkctl/bookie/set_readonly_state_test.go index 01c2aa93..ea5062d2 100644 --- a/pkg/bkctl/bookie/set_readonly_state_test.go +++ b/pkg/bkctl/bookie/set_readonly_state_test.go @@ -30,4 +30,12 @@ func TestSetReadonlyStateCmd(t *testing.T) { assert.Nil(t, nameErr) assert.Nil(t, execErr) assert.Equal(t, "\"Successfully set the readonly state of a bookie\"\n", out.String()) + + // set back to false, otherwise the next test will fail + args = []string{"set-readonly", "false"} + out, execErr, nameErr, err = testBookieCommands(setReadonlyStateCmd, args) + assert.Nil(t, err) + assert.Nil(t, nameErr) + assert.Nil(t, execErr) + assert.Equal(t, "\"Successfully set the readonly state of a bookie\"\n", out.String()) } From 45ca07d6f897de635697b6430ae593718c6ea0d8 Mon Sep 17 00:00:00 2001 From: labuladong Date: Fri, 10 Nov 2023 12:10:36 +0800 Subject: [PATCH 7/7] add force readonly --- pkg/bkctl/bookie/set_readonly_state.go | 8 +++++--- pkg/bookkeeper/bookie.go | 10 +++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/bkctl/bookie/set_readonly_state.go b/pkg/bkctl/bookie/set_readonly_state.go index 1180d7b4..36c67195 100644 --- a/pkg/bkctl/bookie/set_readonly_state.go +++ b/pkg/bkctl/bookie/set_readonly_state.go @@ -50,18 +50,20 @@ func setReadonlyStateCmd(vc *cmdutils.VerbCmd) { desc.ToString(), desc.ExampleToString()) + force := vc.Command.Flags().BoolP("force", "f", false, "force to set readonly state") + vc.SetRunFuncWithNameArg(func() error { - return doSetReadonlyState(vc) + return doSetReadonlyState(vc, *force) }, "the readonly state is boolean") } -func doSetReadonlyState(vc *cmdutils.VerbCmd) error { +func doSetReadonlyState(vc *cmdutils.VerbCmd, force bool) error { admin := cmdutils.NewBookieClient() readonly, err := strconv.ParseBool(vc.NameArg) if err != nil { return err } - err = admin.Bookie().SetReadonlyState(readonly) + err = admin.Bookie().SetReadonlyState(readonly, force) if err == nil { cmdutils.PrintJSON(vc.Command.OutOrStdout(), "Successfully set the readonly state of a bookie") } diff --git a/pkg/bookkeeper/bookie.go b/pkg/bookkeeper/bookie.go index 1569a95a..59685947 100644 --- a/pkg/bookkeeper/bookie.go +++ b/pkg/bookkeeper/bookie.go @@ -44,7 +44,7 @@ type Bookie interface { State() (*bkdata.State, error) // SetReadonlyState sets the readonly state of a bookie - SetReadonlyState(bool) error + SetReadonlyState(bool, bool) error } type bookie struct { @@ -61,12 +61,16 @@ func (c *bookieClient) Bookie() Bookie { } } -func (b *bookie) SetReadonlyState(readonly bool) error { +func (b *bookie) SetReadonlyState(readonly bool, force bool) error { endpoint := b.bk.endpoint(b.basePath, "/state/readonly") request := bkdata.ReadonlyState{ ReadOnly: readonly, } - return b.bk.Client.Put(endpoint, &request) + params := map[string]string{} + if readonly && force { + params["force"] = "true" + } + return b.bk.Client.PutWithQueryParams(endpoint, &request, nil, params) } func (b *bookie) LastLogMark() (map[string]string, error) {