network_test_realm/
lib.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{anyhow, Context as _, Result};
6use std::collections::HashMap;
7use {
8    fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
9    fidl_fuchsia_net_interfaces as fnet_interfaces,
10    fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext,
11};
12
13/// Name of the collection that contains the hermetic network realm.
14pub const HERMETIC_NETWORK_COLLECTION_NAME: &'static str = "enclosed-network";
15
16/// Name of the realm that contains the hermetic network components.
17pub const HERMETIC_NETWORK_REALM_NAME: &'static str = "hermetic-network";
18
19/// Name of the collection that contains the test stub.
20pub const STUB_COLLECTION_NAME: &'static str = "stubs";
21
22/// Name of the component that corresponds to the test stub.
23pub const STUB_COMPONENT_NAME: &'static str = "test-stub";
24
25/// Returns true if the hermetic network realm exists.
26///
27/// The provided `realm_proxy` should correspond to the Network Test Realm
28/// controller component.
29///
30/// # Errors
31///
32/// An error will be returned if the `realm_proxy` encounters an error while
33/// attempting to list the children of the Network Test Realm.
34pub async fn has_hermetic_network_realm(realm_proxy: &fcomponent::RealmProxy) -> Result<bool> {
35    let child_ref = create_hermetic_network_realm_child_ref();
36    has_running_child(
37        fdecl::CollectionRef { name: HERMETIC_NETWORK_COLLECTION_NAME.to_string() },
38        &child_ref,
39        realm_proxy,
40    )
41    .await
42}
43
44/// Returns true if the hermetic-network realm contains a stub.
45///
46/// The provided `realm_proxy` should correspond to the hermetic-network realm.
47///
48/// # Errors
49///
50/// An error will be returned if the `realm_proxy` encounters an error while
51/// attempting to list the children of the hermetic-network realm.
52pub async fn has_stub(realm_proxy: &fcomponent::RealmProxy) -> Result<bool> {
53    let child_ref = create_stub_child_ref();
54    has_running_child(
55        fdecl::CollectionRef { name: STUB_COLLECTION_NAME.to_string() },
56        &child_ref,
57        realm_proxy,
58    )
59    .await
60}
61
62/// Returns true if the `realm_proxy` contains the `expected_child_ref` within
63/// the provided `collection_ref`.
64async fn has_running_child(
65    collection_ref: fdecl::CollectionRef,
66    expected_child_ref: &fdecl::ChildRef,
67    realm_proxy: &fcomponent::RealmProxy,
68) -> Result<bool> {
69    let (iterator_proxy, server_end) = fidl::endpoints::create_proxy();
70    let list_children_result = realm_proxy
71        .list_children(&collection_ref, server_end)
72        .await
73        .context("failed to list_children")?;
74
75    match list_children_result {
76        Ok(()) => {
77            let children =
78                iterator_proxy.next().await.context("failed to iterate over children")?;
79
80            Ok(children.iter().any(|child| child == expected_child_ref))
81        }
82        Err(error) => match error {
83            // Variants that may be returned by the `ListChildren` method.
84            // `CollectionNotFound` means that the hermetic network realm does
85            // not exist. All other errors are propagated.
86            fcomponent::Error::CollectionNotFound => Ok(false),
87            fcomponent::Error::AccessDenied
88            | fcomponent::Error::InstanceDied
89            | fcomponent::Error::InvalidArguments
90            // Variants that are not returned by the `ListChildren` method.
91            | fcomponent::Error::InstanceAlreadyExists
92            | fcomponent::Error::InstanceAlreadyStarted
93            | fcomponent::Error::InstanceCannotResolve
94            | fcomponent::Error::InstanceCannotUnresolve
95            | fcomponent::Error::InstanceCannotStart
96            | fcomponent::Error::InstanceNotFound
97            | fcomponent::Error::Internal
98            | fcomponent::Error::ResourceNotFound
99            | fcomponent::Error::ResourceUnavailable
100            | fcomponent::Error::Unsupported
101            | fcomponent::ErrorUnknown!()
102                => {
103                Err(anyhow!("failed to list children: {:?}", error))
104            }
105        },
106    }
107}
108
109/// Returns a `fdecl::ChildRef` that corresponds to the hermetic network realm.
110pub fn create_hermetic_network_realm_child_ref() -> fdecl::ChildRef {
111    fdecl::ChildRef {
112        name: HERMETIC_NETWORK_REALM_NAME.to_string(),
113        collection: Some(HERMETIC_NETWORK_COLLECTION_NAME.to_string()),
114    }
115}
116
117/// Returns a `fdecl::ChildRef` that corresponds to the test stub.
118pub fn create_stub_child_ref() -> fdecl::ChildRef {
119    fdecl::ChildRef {
120        name: STUB_COMPONENT_NAME.to_string(),
121        collection: Some(STUB_COLLECTION_NAME.to_string()),
122    }
123}
124
125/// Returns the id for the interface with `interface_name`.
126///
127/// If the interface is not found then, None is returned.
128pub async fn get_interface_id<'a>(
129    interface_name: &'a str,
130    state_proxy: &'a fnet_interfaces::StateProxy,
131) -> Result<Option<u64>> {
132    let stream =
133        fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
134            &state_proxy,
135            fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
136        )
137        .context("failed to get interface stream")?;
138    let interfaces = fnet_interfaces_ext::existing(
139        stream,
140        HashMap::<u64, fidl_fuchsia_net_interfaces_ext::PropertiesAndState<(), _>>::new(),
141    )
142    .await
143    .context("failed to get existing interfaces")?;
144    Ok(interfaces.values().find_map(
145        |fidl_fuchsia_net_interfaces_ext::PropertiesAndState {
146             properties: fidl_fuchsia_net_interfaces_ext::Properties { id, name, .. },
147             state: _,
148         }| {
149            if name == interface_name {
150                Some(id.get())
151            } else {
152                None
153            }
154        },
155    ))
156}