forked from googleforgames/quilkin
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor CLI organisation, and improve termination (googleforgames#570)
- Loading branch information
1 parent
1bcfe46
commit b9cd1f6
Showing
12 changed files
with
373 additions
and
294 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright 2022 Google LLC | ||
* | ||
* 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. | ||
*/ | ||
|
||
mod generate_config_schema; | ||
mod manage; | ||
mod run; | ||
|
||
use std::path::PathBuf; | ||
|
||
use crate::Config; | ||
|
||
const VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
|
||
#[derive(clap::Parser)] | ||
pub struct Cli { | ||
#[clap( | ||
short, | ||
long, | ||
env = "QUILKIN_CONFIG", | ||
default_value = "quilkin.yaml", | ||
help = "The YAML configuration file." | ||
)] | ||
config: PathBuf, | ||
#[clap( | ||
short, | ||
long, | ||
env, | ||
help = "Whether Quilkin will report any results to stdout/stderr." | ||
)] | ||
quiet: bool, | ||
#[clap(subcommand)] | ||
command: Commands, | ||
} | ||
|
||
#[derive(clap::Subcommand)] | ||
enum Commands { | ||
Run(run::Run), | ||
GenerateConfigSchema(generate_config_schema::GenerateConfigSchema), | ||
Manage(manage::Manage), | ||
} | ||
|
||
impl Cli { | ||
/// Drives the main quilkin application lifecycle using the command line | ||
/// arguments. | ||
pub async fn drive(self) -> crate::Result<()> { | ||
let version: std::borrow::Cow<'static, str> = if cfg!(debug_assertions) { | ||
format!("{VERSION}+debug").into() | ||
} else { | ||
VERSION.into() | ||
}; | ||
|
||
if !self.quiet { | ||
let env_filter = tracing_subscriber::EnvFilter::builder() | ||
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into()) | ||
.from_env_lossy(); | ||
tracing_subscriber::fmt() | ||
.json() | ||
.with_env_filter(env_filter) | ||
.init(); | ||
} | ||
|
||
tracing::info!( | ||
version = &*version, | ||
commit = crate::metadata::build::GIT_COMMIT_HASH, | ||
"Starting Quilkin" | ||
); | ||
|
||
match &self.command { | ||
Commands::Run(runner) => runner.run(&self).await, | ||
Commands::Manage(manager) => manager.manage(&self).await, | ||
Commands::GenerateConfigSchema(generator) => generator.generate_config_schema(), | ||
} | ||
} | ||
|
||
/// Searches for the configuration file, and panics if not found. | ||
fn read_config(&self) -> Config { | ||
std::fs::File::open(&self.config) | ||
.or_else(|error| { | ||
if cfg!(unix) { | ||
std::fs::File::open("/etc/quilkin/quilkin.yaml") | ||
} else { | ||
Err(error) | ||
} | ||
}) | ||
.map_err(eyre::Error::from) | ||
.and_then(|file| Config::from_reader(file).map_err(From::from)) | ||
.unwrap() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright 2022 Google LLC | ||
* | ||
* 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. | ||
*/ | ||
|
||
#[derive(clap::Args)] | ||
pub struct GenerateConfigSchema { | ||
#[clap( | ||
short, | ||
long, | ||
default_value = ".", | ||
help = "The directory to write configuration files." | ||
)] | ||
output_directory: std::path::PathBuf, | ||
#[clap( | ||
min_values = 1, | ||
default_value = "all", | ||
help = "A list of one or more filter IDs to generate or 'all' to generate all available filter schemas." | ||
)] | ||
filter_ids: Vec<String>, | ||
} | ||
|
||
impl GenerateConfigSchema { | ||
pub fn generate_config_schema(&self) -> crate::Result<()> { | ||
let set = crate::filters::FilterSet::default(); | ||
type SchemaIterator<'r> = | ||
Box<dyn Iterator<Item = (&'static str, schemars::schema::RootSchema)> + 'r>; | ||
|
||
let schemas = (self.filter_ids.len() == 1 && self.filter_ids[0].to_lowercase() == "all") | ||
.then(|| { | ||
Box::new( | ||
set.iter() | ||
.map(|factory| (factory.name(), factory.config_schema())), | ||
) as SchemaIterator | ||
}) | ||
.unwrap_or_else(|| { | ||
Box::new(self.filter_ids.iter().filter_map(|id| { | ||
let item = set.get(id); | ||
|
||
if item.is_none() { | ||
tracing::error!("{id} not found in filter set."); | ||
} | ||
|
||
item.map(|item| (item.name(), item.config_schema())) | ||
})) as SchemaIterator | ||
}); | ||
|
||
for (id, schema) in schemas { | ||
let mut path = self.output_directory.join(id); | ||
path.set_extension("yaml"); | ||
|
||
tracing::info!("Writing {id} schema to {}", path.display()); | ||
|
||
std::fs::write(path, serde_yaml::to_string(&schema)?)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2022 Google LLC | ||
* | ||
* 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. | ||
*/ | ||
|
||
#[derive(clap::Args)] | ||
pub struct Manage { | ||
#[clap(subcommand)] | ||
provider: Providers, | ||
} | ||
|
||
#[derive(clap::Subcommand)] | ||
enum Providers { | ||
Agones { | ||
#[clap( | ||
short, | ||
long, | ||
default_value = "default", | ||
help = "Namespace under which the proxies run." | ||
)] | ||
config_namespace: String, | ||
#[clap( | ||
short, | ||
long, | ||
default_value = "default", | ||
help = "Namespace under which the game servers run." | ||
)] | ||
gameservers_namespace: String, | ||
}, | ||
|
||
File, | ||
} | ||
|
||
impl Manage { | ||
pub async fn manage(&self, cli: &crate::Cli) -> crate::Result<()> { | ||
let config = std::sync::Arc::new(cli.read_config()); | ||
|
||
let provider_task = match &self.provider { | ||
Providers::Agones { | ||
gameservers_namespace, | ||
config_namespace, | ||
} => tokio::spawn(crate::config::watch::agones( | ||
gameservers_namespace.clone(), | ||
config_namespace.clone(), | ||
config.clone(), | ||
)), | ||
Providers::File => { | ||
tokio::spawn(crate::config::watch::fs(config.clone(), cli.config.clone())) | ||
} | ||
}; | ||
|
||
tokio::select! { | ||
result = crate::xds::server::spawn(config) => result, | ||
result = provider_task => result.map_err(From::from).and_then(|result| result), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2021 Google LLC | ||
* | ||
* 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 tokio::{signal, sync::watch}; | ||
use tracing::{debug, info, span, Level}; | ||
|
||
#[cfg(doc)] | ||
use crate::filters::FilterFactory; | ||
|
||
#[derive(clap::Args)] | ||
pub struct Run {} | ||
|
||
impl Run { | ||
/// Start and run a proxy. Any passed in [`FilterFactory`]s are included | ||
/// alongside the default filter factories. | ||
pub async fn run(&self, cli: &crate::Cli) -> crate::Result<()> { | ||
let config = cli.read_config(); | ||
let span = span!(Level::INFO, "source::run"); | ||
let _enter = span.enter(); | ||
|
||
let server = crate::Server::try_from(config)?; | ||
|
||
#[cfg(target_os = "linux")] | ||
let mut sig_term_fut = signal::unix::signal(signal::unix::SignalKind::terminate())?; | ||
|
||
let (shutdown_tx, shutdown_rx) = watch::channel::<()>(()); | ||
tokio::spawn(async move { | ||
#[cfg(target_os = "linux")] | ||
let sig_term = sig_term_fut.recv(); | ||
#[cfg(not(target_os = "linux"))] | ||
let sig_term = std::future::pending(); | ||
|
||
tokio::select! { | ||
_ = signal::ctrl_c() => { | ||
debug!("Received SIGINT") | ||
} | ||
_ = sig_term => { | ||
debug!("Received SIGTERM") | ||
} | ||
} | ||
|
||
info!("Shutting down"); | ||
// Don't unwrap in order to ensure that we execute | ||
// any subsequent shutdown tasks. | ||
shutdown_tx.send(()).ok(); | ||
}); | ||
|
||
if let Err(err) = server.run(shutdown_rx).await { | ||
info! (error = %err, "Shutting down with error"); | ||
Err(err) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
} |
Oops, something went wrong.