fidl_fuchsia_net_filter_ext/
sync.rs1use crate::{
6 Change, CommitError, ControllerCreationError, ControllerId, PushChangesError,
7 handle_change_validation_result, handle_commit_result,
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, pretend_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(pretend_invalid_resource()),
131 Change::Remove(test_resource_id()),
132 ],
133 zx::MonotonicInstant::INFINITE,
134 );
135
136 assert_matches!(
137 result,
138 Err(PushChangesError::ErrorOnChange(errors)) if errors == vec![(
139 Change::Create(pretend_invalid_resource()),
140 ChangeValidationError::InvalidPortMatcher
141 )]
142 );
143
144 run_controller.await;
145 }
146
147 #[fuchsia::test(threads = 2)]
148 async fn controller_commit_reports_invalid_change() {
149 let (control_sync, request_stream) =
150 fidl::endpoints::create_sync_proxy_and_stream::<fnet_filter::ControlMarker>();
151
152 let run_controller = fuchsia_async::Task::spawn(async {
153 let mut stream = handle_open_controller(request_stream).await;
154 handle_push_changes(
155 &mut stream,
156 fnet_filter::ChangeValidationResult::Ok(fnet_filter::Empty {}),
157 )
158 .await;
159 handle_commit(
160 &mut stream,
161 fnet_filter::CommitResult::ErrorOnChange(vec![
162 fnet_filter::CommitError::Ok,
163 fnet_filter::CommitError::NamespaceNotFound,
164 fnet_filter::CommitError::Ok,
165 ]),
166 )
167 .await;
168 });
169
170 let mut controller = Controller::new(
171 &control_sync,
172 &ControllerId(String::from("test")),
173 zx::MonotonicInstant::INFINITE,
174 )
175 .expect("create controller");
176 controller
177 .push_changes(
178 vec![
179 Change::Create(test_resource()),
180 Change::Remove(unknown_resource_id()),
181 Change::Remove(test_resource_id()),
182 ],
183 zx::MonotonicInstant::INFINITE,
184 )
185 .expect("push changes");
186
187 let result = controller.commit(zx::MonotonicInstant::INFINITE);
188 assert_matches!(
189 result,
190 Err(CommitError::ErrorOnChange(errors)) if errors == vec![(
191 Change::Remove(unknown_resource_id()),
192 ChangeCommitError::NamespaceNotFound,
193 )]
194 );
195
196 run_controller.await;
197 }
198}