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