Skip to main content

etcd_client/rpc/
cluster.rs

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