From 6399c19231ee12f7f7bdb3ed2fa90ece3c0e4000 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 9 Oct 2023 09:40:59 -0400 Subject: [PATCH] feat: remove pods; implement deployments/stateful sets (#75) --- src/config.rs | 2 + src/lib.rs | 492 +++++++++++++++--- src/main.rs | 12 +- src/resources/deployment.rs | 17 + src/resources/mod.rs | 8 +- src/resources/pvc.rs | 2 +- src/resources/stateful_set.rs | 15 + src/resources/tests.rs | 15 +- src/routes.rs | 12 +- src/template_parser.rs | 20 +- src/tests/mod.rs | 3 +- templates/ci/stacks-devnet-api.template.yaml | 5 +- .../bitcoind-chain-coordinator.template.yaml | 113 ++++ .../stacks-blockchain.template.yaml | 69 +++ .../bitcoind-chain-coordinator.template.yaml | 76 --- .../pods/stacks-blockchain-api.template.yaml | 42 -- .../pods/stacks-blockchain.template.yaml | 38 -- .../stacks-blockchain-api-pg.template.yaml | 15 - .../bitcoind-chain-coordinator.template.yaml | 18 +- .../stacks-blockchain-api.template.yaml | 16 +- .../services/stacks-blockchain.template.yaml | 14 +- templates/stacks-devnet-api.template.yaml | 5 +- .../stacks-blockchain-api.template.yaml | 86 +++ 23 files changed, 813 insertions(+), 282 deletions(-) create mode 100644 src/resources/deployment.rs create mode 100644 src/resources/stateful_set.rs create mode 100644 templates/deployments/bitcoind-chain-coordinator.template.yaml create mode 100644 templates/deployments/stacks-blockchain.template.yaml delete mode 100644 templates/pods/bitcoind-chain-coordinator.template.yaml delete mode 100644 templates/pods/stacks-blockchain-api.template.yaml delete mode 100644 templates/pods/stacks-blockchain.template.yaml delete mode 100644 templates/pvcs/stacks-blockchain-api-pg.template.yaml create mode 100644 templates/stateful-sets/stacks-blockchain-api.template.yaml diff --git a/src/config.rs b/src/config.rs index 465b799..efea780 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ const CONTRACT_DIR: &str = "/etc/stacks-network/project/contracts"; #[derive(Serialize, Deserialize, Debug)] pub struct ValidatedStacksDevnetConfig { pub namespace: String, + pub user_id: String, pub devnet_config: DevnetConfig, pub accounts: BTreeMap, pub project_manifest_yaml_string: String, @@ -72,6 +73,7 @@ impl StacksDevnetConfig { Ok(ValidatedStacksDevnetConfig { namespace: self.namespace, + user_id: user_id.to_owned(), devnet_config: devnet_config.to_owned(), accounts: self.network_manifest.accounts, project_manifest_yaml_string: project_manifest_yaml_string.to_owned(), diff --git a/src/lib.rs b/src/lib.rs index 35d9791..400de22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,17 +4,22 @@ use futures::future::try_join4; use hiro_system_kit::{slog, Logger}; use hyper::{body::Bytes, Body, Client as HttpClient, Request, Response, Uri}; use k8s_openapi::{ - api::core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Service}, + api::{ + apps::v1::{Deployment, StatefulSet}, + core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Service}, + }, NamespaceResourceScope, }; use kube::{ - api::{Api, DeleteParams, PostParams}, + api::{Api, DeleteParams, ListParams, PostParams}, config::KubeConfigOptions, Client, Config, }; use resources::{ + deployment::StacksDevnetDeployment, pvc::StacksDevnetPvc, service::{get_service_port, ServicePort}, + stateful_set::StacksDevnetStatefulSet, StacksDevnetResource, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -37,6 +42,10 @@ use crate::resources::configmap::StacksDevnetConfigmap; use crate::resources::pod::StacksDevnetPod; use crate::resources::service::{get_service_url, StacksDevnetService}; +const COMPONENT_SELECTOR: &str = "app.kubernetes.io/component"; +const USER_SELECTOR: &str = "app.kubernetes.io/instance"; +const NAME_SELECTOR: &str = "app.kubernetes.io/name"; + #[derive(Clone, Debug)] pub struct DevNetError { pub message: String, @@ -173,6 +182,7 @@ impl StacksDevnetApiK8sManager { config: ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let context = format!("NAMESPACE: {}", &namespace); let namespace_exists = self.check_namespace_exists(&namespace).await?; @@ -192,7 +202,9 @@ impl StacksDevnetApiK8sManager { } } - let any_assets_exist = self.check_any_devnet_assets_exist(&namespace).await?; + let any_assets_exist = self + .check_any_devnet_assets_exist(&namespace, &user_id) + .await?; if any_assets_exist { let msg = format!( "cannot create devnet because assets already exist {}", @@ -205,25 +217,45 @@ impl StacksDevnetApiK8sManager { }); }; - self.deploy_bitcoin_node_pod(&config).await?; + self.deploy_bitcoin_node(&config).await?; sleep(Duration::from_secs(5)); - self.deploy_stacks_node_pod(&config).await?; + self.deploy_stacks_blockchain(&config).await?; if !config.disable_stacks_api { - self.deploy_stacks_api_pod(&namespace).await?; + self.deploy_stacks_blockchain_api(&config).await?; } Ok(()) } - pub async fn delete_devnet(&self, namespace: &str) -> Result<(), DevNetError> { - match self.check_any_devnet_assets_exist(&namespace).await? { + pub async fn delete_devnet(&self, namespace: &str, user_id: &str) -> Result<(), DevNetError> { + match self + .check_any_devnet_assets_exist(namespace, user_id) + .await? + { true => { let mut errors = vec![]; - let pods: Vec = StacksDevnetPod::iter().map(|p| p.to_string()).collect(); - for pod in pods { - if let Err(e) = self.delete_resource::(namespace, &pod).await { + let deployments: Vec = StacksDevnetDeployment::iter() + .map(|p| p.to_string()) + .collect(); + for deployment in deployments { + if let Err(e) = self + .delete_resource::(namespace, &deployment) + .await + { + errors.push(e); + } + } + + let stateful_sets: Vec = StacksDevnetStatefulSet::iter() + .map(|p| p.to_string()) + .collect(); + for stateful_set in stateful_sets { + if let Err(e) = self + .delete_resource::(namespace, &stateful_set) + .await + { errors.push(e); } } @@ -248,15 +280,17 @@ impl StacksDevnetApiK8sManager { } } - let pvcs: Vec = StacksDevnetPvc::iter().map(|s| s.to_string()).collect(); + let pvcs: Vec = + StacksDevnetPvc::iter().map(|pvc| pvc.to_string()).collect(); for pvc in pvcs { if let Err(e) = self - .delete_resource::(namespace, &pvc) + .delete_resource_by_label::(namespace, &pvc, user_id) .await { errors.push(e); } } + if errors.is_empty() { Ok(()) } else if errors.len() == 1 { @@ -333,6 +367,7 @@ impl StacksDevnetApiK8sManager { pub async fn check_any_devnet_assets_exist( &self, namespace: &str, + user_id: &str, ) -> Result { self.ctx.try_log(|logger| { slog::info!( @@ -341,9 +376,27 @@ impl StacksDevnetApiK8sManager { &namespace ) }); + for deployment in StacksDevnetDeployment::iter() { + if self + .check_resource_exists::(namespace, &deployment.to_string()) + .await? + { + return Ok(true); + } + } + + for stateful_set in StacksDevnetStatefulSet::iter() { + if self + .check_resource_exists::(namespace, &stateful_set.to_string()) + .await? + { + return Ok(true); + } + } + for pod in StacksDevnetPod::iter() { if self - .check_resource_exists::(namespace, &pod.to_string()) + .check_resource_exists_by_label::(namespace, &pod.to_string(), user_id) .await? { return Ok(true); @@ -370,13 +423,16 @@ impl StacksDevnetApiK8sManager { for pvc in StacksDevnetPvc::iter() { if self - .check_resource_exists::(namespace, &pvc.to_string()) + .check_resource_exists_by_label::( + namespace, + &pvc.to_string(), + user_id, + ) .await? { return Ok(true); } } - Ok(false) } @@ -391,36 +447,36 @@ impl StacksDevnetApiK8sManager { &namespace ) }); - for pod in StacksDevnetPod::iter() { + for deployment in StacksDevnetDeployment::iter() { if !self - .check_resource_exists::(namespace, &pod.to_string()) + .check_resource_exists::(namespace, &deployment.to_string()) .await? { return Ok(false); } } - for configmap in StacksDevnetConfigmap::iter() { + for stateful_set in StacksDevnetStatefulSet::iter() { if !self - .check_resource_exists::(namespace, &configmap.to_string()) + .check_resource_exists::(namespace, &stateful_set.to_string()) .await? { return Ok(false); } } - for service in StacksDevnetService::iter() { + for configmap in StacksDevnetConfigmap::iter() { if !self - .check_resource_exists::(namespace, &service.to_string()) + .check_resource_exists::(namespace, &configmap.to_string()) .await? { return Ok(false); } } - for pvc in StacksDevnetPvc::iter() { + for service in StacksDevnetService::iter() { if !self - .check_resource_exists::(namespace, &pvc.to_string()) + .check_resource_exists::(namespace, &service.to_string()) .await? { return Ok(false); @@ -433,6 +489,7 @@ impl StacksDevnetApiK8sManager { async fn get_pod_status_info( &self, namespace: &str, + user_id: &str, pod: StacksDevnetPod, ) -> Result { let context = format!("NAMESPACE: {}, POD: {}", namespace, pod); @@ -441,24 +498,38 @@ impl StacksDevnetApiK8sManager { slog::info!(logger, "getting pod status {}", context) }); let pod_api: Api = Api::namespaced(self.client.to_owned(), &namespace); - let pod_name = pod.to_string(); - match pod_api.get_status(&pod_name).await { - Ok(pod_with_status) => match pod_with_status.status { - Some(status) => { - self.ctx.try_log(|logger: &hiro_system_kit::Logger| { - slog::info!(logger, "successfully retrieved pod status {}", context) - }); - let start_time = match status.start_time { - Some(st) => Some(st.0.to_string()), - None => None, - }; - Ok(PodStatusResponse { - status: status.phase, - start_time, - }) + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={pod}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={pod}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + match pod_api.list(&lp).await { + Ok(pods) => { + let pod_with_status = &pods.items[0]; + match &pod_with_status.status { + Some(status) => { + self.ctx.try_log(|logger: &hiro_system_kit::Logger| { + slog::info!(logger, "successfully retrieved pod status {}", context) + }); + let start_time = match &status.start_time { + Some(st) => Some(st.0.to_string()), + None => None, + }; + Ok(PodStatusResponse { + status: status.phase.to_owned(), + start_time, + }) + } + None => Ok(PodStatusResponse::default()), } - None => Ok(PodStatusResponse::default()), - }, + } Err(e) => { let (msg, code) = match e { kube::Error::Api(api_error) => (api_error.message, api_error.code), @@ -554,6 +625,7 @@ impl StacksDevnetApiK8sManager { pub async fn get_devnet_info( &self, namespace: &str, + user_id: &str, ) -> Result { let context = format!("NAMESPACE: {}", namespace); @@ -587,9 +659,17 @@ impl StacksDevnetApiK8sManager { }, chain_info, ) = try_join4( - self.get_pod_status_info(&namespace, StacksDevnetPod::BitcoindNode), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchain), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchainApi), + self.get_pod_status_info(&namespace, user_id, StacksDevnetPod::BitcoindNode), + self.get_pod_status_info( + &namespace, + user_id, + StacksDevnetPod::StacksBlockchain, + ), + self.get_pod_status_info( + &namespace, + user_id, + StacksDevnetPod::StacksBlockchainApi, + ), self.get_stacks_v2_info(&namespace), ) .await?; @@ -644,6 +724,69 @@ impl StacksDevnetApiK8sManager { } } + async fn get_resource_by_label>( + &self, + namespace: &str, + name: &str, + user_id: &str, + ) -> Result, DevNetError> + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + K: Serialize, + { + let resource_api: Api = Api::namespaced(self.client.to_owned(), &namespace); + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={name}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={name}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + let resource_details = format!( + "RESOURCE: {}, NAME: {}, NAMESPACE: {}", + std::any::type_name::(), + name, + namespace + ); + self.ctx + .try_log(|logger| slog::info!(logger, "fetching {}", resource_details)); + + match resource_api.list(&lp).await { + Ok(pods) => { + if pods.items.len() > 0 { + let pod = &pods.items[0]; + Ok(Some(pod.clone())) + } else { + Ok(None) + } + } + Err(e) => { + let (msg, code) = match e { + kube::Error::Api(api_error) => { + if api_error.code == 404 { + return Ok(None); + } + (api_error.message, api_error.code) + } + e => (e.to_string(), 500), + }; + let msg = format!("failed to fetch {}, ERROR: {}", resource_details, msg); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + Err(DevNetError { + message: msg, + code: code, + }) + } + } + } + async fn get_resource>( &self, namespace: &str, @@ -697,6 +840,28 @@ impl StacksDevnetApiK8sManager { } } + async fn check_resource_exists_by_label>( + &self, + namespace: &str, + name: &str, + user_id: &str, + ) -> Result + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + K: Serialize, + { + match self + .get_resource_by_label::(namespace, name, user_id) + .await? + { + Some(_) => Ok(true), + None => Ok(false), + } + } + async fn check_resource_exists>( &self, namespace: &str, @@ -772,21 +937,140 @@ impl StacksDevnetApiK8sManager { } } - async fn deploy_pod(&self, pod: StacksDevnetPod, namespace: &str) -> Result<(), DevNetError> { - let mut pod: Pod = self.get_resource_from_file(StacksDevnetResource::Pod(pod))?; + async fn deploy_deployment( + &self, + deployment: StacksDevnetDeployment, + namespace: &str, + user_id: &str, + ) -> Result<(), DevNetError> { + let mut deployment: Deployment = + self.get_resource_from_file(StacksDevnetResource::Deployment(deployment))?; + + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); - pod.metadata.namespace = Some(namespace.to_owned()); - self.deploy_resource(namespace, pod, "pod").await + if let Some(mut labels) = deployment.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + deployment.metadata.labels = Some(labels); + } + + if let Some(mut spec) = deployment.clone().spec { + if let Some(mut match_labels) = spec.selector.match_labels { + if let Some(match_label) = match_labels.get_mut(&key) { + *match_label = user_id.clone(); + } else { + match_labels.insert(key.clone(), user_id.clone()); + } + spec.selector.match_labels = Some(match_labels); + } + + if let Some(mut metadata) = spec.template.metadata { + if let Some(mut labels) = metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + metadata.labels = Some(labels); + } + spec.template.metadata = Some(metadata); + } + + deployment.spec = Some(spec); + } + + deployment.metadata.namespace = Some(namespace.to_owned()); + self.deploy_resource(namespace, deployment, "deployment") + .await + } + + async fn deploy_stateful_set( + &self, + stateful_set: StacksDevnetStatefulSet, + namespace: &str, + user_id: &str, + ) -> Result<(), DevNetError> { + let mut stateful_set: StatefulSet = + self.get_resource_from_file(StacksDevnetResource::StatefulSet(stateful_set))?; + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); + + if let Some(mut labels) = stateful_set.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + stateful_set.metadata.labels = Some(labels); + } + + if let Some(mut spec) = stateful_set.clone().spec { + if let Some(mut match_labels) = spec.selector.match_labels { + if let Some(match_label) = match_labels.get_mut(&key) { + *match_label = user_id.clone(); + } else { + match_labels.insert(key.clone(), user_id.clone()); + } + spec.selector.match_labels = Some(match_labels); + } + + if let Some(mut metadata) = spec.template.metadata { + if let Some(mut labels) = metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + metadata.labels = Some(labels); + } + spec.template.metadata = Some(metadata); + } + + stateful_set.spec = Some(spec); + } + + stateful_set.metadata.namespace = Some(namespace.to_owned()); + self.deploy_resource(namespace, stateful_set, "stateful_set") + .await } async fn deploy_service( &self, service: StacksDevnetService, namespace: &str, + user_id: &str, ) -> Result<(), DevNetError> { let mut service: Service = self.get_resource_from_file(StacksDevnetResource::Service(service))?; + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); + + if let Some(mut labels) = service.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + service.metadata.labels = Some(labels); + } + + if let Some(mut spec) = service.clone().spec { + if let Some(mut selector_map) = spec.selector { + if let Some(selector_entry) = selector_map.get_mut(&key) { + *selector_entry = user_id.clone(); + } else { + selector_map.insert(key.clone(), user_id.clone()); + } + spec.selector = Some(selector_map); + } + + service.spec = Some(spec); + } service.metadata.namespace = Some(namespace.to_owned()); self.deploy_resource(namespace, service, "service").await } @@ -813,20 +1097,12 @@ impl StacksDevnetApiK8sManager { .await } - async fn deploy_pvc(&self, pvc: StacksDevnetPvc, namespace: &str) -> Result<(), DevNetError> { - let mut pvc: PersistentVolumeClaim = - self.get_resource_from_file(StacksDevnetResource::Pvc(pvc))?; - - pvc.metadata.namespace = Some(namespace.to_owned()); - - self.deploy_resource(namespace, pvc, "pvc").await - } - - async fn deploy_bitcoin_node_pod( + async fn deploy_bitcoin_node( &self, config: &ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let devnet_config = &config.devnet_config; let bitcoin_rpc_port = @@ -909,20 +1185,21 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pod(StacksDevnetPod::BitcoindNode, &namespace) + self.deploy_deployment(StacksDevnetDeployment::BitcoindNode, &namespace, &user_id) .await?; - self.deploy_service(StacksDevnetService::BitcoindNode, namespace) + self.deploy_service(StacksDevnetService::BitcoindNode, namespace, &user_id) .await?; Ok(()) } - async fn deploy_stacks_node_pod( + async fn deploy_stacks_blockchain( &self, config: &ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let devnet_config = &config.devnet_config; let chain_coordinator_ingestion_port = @@ -1086,16 +1363,25 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pod(StacksDevnetPod::StacksBlockchain, &namespace) - .await?; + self.deploy_deployment( + StacksDevnetDeployment::StacksBlockchain, + &namespace, + &user_id, + ) + .await?; - self.deploy_service(StacksDevnetService::StacksBlockchain, namespace) + self.deploy_service(StacksDevnetService::StacksBlockchain, namespace, &user_id) .await?; Ok(()) } - async fn deploy_stacks_api_pod(&self, namespace: &str) -> Result<(), DevNetError> { + async fn deploy_stacks_blockchain_api( + &self, + config: &ValidatedStacksDevnetConfig, + ) -> Result<(), DevNetError> { + let namespace = &config.namespace; + let user_id = &config.user_id; // configmap env vars for pg conatainer let stacks_api_pg_env = Vec::from([ ("POSTGRES_PASSWORD".into(), "postgres".into()), @@ -1144,14 +1430,19 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pvc(StacksDevnetPvc::StacksBlockchainApiPg, &namespace) - .await?; - - self.deploy_pod(StacksDevnetPod::StacksBlockchainApi, &namespace) - .await?; + self.deploy_stateful_set( + StacksDevnetStatefulSet::StacksBlockchainApi, + &namespace, + user_id, + ) + .await?; - self.deploy_service(StacksDevnetService::StacksBlockchainApi, &namespace) - .await?; + self.deploy_service( + StacksDevnetService::StacksBlockchainApi, + &namespace, + &user_id, + ) + .await?; Ok(()) } @@ -1199,6 +1490,63 @@ impl StacksDevnetApiK8sManager { } } } + + async fn delete_resource_by_label>( + &self, + namespace: &str, + resource_name: &str, + user_id: &str, + ) -> Result<(), DevNetError> + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + { + let api: Api = Api::namespaced(self.client.to_owned(), &namespace); + let dp = DeleteParams::default(); + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={resource_name}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={resource_name}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + let resource_details = format!( + "RESOURCE: {}, NAME: {}, NAMESPACE: {}", + std::any::type_name::(), + resource_name, + namespace + ); + self.ctx + .try_log(|logger| slog::info!(logger, "deleting {}", resource_details)); + match api.delete_collection(&dp, &lp).await { + Ok(_) => { + self.ctx.try_log(|logger| { + slog::info!(logger, "successfully deleted {}", resource_details) + }); + Ok(()) + } + Err(e) => { + let e = match e { + kube::Error::Api(api_error) => (api_error.message, api_error.code), + e => (e.to_string(), 500), + }; + let msg = format!("failed to delete {}, ERROR: {}", resource_details, e.0); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + Err(DevNetError { + message: msg, + code: e.1, + }) + } + } + } + pub async fn delete_namespace(&self, namespace_str: &str) -> Result<(), DevNetError> { if cfg!(debug_assertions) { use kube::ResourceExt; diff --git a/src/main.rs b/src/main.rs index 5974cc1..491eebb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,9 +148,15 @@ async fn handle_request( // so it must be a request to DELETE a network or GET network info if path_parts.subroute.is_none() { return match method { - &Method::DELETE => handle_delete_devnet(k8s_manager, &network, responder).await, - &Method::GET => handle_get_devnet(k8s_manager, &network, responder, ctx).await, - &Method::HEAD => handle_check_devnet(k8s_manager, &network, responder).await, + &Method::DELETE => { + handle_delete_devnet(k8s_manager, &network, &user_id, responder).await + } + &Method::GET => { + handle_get_devnet(k8s_manager, &network, &user_id, responder, ctx).await + } + &Method::HEAD => { + handle_check_devnet(k8s_manager, &network, &user_id, responder).await + } _ => responder .err_method_not_allowed("can only GET/DELETE/HEAD at provided route".into()), }; diff --git a/src/resources/deployment.rs b/src/resources/deployment.rs new file mode 100644 index 0000000..8635cd6 --- /dev/null +++ b/src/resources/deployment.rs @@ -0,0 +1,17 @@ +use std::fmt; +use strum_macros::EnumIter; + +#[derive(EnumIter, Debug)] +pub enum StacksDevnetDeployment { + BitcoindNode, + StacksBlockchain, +} + +impl fmt::Display for StacksDevnetDeployment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + StacksDevnetDeployment::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), + StacksDevnetDeployment::StacksBlockchain => write!(f, "stacks-blockchain"), + } + } +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 918143b..ff8041d 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -1,18 +1,22 @@ use self::{ - configmap::StacksDevnetConfigmap, pod::StacksDevnetPod, pvc::StacksDevnetPvc, - service::StacksDevnetService, + configmap::StacksDevnetConfigmap, deployment::StacksDevnetDeployment, pod::StacksDevnetPod, + pvc::StacksDevnetPvc, service::StacksDevnetService, stateful_set::StacksDevnetStatefulSet, }; pub mod configmap; +pub mod deployment; pub mod pod; pub mod pvc; pub mod service; +pub mod stateful_set; pub enum StacksDevnetResource { Configmap(StacksDevnetConfigmap), + Deployment(StacksDevnetDeployment), Pod(StacksDevnetPod), Pvc(StacksDevnetPvc), Service(StacksDevnetService), + StatefulSet(StacksDevnetStatefulSet), Namespace, } diff --git a/src/resources/pvc.rs b/src/resources/pvc.rs index 81605e8..58404b4 100644 --- a/src/resources/pvc.rs +++ b/src/resources/pvc.rs @@ -9,7 +9,7 @@ pub enum StacksDevnetPvc { impl fmt::Display for StacksDevnetPvc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api-pg"), + StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api"), } } } diff --git a/src/resources/stateful_set.rs b/src/resources/stateful_set.rs new file mode 100644 index 0000000..c843604 --- /dev/null +++ b/src/resources/stateful_set.rs @@ -0,0 +1,15 @@ +use std::fmt; +use strum_macros::EnumIter; + +#[derive(EnumIter, Debug)] +pub enum StacksDevnetStatefulSet { + StacksBlockchainApi, +} + +impl fmt::Display for StacksDevnetStatefulSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + StacksDevnetStatefulSet::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + } + } +} diff --git a/src/resources/tests.rs b/src/resources/tests.rs index c0ff4d0..38a6b7d 100644 --- a/src/resources/tests.rs +++ b/src/resources/tests.rs @@ -1,6 +1,7 @@ use super::{ - pvc::StacksDevnetPvc, + deployment::StacksDevnetDeployment, service::{get_service_from_path_part, get_service_port, get_user_facing_port, ServicePort}, + stateful_set::StacksDevnetStatefulSet, StacksDevnetConfigmap, StacksDevnetPod, StacksDevnetService, }; use test_case::test_case; @@ -24,9 +25,15 @@ fn it_prints_correct_name_for_pod(pod: StacksDevnetPod) -> String { pod.to_string() } -#[test_case(StacksDevnetPvc::StacksBlockchainApiPg => is equal_to "stacks-blockchain-api-pg".to_string(); "for StacksBlockchainApiPg")] -fn it_prints_correct_name_for_pvc(pvc: StacksDevnetPvc) -> String { - pvc.to_string() +#[test_case(StacksDevnetDeployment::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] +#[test_case(StacksDevnetDeployment::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] +fn it_prints_correct_name_for_deployment(deployment: StacksDevnetDeployment) -> String { + deployment.to_string() +} + +#[test_case(StacksDevnetStatefulSet::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] +fn it_prints_correct_name_for_stateful_set(pod: StacksDevnetStatefulSet) -> String { + pod.to_string() } #[test_case(StacksDevnetService::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] diff --git a/src/routes.rs b/src/routes.rs index 1d807cf..a940e8a 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -63,9 +63,10 @@ pub async fn handle_new_devnet( pub async fn handle_delete_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ) -> Result, Infallible> { - match k8s_manager.delete_devnet(network).await { + match k8s_manager.delete_devnet(network, user_id).await { Ok(_) => responder.ok(), Err(e) => { let msg = format!("error deleting network {}: {}", &network, e.message); @@ -77,10 +78,11 @@ pub async fn handle_delete_devnet( pub async fn handle_get_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ctx: Context, ) -> Result, Infallible> { - match k8s_manager.get_devnet_info(&network).await { + match k8s_manager.get_devnet_info(&network, user_id).await { Ok(devnet_info) => match serde_json::to_vec(&devnet_info) { Ok(body) => responder.ok_with_json(Body::from(body)), Err(e) => { @@ -100,9 +102,13 @@ pub async fn handle_get_devnet( pub async fn handle_check_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ) -> Result, Infallible> { - match k8s_manager.check_any_devnet_assets_exist(&network).await { + match k8s_manager + .check_any_devnet_assets_exist(network, user_id) + .await + { Ok(assets_exist) => match assets_exist { true => responder.ok(), false => responder.err_not_found("not found".to_string()), diff --git a/src/template_parser.rs b/src/template_parser.rs index 521662f..afa97d0 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -1,12 +1,12 @@ use crate::resources::{ - configmap::StacksDevnetConfigmap, pod::StacksDevnetPod, pvc::StacksDevnetPvc, - service::StacksDevnetService, StacksDevnetResource, + configmap::StacksDevnetConfigmap, deployment::StacksDevnetDeployment, + service::StacksDevnetService, stateful_set::StacksDevnetStatefulSet, StacksDevnetResource, }; pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { match resource { - StacksDevnetResource::Pod(StacksDevnetPod::BitcoindNode) => { - include_str!("../templates/pods/bitcoind-chain-coordinator.template.yaml") + StacksDevnetResource::Deployment(StacksDevnetDeployment::BitcoindNode) => { + include_str!("../templates/deployments/bitcoind-chain-coordinator.template.yaml") } StacksDevnetResource::Service(StacksDevnetService::BitcoindNode) => { include_str!("../templates/services/bitcoind-chain-coordinator.template.yaml") @@ -29,27 +29,25 @@ pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApi) => { include_str!("../templates/configmaps/stacks-blockchain-api.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchainApi) => { - include_str!("../templates/pods/stacks-blockchain-api.template.yaml") + StacksDevnetResource::StatefulSet(StacksDevnetStatefulSet::StacksBlockchainApi) => { + include_str!("../templates/stateful-sets/stacks-blockchain-api.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApiPg) => { include_str!("../templates/configmaps/stacks-blockchain-api-pg.template.yaml") } - StacksDevnetResource::Pvc(StacksDevnetPvc::StacksBlockchainApiPg) => { - include_str!("../templates/pvcs/stacks-blockchain-api-pg.template.yaml") - } StacksDevnetResource::Service(StacksDevnetService::StacksBlockchainApi) => { include_str!("../templates/services/stacks-blockchain-api.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchain) => { include_str!("../templates/configmaps/stacks-blockchain.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchain) => { - include_str!("../templates/pods/stacks-blockchain.template.yaml") + StacksDevnetResource::Deployment(StacksDevnetDeployment::StacksBlockchain) => { + include_str!("../templates/deployments/stacks-blockchain.template.yaml") } StacksDevnetResource::Service(StacksDevnetService::StacksBlockchain) => { include_str!("../templates/services/stacks-blockchain.template.yaml") } StacksDevnetResource::Namespace => include_str!("../templates/namespace.template.yaml"), + StacksDevnetResource::Pod(_) | StacksDevnetResource::Pvc(_) => unreachable!(), } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index e4f8b3f..86f3763 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -144,6 +144,7 @@ async fn it_responds_to_valid_requests_with_deploy( let mut config = get_template_config(); config.namespace = namespace.to_owned(); let validated_config = config.to_validated_config(&namespace, ctx.clone()).unwrap(); + let user_id = &namespace; let _ = k8s_manager.deploy_devnet(validated_config).await.unwrap(); // short delay to allow assets to start sleep(Duration::new(5, 0)); @@ -168,7 +169,7 @@ async fn it_responds_to_valid_requests_with_deploy( let mut status = response.status(); if tear_down { - match k8s_manager.delete_devnet(namespace).await { + match k8s_manager.delete_devnet(namespace, user_id).await { Ok(_) => {} Err(e) => { body_str = e.message; diff --git a/templates/ci/stacks-devnet-api.template.yaml b/templates/ci/stacks-devnet-api.template.yaml index 3948dda..1c1b679 100644 --- a/templates/ci/stacks-devnet-api.template.yaml +++ b/templates/ci/stacks-devnet-api.template.yaml @@ -12,7 +12,10 @@ metadata: rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] - verbs: ["get", "delete", "create"] + verbs: ["get", "delete", "create", "list", "deletecollection"] + - apiGroups: ["apps"] + resources: ["deployments", "statefulsets"] + verbs: ["get", "delete", "create", "list"] - apiGroups: [""] resources: ["namespaces"] verbs: ["get"] diff --git a/templates/deployments/bitcoind-chain-coordinator.template.yaml b/templates/deployments/bitcoind-chain-coordinator.template.yaml new file mode 100644 index 0000000..5e747f9 --- /dev/null +++ b/templates/deployments/bitcoind-chain-coordinator.template.yaml @@ -0,0 +1,113 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + name: bitcoind-chain-coordinator + namespace: "{namespace}" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + template: + metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - command: + - /usr/local/bin/bitcoind + - -conf=/etc/bitcoin/bitcoin.conf + - -nodebuglogfile + - -pid=/run/bitcoind.pid + image: quay.io/hirosystems/bitcoind:devnet-v3 + imagePullPolicy: IfNotPresent + name: bitcoind + ports: + - containerPort: 18444 + name: p2p + protocol: TCP + - containerPort: 18443 + name: rpc + protocol: TCP + volumeMounts: + - mountPath: /etc/bitcoin + name: bitcoind + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + - command: + - ./stacks-network + - --namespace=$(NAMESPACE) + - --manifest-path=/etc/stacks-network/project/Clarinet.toml + - --network-manifest-path=/etc/stacks-network/project/settings/Devnet.toml + - --deployment-plan-path=/etc/stacks-network/project/deployments/default.devnet-plan.yaml + - --project-root-path=/etc/stacks-network/project/ + env: + - name: NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + image: hirosystems/stacks-network-orchestrator@sha256:e9c88e46adb10deba74e29883533de747a2200665f919d1708a5ea0f2638d32a + imagePullPolicy: IfNotPresent + name: chain-coordinator + ports: + - containerPort: 20445 + name: coordinator-in + protocol: TCP + - containerPort: 20446 + name: coordinator-con + protocol: TCP + volumeMounts: + - mountPath: /etc/stacks-network/project + name: project-manifest + - mountPath: /etc/stacks-network/project/settings + name: devnet + - mountPath: /etc/stacks-network/project/deployments + name: deployment-plan + - mountPath: /etc/stacks-network/project/contracts + name: project-dir + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + memory: 256Mi + volumes: + - configMap: + name: bitcoind + name: bitcoind + - configMap: + name: project-manifest + name: project-manifest + - configMap: + name: devnet + name: devnet + - configMap: + name: deployment-plan + name: deployment-plan + - configMap: + name: project-dir + name: project-dir \ No newline at end of file diff --git a/templates/deployments/stacks-blockchain.template.yaml b/templates/deployments/stacks-blockchain.template.yaml new file mode 100644 index 0000000..6b7f003 --- /dev/null +++ b/templates/deployments/stacks-blockchain.template.yaml @@ -0,0 +1,69 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + name: stacks-blockchain + namespace: "{namespace}" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + template: + metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - command: + - stacks-node + - start + - --config=/src/stacks-blockchain/Stacks.toml + env: + - name: STACKS_LOG_PP + value: "1" + - name: BLOCKSTACK_USE_TEST_GENESIS_CHAINSTATE + value: "1" + - name: STACKS_LOG_DEBUG + value: "0" + image: quay.io/hirosystems/stacks-node:devnet-v3 + imagePullPolicy: IfNotPresent + name: stacks-blockchain + ports: + - containerPort: 20444 + name: p2p + protocol: TCP + - containerPort: 20443 + name: rpc + protocol: TCP + volumeMounts: + - mountPath: /src/stacks-blockchain + name: stacks-blockchain + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumes: + - configMap: + name: stacks-blockchain + name: stacks-blockchain \ No newline at end of file diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml deleted file mode 100644 index 4db1864..0000000 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: bitcoind-chain-coordinator - name: bitcoind-chain-coordinator - namespace: "{namespace}" -spec: - containers: - - command: - - /usr/local/bin/bitcoind - - -conf=/etc/bitcoin/bitcoin.conf - - -nodebuglogfile - - -pid=/run/bitcoind.pid - image: quay.io/hirosystems/bitcoind:devnet-v3 - imagePullPolicy: IfNotPresent - name: bitcoind - ports: - - containerPort: 18444 - name: p2p - protocol: TCP - - containerPort: 18443 - name: rpc - protocol: TCP - volumeMounts: - - mountPath: /etc/bitcoin - name: bitcoind - readOnly: true - - command: - - ./stacks-network - - --namespace=$(NAMESPACE) - - --manifest-path=/etc/stacks-network/project/Clarinet.toml - - --network-manifest-path=/etc/stacks-network/project/settings/Devnet.toml - - --deployment-plan-path=/etc/stacks-network/project/deployments/default.devnet-plan.yaml - - --project-root-path=/etc/stacks-network/project/ - env: - - name: NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator:rpc-error - imagePullPolicy: Always - name: chain-coordinator - ports: - - containerPort: 20445 - name: coordinator-in - protocol: TCP - - containerPort: 20446 - name: coordinator-con - protocol: TCP - volumeMounts: - - mountPath: /etc/stacks-network/project - name: project-manifest - - mountPath: /etc/stacks-network/project/settings - name: devnet - - mountPath: /etc/stacks-network/project/deployments - name: deployment-plan - - mountPath: /etc/stacks-network/project/contracts - name: project-dir - volumes: - - configMap: - name: bitcoind - name: bitcoind - - configMap: - name: project-manifest - name: project-manifest - - configMap: - name: devnet - name: devnet - - configMap: - name: deployment-plan - name: deployment-plan - - configMap: - name: project-dir - name: project-dir diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml deleted file mode 100644 index 94f58de..0000000 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: stacks-blockchain-api - name: stacks-blockchain-api - namespace: "{namespace}" -spec: - containers: - - name: stacks-blockchain-api - envFrom: - - configMapRef: - name: stacks-blockchain-api - optional: false - image: hirosystems/stacks-blockchain-api - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3999 - name: api - protocol: TCP - - containerPort: 3700 - name: eventport - protocol: TCP - - name: postgres - envFrom: - - configMapRef: - name: stacks-blockchain-api-pg - optional: false - image: postgres:14 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 5432 - name: postgres - protocol: TCP - volumeMounts: - - mountPath: /var/lib/postgresql/data - name: stacks-blockchain-api-pg - subPath: postgres - volumes: - - name: stacks-blockchain-api-pg - persistentVolumeClaim: - claimName: stacks-blockchain-api-pg diff --git a/templates/pods/stacks-blockchain.template.yaml b/templates/pods/stacks-blockchain.template.yaml deleted file mode 100644 index 421c4a2..0000000 --- a/templates/pods/stacks-blockchain.template.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: stacks-blockchain - name: stacks-blockchain - namespace: "{namespace}" -spec: - containers: - - command: - - stacks-node - - start - - --config=/src/stacks-blockchain/Stacks.toml - env: - - name: STACKS_LOG_PP - value: "1" - - name: BLOCKSTACK_USE_TEST_GENESIS_CHAINSTATE - value: "1" - - name: STACKS_LOG_DEBUG - value: "0" - image: quay.io/hirosystems/stacks-node:devnet-v3 - imagePullPolicy: IfNotPresent - name: stacks-blockchain - ports: - - containerPort: 20444 - name: p2p - protocol: TCP - - containerPort: 20443 - name: rpc - protocol: TCP - volumeMounts: - - mountPath: /src/stacks-blockchain - name: stacks-blockchain - readOnly: true - volumes: - - configMap: - name: stacks-blockchain - name: stacks-blockchain \ No newline at end of file diff --git a/templates/pvcs/stacks-blockchain-api-pg.template.yaml b/templates/pvcs/stacks-blockchain-api-pg.template.yaml deleted file mode 100644 index 97df3f3..0000000 --- a/templates/pvcs/stacks-blockchain-api-pg.template.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: stacks-blockchain-api-pg - namespace: "{namespace}" -spec: - accessModes: - - ReadWriteOnce - resources: - limits: - storage: 750Mi - requests: - storage: 500Mi - storageClassName: premium-rwo - volumeMode: Filesystem diff --git a/templates/services/bitcoind-chain-coordinator.template.yaml b/templates/services/bitcoind-chain-coordinator.template.yaml index 9c782e0..ddcaac7 100644 --- a/templates/services/bitcoind-chain-coordinator.template.yaml +++ b/templates/services/bitcoind-chain-coordinator.template.yaml @@ -1,25 +1,33 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator name: bitcoind-chain-coordinator namespace: "{namespace}" spec: ports: - - name: p2p + - name: tcp-p2p port: 18444 protocol: TCP targetPort: 18444 - - name: rpc + - name: tcp-rpc port: 18443 protocol: TCP targetPort: 18443 - - name: coordinator-in + - name: http-coordinator-in port: 20445 protocol: TCP targetPort: 20445 - - name: coordinator-con + - name: http-coordinator-con port: 20446 protocol: TCP targetPort: 20446 selector: - name: bitcoind-chain-coordinator + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator diff --git a/templates/services/stacks-blockchain-api.template.yaml b/templates/services/stacks-blockchain-api.template.yaml index ea5a95b..02f9456 100644 --- a/templates/services/stacks-blockchain-api.template.yaml +++ b/templates/services/stacks-blockchain-api.template.yaml @@ -1,21 +1,29 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api name: stacks-blockchain-api namespace: "{namespace}" spec: ports: - - name: api + - name: http-api port: 3999 protocol: TCP targetPort: 3999 - - name: postgres + - name: tcp-postgres port: 5432 protocol: TCP targetPort: 5432 - - name: eventport + - name: tcp-eventport port: 3700 protocol: TCP targetPort: 3700 selector: - name: stacks-blockchain-api + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api diff --git a/templates/services/stacks-blockchain.template.yaml b/templates/services/stacks-blockchain.template.yaml index 0daa77e..da0c733 100644 --- a/templates/services/stacks-blockchain.template.yaml +++ b/templates/services/stacks-blockchain.template.yaml @@ -1,15 +1,23 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain name: stacks-blockchain namespace: "{namespace}" spec: ports: - - name: p2p + - name: tcp-p2p port: 20444 protocol: TCP - - name: rpc + - name: http-rpc port: 20443 protocol: TCP selector: - name: stacks-blockchain + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index cf2e91f..2f2727d 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -12,7 +12,10 @@ metadata: rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] - verbs: ["get", "delete", "create"] + verbs: ["get", "delete", "create", "list", "deletecollection"] + - apiGroups: ["apps"] + resources: ["deployments", "statefulsets"] + verbs: ["get", "delete", "create", "list"] - apiGroups: [""] resources: ["namespaces"] verbs: ["get"] diff --git a/templates/stateful-sets/stacks-blockchain-api.template.yaml b/templates/stateful-sets/stacks-blockchain-api.template.yaml new file mode 100644 index 0000000..81e8101 --- /dev/null +++ b/templates/stateful-sets/stacks-blockchain-api.template.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + name: stacks-blockchain-api + namespace: "{namespace}" +spec: + replicas: 1 + serviceName: stacks-blockchain-api + selector: + matchLabels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + template: + metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - name: stacks-blockchain-api + envFrom: + - configMapRef: + name: stacks-blockchain-api + optional: false + image: hirosystems/stacks-blockchain-api + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3999 + name: api + protocol: TCP + - containerPort: 3700 + name: eventport + protocol: TCP + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + - name: postgres + envFrom: + - configMapRef: + name: stacks-blockchain-api-pg + optional: false + image: postgres:15 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5432 + name: postgres + protocol: TCP + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: pg + subPath: postgres + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + memory: 512Mi + volumeClaimTemplates: + - metadata: + name: pg + spec: + accessModes: + - ReadWriteOnce + storageClassName: premium-rwo + resources: + requests: + storage: 1Gi \ No newline at end of file