Skip to content

Commit f495335

Browse files
committed
feat: pinnable context names per profile
1 parent b534f18 commit f495335

3 files changed

Lines changed: 41 additions & 4 deletions

File tree

src/cmd/deploy.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{path::Path, sync::Arc};
1+
use std::{collections::HashMap, path::Path, sync::Arc};
22

33
use miette::Diagnostic;
44

@@ -17,11 +17,24 @@ pub enum InputError {
1717
Serde(#[from] serde_json::Error),
1818
}
1919

20+
#[derive(Debug, Diagnostic, thiserror::Error)]
21+
pub enum ContextError {
22+
#[error("attempted to deploy to the wrong cluster (expected `{expected}`, got `{actual}`)")]
23+
Mismatch { expected: String, actual: String },
24+
#[error("no active kubernetes context found")]
25+
Missing,
26+
#[error("failed to read kubeconfig")]
27+
KubeConfig(#[from] kube::config::KubeconfigError),
28+
}
29+
2030
#[derive(Debug, Diagnostic, thiserror::Error)]
2131
pub enum Error {
2232
#[error("failed to read input file")]
2333
#[diagnostic(transparent)]
2434
Input(#[from] InputError),
35+
#[error("failed verify kubernetes context")]
36+
#[diagnostic(transparent)]
37+
Context(#[from] ContextError),
2538
#[error("failed to deploy")]
2639
#[diagnostic(transparent)]
2740
Deploy(#[from] DeployError),
@@ -35,7 +48,29 @@ async fn read_input(path: impl AsRef<Path>) -> Result<Output, InputError> {
3548
Ok(serde_json::from_slice(&content)?)
3649
}
3750

38-
pub async fn run(config: Config, input_file: &Path) -> Result<(), Error> {
51+
async fn ensure_context<'a>(
52+
profile: Option<&str>,
53+
mappings: &'a HashMap<String, String>,
54+
) -> Result<Option<&'a str>, ContextError> {
55+
let Some(expected) = profile.and_then(|profile| mappings.get(profile)) else {
56+
return Ok(None);
57+
};
58+
59+
let kubeconfig = kube::config::Kubeconfig::read()?;
60+
let current_context = kubeconfig.current_context.ok_or(ContextError::Missing)?;
61+
if current_context.ne(expected) {
62+
return Err(ContextError::Mismatch {
63+
expected: expected.to_string(),
64+
actual: current_context.to_string(),
65+
});
66+
}
67+
68+
Ok(Some(expected.as_str()))
69+
}
70+
71+
pub async fn run(profile: Option<&str>, config: Config, input_file: &Path) -> Result<(), Error> {
72+
ensure_context(profile, &config.context_mappings).await?;
73+
3974
let input = read_input(input_file).await?;
4075
let root = progress::tree();
4176
let handle = progress::setup_line_renderer(&root);

src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub struct Config {
2626
pub tag_format: String,
2727
#[serde(default)]
2828
pub use_monolithic_push: bool,
29+
#[serde(default)]
30+
pub context_mappings: HashMap<String, String>,
2931
}
3032

3133
#[derive(Clone, Debug, Serialize, Deserialize)]

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ async fn run(opts: Opts) -> Result<(), AppError> {
162162
input_file,
163163
} => {
164164
let config = config::load_from_path(profile.as_deref(), config_path).await?;
165-
cmd::deploy::run(config, &input_file).await?;
165+
cmd::deploy::run(profile.as_deref(), config, &input_file).await?;
166166
}
167167
Cmd::Run {
168168
profile,
@@ -186,7 +186,7 @@ async fn run(opts: Opts) -> Result<(), AppError> {
186186

187187
dest.sync_all().await?;
188188

189-
cmd::deploy::run(config, dest.file_path()).await?;
189+
cmd::deploy::run(profile.as_deref(), config, dest.file_path()).await?;
190190
}
191191
}
192192

0 commit comments

Comments
 (0)