Skip to main content

openstack_keystone_distributed_storage/
lib.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//! # Keystone distributed storage.
15//!
16//! A distributed storage for OpenStack Keystone backed by Raft and Fjall KV
17//! database.
18
19use std::io;
20use std::path::Path;
21use std::sync::Arc;
22
23use fjall::Database;
24use openraft::RaftTypeConfig;
25
26pub mod app;
27pub mod grpc;
28pub mod network;
29mod proto_impl;
30mod types;
31pub mod store {
32    pub mod log_store;
33    pub mod state_machine;
34}
35
36pub use store::log_store::FjallLogStore;
37pub use store::state_machine::FjallStateMachine;
38pub use types::StoreError;
39
40pub mod protobuf {
41    pub mod api {
42        // Import the traits into this specific scope
43        use serde::{Deserialize, Serialize};
44        tonic::include_proto!("keystone.api");
45    }
46    pub mod raft {
47        // Import the traits into this specific scope
48        use serde::{Deserialize, Serialize};
49        tonic::include_proto!("keystone.raft");
50    }
51}
52pub use crate::protobuf as pb;
53
54openraft::declare_raft_types!(
55    /// Declare the type configuration for example K/V store.
56    pub TypeConfig:
57        D = pb::api::SetRequest,
58        R = pb::api::Response,
59        LeaderId = pb::raft::LeaderId,
60        Vote = pb::raft::Vote,
61        Entry = pb::raft::Entry,
62        Node = pb::raft::Node,
63        SnapshotData = Vec<u8>,
64);
65
66/// Create a pair of `FjallLogStore` and `FjallStateMachine` that are backed by
67/// a same fjall db instance.
68pub async fn new<C, P: AsRef<Path>>(
69    db_path: P,
70) -> Result<(FjallLogStore<C>, FjallStateMachine), io::Error>
71where
72    C: RaftTypeConfig,
73{
74    let db_path = db_path.as_ref();
75    let snapshot_dir = db_path.join("snapshots");
76    let db = Database::builder(db_path)
77        .open()
78        .map_err(|e| io::Error::other(e.to_string()))?;
79
80    let db = Arc::new(db);
81    Ok((
82        FjallLogStore::new(db.clone())?,
83        FjallStateMachine::new(db, snapshot_dir)?,
84    ))
85}
86
87#[cfg(test)]
88mod tests {
89    use std::sync::Arc;
90
91    use openraft::StorageError;
92    use openraft::testing::log::StoreBuilder;
93    use openraft::testing::log::Suite;
94    use openraft::type_config::TypeConfigExt;
95    use tempfile::TempDir;
96    use tracing_test::traced_test;
97
98    use super::TypeConfig;
99    use super::store::log_store::FjallLogStore;
100    use super::store::state_machine::FjallStateMachine;
101
102    struct FjallBuilder {}
103
104    impl StoreBuilder<TypeConfig, FjallLogStore<TypeConfig>, Arc<FjallStateMachine>, TempDir>
105        for FjallBuilder
106    {
107        async fn build(
108            &self,
109        ) -> Result<
110            (TempDir, FjallLogStore<TypeConfig>, Arc<FjallStateMachine>),
111            StorageError<TypeConfig>,
112        > {
113            let td =
114                TempDir::new().map_err(|e| StorageError::read(TypeConfig::err_from_error(&e)))?;
115            let (log_store, sm) = crate::new(td.path())
116                .await
117                .map_err(|e| StorageError::read(TypeConfig::err_from_error(&e)))?;
118            Ok((td, log_store, Arc::new(sm)))
119        }
120    }
121
122    #[test]
123    #[traced_test]
124    pub fn test_fjall_store() {
125        TypeConfig::run(async {
126            Suite::test_all(FjallBuilder {}).await.unwrap();
127        });
128    }
129}