fidl_fuchsia_net_filter_ext/
sync.rs
1use crate::{
6 handle_change_validation_result, handle_commit_result, Change, CommitError,
7 ControllerCreationError, ControllerId, PushChangesError,
8};
9use fidl::marker::SourceBreaking;
10use fidl_fuchsia_net_filter as fnet_filter;
11
12pub struct Controller {
14 controller: fnet_filter::NamespaceControllerSynchronousProxy,
15 id: ControllerId,
19 pending_changes: Vec<Change>,
23}
24
25impl Controller {
26 pub fn new(
32 control: &fnet_filter::ControlSynchronousProxy,
33 ControllerId(id): &ControllerId,
34 deadline: zx::MonotonicInstant,
35 ) -> Result<Self, ControllerCreationError> {
36 let (controller, server_end) = fidl::endpoints::create_sync_proxy();
37 control.open_controller(id, server_end).map_err(ControllerCreationError::OpenController)?;
38
39 let fnet_filter::NamespaceControllerEvent::OnIdAssigned { id } =
40 controller.wait_for_event(deadline).map_err(ControllerCreationError::IdAssignment)?;
41 Ok(Self { controller, id: ControllerId(id), pending_changes: Vec::new() })
42 }
43
44 pub fn id(&self) -> &ControllerId {
45 &self.id
46 }
47
48 pub fn push_changes(
49 &mut self,
50 changes: Vec<Change>,
51 deadline: zx::MonotonicInstant,
52 ) -> Result<(), PushChangesError> {
53 let fidl_changes = changes.iter().cloned().map(Into::into).collect::<Vec<_>>();
54 let result = self
55 .controller
56 .push_changes(&fidl_changes, deadline)
57 .map_err(PushChangesError::CallMethod)?;
58 handle_change_validation_result(result, &changes)?;
59 self.pending_changes.extend(changes);
63 Ok(())
64 }
65
66 pub fn commit_with_options(
67 &mut self,
68 options: fnet_filter::CommitOptions,
69 deadline: zx::MonotonicInstant,
70 ) -> Result<(), CommitError> {
71 let committed_changes = std::mem::take(&mut self.pending_changes);
72 let result = self.controller.commit(options, deadline).map_err(CommitError::CallMethod)?;
73 handle_commit_result(result, committed_changes)
74 }
75
76 pub fn commit(&mut self, deadline: zx::MonotonicInstant) -> Result<(), CommitError> {
77 self.commit_with_options(fnet_filter::CommitOptions::default(), deadline)
78 }
79
80 pub fn commit_idempotent(&mut self, deadline: zx::MonotonicInstant) -> Result<(), CommitError> {
81 self.commit_with_options(
82 fnet_filter::CommitOptions {
83 idempotent: Some(true),
84 __source_breaking: SourceBreaking,
85 },
86 deadline,
87 )
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::tests::{
95 handle_commit, handle_open_controller, handle_push_changes, invalid_resource,
96 test_resource, test_resource_id, unknown_resource_id,
97 };
98 use crate::{ChangeCommitError, ChangeValidationError};
99 use assert_matches::assert_matches;
100
101 #[fuchsia::test(threads = 2)]
102 async fn controller_push_changes_reports_invalid_change() {
103 let (control_sync, request_stream) =
104 fidl::endpoints::create_sync_proxy_and_stream::<fnet_filter::ControlMarker>();
105
106 let run_controller = fuchsia_async::Task::spawn(async {
107 let mut stream = handle_open_controller(request_stream).await;
108 handle_push_changes(
109 &mut stream,
110 fnet_filter::ChangeValidationResult::ErrorOnChange(vec![
111 fnet_filter::ChangeValidationError::Ok,
112 fnet_filter::ChangeValidationError::InvalidPortMatcher,
113 fnet_filter::ChangeValidationError::NotReached,
114 ]),
115 )
116 .await;
117 });
118
119 let mut controller = Controller::new(
120 &control_sync,
121 &ControllerId(String::from("test")),
122 zx::MonotonicInstant::INFINITE,
123 )
124 .expect("create controller");
125 let result = controller.push_changes(
126 vec![
127 Change::Create(test_resource()),
128 Change::Create(invalid_resource()),
129 Change::Remove(test_resource_id()),
130 ],
131 zx::MonotonicInstant::INFINITE,
132 );
133
134 assert_matches!(
135 result,
136 Err(PushChangesError::ErrorOnChange(errors)) if errors == vec![(
137 Change::Create(invalid_resource()),
138 ChangeValidationError::InvalidPortMatcher
139 )]
140 );
141
142 run_controller.await;
143 }
144
145 #[fuchsia::test(threads = 2)]
146 async fn controller_commit_reports_invalid_change() {
147 let (control_sync, request_stream) =
148 fidl::endpoints::create_sync_proxy_and_stream::<fnet_filter::ControlMarker>();
149
150 let run_controller = fuchsia_async::Task::spawn(async {
151 let mut stream = handle_open_controller(request_stream).await;
152 handle_push_changes(
153 &mut stream,
154 fnet_filter::ChangeValidationResult::Ok(fnet_filter::Empty {}),
155 )
156 .await;
157 handle_commit(
158 &mut stream,
159 fnet_filter::CommitResult::ErrorOnChange(vec![
160 fnet_filter::CommitError::Ok,
161 fnet_filter::CommitError::NamespaceNotFound,
162 fnet_filter::CommitError::Ok,
163 ]),
164 )
165 .await;
166 });
167
168 let mut controller = Controller::new(
169 &control_sync,
170 &ControllerId(String::from("test")),
171 zx::MonotonicInstant::INFINITE,
172 )
173 .expect("create controller");
174 controller
175 .push_changes(
176 vec![
177 Change::Create(test_resource()),
178 Change::Remove(unknown_resource_id()),
179 Change::Remove(test_resource_id()),
180 ],
181 zx::MonotonicInstant::INFINITE,
182 )
183 .expect("push changes");
184
185 let result = controller.commit(zx::MonotonicInstant::INFINITE);
186 assert_matches!(
187 result,
188 Err(CommitError::ErrorOnChange(errors)) if errors == vec![(
189 Change::Remove(unknown_resource_id()),
190 ChangeCommitError::NamespaceNotFound,
191 )]
192 );
193
194 run_controller.await;
195 }
196}