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::{Context as _, Result, anyhow};
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::Error::DependencyCycle
102            | fcomponent::ErrorUnknown!()
103                => {
104                Err(anyhow!("failed to list children: {:?}", error))
105                }
106        },
107    }
108}
109
110/// Returns a `fdecl::ChildRef` that corresponds to the hermetic network realm.
111pub fn create_hermetic_network_realm_child_ref() -> fdecl::ChildRef {
112    fdecl::ChildRef {
113        name: HERMETIC_NETWORK_REALM_NAME.to_string(),
114        collection: Some(HERMETIC_NETWORK_COLLECTION_NAME.to_string()),
115    }
116}
117
118/// Returns a `fdecl::ChildRef` that corresponds to the test stub.
119pub fn create_stub_child_ref() -> fdecl::ChildRef {
120    fdecl::ChildRef {
121        name: STUB_COMPONENT_NAME.to_string(),
122        collection: Some(STUB_COLLECTION_NAME.to_string()),
123    }
124}
125
126/// Returns the id for the interface with `interface_name`.
127///
128/// If the interface is not found then, None is returned.
129pub async fn get_interface_id<'a>(
130    interface_name: &'a str,
131    state_proxy: &'a fnet_interfaces::StateProxy,
132) -> Result<Option<u64>> {
133    let stream =
134        fnet_interfaces_ext::event_stream_from_state::<fnet_interfaces_ext::DefaultInterest>(
135            &state_proxy,
136            fnet_interfaces_ext::IncludedAddresses::OnlyAssigned,
137        )
138        .context("failed to get interface stream")?;
139    let interfaces = fnet_interfaces_ext::existing(
140        stream,
141        HashMap::<u64, fidl_fuchsia_net_interfaces_ext::PropertiesAndState<(), _>>::new(),
142    )
143    .await
144    .context("failed to get existing interfaces")?;
145    Ok(interfaces.values().find_map(
146        |fidl_fuchsia_net_interfaces_ext::PropertiesAndState {
147             properties: fidl_fuchsia_net_interfaces_ext::Properties { id, name, .. },
148             state: _,
149         }| { if name == interface_name { Some(id.get()) } else { None } },
150    ))
151}