initial commit

This commit is contained in:
zhangjingqiang 2023-03-09 17:55:45 +08:00
commit 13716f4923
1425 changed files with 163227 additions and 0 deletions

View 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"] }

View 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 })
}
}
}

View 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>;

View 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(())
}
}

View 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)
}

View 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()?)
}

View 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(())
}
}

View 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(())
}
}

View 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()?)
}