157 lines
4.4 KiB
Rust
157 lines
4.4 KiB
Rust
/*
|
|
Copyright The containerd Authors.
|
|
|
|
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.
|
|
*/
|
|
|
|
#![cfg_attr(feature = "docs", doc = include_str!("../README.md"))]
|
|
|
|
use std::{env, fmt, fs, os::unix::io::FromRawFd, process};
|
|
|
|
/// Logging binary configuration received from containerd.
|
|
#[derive(Debug)]
|
|
pub struct Config {
|
|
/// Container id.
|
|
pub id: String,
|
|
/// Container namespace.
|
|
pub namespace: String,
|
|
/// Stdout to forward logs from.
|
|
pub stdout: fs::File,
|
|
/// Stderr to forward logs from.
|
|
pub stderr: fs::File,
|
|
}
|
|
|
|
impl Config {
|
|
/// Creates a new configuration object.
|
|
///
|
|
/// It'll query environment provided by containerd to fill up [Config] structure fields.
|
|
///
|
|
/// # Panics
|
|
/// Function call will panic if the environment is incorrect (note that this should be happen
|
|
/// if launched from containerd).
|
|
///
|
|
fn new() -> Config {
|
|
let id = match env::var("CONTAINER_ID") {
|
|
Ok(id) => id,
|
|
Err(_) => handle_err("CONTAINER_ID env not found"),
|
|
};
|
|
|
|
let namespace = match env::var("CONTAINER_NAMESPACE") {
|
|
Ok(ns) => ns,
|
|
Err(_) => handle_err("CONTAINER_NAMESPACE env not found"),
|
|
};
|
|
|
|
let stdout = unsafe { fs::File::from_raw_fd(3) };
|
|
let stderr = unsafe { fs::File::from_raw_fd(4) };
|
|
|
|
Config {
|
|
id,
|
|
namespace,
|
|
stdout,
|
|
stderr,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Signal file wrapper.
|
|
/// containerd uses a file with fd 5 as a signaling mechanism between the daemon and logger process.
|
|
/// This is a wrapper for convenience.
|
|
///
|
|
/// See [logging_unix.go] for details.
|
|
///
|
|
/// [logging_unix.go]: https://github.com/containerd/containerd/blob/dbef1d56d7ebc05bc4553d72c419ed5ce025b05d/runtime/v2/logging/logging_unix.go#L44
|
|
struct Ready(fs::File);
|
|
|
|
impl Ready {
|
|
fn new() -> Ready {
|
|
Ready(unsafe { fs::File::from_raw_fd(5) })
|
|
}
|
|
|
|
/// Signal that we are ready and setup for the container to be started.
|
|
fn signal(self) {
|
|
drop(self.0)
|
|
}
|
|
}
|
|
|
|
/// Driver is a trait to be implemented by v2 logging binaries.
|
|
///
|
|
/// This trait is Rusty alternative to Go's `LoggerFunc`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use containerd_shim_logging::{Config, Driver};
|
|
///
|
|
/// struct Logger;
|
|
///
|
|
/// impl Driver for Logger {
|
|
/// type Error = ();
|
|
///
|
|
/// // Launch logger threads here.
|
|
/// fn new(config: Config) -> Result<Self, Self::Error> {
|
|
/// Ok(Logger {})
|
|
/// }
|
|
///
|
|
/// // Wait for threads to finish.
|
|
/// // In this example `Logger` will finish immediately.
|
|
/// fn wait(self) -> Result<(), Self::Error> {
|
|
/// Ok(())
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
pub trait Driver: Sized {
|
|
/// The error type to be returned from driver routines if something goes wrong.
|
|
type Error: fmt::Debug;
|
|
|
|
/// Create and run a new binary logger from the provided [Config].
|
|
///
|
|
/// Implementations are expected to start the logger driver (typically by spawning threads).
|
|
/// Once returned, the crate will signal containerd that we're ready to log.
|
|
fn new(config: Config) -> Result<Self, Self::Error>;
|
|
|
|
/// Wait for the driver to finish.
|
|
///
|
|
/// Once returned from this function, the binary logger process will shutdown.
|
|
fn wait(self) -> Result<(), Self::Error>;
|
|
}
|
|
|
|
/// Entry point to run the logging driver.
|
|
///
|
|
/// Typically `run` must be called from the `main` function to launch the driver.
|
|
pub fn run<D: Driver>() {
|
|
let config = Config::new();
|
|
let ready = Ready::new();
|
|
|
|
// Initialize log driver
|
|
let logger = match D::new(config) {
|
|
Ok(driver) => driver,
|
|
Err(err) => handle_err(err),
|
|
};
|
|
|
|
// Signal ready to pump log data
|
|
ready.signal();
|
|
|
|
// Run and block until exit
|
|
if let Err(err) = logger.wait() {
|
|
handle_err(err)
|
|
} else {
|
|
process::exit(0);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn handle_err(err: impl fmt::Debug) -> ! {
|
|
eprintln!("{:?}", err);
|
|
process::exit(1);
|
|
}
|