| // 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. |
| |
| #pragma once |
| |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "kudu/gutil/strings/stringpiece.h" |
| #include "kudu/util/status.h" |
| |
| namespace kudu { |
| namespace tools { |
| |
| class Action; |
| class Mode; |
| |
| // The command line tool is structured as a tree with two kinds of nodes: modes |
| // and actions. Actions are leaf nodes, each representing a particular |
| // operation that the tool can take. Modes are non-leaf nodes that are |
| // basically just intuitive groupings of actions. |
| // |
| // Regardless of type, every node has a name which is used to match it against |
| // a command line argument during parsing. Additionally, every node has a |
| // description, displayed (along with the name) in help text. |
| // |
| // Every node (be it action or mode) has pointers to its children, but not to |
| // its parent mode. As such, operations that require information from the |
| // parent modes expect the caller to provide those modes as a "mode chain". |
| // |
| // Sample node tree: |
| // |
| // root |
| // | |
| // | |
| // fs |
| // | | |
| // +--+ +--+ |
| // | | |
| // format dump |
| // | | |
| // +--+ +--+ |
| // | | |
| // cfile uuid |
| // |
| // Given this tree: |
| // - "<program> fs" will show all of fs's possible actions. |
| // - "<program> fs format" will format a filesystem. |
| // - "<program> fs dump uuid" will print a filesystem's UUID. |
| |
| // Builds a new mode (non-leaf) node. |
| class ModeBuilder { |
| public: |
| // Creates a new ModeBuilder with a specific name (e.g. "fs"). The name |
| // uniquely identifies the mode amongst its siblings in the tree. |
| explicit ModeBuilder(std::string name); |
| |
| // Sets the description of this mode (e.g. "Operate on a local Kudu |
| // filesystem"), to be used when printing help. |
| // |
| // Required. |
| ModeBuilder& Description(const std::string& desc); |
| |
| // Adds a new mode (non-leaf child node) to this builder. |
| ModeBuilder& AddMode(std::unique_ptr<Mode> mode); |
| |
| // Adds a new action (leaf child node) to this builder. |
| ModeBuilder& AddAction(std::unique_ptr<Action> action); |
| |
| // Creates a mode using builder state. |
| // |
| // May only be called once. |
| std::unique_ptr<Mode> Build(); |
| |
| private: |
| const std::string name_; |
| |
| std::string description_; |
| |
| std::vector<std::unique_ptr<Mode>> submodes_; |
| |
| std::vector<std::unique_ptr<Action>> actions_; |
| }; |
| |
| // A non-leaf node in the tree, representing a logical grouping for actions or |
| // more modes. |
| class Mode { |
| public: |
| |
| // Returns the help for this mode given its parent mode chain. |
| std::string BuildHelp(const std::vector<Mode*>& chain) const; |
| |
| // Returns the help xml for this mode and all child modes |
| std::string BuildHelpXML(const std::vector<Mode*>& chain) const; |
| |
| const std::string& name() const { return name_; } |
| |
| const std::string& description() const { return description_; } |
| |
| const std::vector<std::unique_ptr<Mode>>& modes() const { return submodes_; } |
| |
| const std::vector<std::unique_ptr<Action>>& actions() const { return actions_; } |
| |
| private: |
| friend class ModeBuilder; |
| |
| Mode() = default; |
| |
| std::string name_; |
| |
| std::string description_; |
| |
| std::vector<std::unique_ptr<Mode>> submodes_; |
| |
| std::vector<std::unique_ptr<Action>> actions_; |
| }; |
| |
| // Function signature for any operation represented by an Action. |
| // |
| // When run, the operation receives the parent mode chain, the current action, |
| // and the command line arguments. |
| // |
| // Prior to running, arguments registered by the action are put into the |
| // context. In the event that a required parameter is missing or there are |
| // unexpected arguments on the command line, an error is returned and the |
| // runner will not be invoked. |
| // |
| // Note: only required and variadic args are put in the context; it is expected |
| // that operations access optional args via gflag variables (i.e. FLAGS_foo). |
| struct RunnerContext { |
| std::vector<Mode*> chain; |
| const Action* action; |
| std::unordered_map<std::string, std::string> required_args; |
| std::vector<std::string> variadic_args; |
| }; |
| typedef std::function<Status(const RunnerContext&)> ActionRunner; |
| |
| // Describes all of the arguments used by an action. At runtime, the tool will |
| // parse these arguments out of the command line and marshal them into a |
| // key/value argument map (see Run()). |
| struct ActionArgsDescriptor { |
| struct Arg { |
| std::string name; |
| std::string description; |
| }; |
| |
| // Holds an optional command line argument flag. |
| struct Flag { |
| // The gflag name. |
| std::string name; |
| // A default value to override the default gflag value. |
| std::optional<std::string> default_value; |
| // A description to override the gflag description. |
| std::optional<std::string> description; |
| }; |
| |
| // Positional (required) command line arguments. |
| std::vector<Arg> required; |
| |
| // Key-value command line arguments. These must correspond to defined gflags. |
| // |
| // Optional by definition, though some are required internally |
| // (e.g. fs_wal_dir). |
| std::vector<Flag> optional; |
| |
| // Variable length command line argument. There may be at most one per |
| // Action, and it's always found at the end of the command line. |
| std::optional<Arg> variadic; |
| }; |
| |
| // Builds a new action (leaf) node. |
| class ActionBuilder { |
| public: |
| // Creates a new ActionBuilder with a specific name (e.g. "format") and |
| // action runner. The name uniquely identifies the action amongst its |
| // siblings in the tree. |
| ActionBuilder(std::string name, ActionRunner runner); |
| virtual ~ActionBuilder() = default; |
| |
| // Sets the description of this action (e.g. "Format a new Kudu filesystem"), |
| // to be used when printing the parent mode's help and the action's help. |
| // |
| // Required. |
| ActionBuilder& Description(const std::string& description); |
| |
| // Sets the long description of this action. If provided, will added to this |
| // action's help following Description(). |
| ActionBuilder& ExtraDescription(const std::string& extra_description); |
| |
| // Sets the program name to use when running this action. If unset, |
| // argv0 is used. |
| // |
| // This will ensure logging is initialized as if the program binary name was |
| // the passed `program_name`. |
| ActionBuilder& ProgramName(const std::string& program_name); |
| |
| // Add a new required parameter to this builder. |
| // |
| // This parameter will be parsed as a positional argument following the name |
| // of the action. The order in which required parameters are added to the |
| // builder reflects the order they are expected to be parsed from the command |
| // line. |
| ActionBuilder& AddRequiredParameter( |
| const ActionArgsDescriptor::Arg& arg); |
| |
| // Add a new required variable-length parameter to this builder. |
| // |
| // This parameter will be parsed following all other positional parameters. |
| // All remaining positional arguments on the command line will be parsed into |
| // this parameter. |
| // |
| // There may be at most one variadic parameter defined per action. |
| ActionBuilder& AddRequiredVariadicParameter( |
| const ActionArgsDescriptor::Arg& arg); |
| |
| // Add a new optional parameter to this builder. |
| // |
| // This parameter will be parsed by the gflags system, and thus can be |
| // provided by the user at any point in the command line. It must match a |
| // previously-defined gflag; if a gflag with the same name cannot be found, |
| // the tool will crash. |
| // |
| // The default value and description of the flag can be optionally overriden, |
| // for cases where the values are action-dependent. Otherwise, the default |
| // value and description from the gflag declaration will be used. |
| ActionBuilder& AddOptionalParameter( |
| std::string param, |
| std::optional<std::string> default_value = std::nullopt, |
| std::optional<std::string> description = std::nullopt); |
| |
| // Creates an action using builder state. |
| virtual std::unique_ptr<Action> Build(); |
| |
| protected: |
| const std::string name_; |
| |
| std::string description_; |
| |
| std::optional<std::string> extra_description_; |
| |
| std::optional<std::string> program_name_; |
| |
| ActionRunner runner_; |
| |
| ActionArgsDescriptor args_; |
| }; |
| |
| // A leaf node in the tree, representing a logical operation taken by the tool. |
| class Action { |
| public: |
| enum HelpMode { |
| // Return the full help text, including descriptions for each |
| // of the arguments. |
| FULL_HELP, |
| // Return only a single-line usage statement. |
| USAGE_ONLY |
| }; |
| |
| // Returns the help for this action given its parent mode chain. |
| std::string BuildHelp(const std::vector<Mode*>& chain, |
| HelpMode mode = FULL_HELP) const; |
| |
| // Returns the help xml for this action |
| std::string BuildHelpXML(const std::vector<Mode*>& chain) const; |
| |
| // Runs the operation represented by this action, given a parent mode chain |
| // and marshaled command line arguments. |
| Status Run(const std::vector<Mode*>& chain, |
| const std::unordered_map<std::string, std::string>& required_args, |
| const std::vector<std::string>& variadic_args) const; |
| |
| const std::string& name() const { return name_; } |
| |
| const std::string& description() const { return description_; } |
| |
| const std::optional<std::string>& extra_description() const { |
| return extra_description_; |
| } |
| |
| const std::optional<std::string>& program_name() const { |
| return program_name_; |
| } |
| |
| const ActionArgsDescriptor& args() const { return args_; } |
| |
| private: |
| friend class ActionBuilder; |
| |
| Action() = default; |
| |
| // Sets optional flag parameter default value in cases where it has been |
| // overridden from the default gflag value. |
| void SetOptionalParameterDefaultValues() const; |
| |
| std::string name_; |
| |
| std::string description_; |
| |
| std::optional<std::string> extra_description_; |
| |
| std::optional<std::string> program_name_; |
| |
| ActionRunner runner_; |
| |
| ActionArgsDescriptor args_; |
| }; |
| |
| // Append 'to_append' to 'dst', but hard-wrapped at 78 columns. |
| // After any newline, 'continuation_indent' spaces are prepended. |
| void AppendHardWrapped(StringPiece to_append, |
| int continuation_indent, |
| std::string* dst); |
| |
| // Returns new nodes for each major mode. |
| std::unique_ptr<Mode> BuildClusterMode(); |
| std::unique_ptr<Mode> BuildDiagnoseMode(); |
| std::unique_ptr<Mode> BuildFsMode(); |
| std::unique_ptr<Mode> BuildHmsMode(); |
| std::unique_ptr<Mode> BuildLocalReplicaMode(); |
| std::unique_ptr<Mode> BuildMasterMode(); |
| std::unique_ptr<Mode> BuildPbcMode(); |
| std::unique_ptr<Mode> BuildPerfMode(); |
| std::unique_ptr<Mode> BuildRemoteReplicaMode(); |
| std::unique_ptr<Mode> BuildTableMode(); |
| std::unique_ptr<Mode> BuildTabletMode(); |
| std::unique_ptr<Mode> BuildTestMode(); |
| std::unique_ptr<Mode> BuildTxnMode(); |
| std::unique_ptr<Mode> BuildTServerMode(); |
| std::unique_ptr<Mode> BuildWalMode(); |
| |
| } // namespace tools |
| } // namespace kudu |