mirror of
https://github.com/bytedance/g3.git
synced 2026-05-03 06:00:42 +00:00
initial commit
This commit is contained in:
commit
13716f4923
1425 changed files with 163227 additions and 0 deletions
20
g3proxy/utils/ctl/Cargo.toml
Normal file
20
g3proxy/utils/ctl/Cargo.toml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "g3proxy-ctl"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
clap = "4.0"
|
||||
clap_complete = "4.0"
|
||||
tokio = { version = "1.0", features = ["rt", "net", "macros", "io-util", "fs"] }
|
||||
tokio-util = { version = "0.7", features = ["compat"] }
|
||||
futures-util = "0.3"
|
||||
capnp-rpc = "0.16"
|
||||
capnp = "0.16"
|
||||
serde_json = "1.0"
|
||||
g3proxy-proto = { path = "../../proto" }
|
||||
g3-types = { path = "../../../lib/g3-types", features = ["resolve"] }
|
||||
59
g3proxy/utils/ctl/src/common.rs
Normal file
59
g3proxy/utils/ctl/src/common.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use g3proxy_proto::types_capnp::{fetch_result, operation_result};
|
||||
|
||||
use super::{CommandError, CommandResult};
|
||||
|
||||
pub(crate) fn print_list_text(list: capnp::text_list::Reader<'_>) -> CommandResult<()> {
|
||||
for text in list.iter() {
|
||||
println!("{}", text?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn parse_operation_result(r: operation_result::Reader<'_>) -> CommandResult<()> {
|
||||
match r.which().unwrap() {
|
||||
operation_result::Which::Ok(ok) => {
|
||||
let notice = ok?;
|
||||
println!("notice: {notice}");
|
||||
Ok(())
|
||||
}
|
||||
operation_result::Which::Err(err) => {
|
||||
let e = err?;
|
||||
let code = e.get_code();
|
||||
let reason = e.get_reason()?.to_string();
|
||||
Err(CommandError::Api { code, reason })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_fetch_result<T>(
|
||||
r: fetch_result::Reader<'_, T>,
|
||||
) -> CommandResult<<T as capnp::traits::Owned>::Reader<'_>>
|
||||
where
|
||||
T: capnp::traits::Owned,
|
||||
{
|
||||
match r.which().unwrap() {
|
||||
fetch_result::Which::Data(data) => Ok(data?),
|
||||
fetch_result::Which::Err(err) => {
|
||||
let e = err?;
|
||||
let code = e.get_code();
|
||||
let reason = e.get_reason()?.to_string();
|
||||
Err(CommandError::Api { code, reason })
|
||||
}
|
||||
}
|
||||
}
|
||||
29
g3proxy/utils/ctl/src/error.rs
Normal file
29
g3proxy/utils/ctl/src/error.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CommandError {
|
||||
#[error("cli error ({0})")]
|
||||
Cli(String),
|
||||
#[error("rpc error ({0:?})")]
|
||||
Rpc(#[from] capnp::Error),
|
||||
#[error("api error (code: {code:?}, reason: {reason:?})")]
|
||||
Api { code: i32, reason: String },
|
||||
}
|
||||
|
||||
pub type CommandResult<T> = Result<T, CommandError>;
|
||||
104
g3proxy/utils/ctl/src/escaper.rs
Normal file
104
g3proxy/utils/ctl/src/escaper.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::{value_parser, Arg, ArgMatches, Command, ValueHint};
|
||||
use futures_util::future::TryFutureExt;
|
||||
|
||||
use g3proxy_proto::escaper_capnp::escaper_control;
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
|
||||
use super::{CommandError, CommandResult};
|
||||
use crate::common::parse_operation_result;
|
||||
|
||||
pub const COMMAND: &str = "escaper";
|
||||
|
||||
const COMMAND_ARG_NAME: &str = "name";
|
||||
|
||||
const SUBCOMMAND_PUBLISH: &str = "publish";
|
||||
const SUBCOMMAND_PUBLISH_ARG_FILE: &str = "file";
|
||||
const SUBCOMMAND_PUBLISH_ARG_DATA: &str = "data";
|
||||
|
||||
pub fn command() -> Command {
|
||||
Command::new(COMMAND)
|
||||
.arg(Arg::new(COMMAND_ARG_NAME).required(true).num_args(1))
|
||||
.subcommand(
|
||||
Command::new(SUBCOMMAND_PUBLISH)
|
||||
.arg(
|
||||
Arg::new(SUBCOMMAND_PUBLISH_ARG_FILE)
|
||||
.value_name("FILE PATH")
|
||||
.num_args(1)
|
||||
.value_hint(ValueHint::FilePath)
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.required_unless_present(SUBCOMMAND_PUBLISH_ARG_DATA)
|
||||
.conflicts_with(SUBCOMMAND_PUBLISH_ARG_DATA)
|
||||
.short('f')
|
||||
.long("file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(SUBCOMMAND_PUBLISH_ARG_DATA)
|
||||
.value_name("JSON DATA")
|
||||
.num_args(1)
|
||||
.required_unless_present(SUBCOMMAND_PUBLISH_ARG_FILE)
|
||||
.conflicts_with(SUBCOMMAND_PUBLISH_ARG_FILE),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
async fn publish(client: &escaper_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let data = if let Some(file) = args.get_one::<PathBuf>(SUBCOMMAND_PUBLISH_ARG_FILE) {
|
||||
tokio::fs::read_to_string(file).await.map_err(|e| {
|
||||
CommandError::Cli(format!(
|
||||
"failed to read contents of file {}: {e:?}",
|
||||
file.display()
|
||||
))
|
||||
})?
|
||||
} else if let Some(data) = args.get_one::<String>(SUBCOMMAND_PUBLISH_ARG_DATA) {
|
||||
data.to_string()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if let Err(e) = serde_json::Value::from_str(&data) {
|
||||
return Err(CommandError::Cli(format!(
|
||||
"the data to publish is not valid json: {e:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut req = client.publish_request();
|
||||
req.get().set_data(&data);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn run(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(COMMAND_ARG_NAME).unwrap();
|
||||
|
||||
if let Some((subcommand, args)) = args.subcommand() {
|
||||
match subcommand {
|
||||
SUBCOMMAND_PUBLISH => {
|
||||
super::proc::get_escaper(client, name)
|
||||
.and_then(|escaper| async move { publish(&escaper, args).await })
|
||||
.await
|
||||
}
|
||||
cmd => Err(CommandError::Cli(format!("unsupported subcommand {cmd}"))),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
211
g3proxy/utils/ctl/src/main.rs
Normal file
211
g3proxy/utils/ctl/src/main.rs
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use capnp_rpc::{rpc_twoparty_capnp, twoparty, RpcSystem};
|
||||
use clap::builder::ArgPredicate;
|
||||
use clap::{value_parser, Arg, ArgMatches, Command, ValueHint};
|
||||
use clap_complete::Shell;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
|
||||
mod common;
|
||||
mod error;
|
||||
mod proc;
|
||||
|
||||
mod escaper;
|
||||
mod resolver;
|
||||
mod server;
|
||||
mod user_group;
|
||||
|
||||
use error::{CommandError, CommandResult};
|
||||
|
||||
const DEFAULT_SYS_CONTROL_DIR: &str = "/run/g3proxy";
|
||||
const DEFAULT_TMP_CONTROL_DIR: &str = "/tmp/g3";
|
||||
|
||||
const GLOBAL_ARG_COMPLETION: &str = "completion";
|
||||
const GLOBAL_ARG_CONTROL_DIR: &str = "control-dir";
|
||||
const GLOBAL_ARG_GROUP: &str = "daemon-group";
|
||||
const GLOBAL_ARG_PID: &str = "pid";
|
||||
|
||||
async fn connect_to_daemon(args: &ArgMatches) -> anyhow::Result<UnixStream> {
|
||||
let control_dir = args.get_one::<PathBuf>(GLOBAL_ARG_CONTROL_DIR).unwrap();
|
||||
let daemon_group = args
|
||||
.get_one::<String>(GLOBAL_ARG_GROUP)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_default();
|
||||
|
||||
let socket_path = match args.get_one::<usize>(GLOBAL_ARG_PID) {
|
||||
Some(pid) => control_dir.join(format!("{daemon_group}_{}.sock", *pid)),
|
||||
None => control_dir.join(format!("{daemon_group}.sock")),
|
||||
};
|
||||
|
||||
let mut stream = tokio::net::UnixStream::connect(&socket_path)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow!(
|
||||
"failed to connect to control socket {}: {e:?}",
|
||||
socket_path.display()
|
||||
)
|
||||
})?;
|
||||
stream
|
||||
.write_all(b"capnp\n")
|
||||
.await
|
||||
.map_err(|e| anyhow!("enter capnp mode failed: {e:?}"))?;
|
||||
stream
|
||||
.flush()
|
||||
.await
|
||||
.map_err(|e| anyhow!("enter capnp mod failed: {e:?}"))?;
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
fn dir_exist(dir: &str) -> bool {
|
||||
let path = PathBuf::from_str(dir).unwrap();
|
||||
std::fs::read_dir(path).is_ok()
|
||||
}
|
||||
|
||||
fn auto_detect_control_dir() -> &'static str {
|
||||
if dir_exist(DEFAULT_SYS_CONTROL_DIR) {
|
||||
DEFAULT_SYS_CONTROL_DIR
|
||||
} else {
|
||||
DEFAULT_TMP_CONTROL_DIR
|
||||
}
|
||||
}
|
||||
|
||||
fn build_cli_args() -> Command {
|
||||
Command::new("g3proxy-ctl")
|
||||
.arg(
|
||||
Arg::new(GLOBAL_ARG_COMPLETION)
|
||||
.num_args(1)
|
||||
.value_name("SHELL")
|
||||
.long("completion")
|
||||
.value_parser(value_parser!(Shell))
|
||||
.exclusive(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(GLOBAL_ARG_CONTROL_DIR)
|
||||
.help("Directory that contains the control socket")
|
||||
.value_name("CONTROL DIR")
|
||||
.value_hint(ValueHint::DirPath)
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.short('C')
|
||||
.long("control-dir")
|
||||
.default_value(auto_detect_control_dir())
|
||||
.default_value_if(GLOBAL_ARG_COMPLETION, ArgPredicate::IsPresent, None),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(GLOBAL_ARG_GROUP)
|
||||
.required_unless_present_any([GLOBAL_ARG_PID, GLOBAL_ARG_COMPLETION])
|
||||
.num_args(1)
|
||||
.value_name("GROUP NAME")
|
||||
.help("Daemon group name")
|
||||
.short('G')
|
||||
.long("daemon-group"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(GLOBAL_ARG_PID)
|
||||
.help("Daemon pid")
|
||||
.required_unless_present_any([GLOBAL_ARG_GROUP, GLOBAL_ARG_COMPLETION])
|
||||
.num_args(1)
|
||||
.value_name("PID")
|
||||
.value_parser(value_parser!(usize))
|
||||
.short('p')
|
||||
.long("daemon-pid"),
|
||||
)
|
||||
.subcommand(proc::commands::version())
|
||||
.subcommand(proc::commands::offline())
|
||||
.subcommand(proc::commands::force_quit())
|
||||
.subcommand(proc::commands::force_quit_all())
|
||||
.subcommand(proc::commands::list())
|
||||
.subcommand(proc::commands::reload_user_group())
|
||||
.subcommand(proc::commands::reload_resolver())
|
||||
.subcommand(proc::commands::reload_auditor())
|
||||
.subcommand(proc::commands::reload_escaper())
|
||||
.subcommand(proc::commands::reload_server())
|
||||
.subcommand(user_group::command())
|
||||
.subcommand(resolver::command())
|
||||
.subcommand(escaper::command())
|
||||
.subcommand(server::command())
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let args = build_cli_args().get_matches();
|
||||
|
||||
if let Some(target) = args.get_one::<Shell>(GLOBAL_ARG_COMPLETION) {
|
||||
let mut app = build_cli_args();
|
||||
let bin_name = app.get_name().to_string();
|
||||
clap_complete::generate(*target, &mut app, bin_name, &mut io::stdout());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let stream = connect_to_daemon(&args).await?;
|
||||
|
||||
let (reader, writer) = tokio::io::split(stream);
|
||||
let reader = tokio_util::compat::TokioAsyncReadCompatExt::compat(reader);
|
||||
let writer = tokio_util::compat::TokioAsyncWriteCompatExt::compat_write(writer);
|
||||
let rpc_network = Box::new(twoparty::VatNetwork::new(
|
||||
reader,
|
||||
writer,
|
||||
rpc_twoparty_capnp::Side::Client,
|
||||
Default::default(),
|
||||
));
|
||||
let mut rpc_system = RpcSystem::new(rpc_network, None);
|
||||
let proc_control: proc_control::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server);
|
||||
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async move {
|
||||
tokio::task::spawn_local(async move {
|
||||
rpc_system
|
||||
.await
|
||||
.map_err(|e| eprintln!("rpc system error: {e:?}"))
|
||||
});
|
||||
|
||||
if let Some((subcommand, args)) = args.subcommand() {
|
||||
match subcommand {
|
||||
proc::COMMAND_VERSION => proc::version(&proc_control).await,
|
||||
proc::COMMAND_OFFLINE => proc::offline(&proc_control).await,
|
||||
proc::COMMAND_FORCE_QUIT => proc::force_quit(&proc_control, args).await,
|
||||
proc::COMMAND_FORCE_QUIT_ALL => proc::force_quit_all(&proc_control).await,
|
||||
proc::COMMAND_LIST => proc::list(&proc_control, args).await,
|
||||
proc::COMMAND_RELOAD_USER_GROUP => {
|
||||
proc::reload_user_group(&proc_control, args).await
|
||||
}
|
||||
proc::COMMAND_RELOAD_RESOLVER => {
|
||||
proc::reload_resolver(&proc_control, args).await
|
||||
}
|
||||
proc::COMMAND_RELOAD_AUDITOR => proc::reload_auditor(&proc_control, args).await,
|
||||
proc::COMMAND_RELOAD_ESCAPER => proc::reload_escaper(&proc_control, args).await,
|
||||
proc::COMMAND_RELOAD_SERVER => proc::reload_server(&proc_control, args).await,
|
||||
user_group::COMMAND => user_group::run(&proc_control, args).await,
|
||||
resolver::COMMAND => resolver::run(&proc_control, args).await,
|
||||
escaper::COMMAND => escaper::run(&proc_control, args).await,
|
||||
server::COMMAND => server::run(&proc_control, args).await,
|
||||
cmd => Err(CommandError::Cli(format!("invalid subcommand {cmd}"))),
|
||||
}
|
||||
} else {
|
||||
Err(CommandError::Cli("no subcommand found".to_string()))
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(anyhow::Error::new)
|
||||
}
|
||||
272
g3proxy/utils/ctl/src/proc.rs
Normal file
272
g3proxy/utils/ctl/src/proc.rs
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use clap::ArgMatches;
|
||||
|
||||
use g3proxy_proto::escaper_capnp::escaper_control;
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
use g3proxy_proto::resolver_capnp::resolver_control;
|
||||
use g3proxy_proto::server_capnp::server_control;
|
||||
use g3proxy_proto::user_group_capnp::user_group_control;
|
||||
|
||||
use super::CommandResult;
|
||||
use crate::common::{parse_fetch_result, parse_operation_result, print_list_text};
|
||||
|
||||
pub const COMMAND_VERSION: &str = "version";
|
||||
pub const COMMAND_OFFLINE: &str = "offline";
|
||||
|
||||
pub const COMMAND_FORCE_QUIT: &str = "force-quit";
|
||||
pub const COMMAND_FORCE_QUIT_ALL: &str = "force-quit-all";
|
||||
|
||||
pub const COMMAND_LIST: &str = "list";
|
||||
|
||||
const COMMAND_LIST_ARG_RESOURCE: &str = "resource";
|
||||
const RESOURCE_VALUE_USER_GROUP: &str = "user-group";
|
||||
const RESOURCE_VALUE_RESOLVER: &str = "resolver";
|
||||
const RESOURCE_VALUE_AUDITOR: &str = "auditor";
|
||||
const RESOURCE_VALUE_ESCAPER: &str = "escaper";
|
||||
const RESOURCE_VALUE_SERVER: &str = "server";
|
||||
|
||||
pub const COMMAND_RELOAD_USER_GROUP: &str = "reload-user-group";
|
||||
pub const COMMAND_RELOAD_RESOLVER: &str = "reload-resolver";
|
||||
pub const COMMAND_RELOAD_AUDITOR: &str = "reload-auditor";
|
||||
pub const COMMAND_RELOAD_ESCAPER: &str = "reload-escaper";
|
||||
pub const COMMAND_RELOAD_SERVER: &str = "reload-server";
|
||||
|
||||
const SUBCOMMAND_ARG_NAME: &str = "name";
|
||||
|
||||
pub mod commands {
|
||||
use super::*;
|
||||
use clap::{Arg, Command};
|
||||
|
||||
pub fn version() -> Command {
|
||||
Command::new(COMMAND_VERSION)
|
||||
}
|
||||
|
||||
pub fn offline() -> Command {
|
||||
Command::new(COMMAND_OFFLINE).about("Put this daemon into offline mode")
|
||||
}
|
||||
|
||||
pub fn force_quit() -> Command {
|
||||
Command::new(COMMAND_FORCE_QUIT)
|
||||
.about("Force quit offline server with the same name")
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
|
||||
pub fn force_quit_all() -> Command {
|
||||
Command::new(COMMAND_FORCE_QUIT_ALL).about("Force quit all offline servers")
|
||||
}
|
||||
|
||||
pub fn list() -> Command {
|
||||
Command::new(COMMAND_LIST).arg(
|
||||
Arg::new(COMMAND_LIST_ARG_RESOURCE)
|
||||
.required(true)
|
||||
.num_args(1)
|
||||
.value_parser([
|
||||
RESOURCE_VALUE_USER_GROUP,
|
||||
RESOURCE_VALUE_RESOLVER,
|
||||
RESOURCE_VALUE_AUDITOR,
|
||||
RESOURCE_VALUE_ESCAPER,
|
||||
RESOURCE_VALUE_SERVER,
|
||||
])
|
||||
.ignore_case(true),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn reload_user_group() -> Command {
|
||||
Command::new(COMMAND_RELOAD_USER_GROUP)
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
|
||||
pub fn reload_resolver() -> Command {
|
||||
Command::new(COMMAND_RELOAD_RESOLVER)
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
|
||||
pub fn reload_auditor() -> Command {
|
||||
Command::new(COMMAND_RELOAD_AUDITOR)
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
|
||||
pub fn reload_escaper() -> Command {
|
||||
Command::new(COMMAND_RELOAD_ESCAPER)
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
|
||||
pub fn reload_server() -> Command {
|
||||
Command::new(COMMAND_RELOAD_SERVER)
|
||||
.arg(Arg::new(SUBCOMMAND_ARG_NAME).required(true).num_args(1))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn version(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.version_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
let ver = rsp.get()?.get_version()?;
|
||||
println!("{ver}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn offline(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.offline_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn force_quit(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.force_quit_offline_server_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn force_quit_all(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.force_quit_offline_servers_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn list(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
match args
|
||||
.get_one::<String>(COMMAND_LIST_ARG_RESOURCE)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
{
|
||||
RESOURCE_VALUE_USER_GROUP => list_user_group(client).await,
|
||||
RESOURCE_VALUE_RESOLVER => list_resolver(client).await,
|
||||
RESOURCE_VALUE_AUDITOR => list_auditor(client).await,
|
||||
RESOURCE_VALUE_ESCAPER => list_escaper(client).await,
|
||||
RESOURCE_VALUE_SERVER => list_server(client).await,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_user_group(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_user_group_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
async fn list_resolver(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_resolver_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
async fn list_auditor(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_auditor_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
async fn list_escaper(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_escaper_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
async fn list_server(client: &proc_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_server_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn reload_user_group(
|
||||
client: &proc_control::Client,
|
||||
args: &ArgMatches,
|
||||
) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.reload_user_group_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn reload_resolver(
|
||||
client: &proc_control::Client,
|
||||
args: &ArgMatches,
|
||||
) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.reload_resolver_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn reload_auditor(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.reload_auditor_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn reload_escaper(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.reload_escaper_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub async fn reload_server(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(SUBCOMMAND_ARG_NAME).unwrap();
|
||||
let mut req = client.reload_server_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_operation_result(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_user_group(
|
||||
client: &proc_control::Client,
|
||||
name: &str,
|
||||
) -> CommandResult<user_group_control::Client> {
|
||||
let mut req = client.get_user_group_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_fetch_result(rsp.get()?.get_user_group()?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_resolver(
|
||||
client: &proc_control::Client,
|
||||
name: &str,
|
||||
) -> CommandResult<resolver_control::Client> {
|
||||
let mut req = client.get_resolver_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_fetch_result(rsp.get()?.get_resolver()?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_escaper(
|
||||
client: &proc_control::Client,
|
||||
name: &str,
|
||||
) -> CommandResult<escaper_control::Client> {
|
||||
let mut req = client.get_escaper_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_fetch_result(rsp.get()?.get_escaper()?)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_server(
|
||||
client: &proc_control::Client,
|
||||
name: &str,
|
||||
) -> CommandResult<server_control::Client> {
|
||||
let mut req = client.get_server_request();
|
||||
req.get().set_name(name);
|
||||
let rsp = req.send().promise.await?;
|
||||
parse_fetch_result(rsp.get()?.get_server()?)
|
||||
}
|
||||
117
g3proxy/utils/ctl/src/resolver.rs
Normal file
117
g3proxy/utils/ctl/src/resolver.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::{value_parser, Arg, ArgMatches, Command};
|
||||
use futures_util::future::TryFutureExt;
|
||||
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
use g3proxy_proto::resolver_capnp::{
|
||||
query_result, resolver_control, QueryStrategy as RpcQueryStrategy,
|
||||
};
|
||||
|
||||
use g3_types::resolve::QueryStrategy as ResolverQueryStrategy;
|
||||
|
||||
use super::{CommandError, CommandResult};
|
||||
|
||||
pub const COMMAND: &str = "resolver";
|
||||
|
||||
const COMMAND_ARG_NAME: &str = "name";
|
||||
|
||||
const SUBCOMMAND_QUERY: &str = "query";
|
||||
const SUBCOMMAND_QUERY_ARG_DOMAIN: &str = "domain";
|
||||
const SUBCOMMAND_QUERY_ARG_STRATEGY: &str = "strategy";
|
||||
const SUBCOMMAND_QUERY_ARG_RESOLUTION_DELAY: &str = "resolution-delay";
|
||||
|
||||
pub fn command() -> Command {
|
||||
Command::new(COMMAND)
|
||||
.arg(Arg::new(COMMAND_ARG_NAME).required(true).num_args(1))
|
||||
.subcommand(
|
||||
Command::new(SUBCOMMAND_QUERY)
|
||||
.arg(Arg::new(SUBCOMMAND_QUERY_ARG_DOMAIN).required(true))
|
||||
.arg(
|
||||
Arg::new(SUBCOMMAND_QUERY_ARG_STRATEGY)
|
||||
.short('s')
|
||||
.long("strategy")
|
||||
.alias("query-strategy")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(SUBCOMMAND_QUERY_ARG_RESOLUTION_DELAY)
|
||||
.long(SUBCOMMAND_QUERY_ARG_RESOLUTION_DELAY)
|
||||
.num_args(1)
|
||||
.value_parser(value_parser!(u16))
|
||||
.default_value("50"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
async fn query_domain(client: &resolver_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let domain = args.get_one::<String>(SUBCOMMAND_QUERY_ARG_DOMAIN).unwrap();
|
||||
let mut req = client.query_request();
|
||||
req.get().set_domain(domain);
|
||||
|
||||
if let Some(delay) = args.get_one::<u16>(SUBCOMMAND_QUERY_ARG_RESOLUTION_DELAY) {
|
||||
req.get().set_resolution_delay(*delay);
|
||||
}
|
||||
|
||||
if let Some(qs) = args.get_one::<String>(SUBCOMMAND_QUERY_ARG_STRATEGY) {
|
||||
let qs = ResolverQueryStrategy::from_str(qs)
|
||||
.map_err(|_| CommandError::Cli("invalid query strategy".to_string()))?;
|
||||
let qs = match qs {
|
||||
ResolverQueryStrategy::Ipv4Only => RpcQueryStrategy::Ipv4Only,
|
||||
ResolverQueryStrategy::Ipv4First => RpcQueryStrategy::Ipv4First,
|
||||
ResolverQueryStrategy::Ipv6Only => RpcQueryStrategy::Ipv6Only,
|
||||
ResolverQueryStrategy::Ipv6First => RpcQueryStrategy::Ipv6First,
|
||||
};
|
||||
req.get().set_strategy(qs);
|
||||
}
|
||||
|
||||
let rsp = req.send().promise.await?;
|
||||
let result = rsp.get()?.get_result()?;
|
||||
match result.which().unwrap() {
|
||||
query_result::Which::Ip(ips) => {
|
||||
let ips = ips?;
|
||||
println!("query results:");
|
||||
for ip in ips {
|
||||
println!("{}", ip?);
|
||||
}
|
||||
}
|
||||
query_result::Which::Err(reason) => {
|
||||
let reason = reason?;
|
||||
println!("query error: {reason}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(COMMAND_ARG_NAME).unwrap();
|
||||
|
||||
if let Some((subcommand, args)) = args.subcommand() {
|
||||
match subcommand {
|
||||
SUBCOMMAND_QUERY => {
|
||||
super::proc::get_resolver(client, name)
|
||||
.and_then(|resolver| async move { query_domain(&resolver, args).await })
|
||||
.await
|
||||
}
|
||||
cmd => Err(CommandError::Cli(format!("unsupported subcommand {cmd}"))),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
63
g3proxy/utils/ctl/src/server.rs
Normal file
63
g3proxy/utils/ctl/src/server.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use futures_util::future::TryFutureExt;
|
||||
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
use g3proxy_proto::server_capnp::server_control;
|
||||
|
||||
use super::{CommandError, CommandResult};
|
||||
|
||||
pub const COMMAND: &str = "server";
|
||||
|
||||
const COMMAND_ARG_NAME: &str = "name";
|
||||
|
||||
const SUBCOMMAND_STATUS: &str = "status";
|
||||
|
||||
pub fn command() -> Command {
|
||||
Command::new(COMMAND)
|
||||
.arg(Arg::new(COMMAND_ARG_NAME).required(true).num_args(1))
|
||||
.subcommand(Command::new(SUBCOMMAND_STATUS))
|
||||
}
|
||||
|
||||
async fn status(client: &server_control::Client) -> CommandResult<()> {
|
||||
let req = client.status_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
let stats = rsp.get()?.get_status()?;
|
||||
println!("online: {}", stats.get_online());
|
||||
println!("alive tasks: {}", stats.get_alive_task_count());
|
||||
println!("total conn: {}", stats.get_total_conn_count());
|
||||
println!("total task: {}", stats.get_total_task_count());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(COMMAND_ARG_NAME).unwrap();
|
||||
|
||||
if let Some((subcommand, _)) = args.subcommand() {
|
||||
match subcommand {
|
||||
SUBCOMMAND_STATUS => {
|
||||
super::proc::get_server(client, name)
|
||||
.and_then(|server| async move { status(&server).await })
|
||||
.await
|
||||
}
|
||||
cmd => Err(CommandError::Cli(format!("supported subcommand {cmd}"))),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
65
g3proxy/utils/ctl/src/user_group.rs
Normal file
65
g3proxy/utils/ctl/src/user_group.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2023 ByteDance and/or its affiliates.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
|
||||
use g3proxy_proto::proc_capnp::proc_control;
|
||||
use g3proxy_proto::user_group_capnp::user_group_control;
|
||||
|
||||
use super::common::print_list_text;
|
||||
use super::{CommandError, CommandResult};
|
||||
|
||||
pub const COMMAND: &str = "user-group";
|
||||
|
||||
const COMMAND_ARG_NAME: &str = "name";
|
||||
|
||||
const SUBCOMMAND_LIST_STATIC_USER: &str = "list-static-user";
|
||||
const SUBCOMMAND_LIST_DYNAMIC_USER: &str = "list-dynamic-user";
|
||||
|
||||
pub fn command() -> Command {
|
||||
Command::new(COMMAND)
|
||||
.arg(Arg::new(COMMAND_ARG_NAME).required(true).num_args(1))
|
||||
.subcommand(Command::new(SUBCOMMAND_LIST_STATIC_USER))
|
||||
.subcommand(Command::new(SUBCOMMAND_LIST_DYNAMIC_USER))
|
||||
}
|
||||
|
||||
pub async fn run(client: &proc_control::Client, args: &ArgMatches) -> CommandResult<()> {
|
||||
let name = args.get_one::<String>(COMMAND_ARG_NAME).unwrap();
|
||||
|
||||
let user_group = super::proc::get_user_group(client, name).await?;
|
||||
|
||||
if let Some((subcommand, _)) = args.subcommand() {
|
||||
match subcommand {
|
||||
SUBCOMMAND_LIST_STATIC_USER => list_static_user(&user_group).await,
|
||||
SUBCOMMAND_LIST_DYNAMIC_USER => list_dynamic_user(&user_group).await,
|
||||
cmd => Err(CommandError::Cli(format!("unsupported subcommand {cmd}"))),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_static_user(client: &user_group_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_static_user_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
|
||||
async fn list_dynamic_user(client: &user_group_control::Client) -> CommandResult<()> {
|
||||
let req = client.list_dynamic_user_request();
|
||||
let rsp = req.send().promise.await?;
|
||||
print_list_text(rsp.get()?.get_result()?)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue