feat: vault_password_file or nothing at all is shorthand for just using the local gman provider for secret management
This commit is contained in:
+28
-66
@@ -30,7 +30,7 @@ pub struct AppConfig {
|
|||||||
pub wrap: Option<String>,
|
pub wrap: Option<String>,
|
||||||
pub wrap_code: bool,
|
pub wrap_code: bool,
|
||||||
pub(crate) vault_password_file: Option<PathBuf>,
|
pub(crate) vault_password_file: Option<PathBuf>,
|
||||||
pub(crate) secrets_provider: SupportedProvider,
|
pub(crate) secrets_provider: Option<SupportedProvider>,
|
||||||
|
|
||||||
pub function_calling_support: bool,
|
pub function_calling_support: bool,
|
||||||
pub mapping_tools: IndexMap<String, String>,
|
pub mapping_tools: IndexMap<String, String>,
|
||||||
@@ -96,7 +96,7 @@ impl Default for AppConfig {
|
|||||||
wrap: None,
|
wrap: None,
|
||||||
wrap_code: false,
|
wrap_code: false,
|
||||||
vault_password_file: None,
|
vault_password_file: None,
|
||||||
secrets_provider: SupportedProvider::default(),
|
secrets_provider: None,
|
||||||
|
|
||||||
function_calling_support: true,
|
function_calling_support: true,
|
||||||
mapping_tools: Default::default(),
|
mapping_tools: Default::default(),
|
||||||
@@ -212,7 +212,6 @@ impl AppConfig {
|
|||||||
|
|
||||||
clients: config.clients,
|
clients: config.clients,
|
||||||
};
|
};
|
||||||
app_config.migrate_legacy_password_file();
|
|
||||||
app_config.load_envs();
|
app_config.load_envs();
|
||||||
if let Some(wrap) = app_config.wrap.clone() {
|
if let Some(wrap) = app_config.wrap.clone() {
|
||||||
app_config.set_wrap(&wrap)?;
|
app_config.set_wrap(&wrap)?;
|
||||||
@@ -223,17 +222,6 @@ impl AppConfig {
|
|||||||
Ok(app_config)
|
Ok(app_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn migrate_legacy_password_file(&mut self) {
|
|
||||||
let Some(legacy) = self.vault_password_file.take() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if let SupportedProvider::Local { provider_def } = &mut self.secrets_provider
|
|
||||||
&& provider_def.password_file.is_none()
|
|
||||||
{
|
|
||||||
provider_def.password_file = Some(legacy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_model(&mut self) -> Result<()> {
|
pub fn resolve_model(&mut self) -> Result<()> {
|
||||||
if self.model_id.is_empty() {
|
if self.model_id.is_empty() {
|
||||||
let models = list_models(self, crate::client::ModelType::Chat);
|
let models = list_models(self, crate::client::ModelType::Chat);
|
||||||
@@ -790,66 +778,40 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn migrate_legacy_copies_into_empty_local_provider() {
|
fn default_secrets_provider_is_none() {
|
||||||
let mut app = AppConfig {
|
let app = AppConfig::default();
|
||||||
vault_password_file: Some(PathBuf::from("/tmp/test-coyote-password")),
|
assert!(app.secrets_provider.is_none());
|
||||||
..AppConfig::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
app.migrate_legacy_password_file();
|
|
||||||
|
|
||||||
match &app.secrets_provider {
|
|
||||||
SupportedProvider::Local { provider_def } => {
|
|
||||||
assert_eq!(
|
|
||||||
provider_def.password_file,
|
|
||||||
Some(PathBuf::from("/tmp/test-coyote-password"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => panic!("expected Local provider"),
|
|
||||||
}
|
|
||||||
assert!(app.vault_password_file.is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn migrate_legacy_does_not_overwrite_existing_password_file() {
|
fn secrets_provider_can_hold_non_local_variant() {
|
||||||
let mut app = AppConfig {
|
let app = AppConfig {
|
||||||
vault_password_file: Some(PathBuf::from("/tmp/legacy")),
|
secrets_provider: Some(SupportedProvider::Gopass {
|
||||||
..AppConfig::default()
|
|
||||||
};
|
|
||||||
if let SupportedProvider::Local { provider_def } = &mut app.secrets_provider {
|
|
||||||
provider_def.password_file = Some(PathBuf::from("/tmp/explicit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.migrate_legacy_password_file();
|
|
||||||
|
|
||||||
match &app.secrets_provider {
|
|
||||||
SupportedProvider::Local { provider_def } => {
|
|
||||||
assert_eq!(
|
|
||||||
provider_def.password_file,
|
|
||||||
Some(PathBuf::from("/tmp/explicit"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => panic!("expected Local provider"),
|
|
||||||
}
|
|
||||||
assert!(app.vault_password_file.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn migrate_legacy_noop_for_non_local_provider() {
|
|
||||||
let mut app = AppConfig {
|
|
||||||
vault_password_file: Some(PathBuf::from("/tmp/orphaned")),
|
|
||||||
secrets_provider: SupportedProvider::Gopass {
|
|
||||||
provider_def: Default::default(),
|
provider_def: Default::default(),
|
||||||
},
|
}),
|
||||||
..AppConfig::default()
|
..AppConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
app.migrate_legacy_password_file();
|
|
||||||
|
|
||||||
assert!(app.vault_password_file.is_none());
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
app.secrets_provider,
|
app.secrets_provider,
|
||||||
SupportedProvider::Gopass { .. }
|
Some(SupportedProvider::Gopass { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_config_copies_secrets_provider() {
|
||||||
|
let cfg = Config {
|
||||||
|
model_id: "test-model".to_string(),
|
||||||
|
clients: vec![ClientConfig::default()],
|
||||||
|
secrets_provider: Some(SupportedProvider::Gopass {
|
||||||
|
provider_def: Default::default(),
|
||||||
|
}),
|
||||||
|
..Config::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = AppConfig::from_config(cfg).unwrap();
|
||||||
|
assert!(matches!(
|
||||||
|
app.secrets_provider,
|
||||||
|
Some(SupportedProvider::Gopass { .. })
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -190,7 +190,7 @@ pub struct Config {
|
|||||||
pub(super) vault_password_file: Option<PathBuf>,
|
pub(super) vault_password_file: Option<PathBuf>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub(super) secrets_provider: SupportedProvider,
|
pub(super) secrets_provider: Option<SupportedProvider>,
|
||||||
|
|
||||||
pub function_calling_support: bool,
|
pub function_calling_support: bool,
|
||||||
pub mapping_tools: IndexMap<String, String>,
|
pub mapping_tools: IndexMap<String, String>,
|
||||||
@@ -256,7 +256,7 @@ impl Default for Config {
|
|||||||
wrap: None,
|
wrap: None,
|
||||||
wrap_code: false,
|
wrap_code: false,
|
||||||
vault_password_file: None,
|
vault_password_file: None,
|
||||||
secrets_provider: SupportedProvider::default(),
|
secrets_provider: None,
|
||||||
|
|
||||||
function_calling_support: true,
|
function_calling_support: true,
|
||||||
mapping_tools: Default::default(),
|
mapping_tools: Default::default(),
|
||||||
|
|||||||
@@ -907,44 +907,52 @@ impl RequestContext {
|
|||||||
("messages_file", display_path(&self.messages_file())),
|
("messages_file", display_path(&self.messages_file())),
|
||||||
];
|
];
|
||||||
|
|
||||||
items.push(("secrets_provider", app.secrets_provider.to_string()));
|
|
||||||
match &app.secrets_provider {
|
match &app.secrets_provider {
|
||||||
SupportedProvider::Local { provider_def } => {
|
None => {
|
||||||
let path = provider_def
|
items.push(("secrets_provider", "local".to_string()));
|
||||||
.password_file
|
items.push(("vault_password_file", display_path(&app.vault_password_file())));
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(gman::config::Config::local_provider_password_file);
|
|
||||||
items.push(("vault_password_file", display_path(&path)));
|
|
||||||
}
|
}
|
||||||
SupportedProvider::AwsSecretsManager { provider_def } => {
|
Some(provider) => {
|
||||||
if let Some(p) = &provider_def.aws_profile {
|
items.push(("secrets_provider", provider.to_string()));
|
||||||
items.push(("aws_profile", p.clone()));
|
match provider {
|
||||||
}
|
SupportedProvider::Local { provider_def } => {
|
||||||
if let Some(r) = &provider_def.aws_region {
|
let path = provider_def
|
||||||
items.push(("aws_region", r.clone()));
|
.password_file
|
||||||
}
|
.clone()
|
||||||
}
|
.unwrap_or_else(gman::config::Config::local_provider_password_file);
|
||||||
SupportedProvider::GcpSecretManager { provider_def } => {
|
items.push(("vault_password_file", display_path(&path)));
|
||||||
if let Some(id) = &provider_def.gcp_project_id {
|
}
|
||||||
items.push(("gcp_project_id", id.clone()));
|
SupportedProvider::AwsSecretsManager { provider_def } => {
|
||||||
}
|
if let Some(p) = &provider_def.aws_profile {
|
||||||
}
|
items.push(("aws_profile", p.clone()));
|
||||||
SupportedProvider::AzureKeyVault { provider_def } => {
|
}
|
||||||
if let Some(n) = &provider_def.vault_name {
|
if let Some(r) = &provider_def.aws_region {
|
||||||
items.push(("azure_vault_name", n.clone()));
|
items.push(("aws_region", r.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SupportedProvider::Gopass { provider_def } => {
|
SupportedProvider::GcpSecretManager { provider_def } => {
|
||||||
if let Some(s) = &provider_def.store {
|
if let Some(id) = &provider_def.gcp_project_id {
|
||||||
items.push(("gopass_store", s.clone()));
|
items.push(("gcp_project_id", id.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SupportedProvider::OnePassword { provider_def } => {
|
SupportedProvider::AzureKeyVault { provider_def } => {
|
||||||
if let Some(v) = &provider_def.vault {
|
if let Some(n) = &provider_def.vault_name {
|
||||||
items.push(("op_vault", v.clone()));
|
items.push(("azure_vault_name", n.clone()));
|
||||||
}
|
}
|
||||||
if let Some(a) = &provider_def.account {
|
}
|
||||||
items.push(("op_account", a.clone()));
|
SupportedProvider::Gopass { provider_def } => {
|
||||||
|
if let Some(s) = &provider_def.store {
|
||||||
|
items.push(("gopass_store", s.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SupportedProvider::OnePassword { provider_def } => {
|
||||||
|
if let Some(v) = &provider_def.vault {
|
||||||
|
items.push(("op_vault", v.clone()));
|
||||||
|
}
|
||||||
|
if let Some(a) = &provider_def.account {
|
||||||
|
items.push(("op_account", a.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-4
@@ -42,12 +42,17 @@ impl Vault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(config: &AppConfig) -> Self {
|
pub fn init(config: &AppConfig) -> Self {
|
||||||
let mut provider = config.secrets_provider.clone();
|
let mut provider = match &config.secrets_provider {
|
||||||
|
Some(p) => p.clone(),
|
||||||
|
None => SupportedProvider::Local {
|
||||||
|
provider_def: LocalProvider {
|
||||||
|
password_file: Some(config.vault_password_file()),
|
||||||
|
..LocalProvider::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
if let SupportedProvider::Local { provider_def } = &mut provider {
|
if let SupportedProvider::Local { provider_def } = &mut provider {
|
||||||
if provider_def.password_file.is_none() {
|
|
||||||
provider_def.password_file = Some(config.vault_password_file());
|
|
||||||
}
|
|
||||||
ensure_password_file_initialized(provider_def)
|
ensure_password_file_initialized(provider_def)
|
||||||
.expect("Failed to initialize password file");
|
.expect("Failed to initialize password file");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user