diff --git a/cli/command/cluster/add.go b/cli/command/cluster/add.go index ab8b6bf8f..2504d94de 100644 --- a/cli/command/cluster/add.go +++ b/cli/command/cluster/add.go @@ -56,7 +56,7 @@ var ( type addOptions struct { name string - descriotion string + description string filename string deployType string } @@ -80,7 +80,7 @@ func NewAddCommand(curveadm *cli.CurveAdm) *cobra.Command { } flags := cmd.Flags() - flags.StringVarP(&options.descriotion, "description", "m", "", "Description for cluster") + flags.StringVarP(&options.description, "description", "m", "", "Description for cluster") flags.StringVarP(&options.filename, "topology", "f", "", "Specify the path of topology file") flags.StringVar(&options.deployType, "type", "develop", "Specify the type of cluster") return cmd @@ -185,7 +185,7 @@ func runAdd(curveadm *cli.CurveAdm, options addOptions) error { // 4) insert cluster (with topology) into database uuid := uuid.NewString() - err = storage.InsertCluster(name, uuid, options.descriotion, data, options.deployType) + err = storage.InsertCluster(name, uuid, options.description, data, options.deployType) if err != nil { return errno.ERR_INSERT_CLUSTER_FAILED.E(err) } diff --git a/cli/command/config/cmd.go b/cli/command/config/cmd.go index 858ebc615..fdb6b5a2b 100644 --- a/cli/command/config/cmd.go +++ b/cli/command/config/cmd.go @@ -42,6 +42,7 @@ func NewConfigCommand(curveadm *cli.CurveAdm) *cobra.Command { NewShowCommand(curveadm), NewDiffCommand(curveadm), NewCommitCommand(curveadm), + NewExportCommand(curveadm), ) return cmd } diff --git a/cli/command/config/export.go b/cli/command/config/export.go new file mode 100644 index 000000000..f42b9072a --- /dev/null +++ b/cli/command/config/export.go @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed 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. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-11-9 + * Author: Jiang Jun (youarefree123) + */ + +// __SIGN_BY_YOUAREFREE123__ + +package config + +import ( + "path" + "strings" + + "github.com/fatih/color" + "github.com/opencurve/curveadm/cli/cli" + comm "github.com/opencurve/curveadm/internal/common" + "github.com/opencurve/curveadm/internal/configure/topology" + "github.com/opencurve/curveadm/internal/errno" + "github.com/opencurve/curveadm/internal/playbook" + cliutil "github.com/opencurve/curveadm/internal/utils" + "github.com/spf13/cobra" +) + +var ( + GET_EXPORT_PLAYBOOK_STEPS = []int{ + playbook.EXPORT_TOOLSV2_CONF, + } +) + +type exportOptions struct { + output string +} + +func checkExportOptions(curveadm *cli.CurveAdm, options exportOptions) error { + if !strings.HasPrefix(options.output, "/") { + return errno.ERR_EXPORT_TOOLSV2_CONF_REQUIRE_ABSOLUTE_PATH. + F("/path/to/curve.yaml: %s", options.output) + } + return nil +} + +func NewExportCommand(curveadm *cli.CurveAdm) *cobra.Command { + var options exportOptions + + cmd := &cobra.Command{ + Use: "export [OPTIONS]", + Short: "Export curve.yaml", + Args: cliutil.NoArgs, + PreRunE: func(cmd *cobra.Command, args []string) error { + return checkExportOptions(curveadm, options) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runExport(curveadm, options) + }, + DisableFlagsInUseLine: true, + } + + flags := cmd.Flags() + flags.StringVarP(&options.output, "path", "p", path.Join(curveadm.PluginDir(), "curve.yaml"), "Path where the exported YAML is stored") + return cmd +} + +func genExportPlaybook(curveadm *cli.CurveAdm, + dcs []*topology.DeployConfig, + options exportOptions) (*playbook.Playbook, error) { + + steps := GET_EXPORT_PLAYBOOK_STEPS + pb := playbook.NewPlaybook(curveadm) + for _, step := range steps { + pb.AddStep(&playbook.PlaybookStep{ + Type: step, + Configs: dcs[:1], + Options: map[string]interface{}{ + comm.KEY_TOOLSV2_CONF_PATH: options.output, + }, + }) + } + + return pb, nil +} + +func runExport(curveadm *cli.CurveAdm, options exportOptions) error { + // 1) parse cluster topology + dcs, err := curveadm.ParseTopology() + if err != nil { + return err + } + + // 2) generate get export playbook + pb, err := genExportPlaybook(curveadm, dcs, options) + if err != nil { + return err + } + + // 3) run playground + err = pb.Run() + if err != nil { + return err + } + + // 4) print success prompt + curveadm.WriteOutln("") + curveadm.WriteOutln(color.GreenString("Export curve.yaml to %s success ^_^"), options.output) + return err +} diff --git a/internal/common/common.go b/internal/common/common.go index 8e67c6485..4a790fcc3 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -49,6 +49,7 @@ const ( POOLSET = "poolset" POOLSET_DISK_TYPE = "poolset-disktype" KEY_NUMBER_OF_CHUNKSERVER = "NUMBER_OF_CHUNKSERVER" + KEY_TOOLSV2_CONF_PATH = "TOOLSV2_CONF_PATH" // format KEY_ALL_FORMAT_STATUS = "ALL_FORMAT_STATUS" diff --git a/internal/configure/topology/variables.go b/internal/configure/topology/variables.go index 02cb4bb65..a2b246805 100644 --- a/internal/configure/topology/variables.go +++ b/internal/configure/topology/variables.go @@ -122,6 +122,8 @@ var ( {name: "cluster_snapshotclone_proxy_addr", kind: []string{KIND_CURVEBS}}, {name: "cluster_snapshotclone_dummy_port", kind: []string{KIND_CURVEBS}}, {name: "cluster_snapshotclone_nginx_upstream", kind: []string{KIND_CURVEBS}}, + {name: "cluster_snapshot_addr"}, // tools-v2: compatible with some old version image + {name: "cluster_snapshot_dummy_addr"}, // tools-v2 {name: "cluster_metaserver_addr", kind: []string{KIND_CURVEFS}}, } ) @@ -301,6 +303,10 @@ func getValue(name string, dcs []*DeployConfig, idx int) string { return joinDummyPort(dcs, ROLE_SNAPSHOTCLONE) case "cluster_snapshotclone_nginx_upstream": return joinNginxUpstreamServer(dcs) + case "cluster_snapshot_addr": + return joinPeer(dcs, ROLE_SNAPSHOTCLONE, SELECT_LISTEN_PORT) + case "cluster_snapshot_dummy_addr": + return joinPeer(dcs, ROLE_SNAPSHOTCLONE, SELECT_LISTEN_DUMMY_PORT) case "cluster_metaserver_addr": return joinPeer(dcs, ROLE_METASERVER, SELECT_LISTEN_PORT) } diff --git a/internal/errno/errno.go b/internal/errno/errno.go index 98bca1835..3582d1cdb 100644 --- a/internal/errno/errno.go +++ b/internal/errno/errno.go @@ -253,8 +253,8 @@ var ( ERR_UNSUPPORT_CLEAN_ITEM = EC(210005, "unsupport clean item") ERR_NO_SERVICES_MATCHED = EC(210006, "no services matched") // TODO: please check pool set disk type - ERR_INVALID_DISK_TYPE = EC(210007, "poolset disk type must be lowercase and can only be one of ssd, hdd and nvme") - ERR_UNSUPPORT_DEPLOY_TYPE = EC(210008, "unknown deploy type") + ERR_INVALID_DISK_TYPE = EC(210007, "poolset disk type must be lowercase and can only be one of ssd, hdd and nvme") + ERR_UNSUPPORT_DEPLOY_TYPE = EC(210008, "unknown deploy type") // 220: commad options (client common) ERR_UNSUPPORT_CLIENT_KIND = EC(220000, "unsupport client kind") // 221: command options (client/bs) @@ -272,6 +272,8 @@ var ( ERR_VOLUME_BLOCKSIZE_BE_MULTIPLE_OF_512 = EC(221011, "volume block size be a multiple of 512B, like 1KiB, 2KiB, 3KiB...") // 222: command options (client/fs) ERR_FS_MOUNTPOINT_REQUIRE_ABSOLUTE_PATH = EC(222000, "mount point must be an absolute path") + // 223: command options (export) + ERR_EXPORT_TOOLSV2_CONF_REQUIRE_ABSOLUTE_PATH = EC(223000, "/path/to/curve.yaml must be an absolute path") // 230: command options (playground) ERR_UNSUPPORT_PLAYGROUND_KIND = EC(230000, "unsupport playground kind") diff --git a/internal/playbook/factory.go b/internal/playbook/factory.go index f62a7b3e5..a54cb8cbb 100644 --- a/internal/playbook/factory.go +++ b/internal/playbook/factory.go @@ -83,6 +83,7 @@ const ( GET_CLIENT_STATUS INSTALL_CLIENT UNINSTALL_CLIENT + EXPORT_TOOLSV2_CONF // bs FORMAT_CHUNKFILE_POOL @@ -247,6 +248,8 @@ func (p *Playbook) createTasks(step *PlaybookStep) (*tasks.Tasks, error) { t, err = comm.NewInstallClientTask(curveadm, config.GetCC(i)) case UNINSTALL_CLIENT: t, err = comm.NewUninstallClientTask(curveadm, nil) + case EXPORT_TOOLSV2_CONF: + t, err = comm.NewExportToolsV2ConfTask(curveadm, config.GetDC(i)) // bs case FORMAT_CHUNKFILE_POOL: t, err = bs.NewFormatChunkfilePoolTask(curveadm, config.GetFC(i)) diff --git a/internal/task/step/file.go b/internal/task/step/file.go index cfd4fc575..8dc4afbc7 100644 --- a/internal/task/step/file.go +++ b/internal/task/step/file.go @@ -196,7 +196,7 @@ func (s *InstallFile) Execute(ctx *context.Context) error { } func (s *Filter) kvSplit(line string, key, value *string) error { - pattern := fmt.Sprintf(REGEX_KV_SPLIT, s.KVFieldSplit, s.KVFieldSplit) + pattern := fmt.Sprintf(REGEX_KV_SPLIT, strings.TrimSpace(s.KVFieldSplit), s.KVFieldSplit) regex, err := regexp.Compile(pattern) if err != nil { return errno.ERR_BUILD_REGEX_FAILED.E(err) diff --git a/internal/task/task/bs/map.go b/internal/task/task/bs/map.go index b22a31ebf..da8be9f53 100644 --- a/internal/task/task/bs/map.go +++ b/internal/task/task/bs/map.go @@ -37,7 +37,7 @@ import ( ) const ( - TOOLS_V2_CONFIG_DELIMITER = ": " + TOOLS_V2_CONFIG_DELIMITER = ":" TOOLS_V2_CONFIG_SRC_PATH = "/curvebs/conf/curve.yaml" TOOLS_V2_CONFIG_DEST_PATH = "/etc/curve/curve.yaml" ) diff --git a/internal/task/task/common/export_toolsv2_conf.go b/internal/task/task/common/export_toolsv2_conf.go new file mode 100644 index 000000000..09593df93 --- /dev/null +++ b/internal/task/task/common/export_toolsv2_conf.go @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 NetEase Inc. + * + * Licensed 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. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-11-12 + * Author: Jiang Jun (youarefree123) + */ + +package common + +import ( + "fmt" + + "github.com/opencurve/curveadm/cli/cli" + comm "github.com/opencurve/curveadm/internal/common" + "github.com/opencurve/curveadm/internal/configure/topology" + "github.com/opencurve/curveadm/internal/task/step" + "github.com/opencurve/curveadm/internal/task/task" +) + +func NewExportToolsV2ConfTask(curveadm *cli.CurveAdm, dc *topology.DeployConfig) (*task.Task, error) { + serviceId := curveadm.GetServiceId(dc.GetId()) + containerId, err := curveadm.Storage().GetContainerId(serviceId) + if err != nil { + return nil, err + } + + hc, err := curveadm.GetHost(dc.GetHost()) + if err != nil { + return nil, err + } + + var ToolsV2Conf string + localPath := curveadm.MemStorage().Get(comm.KEY_TOOLSV2_CONF_PATH).(string) + subname := fmt.Sprintf("output=%s", localPath) + t := task.NewTask("Export curve.yaml", subname, hc.GetSSHConfig()) + + t.AddStep(&step.ReadFile{ + ContainerId: containerId, + ContainerSrcPath: dc.GetProjectLayout().ToolsV2ConfSystemPath, + Content: &ToolsV2Conf, + ExecOptions: curveadm.ExecOptions(), + }) + + t.AddStep(&step.InstallFile{ + Content: &ToolsV2Conf, + HostDestPath: localPath, + ExecOptions: curveadm.ExecOptions(), + }) + + return t, nil +} diff --git a/internal/task/task/common/sync_config.go b/internal/task/task/common/sync_config.go index b247a56a5..dd762374a 100644 --- a/internal/task/task/common/sync_config.go +++ b/internal/task/task/common/sync_config.go @@ -39,7 +39,7 @@ import ( const ( DEFAULT_CONFIG_DELIMITER = "=" ETCD_CONFIG_DELIMITER = ": " - TOOLS_V2_CONFIG_DELIMITER = ": " + TOOLS_V2_CONFIG_DELIMITER = ":" CURVE_CRONTAB_FILE = "/tmp/curve_crontab" ) diff --git a/internal/task/task/fs/mount.go b/internal/task/task/fs/mount.go index 124d3f8ef..c3148dc6f 100644 --- a/internal/task/task/fs/mount.go +++ b/internal/task/task/fs/mount.go @@ -45,7 +45,7 @@ const ( FORMAT_MOUNT_OPTION = "type=bind,source=%s,target=%s,bind-propagation=rshared" CLIENT_CONFIG_DELIMITER = "=" - TOOLS_V2_CONFIG_DELIMITER = ": " + TOOLS_V2_CONFIG_DELIMITER = ":" KEY_CURVEBS_CLUSTER = "curvebs.cluster"