blob: ff65f4ec1b4592b6800389d34b64ce6831168ea2 [file] [log] [blame]
// 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