split module into smaller pieces

This commit is contained in:
James Andariese 2023-10-31 06:50:05 -05:00
parent 97fadf6c92
commit 11fd970400
3 changed files with 160 additions and 152 deletions

117
src/cfg.rs Normal file
View File

@ -0,0 +1,117 @@
use crate::{*,errors::*};
#[derive(ClapParser, Debug, Clone)]
/// git-remote-k8s
///
/// This should usually be run by git. You can set git up to use it
/// by running git remote add k k8s://default/ns/repo.
///
/// see https://git.strudelline.net/cascade/git-remote-k8s for more info
pub struct Config {
#[arg(
short,
long,
env = "GIT_REMOTE_K8S_IMAGE",
default_value = "alpine/git:latest"
)]
/// Docker image used for git Jobs
pub image: String,
#[arg(index = 1)]
/// remote name
pub remote_name: String,
#[arg(index = 2)]
/// remote URL
pub remote_url: String,
#[arg(
short,
long,
env = "GIT_REMOTE_K8S_DEBUG",
action=ArgAction::Count,
)]
/// verbosity, may be specified more than once
pub verbose: u8,
#[arg(
long("storage-class-name"),
env = "GIT_REMOTE_K8S_PVC_STORAGECLASSNAME"
)]
/// storageClassName to use for the backing PVC _if it is created_.
///
/// this will be used to create a new PVC but if a PVC already
/// exists by the requested name, it will be used.
pub storageclass: Option<String>,
#[arg(
short('s'),
long("initial-volume-size"),
default_value("1Gi"),
env = "GIT_REMOTE_K8S_INITIAL_VOLUME_SIZE"
)]
pub initial_size: String,
}
impl Config {
/// parse and validate a k8s remote pvc short-URL into a triple of Strings of the form (context, namespace, pvc)
///
/// this utilizes a regex instead of url::Url to ensure that it returns sensible errors
// TODO: find a way to memoize this cleanly. probably give it access to a memoizing context from AppContext.
pub fn parse_and_validate(&self) -> Result<(String, String, String)> {
let caps = REMOTE_PATTERN
.captures(&self.remote_url)
.ok_or(ConfigError::RemoteInvalid)?;
let scheme = if caps.name("scheme_prefix").is_none() {
SCHEME
} else {
caps.name("scheme")
.ok_or(ConfigError::RemoteInvalidScheme)?
.as_str()
};
if scheme != SCHEME {
bail!(ConfigError::RemoteInvalidScheme);
}
let kctx = caps
.name("context")
.ok_or(ConfigError::RemoteNoContext)?
.as_str();
let ns = caps
.name("namespace")
.ok_or(ConfigError::RemoteNoNamespace)?
.as_str();
let pvc = caps.name("pvc").ok_or(ConfigError::RemoteNoPVC)?.as_str();
// regex::Regex::find(REMOTE_PATTERN);
if kctx == "" {
bail!(ConfigError::RemoteNoContext);
};
if ns == "" {
bail!(ConfigError::RemoteNoNamespace);
};
if pvc == "" {
bail!(ConfigError::RemoteNoPVC);
};
if let Some(trailing) = caps.name("trailing") {
if trailing.as_str() != "" {
bail!(ConfigError::RemoteTrailingElements);
}
}
Ok((kctx.to_owned(), ns.to_owned(), pvc.to_owned()))
}
pub fn get_remote_context(&self) -> Result<String> {
let (r, _, _) = self.parse_and_validate()?;
Ok(r)
}
pub fn get_remote_namespace(&self) -> Result<String> {
let (_, r, _) = self.parse_and_validate()?;
Ok(r)
}
pub fn get_remote_pvc(&self) -> Result<String> {
let (_, _, r) = self.parse_and_validate()?;
Ok(r)
}
}

40
src/errors.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::*;
#[derive(ThisError, Debug)]
pub enum ApplicationError {
/// cluster state problems
#[error("cluster is in an inconsistent state")]
RemoteClusterInconsistent,
/// pod state problems
#[error("pod metadata doesn't contain a name")]
PodNoName,
#[error("pod metadata doesn't contain a namespace")]
PodNoNamespace,
#[error("couldn't open pod's stdin")]
PodCouldNotOpenStdin,
#[error("couldn't open pod's stdout")]
PodCouldNotOpenStdout,
#[error("couldn't open pod's stderr")]
PodCouldNotOpenStderr,
#[error("pod failed to start")]
PodCouldNotStart,
#[error("worker pod did not continue running")]
PodDidNotWait,
}
#[derive(ThisError, Debug)]
pub enum ConfigError {
#[error("no namespace present in remote URL")]
RemoteNoNamespace,
#[error("trailing elements after pvc in remote URL")]
RemoteTrailingElements,
#[error("no context present in remote URL")]
RemoteNoContext,
#[error("no PVC name present in remote URL")]
RemoteNoPVC,
#[error("invalid remote URL")]
RemoteInvalid,
#[error("remote URL has an invalid (or no) scheme")]
RemoteInvalidScheme,
}

View File

@ -42,159 +42,10 @@ static REMOTE_PATTERN: Lazy<regex::Regex> = Lazy::new(|| {
#[cfg(test)]
mod test;
#[derive(ClapParser, Debug, Clone)]
/// git-remote-k8s
///
/// This should usually be run by git. You can set git up to use it
/// by running git remote add k k8s://default/ns/repo.
///
/// see https://git.strudelline.net/cascade/git-remote-k8s for more info
struct Config {
#[arg(
short,
long,
env = "GIT_REMOTE_K8S_IMAGE",
default_value = "alpine/git:latest"
)]
/// Docker image used for git Jobs
image: String,
mod cfg;
mod errors;
#[arg(index = 1)]
/// remote name
remote_name: String,
#[arg(index = 2)]
/// remote URL
remote_url: String,
#[arg(
short,
long,
env = "GIT_REMOTE_K8S_DEBUG",
action=ArgAction::Count,
)]
/// verbosity, may be specified more than once
verbose: u8,
#[arg(
long("storage-class-name"),
env = "GIT_REMOTE_K8S_PVC_STORAGECLASSNAME"
)]
/// storageClassName to use for the backing PVC _if it is created_.
///
/// this will be used to create a new PVC but if a PVC already
/// exists by the requested name, it will be used.
storageclass: Option<String>,
#[arg(
short('s'),
long("initial-volume-size"),
default_value("1Gi"),
env = "GIT_REMOTE_K8S_INITIAL_VOLUME_SIZE"
)]
initial_size: String,
}
#[derive(ThisError, Debug)]
pub enum ApplicationError {
/// cluster state problems
#[error("cluster is in an inconsistent state")]
RemoteClusterInconsistent,
/// pod state problems
#[error("pod metadata doesn't contain a name")]
PodNoName,
#[error("pod metadata doesn't contain a namespace")]
PodNoNamespace,
#[error("couldn't open pod's stdin")]
PodCouldNotOpenStdin,
#[error("couldn't open pod's stdout")]
PodCouldNotOpenStdout,
#[error("couldn't open pod's stderr")]
PodCouldNotOpenStderr,
#[error("pod failed to start")]
PodCouldNotStart,
#[error("worker pod did not continue running")]
PodDidNotWait,
}
#[derive(ThisError, Debug)]
pub enum ConfigError {
#[error("no namespace present in remote URL")]
RemoteNoNamespace,
#[error("trailing elements after pvc in remote URL")]
RemoteTrailingElements,
#[error("no context present in remote URL")]
RemoteNoContext,
#[error("no PVC name present in remote URL")]
RemoteNoPVC,
#[error("invalid remote URL")]
RemoteInvalid,
#[error("remote URL has an invalid (or no) scheme")]
RemoteInvalidScheme,
}
impl Config {
/// parse and validate a k8s remote pvc short-URL into a triple of Strings of the form (context, namespace, pvc)
///
/// this utilizes a regex instead of url::Url to ensure that it returns sensible errors
// TODO: find a way to memoize this cleanly. probably give it access to a memoizing context from AppContext.
fn parse_and_validate(&self) -> Result<(String, String, String)> {
let caps = REMOTE_PATTERN
.captures(&self.remote_url)
.ok_or(ConfigError::RemoteInvalid)?;
let scheme = if caps.name("scheme_prefix").is_none() {
SCHEME
} else {
caps.name("scheme")
.ok_or(ConfigError::RemoteInvalidScheme)?
.as_str()
};
if scheme != SCHEME {
bail!(ConfigError::RemoteInvalidScheme);
}
let kctx = caps
.name("context")
.ok_or(ConfigError::RemoteNoContext)?
.as_str();
let ns = caps
.name("namespace")
.ok_or(ConfigError::RemoteNoNamespace)?
.as_str();
let pvc = caps.name("pvc").ok_or(ConfigError::RemoteNoPVC)?.as_str();
// regex::Regex::find(REMOTE_PATTERN);
if kctx == "" {
bail!(ConfigError::RemoteNoContext);
};
if ns == "" {
bail!(ConfigError::RemoteNoNamespace);
};
if pvc == "" {
bail!(ConfigError::RemoteNoPVC);
};
if let Some(trailing) = caps.name("trailing") {
if trailing.as_str() != "" {
bail!(ConfigError::RemoteTrailingElements);
}
}
Ok((kctx.to_owned(), ns.to_owned(), pvc.to_owned()))
}
fn get_remote_context(&self) -> Result<String> {
let (r, _, _) = self.parse_and_validate()?;
Ok(r)
}
fn get_remote_namespace(&self) -> Result<String> {
let (_, r, _) = self.parse_and_validate()?;
Ok(r)
}
fn get_remote_pvc(&self) -> Result<String> {
let (_, _, r) = self.parse_and_validate()?;
Ok(r)
}
}
use crate::{cfg::*,errors::*};
struct AppContext {
config: Arc<Config>,