Skip to main content

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 fidl_fuchsia_component as fcomponent;
7use fidl_fuchsia_component_decl as fdecl;
8use fidl_fuchsia_net_interfaces as fnet_interfaces;
9use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
10use std::collections::HashMap;
11
12/// Name of the collection that contains the hermetic network realm.
13pub const HERMETIC_NETWORK_COLLECTION_NAME: &'static str = "enclosed-network";
14
15/// Name of the realm that contains the hermetic network components.
16pub const HERMETIC_NETWORK_REALM_NAME: &'static str = "hermetic-network";
17
18/// Name of the collection that contains the test stub.
19pub const STUB_COLLECTION_NAME: &'static str = "stubs";
20
21/// Name of the component that corresponds to the test stub.
22pub const STUB_COMPONENT_NAME: &'static str = "test-stub";
23
24/// Returns true if the hermetic network realm exists.
25///
26/// The provided `realm_proxy` should correspond to the Network Test Realm
27/// controller component.
28///
29/// # Errors
30///
31/// An error will be returned if the `realm_proxy` encounters an error while
32/// attempting to list the children of the Network Test Realm.
33pub async fn has_hermetic_network_realm(realm_proxy: &fcomponent::RealmProxy) -> Result<bool> {
34    let child_ref = create_hermetic_network_realm_child_ref();
35    has_running_child(
36        fdecl::CollectionRef { name: HERMETIC_NETWORK_COLLECTION_NAME.to_string() },
37        &child_ref,
38        realm_proxy,
39    )
40    .await
41}
42
43/// Returns true if the hermetic-network realm contains a stub.
44///
45/// The provided `realm_proxy` should correspond to the hermetic-network realm.
46///
47/// # Errors
48///
49/// An error will be returned if the `realm_proxy` encounters an error while
50/// attempting to list the children of the hermetic-network realm.
51pub async fn has_stub(realm_proxy: &fcomponent::RealmProxy) -> Result<bool> {
52    let child_ref = create_stub_child_ref();
53    has_running_child(
54        fdecl::CollectionRef { name: STUB_COLLECTION_NAME.to_string() },
55        &child_ref,
56        realm_proxy,
57    )
58    .await
59}
60
61/// Returns true if the `realm_proxy` contains the `expected_child_ref` within
62/// the provided `collection_ref`.
63async fn has_running_child(
64    collection_ref: fdecl::CollectionRef,
65    expected_child_ref: &fdecl::ChildRef,
66    realm_proxy: &fcomponent::RealmProxy,
67) -> Result<bool> {
68    let (iterator_proxy, server_end) = fidl::endpoints::create_proxy();
69    let list_children_result = realm_proxy
70        .list_children(&collection_ref, server_end)
71        .await
72        .context("failed to list_children")?;
73
74    match list_children_result {
75        Ok(()) => {
76            let children =
77                iterator_proxy.next().await.context("failed to iterate over children")?;
78
79            Ok(children.iter().any(|child| child == expected_child_ref))
80        }
81        Err(error) => match error {
82            // Variants that may be returned by the `ListChildren` method.
83            // `CollectionNotFound` means that the hermetic network realm does
84            // not exist. All other errors are propagated.
85            fcomponent::Error::CollectionNotFound => Ok(false),
86            fcomponent::Error::AccessDenied
87            | fcomponent::Error::InstanceDied
88            | fcomponent::Error::InvalidArguments
89            // Variants that are not returned by the `ListChildren` method.
90            | fcomponent::Error::InstanceAlreadyExists
91            | fcomponent::Error::InstanceAlreadyStarted
92            | fcomponent::Error::InstanceCannotResolve
93            | fcomponent::Error::InstanceCannotUnresolve
94            | fcomponent::Error::InstanceCannotStart
95            | fcomponent::Error::InstanceNotFound
96            | fcomponent::Error::Internal
97            | fcomponent::Error::ResourceNotFound
98            | fcomponent::Error::ResourceUnavailable
99            | fcomponent::Error::Unsupported
100            | fcomponent::Error::DependencyCycle
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            Default::default(),
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         }| { if name == interface_name { Some(id.get()) } else { None } },
149    ))
150}