Skip to content

Commit

Permalink
fix: handle provisioner namespaces properly
Browse files Browse the repository at this point in the history
  • Loading branch information
lleyton committed Jan 25, 2024
1 parent ef9e856 commit d377a21
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 24 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ aws-sdk-ec2 = { version = "1.9.0", default-features = false, features = ["rt-tok
aws-sdk-ssm = { version = "1.7.0", default-features = false, features = ["rt-tokio", "behavior-version-latest"] }
aws-smithy-runtime = { version = "1.1.1", default-features = false, features = ["client", "connector-hyper-0-14-x"] }
hyper-rustls = { version = "0.24.2", features = ["http2", "webpki-roots", "webpki-tokio"] }
itertools = "0.12.0"
# opentelemetry = { version = "0.18.0", features = ["trace", "rt-tokio"] }
# opentelemetry-otlp = { version = "0.11.0", features = ["tokio"] }
# tonic = { version = "0.8.3" }
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ kind: Service
metadata:
name: whoami
annotations:
# If the provisioner is in another namespace, you need to specify that
# for example, with a namespace of example, the value would be example/digitalocean
chisel-operator.io/exit-node-provisioner: "digitalocean"
spec:
selector:
Expand All @@ -167,7 +169,7 @@ This is useful if you want to just allocate an entire exit node for a single ser

You can also manually allocate exit nodes, but still let the operator manage the Chisel client deployment. This is useful if you want to allocate a single exit node for multiple services, in case you're on a budget and don't want to pay for multiple exit nodes for each service.

To do this, create an `ExitNode` resource with the annotation `chisel-operator.io/exit-node-provisioner` set to the name of the `ExitNodeProvisioner` resource.
To do this, create an `ExitNode` resource with the annotation `chisel-operator.io/exit-node-provisioner` set to the name (and namespace if the provisioner is in a different namespace) of the `ExitNodeProvisioner` resource.

```yaml
apiVersion: chisel-operator.io/v1
Expand All @@ -176,6 +178,8 @@ metadata:
name: my-exit-node
namespace: default
annotations:
# If the provisioner is in another namespace, you need to specify that
# for example, with a namespace of example, the value would be example/digitalocean
chisel-operator.io/exit-node-provisioner: "digitalocean"
spec:
# IP address of exit node
Expand Down Expand Up @@ -270,4 +274,5 @@ It is recommended you use an Ingress controller to expose your services. This gr
Feel free to open a pull request or an issue if you have any suggestions or improvements. We're open to any ideas!

## Legal

Fyra Labs disclaims all liability related to usage of chisel-operator. Please proxy responsibly. See [LICENSE.md](LICENSE.md) for additional details. Contact [email protected] with complaints.
9 changes: 7 additions & 2 deletions src/cloud/aws.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{cloud_init::generate_cloud_init_config, pwgen::generate_password, Provisioner};
use crate::{
cloud::CHISEL_PORT,
ops::{ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL},
ops::{parse_provisioner_label_value, ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL},
};
use async_trait::async_trait;
use aws_config::{BehaviorVersion, Region};
Expand All @@ -10,6 +10,7 @@ use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
use base64::Engine;
use color_eyre::eyre::anyhow;
use k8s_openapi::api::core::v1::Secret;
use kube::ResourceExt;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::{debug, warn};
Expand Down Expand Up @@ -141,9 +142,13 @@ impl Provisioner for AWSProvisioner {

let ec2_client = aws_sdk_ec2::Client::new(&aws_api);

let current_namespace = exit_node.namespace().unwrap();
let (_provisioner_namespace, provsioner_name) =
parse_provisioner_label_value(&current_namespace, provisioner);

let name = format!(
"{}-{}",
provisioner,
provsioner_name,
exit_node.metadata.name.as_ref().unwrap()
);

Expand Down
11 changes: 9 additions & 2 deletions src/cloud/digitalocean.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use super::{cloud_init::generate_cloud_init_config, pwgen::generate_password, Provisioner};
use crate::ops::{ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL};
use crate::ops::{
parse_provisioner_label_value, ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL,
};
use async_trait::async_trait;
use color_eyre::eyre::{anyhow, Error};
use digitalocean_rs::DigitalOceanApi;
use k8s_openapi::api::core::v1::Secret;
use kube::ResourceExt;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::{debug, info, warn};
Expand Down Expand Up @@ -81,9 +84,13 @@ impl Provisioner for DigitalOceanProvisioner {
)
})?;

let current_namespace = exit_node.namespace().unwrap();
let (_provisioner_namespace, provsioner_name) =
parse_provisioner_label_value(&current_namespace, provisioner);

let name = format!(
"{}-{}",
provisioner,
provsioner_name,
exit_node.metadata.name.as_ref().unwrap()
);

Expand Down
15 changes: 11 additions & 4 deletions src/cloud/linode.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use super::{cloud_init::generate_cloud_init_config, pwgen::generate_password, Provisioner};
use crate::ops::{ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL};
use crate::ops::{
parse_provisioner_label_value, ExitNode, ExitNodeStatus, EXIT_NODE_PROVISIONER_LABEL,
};
use async_trait::async_trait;
use base64::Engine;
use color_eyre::eyre::{anyhow, Error};
use k8s_openapi::api::core::v1::Secret;
use kube::ResourceExt;
use linode_rs::LinodeApi;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -70,9 +73,13 @@ impl Provisioner for LinodeProvisioner {
.and_then(|annotations| annotations.get(EXIT_NODE_PROVISIONER_LABEL))
.unwrap();

let name = format!(
let current_namespace = exit_node.namespace().unwrap();
let (_provisioner_namespace, provsioner_name) =
parse_provisioner_label_value(&current_namespace, provisioner);

let name: String = format!(
"{}-{}",
provisioner,
provsioner_name,
exit_node.metadata.name.as_ref().unwrap()
);

Expand Down Expand Up @@ -107,7 +114,7 @@ impl Provisioner for LinodeProvisioner {
name: instance.label,
provider: provisioner.to_string(),
id: Some(instance.id.to_string()),
service_binding: None
service_binding: None,
};

Ok(status)
Expand Down
48 changes: 33 additions & 15 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ use std::{collections::BTreeMap, sync::Arc};
use std::time::Duration;
use tracing::{debug, error, info, instrument, warn};

use crate::ops::{
ExitNode, ExitNodeProvisioner, ExitNodeSpec, ExitNodeStatus, ServiceBinding,
EXIT_NODE_NAME_LABEL, EXIT_NODE_PROVISIONER_LABEL,
use crate::{
cloud::Provisioner,
ops::{
parse_provisioner_label_value, ExitNode, ExitNodeProvisioner, ExitNodeSpec, ExitNodeStatus,
ServiceBinding, EXIT_NODE_NAME_LABEL, EXIT_NODE_PROVISIONER_LABEL,
},
};
use crate::{deployment::create_owned_deployment, error::ReconcileError};

Expand Down Expand Up @@ -85,24 +88,29 @@ async fn find_exit_node_from_label(ctx: Arc<Context>, query: &str) -> Option<Exi
.unwrap_or(false)
})
}

#[instrument(skip(ctx))]
async fn find_exit_node_provisioner_from_label(
ctx: Arc<Context>,
default_namespace: &str,
query: &str,
) -> Option<ExitNodeProvisioner> {
let span = tracing::debug_span!("find_exit_node_provisioner_from_label", ?query);
let _enter = span.enter();
let nodes: Api<ExitNodeProvisioner> = Api::all(ctx.client.clone());

let (namespace, name) = parse_provisioner_label_value(default_namespace, query);

let nodes: Api<ExitNodeProvisioner> = Api::namespaced(ctx.client.clone(), namespace);
let node_list = nodes.list(&ListParams::default().timeout(30)).await.ok()?;
info!(node_list = ?node_list, "node list");
let result = node_list.items.into_iter().find(|node| {
node.metadata
.name
.as_ref()
.map(|name| name == query)
.map(|n| n == name)
.unwrap_or(false)
});
debug!(query = ?query, ?result, "Query result");
debug!(name = ?name, ?result, "Query result");

result
}
Expand Down Expand Up @@ -161,7 +169,9 @@ async fn select_exit_node_local(
.metadata
.annotations
.as_ref()
.map(|annotations| annotations.contains_key(EXIT_NODE_PROVISIONER_LABEL))
.map(|annotations: &BTreeMap<String, String>| {
annotations.contains_key(EXIT_NODE_PROVISIONER_LABEL)
})
.unwrap_or(false);

let has_service_binding = node
Expand Down Expand Up @@ -536,12 +546,16 @@ async fn reconcile_nodes(obj: Arc<ExitNode>, ctx: Arc<Context>) -> Result<Action

let old_provider = status.provider.clone();

let old_provisioner =
find_exit_node_provisioner_from_label(ctx.clone(), &old_provider)
.await
.ok_or(ReconcileError::CloudProvisionerNotFound)?;
let old_provisioner = find_exit_node_provisioner_from_label(
ctx.clone(),
&obj.namespace().unwrap(),
&old_provider,
)
.await
.ok_or(ReconcileError::CloudProvisionerNotFound)?;

let old_provisioner_api = old_provisioner.clone().spec.get_inner();
let old_provisioner_api: Box<dyn Provisioner + Send + Sync> =
old_provisioner.clone().spec.get_inner();

let secret = old_provisioner
.find_secret()
Expand Down Expand Up @@ -575,9 +589,13 @@ async fn reconcile_nodes(obj: Arc<ExitNode>, ctx: Arc<Context>) -> Result<Action
}
}

let provisioner = find_exit_node_provisioner_from_label(ctx.clone(), provisioner)
.await
.ok_or(ReconcileError::CloudProvisionerNotFound)?;
let provisioner = find_exit_node_provisioner_from_label(
ctx.clone(),
&obj.namespace().unwrap(),
provisioner,
)
.await
.ok_or(ReconcileError::CloudProvisionerNotFound)?;

let provisioner_api = provisioner.clone().spec.get_inner();

Expand Down
12 changes: 12 additions & 0 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::cloud::{
Provisioner,
};
use color_eyre::Result;
use itertools::Itertools;
use k8s_openapi::api::core::v1::Secret;
use kube::{core::ObjectMeta, Api, CustomResource};
use schemars::JsonSchema;
Expand All @@ -14,6 +15,17 @@ use tracing::debug;
pub const EXIT_NODE_NAME_LABEL: &str = "chisel-operator.io/exit-node-name";
pub const EXIT_NODE_PROVISIONER_LABEL: &str = "chisel-operator.io/exit-node-provisioner";

pub fn parse_provisioner_label_value<'a>(
default_namespace: &'a str,
value: &'a str,
) -> (&'a str, &'a str) {
if let Some(pair) = value.split('/').collect_tuple() {
pair
} else {
(default_namespace, value)
}
}

#[derive(Serialize, Deserialize, Debug, CustomResource, Clone, JsonSchema)]
#[kube(
group = "chisel-operator.io",
Expand Down

0 comments on commit d377a21

Please sign in to comment.