diff --git a/Cargo.lock b/Cargo.lock index 92a915174f2a0..4203ffa791849 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5004,6 +5004,7 @@ dependencies = [ "databend-common-building", "databend-common-grpc", "databend-common-http", + "databend-common-license", "databend-common-meta-api", "databend-common-meta-client", "databend-common-meta-kvapi", diff --git a/src/meta/ee/src/lib.rs b/src/meta/ee/src/lib.rs index 04cdbf59450b7..67a7dd1c53aa5 100644 --- a/src/meta/ee/src/lib.rs +++ b/src/meta/ee/src/lib.rs @@ -70,7 +70,7 @@ pWjW3wxSdeARerxs/BeoWK7FspDtfLaAT8iJe4YEmR0JpkRQ8foWs0ve3w== } /// Parse the JWT token and restore the claims. - fn parse_jwt_token(&self, raw: &str) -> Result, anyhow::Error> { + pub fn parse_jwt_token(&self, raw: &str) -> Result, anyhow::Error> { let public_key = ES256PublicKey::from_pem(&self.public_key)?; let claim = public_key.verify_token::(raw, None)?; @@ -118,4 +118,13 @@ pWjW3wxSdeARerxs/BeoWK7FspDtfLaAT8iJe4YEmR0JpkRQ8foWs0ve3w== Ok(()) } + + pub fn update_license(&self, license: String) -> Result<(), anyhow::Error> { + self.check_license(&license)?; + + let mut x = self.license_token.lock().unwrap(); + *x = Some(license); + + Ok(()) + } } diff --git a/src/meta/service/Cargo.toml b/src/meta/service/Cargo.toml index e2bcdede7bea5..e4db9231ee9b0 100644 --- a/src/meta/service/Cargo.toml +++ b/src/meta/service/Cargo.toml @@ -31,6 +31,7 @@ databend-common-arrow = { workspace = true } databend-common-base = { workspace = true } databend-common-grpc = { workspace = true } databend-common-http = { workspace = true } +databend-common-license = { workspace = true } databend-common-meta-api = { workspace = true } databend-common-meta-client = { workspace = true } databend-common-meta-kvapi = { workspace = true } diff --git a/src/meta/service/src/api/http/v1/ctrl.rs b/src/meta/service/src/api/http/v1/ctrl.rs index 2578db12c70b8..5e51be9eb087c 100644 --- a/src/meta/service/src/api/http/v1/ctrl.rs +++ b/src/meta/service/src/api/http/v1/ctrl.rs @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeMap; use std::sync::Arc; +use databend_common_license::display_jwt_claims::DisplayJWTClaimsExt; use databend_common_meta_sled_store::openraft::async_runtime::watch::WatchReceiver; use databend_common_meta_types::NodeId; use http::StatusCode; @@ -127,3 +129,52 @@ pub async fn trigger_transfer_leader( voter_ids, })) } + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct UpdateLicenseQuery { + pub(crate) license: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct UpdateLicenseResponse { + pub from: NodeId, + pub to: NodeId, + pub voter_ids: Vec, +} + +/// Update the `databend_enterprise_license` of this meta node. +#[poem::handler] +pub async fn update_license( + meta_node: Data<&Arc>, + query: Option>, +) -> poem::Result { + let Some(query) = query else { + return Err(poem::Error::from_string( + "Invalid license", + StatusCode::BAD_REQUEST, + )); + }; + + let metrics = meta_node.raft.metrics().borrow_watched().clone(); + let id = metrics.id; + + let saved = meta_node + .ee_gate + .parse_jwt_token(query.license.as_str()) + .map_err(|e| { + poem::Error::from_string(format!("Invalid license: {}", e), StatusCode::BAD_REQUEST) + })?; + + meta_node + .ee_gate + .update_license(query.license.clone()) + .map_err(|e| poem::Error::from_string(e.to_string(), StatusCode::BAD_REQUEST))?; + + let claim_str = saved.display_jwt_claims().to_string(); + info!("id={} Updated license: {}", id, claim_str); + + let mut resp = BTreeMap::new(); + resp.insert("Success", claim_str); + + Ok(Json(resp)) +} diff --git a/src/meta/service/src/api/http_service.rs b/src/meta/service/src/api/http_service.rs index d376a77698cd0..43f0ee68a5ff9 100644 --- a/src/meta/service/src/api/http_service.rs +++ b/src/meta/service/src/api/http_service.rs @@ -65,6 +65,10 @@ impl HttpService { "/v1/ctrl/trigger_transfer_leader", get(super::http::v1::ctrl::trigger_transfer_leader), ) + .at( + "/v1/ctrl/update_license", + get(super::http::v1::ctrl::update_license), + ) .at( "/v1/cluster/nodes", get(super::http::v1::cluster_state::nodes_handler), diff --git a/src/meta/service/src/meta_service/meta_node.rs b/src/meta/service/src/meta_service/meta_node.rs index c1a9b8362c75f..bf0b2b07de6f5 100644 --- a/src/meta/service/src/meta_service/meta_node.rs +++ b/src/meta/service/src/meta_service/meta_node.rs @@ -108,6 +108,7 @@ pub struct MetaNode { pub sto: RaftStore, pub dispatcher_handle: EventDispatcherHandle, pub raft: MetaRaft, + pub(crate) ee_gate: MetaServiceEnterpriseGate, pub running_tx: watch::Sender<()>, pub running_rx: watch::Receiver<()>, pub join_handles: Mutex>>>, @@ -146,7 +147,7 @@ impl MetaNodeBuilder { let ee_gate = self.ee_gate.clone(); - let net = NetworkFactory::new(sto.clone(), ee_gate); + let net = NetworkFactory::new(sto.clone(), ee_gate.clone()); let log_store = sto.clone(); let sm_store = sto.clone(); @@ -167,6 +168,7 @@ impl MetaNodeBuilder { sto: sto.clone(), dispatcher_handle: EventDispatcherHandle::new(dispatcher_tx), raft: raft.clone(), + ee_gate, running_tx: tx, running_rx: rx, join_handles: Mutex::new(Vec::new()),