loonfs_core/namespace/
bootstrap.rs1use crate::context::MutationContext;
9use crate::error::CoreError;
10use crate::metadata::{InodeRecord, MetadataState};
11use crate::namespace::control::{read_head_object, ControlObjectLoadError};
12use bytes::Bytes;
13use loonfs_api::wire::control::{
14 encode_control_object, ControlObjectKind, HeadState, HeadStateEnvelope, NamespaceState,
15 WriterBlock,
16};
17use loonfs_api::{
18 ChangeSeq, ContentStoreId, ErrorCode, InodeKind, NamespaceId, NamespaceSummary, ROOT_INODE_ID,
19};
20use loonfs_objectstore::keys::wal_head;
21use loonfs_objectstore::{ObjectStore, ObjectStoreError};
22use thiserror::Error;
23
24#[derive(Debug, Clone, Error)]
25pub enum BootstrapNamespaceError {
26 #[error("holder id must not be empty")]
27 EmptyHolderId,
28 #[error("namespace `{namespace_id}` already exists")]
29 NamespaceAlreadyExists { namespace_id: NamespaceId },
30 #[error("namespace `{namespace_id}` is deleted and its id is retired")]
31 NamespaceDeleted { namespace_id: NamespaceId },
32 #[error(transparent)]
33 Head(#[from] ControlObjectLoadError),
34 #[error(transparent)]
40 Core(#[from] CoreError),
41}
42
43impl BootstrapNamespaceError {
44 pub fn code(&self) -> ErrorCode {
50 match self {
51 BootstrapNamespaceError::EmptyHolderId => ErrorCode::InvalidRequest,
52 BootstrapNamespaceError::NamespaceAlreadyExists { .. } => ErrorCode::NamespaceExists,
53 BootstrapNamespaceError::NamespaceDeleted { .. } => ErrorCode::NamespaceDeleted,
54 BootstrapNamespaceError::Head(_) => ErrorCode::ServerError,
55 BootstrapNamespaceError::Core(error) => error.code(),
56 }
57 }
58
59 pub fn details(&self) -> Option<loonfs_api::ErrorDetails> {
63 match self {
64 BootstrapNamespaceError::Core(error) => error.details(),
65 BootstrapNamespaceError::EmptyHolderId
66 | BootstrapNamespaceError::NamespaceAlreadyExists { .. }
67 | BootstrapNamespaceError::NamespaceDeleted { .. }
68 | BootstrapNamespaceError::Head(_) => None,
69 }
70 }
71}
72
73pub(crate) async fn bootstrap_namespace<S: ObjectStore + ?Sized>(
74 store: &S,
75 namespace_id: &NamespaceId,
76 context: &MutationContext,
77 allow_existing: bool,
78) -> Result<NamespaceSummary, BootstrapNamespaceError> {
79 if context.writer_id.trim().is_empty() {
80 return Err(BootstrapNamespaceError::EmptyHolderId);
81 }
82
83 let mut head = HeadState::initial(namespace_id.clone(), ContentStoreId::generate());
87 head.writer = Some(WriterBlock {
88 writer_id: context.writer_id.clone(),
89 acquired_at_ms: context.now_ms,
90 });
91
92 match install_namespace_head(store, namespace_id, &head).await? {
93 NamespaceHeadInstall::Landed => Ok(NamespaceSummary {
94 namespace_id: namespace_id.clone(),
95 }),
96 NamespaceHeadInstall::Exists if allow_existing => Ok(NamespaceSummary {
103 namespace_id: namespace_id.clone(),
104 }),
105 NamespaceHeadInstall::Exists => Err(BootstrapNamespaceError::NamespaceAlreadyExists {
106 namespace_id: namespace_id.clone(),
107 }),
108 NamespaceHeadInstall::Deleted => Err(BootstrapNamespaceError::NamespaceDeleted {
109 namespace_id: namespace_id.clone(),
110 }),
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub(super) enum NamespaceHeadInstall {
117 Landed,
119 Exists,
121 Deleted,
123}
124
125pub(super) async fn install_namespace_head<S: ObjectStore + ?Sized>(
140 store: &S,
141 namespace_id: &NamespaceId,
142 head: &HeadState,
143) -> Result<NamespaceHeadInstall, CoreError> {
144 let object_key = wal_head(namespace_id.as_str());
145 let envelope = HeadStateEnvelope::from_state(ControlObjectKind::WalHead, head.clone())
146 .map_err(|err| CoreError::Internal(format!("failed to build head envelope: {err}")))?;
147 let bytes = encode_control_object(&envelope)
148 .map_err(|err| CoreError::Internal(format!("failed to encode head object: {err}")))?;
149 match store.put_if_absent(&object_key, Bytes::from(bytes)).await {
150 Ok(_) => Ok(NamespaceHeadInstall::Landed),
151 Err(ObjectStoreError::PreconditionFailed { .. }) => {
152 let existing = match read_head_object(store, namespace_id).await {
153 Ok(loaded) => loaded.envelope.state,
154 Err(error) => return Err(CoreError::load_head(error)),
158 };
159 if existing.state == NamespaceState::Deleted {
160 return Ok(NamespaceHeadInstall::Deleted);
161 }
162 Ok(NamespaceHeadInstall::Exists)
163 }
164 Err(error) => Err(CoreError::store(&object_key, &error)),
165 }
166}
167
168pub(crate) fn bootstrap_metadata_state() -> MetadataState {
174 MetadataState::from_rows(
175 vec![InodeRecord {
176 inode_id: ROOT_INODE_ID,
177 inode_kind: InodeKind::Directory,
178 created_seq: ChangeSeq(0),
179 }],
180 Vec::new(),
181 Vec::new(),
182 Vec::new(),
183 Vec::new(),
184 Vec::new(),
185 )
186}