From b818521d37ef0497c54a2cc46ff278628e35f44d Mon Sep 17 00:00:00 2001 From: Cappy Ishihara Date: Sat, 19 Oct 2024 18:20:18 +0700 Subject: [PATCH] Try to fix an issue where cloud-provisioned ExitNodes don't get their secret refs set --- src/cloud/aws.rs | 8 +++++--- src/cloud/digitalocean.rs | 9 +++++---- src/cloud/linode.rs | 9 +++++---- src/cloud/mod.rs | 3 ++- src/daemon.rs | 36 +++++++++++++++++++++++++++++++----- src/ops.rs | 1 + 6 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/cloud/aws.rs b/src/cloud/aws.rs index f723104..b3014ca 100644 --- a/src/cloud/aws.rs +++ b/src/cloud/aws.rs @@ -112,7 +112,7 @@ impl Provisioner for AWSProvisioner { &self, auth: Secret, exit_node: ExitNode, - ) -> color_eyre::Result { + ) -> color_eyre::Result<(ExitNodeStatus, Secret)> { let provisioner = exit_node .metadata .annotations @@ -127,6 +127,8 @@ impl Provisioner for AWSProvisioner { let password = generate_password(32); + let secret = exit_node.generate_secret(password.clone()).await?; + let cloud_init_config = generate_cloud_init_config(&password, CHISEL_PORT); let user_data = base64::engine::general_purpose::STANDARD.encode(cloud_init_config); @@ -222,7 +224,7 @@ impl Provisioner for AWSProvisioner { instance.instance_id.map(|id| id.to_string()).as_deref(), ); - Ok(exit_node) + Ok((exit_node, secret)) } async fn update_exit_node( @@ -268,7 +270,7 @@ impl Provisioner for AWSProvisioner { } else { warn!("No status found for exit node, creating new instance"); // TODO: this should be handled by the controller logic - return self.create_exit_node(auth, exit_node).await; + return self.create_exit_node(auth, exit_node).await.map(|(status, _)| status); } } diff --git a/src/cloud/digitalocean.rs b/src/cloud/digitalocean.rs index eeea31c..d58d987 100644 --- a/src/cloud/digitalocean.rs +++ b/src/cloud/digitalocean.rs @@ -61,12 +61,12 @@ impl Provisioner for DigitalOceanProvisioner { &self, auth: Secret, exit_node: ExitNode, - ) -> color_eyre::Result { + ) -> color_eyre::Result<(ExitNodeStatus, Secret)> { let password = generate_password(32); // create secret for password too - let _secret = exit_node.generate_secret(password.clone()).await?; + let secret = exit_node.generate_secret(password.clone()).await?; let config = generate_cloud_init_config(&password, exit_node.spec.port); @@ -138,7 +138,7 @@ impl Provisioner for DigitalOceanProvisioner { Some(&droplet_id), ); - Ok(exit_node) + Ok((exit_node, secret)) } async fn update_exit_node( @@ -170,7 +170,8 @@ impl Provisioner for DigitalOceanProvisioner { } else { warn!("No status found for exit node, creating new droplet"); // TODO: this should be handled by the controller logic - return self.create_exit_node(auth, exit_node).await; + let (status, _) = self.create_exit_node(auth, exit_node).await?; + return Ok(status); } } diff --git a/src/cloud/linode.rs b/src/cloud/linode.rs index 415d6df..594815e 100644 --- a/src/cloud/linode.rs +++ b/src/cloud/linode.rs @@ -54,10 +54,11 @@ impl Provisioner for LinodeProvisioner { &self, auth: Secret, exit_node: ExitNode, - ) -> color_eyre::Result { + ) -> color_eyre::Result<(ExitNodeStatus, Secret)> { let password = generate_password(32); - let _secret = exit_node.generate_secret(password.clone()).await?; + // Password for the server + let secret = exit_node.generate_secret(password.clone()).await?; let config = generate_cloud_init_config(&password, exit_node.spec.port); @@ -126,7 +127,7 @@ impl Provisioner for LinodeProvisioner { Some(&instance.id.to_string()), ); - Ok(status) + Ok((status, secret)) } async fn delete_exit_node(&self, auth: Secret, exit_node: ExitNode) -> color_eyre::Result<()> { @@ -178,7 +179,7 @@ impl Provisioner for LinodeProvisioner { Ok(status) } else { warn!("No instance status found, creating new instance"); - return self.create_exit_node(auth.clone(), exit_node).await; + return self.create_exit_node(auth.clone(), exit_node).await.map(|(status, _)| status); } } } diff --git a/src/cloud/mod.rs b/src/cloud/mod.rs index 36ab3e8..f6d5210 100644 --- a/src/cloud/mod.rs +++ b/src/cloud/mod.rs @@ -27,7 +27,8 @@ pub trait Provisioner { &self, auth: Secret, exit_node: ExitNode, - ) -> color_eyre::Result; + // Should return the pointer to the password secret for the exit node + ) -> color_eyre::Result<(ExitNodeStatus, Secret)>; async fn update_exit_node( &self, auth: Secret, diff --git a/src/daemon.rs b/src/daemon.rs index db211e0..9797dc9 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -58,6 +58,8 @@ use crate::{deployment::create_owned_deployment, error::ReconcileError}; pub const EXIT_NODE_FINALIZER: &str = "exitnode.chisel-operator.io/finalizer"; pub const SVCS_FINALIZER: &str = "service.chisel-operator.io/finalizer"; +// todo: Refactor everything in here into separate functions, then we can write unit tests for them + // pub fn get_trace_id() -> opentelemetry::trace::TraceId { // // opentelemetry::Context -> opentelemetry::trace::Span // use opentelemetry::trace::TraceContextExt as _; @@ -582,6 +584,7 @@ async fn reconcile_nodes(obj: Arc, ctx: Arc) -> Result, ctx: Arc) -> Result, ctx: Arc) -> Result, ctx: Arc) -> Result { let _node = { + + let mut pass_secret: Option = None; + // if status exists, update, else create let cloud_resource = if let Some(_status) = node.status.as_ref() { info!("Updating cloud resource for {}", node.name_any()); provisioner_api .update_exit_node(secret.clone(), (*node).clone()) .await } else { + // todo: probably update the Provisioner trait to accept a provisioner API handle or + // the provisioner API token *and* then a password secret + // Right now we have the create_exit_node method which returns the password secret alongside the status + + // create cloud resource info!("Creating cloud resource for {}", node.name_any()); - provisioner_api + + let (resource, new_pass_secret) = provisioner_api .create_exit_node(secret.clone(), (*node).clone()) - .await + .await?; + pass_secret = Some(new_pass_secret); + Ok(resource) }; // TODO: Don't replace the entire status and object, sadly JSON is better here - let exitnode_patch = serde_json::json!({ - "status": cloud_resource? - }); + let exitnode_patch = if let Some(p_secret) = pass_secret { + serde_json::json!({ + "status": cloud_resource?, + "spec": { + "auth": p_secret.name_any(), + } + }) + } else { + serde_json::json!({ + "status": cloud_resource?, + }) + }; exit_nodes .patch_status( diff --git a/src/ops.rs b/src/ops.rs index 05f2bb8..ea7ded5 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -88,6 +88,7 @@ impl ExitNode { /// /// Generates a new secret with the `auth` key containing the auth string for chisel in the same namespace as the ExitNode pub async fn generate_secret(&self, password: String) -> Result { + debug!("Generating secret for ExitNode"); let secret_name = self.get_secret_name(); let auth_tmpl = format!("{}:{}", crate::cloud::pwgen::DEFAULT_USERNAME, password);