etcd_client/rpc/
cluster.rs

1//! Etcd Cluster RPC.
2
3use crate::auth::AuthService;
4use crate::channel::Channel;
5use crate::error::Result;
6use crate::rpc::pb::etcdserverpb::cluster_client::ClusterClient as PbClusterClient;
7use crate::rpc::pb::etcdserverpb::{
8    Member as PbMember, MemberAddRequest as PbMemberAddRequest,
9    MemberAddResponse as PbMemberAddResponse, MemberListRequest as PbMemberListRequest,
10    MemberListResponse as PbMemberListResponse, MemberPromoteRequest as PbMemberPromoteRequest,
11    MemberPromoteResponse as PbMemberPromoteResponse, MemberRemoveRequest as PbMemberRemoveRequest,
12    MemberRemoveResponse as PbMemberRemoveResponse, MemberUpdateRequest as PbMemberUpdateRequest,
13    MemberUpdateResponse as PbMemberUpdateResponse,
14};
15use crate::rpc::ResponseHeader;
16use http::HeaderValue;
17use std::sync::RwLock;
18use std::{string::String, sync::Arc};
19use tonic::{IntoRequest, Request};
20
21/// Client for Cluster operations.
22#[repr(transparent)]
23#[derive(Clone)]
24pub struct ClusterClient {
25    inner: PbClusterClient<AuthService<Channel>>,
26}
27
28impl ClusterClient {
29    /// Creates an Cluster client.
30    #[inline]
31    pub(crate) fn new(channel: Channel, auth_token: Arc<RwLock<Option<HeaderValue>>>) -> Self {
32        let inner = PbClusterClient::new(AuthService::new(channel, auth_token));
33        Self { inner }
34    }
35
36    /// Adds a new member into the cluster.
37    #[inline]
38    pub async fn member_add(
39        &mut self,
40        urls: impl Into<Vec<String>>,
41        options: Option<MemberAddOptions>,
42    ) -> Result<MemberAddResponse> {
43        let resp = self
44            .inner
45            .member_add(options.unwrap_or_default().with_urls(urls))
46            .await?
47            .into_inner();
48
49        Ok(MemberAddResponse::new(resp))
50    }
51
52    /// Removes an existing member from the cluster.
53    #[inline]
54    pub async fn member_remove(&mut self, id: u64) -> Result<MemberRemoveResponse> {
55        let resp = self
56            .inner
57            .member_remove(MemberRemoveOptions::new().with_id(id))
58            .await?
59            .into_inner();
60        Ok(MemberRemoveResponse::new(resp))
61    }
62
63    /// Updates the member configuration.
64    #[inline]
65    pub async fn member_update(
66        &mut self,
67        id: u64,
68        url: impl Into<Vec<String>>,
69    ) -> Result<MemberUpdateResponse> {
70        let resp = self
71            .inner
72            .member_update(MemberUpdateOptions::new().with_option(id, url))
73            .await?
74            .into_inner();
75        Ok(MemberUpdateResponse::new(resp))
76    }
77
78    /// Lists all the members in the cluster.
79    #[inline]
80    pub async fn member_list(&mut self) -> Result<MemberListResponse> {
81        let resp = self
82            .inner
83            .member_list(PbMemberListRequest {})
84            .await?
85            .into_inner();
86        Ok(MemberListResponse::new(resp))
87    }
88
89    /// Promotes a member from raft learner (non-voting) to raft voting member.
90    #[inline]
91    pub async fn member_promote(&mut self, id: u64) -> Result<MemberPromoteResponse> {
92        let resp = self
93            .inner
94            .member_promote(MemberPromoteOptions::new().with_id(id))
95            .await?
96            .into_inner();
97        Ok(MemberPromoteResponse::new(resp))
98    }
99}
100
101/// Options for `MemberAdd` operation.
102#[derive(Debug, Default, Clone)]
103#[repr(transparent)]
104pub struct MemberAddOptions(PbMemberAddRequest);
105
106impl MemberAddOptions {
107    #[inline]
108    fn with_urls(mut self, urls: impl Into<Vec<String>>) -> Self {
109        self.0.peer_ur_ls = urls.into();
110        self
111    }
112
113    /// Creates a `MemberAddOptions`.
114    #[inline]
115    pub const fn new() -> Self {
116        Self(PbMemberAddRequest {
117            peer_ur_ls: Vec::new(),
118            is_learner: false,
119        })
120    }
121
122    /// Sets the member as a learner.
123    #[inline]
124    pub const fn with_is_learner(mut self) -> Self {
125        self.0.is_learner = true;
126        self
127    }
128}
129
130impl From<MemberAddOptions> for PbMemberAddRequest {
131    #[inline]
132    fn from(options: MemberAddOptions) -> Self {
133        options.0
134    }
135}
136
137impl IntoRequest<PbMemberAddRequest> for MemberAddOptions {
138    #[inline]
139    fn into_request(self) -> Request<PbMemberAddRequest> {
140        Request::new(self.into())
141    }
142}
143
144/// Response for `MemberAdd` operation.
145#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
146#[derive(Debug, Default, Clone)]
147#[repr(transparent)]
148pub struct MemberAddResponse(PbMemberAddResponse);
149
150impl MemberAddResponse {
151    /// Create a new `MemberAddResponse` from pb cluster response.
152    #[inline]
153    const fn new(resp: PbMemberAddResponse) -> Self {
154        Self(resp)
155    }
156
157    /// Get response header.
158    #[inline]
159    pub fn header(&self) -> Option<&ResponseHeader> {
160        self.0.header.as_ref().map(From::from)
161    }
162
163    /// Get the member information of the added member.
164    #[inline]
165    pub fn member(&self) -> Option<&Member> {
166        self.0.member.as_ref().map(From::from)
167    }
168
169    /// Takes the header out of the response, leaving a [`None`] in its place.
170    #[inline]
171    pub fn take_header(&mut self) -> Option<ResponseHeader> {
172        self.0.header.take().map(ResponseHeader::new)
173    }
174
175    /// Get the member list after adding the new member.
176    #[inline]
177    pub fn member_list(&self) -> &[Member] {
178        unsafe { &*(self.0.members.as_slice() as *const _ as *const [Member]) }
179    }
180}
181
182/// Options for `MemberRemove` operation.
183#[derive(Debug, Default, Clone)]
184// #[repr(transparent)]
185pub struct MemberRemoveOptions(PbMemberRemoveRequest);
186
187impl MemberRemoveOptions {
188    /// Set id
189    #[inline]
190    fn with_id(mut self, id: u64) -> Self {
191        self.0.id = id;
192        self
193    }
194
195    /// Creates a `MemberRemoveOptions`.
196    #[inline]
197    pub const fn new() -> Self {
198        Self(PbMemberRemoveRequest { id: 0 })
199    }
200}
201
202impl From<MemberRemoveOptions> for PbMemberRemoveRequest {
203    #[inline]
204    fn from(options: MemberRemoveOptions) -> Self {
205        options.0
206    }
207}
208
209impl IntoRequest<PbMemberRemoveRequest> for MemberRemoveOptions {
210    #[inline]
211    fn into_request(self) -> Request<PbMemberRemoveRequest> {
212        Request::new(self.into())
213    }
214}
215
216/// Response for `MemberRemove` operation.
217#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
218#[derive(Debug, Default, Clone)]
219#[repr(transparent)]
220pub struct MemberRemoveResponse(PbMemberRemoveResponse);
221
222impl MemberRemoveResponse {
223    /// Create a new `MemberRemoveResponse` from pb cluster response.
224    #[inline]
225    const fn new(resp: PbMemberRemoveResponse) -> Self {
226        Self(resp)
227    }
228
229    /// Get response header.
230    #[inline]
231    pub fn header(&self) -> Option<&ResponseHeader> {
232        self.0.header.as_ref().map(From::from)
233    }
234
235    /// Takes the header out of the response, leaving a [`None`] in its place.
236    #[inline]
237    pub fn take_header(&mut self) -> Option<ResponseHeader> {
238        self.0.header.take().map(ResponseHeader::new)
239    }
240
241    /// A list of all members after removing the member
242    #[inline]
243    pub fn members(&self) -> &[Member] {
244        unsafe { &*(self.0.members.as_slice() as *const _ as *const [Member]) }
245    }
246}
247
248/// Options for `MemberUpdate` operation.
249#[derive(Debug, Default, Clone)]
250// #[repr(transparent)]
251pub struct MemberUpdateOptions(PbMemberUpdateRequest);
252
253impl MemberUpdateOptions {
254    #[inline]
255    fn with_option(mut self, id: u64, url: impl Into<Vec<String>>) -> Self {
256        self.0.id = id;
257        self.0.peer_ur_ls = url.into();
258        self
259    }
260
261    /// Creates a `MemberUpdateOptions`.
262    #[inline]
263    pub const fn new() -> Self {
264        Self(PbMemberUpdateRequest {
265            id: 0,
266            peer_ur_ls: Vec::new(),
267        })
268    }
269}
270
271impl From<MemberUpdateOptions> for PbMemberUpdateRequest {
272    #[inline]
273    fn from(options: MemberUpdateOptions) -> Self {
274        options.0
275    }
276}
277
278impl IntoRequest<PbMemberUpdateRequest> for MemberUpdateOptions {
279    #[inline]
280    fn into_request(self) -> Request<PbMemberUpdateRequest> {
281        Request::new(self.into())
282    }
283}
284
285/// Response for `MemberUpdate` operation.
286#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
287#[derive(Debug, Default, Clone)]
288#[repr(transparent)]
289pub struct MemberUpdateResponse(PbMemberUpdateResponse);
290
291impl MemberUpdateResponse {
292    /// Create a new `MemberUpdateResponse` from pb cluster response.
293    #[inline]
294    const fn new(resp: PbMemberUpdateResponse) -> Self {
295        Self(resp)
296    }
297
298    /// Get response header.
299    #[inline]
300    pub fn header(&self) -> Option<&ResponseHeader> {
301        self.0.header.as_ref().map(From::from)
302    }
303
304    /// Takes the header out of the response, leaving a [`None`] in its place.
305    #[inline]
306    pub fn take_header(&mut self) -> Option<ResponseHeader> {
307        self.0.header.take().map(ResponseHeader::new)
308    }
309
310    /// A list of all members after updating the member.
311    #[inline]
312    pub fn members(&self) -> &[Member] {
313        unsafe { &*(self.0.members.as_slice() as *const _ as *const [Member]) }
314    }
315}
316
317/// Response for `MemberList` operation.
318#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
319#[derive(Debug, Clone)]
320#[repr(transparent)]
321pub struct MemberListResponse(PbMemberListResponse);
322
323impl MemberListResponse {
324    /// Creates a new `MemberListResponse` from pb Member List response.
325    #[inline]
326    const fn new(resp: PbMemberListResponse) -> Self {
327        Self(resp)
328    }
329
330    /// Get response header.
331    #[inline]
332    pub fn header(&self) -> Option<&ResponseHeader> {
333        self.0.header.as_ref().map(From::from)
334    }
335
336    /// Takes the header out of the response, leaving a [`None`] in its place.
337    #[inline]
338    pub fn take_header(&mut self) -> Option<ResponseHeader> {
339        self.0.header.take().map(ResponseHeader::new)
340    }
341
342    /// A list of all members associated with the cluster.
343    #[inline]
344    pub fn members(&self) -> &[Member] {
345        unsafe { &*(self.0.members.as_slice() as *const _ as *const [Member]) }
346    }
347}
348
349/// Cluster member.
350#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
351#[derive(Debug, Clone, PartialEq)]
352#[repr(transparent)]
353pub struct Member(PbMember);
354
355impl Member {
356    /// Member id.
357    #[inline]
358    pub const fn id(&self) -> u64 {
359        self.0.id
360    }
361
362    /// The human-readable name of the member. If the member is not started, the name will be an empty string.
363    #[inline]
364    pub fn name(&self) -> &str {
365        &self.0.name
366    }
367
368    /// The list of URLs the member exposes to the cluster for communication.
369    #[inline]
370    pub fn peer_urls(&self) -> &[String] {
371        &self.0.peer_ur_ls
372    }
373
374    /// The list of URLs the member exposes to clients for communication. If the member is not started, client URLs will be empty.
375    #[inline]
376    pub fn client_urls(&self) -> &[String] {
377        &self.0.client_ur_ls
378    }
379
380    /// Indicates if the member is raft learner.
381    #[inline]
382    pub const fn is_learner(&self) -> bool {
383        self.0.is_learner
384    }
385}
386
387impl From<&PbMember> for &Member {
388    #[inline]
389    fn from(src: &PbMember) -> Self {
390        unsafe { &*(src as *const _ as *const Member) }
391    }
392}
393
394/// Options for `MemberPromote` operation.
395#[derive(Debug, Default, Clone)]
396#[repr(transparent)]
397pub struct MemberPromoteOptions(PbMemberPromoteRequest);
398
399impl MemberPromoteOptions {
400    /// Set id
401    #[inline]
402    fn with_id(mut self, id: u64) -> Self {
403        self.0.id = id;
404        self
405    }
406
407    /// Creates a `MemberPromoteOptions`.
408    #[inline]
409    pub const fn new() -> Self {
410        Self(PbMemberPromoteRequest { id: 0 })
411    }
412}
413
414impl From<MemberPromoteOptions> for PbMemberPromoteRequest {
415    #[inline]
416    fn from(options: MemberPromoteOptions) -> Self {
417        options.0
418    }
419}
420
421impl IntoRequest<PbMemberPromoteRequest> for MemberPromoteOptions {
422    #[inline]
423    fn into_request(self) -> Request<PbMemberPromoteRequest> {
424        Request::new(self.into())
425    }
426}
427
428/// Response for `MemberPromote` operation.
429#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
430#[derive(Debug, Default, Clone)]
431#[repr(transparent)]
432pub struct MemberPromoteResponse(PbMemberPromoteResponse);
433
434impl MemberPromoteResponse {
435    /// Create a new `MemberPromoteResponse` from pb cluster response.
436    #[inline]
437    const fn new(resp: PbMemberPromoteResponse) -> Self {
438        Self(resp)
439    }
440
441    /// Get response header.
442    #[inline]
443    pub fn header(&self) -> Option<&ResponseHeader> {
444        self.0.header.as_ref().map(From::from)
445    }
446
447    /// Takes the header out of the response, leaving a [`None`] in its place.
448    #[inline]
449    pub fn take_header(&mut self) -> Option<ResponseHeader> {
450        self.0.header.take().map(ResponseHeader::new)
451    }
452
453    /// A list of all members after promoting the member.
454    #[inline]
455    pub fn members(&self) -> &[Member] {
456        unsafe { &*(self.0.members.as_slice() as *const _ as *const [Member]) }
457    }
458}