blob: d9faf666e72d76370ec12d34af56adc48e3a0b97 [file] [log] [blame] [view]
# Hub (Components v2)
<<../_v2_banner.md>>
## Definition
The hub provides access to detailed structural information about component
instances at runtime. The hub provides information such as:
- Instance name
- Instance state
- Instance ID
- Children
- Component URL
- Exposed capabilities
## Features
### Immutability
The hubs structure is mostly read-only. It is not possible to create, rename,
delete, or otherwise modify directories and files that form the structure of
the hub itself. However, the [outgoing](/docs/concepts/system/abi/system.md)
directory of an instance may include mutable directories, files, and services
which can be accessed through the hub.
### Scoping
The root of a hub directory is always scoped to a particular [realm](realms.md).
The hub of a realm gives information about:
- The realm itself
- All child realms
- All component instances
The hub of a realm cannot give information about the parent realm. This
structure makes it easy to constrain the parts of the hub particular clients can
access.
If a [realm](realms.md) requests access to its hub, it can also access the hub
of all of its descendant [realms](realms.md), by traversing down the `children/`
subdirectory.
## Requesting the hub
Like other capabilities, hub access is requested with a `use` declaration in the
[component manifest](component_manifests.md). This example shows the manifest
file of a component that accesses its own hub.
```
{
"program": {
"binary": "bin/program"
},
"use": [
{ "runner": "elf" },
{
"directory": "hub",
"from": "framework",
},
]
}
```
Since the hub is a [directory capability](capabilities/directory.md), with the
right routing it can be exposed upwards, offered downwards or even shared
between sibling components. See the [advanced routing](#advanced-routing)
section for more details.
Once the capability is added to the manifest, a component can access the hub
from `/hub`.
## Structure of the hub
### `/hub`
The directory structure of `/hub` looks like:
```
/hub
├── children
├── deleting
├── exec
├── component_type
├── id
└── url
```
A hub contains all the information pertaining to a component instance and its
realm.
Directories:
- [`/hub/children`](#hub-children)
- [`/hub/deleting`](#hub-deleting)
- [`/hub/exec`](#hub-exec)
Files:
- `/hub/component_type`: Contains the string `static` or `dynamic`. A
component is static when its definition exists in the parents component
manifest at compile time. A component is dynamic when the parent defines the
component at runtime.
- `/hub/id`: Contains a non-negative integer corresponding to the version of a
particular instance. A new instance with the same name is a new version and
will be given a new ID.
- `/hub/url`: Contains the URL string corresponding to this component. The URL
is defined by the parent either in its component manifest file or at
runtime.
### `/hub/children` {#hub-children}
The directory structure of `/hub/children` looks like:
```
/hub/children
├── foo
| ├── children
| ├── deleting
| ├── exec
| ├── component_type
| ├── id
| └── url
├── bar
| ├── children
| ├── deleting
| ├── exec
| ├── component_type
| ├── id
| └── url
└── baz:qux
├── children
├── deleting
├── component_type
├── id
└── url
```
The `/hub/children` directory contains the hubs of all created child instances,
whether they be static, dynamic, running or stopped.
Note: A child does not have access to the hub of its parent unless its parent
offers the hub capability to it. For more information, see
[advanced routing](#advanced-routing).
The hub gives a name to each childs hub directory based on this format:
- For static instances, the format is `<instance name>`
- For dynamic instances, the format is `<collection name>:<instance name>`
### `/hub/deleting` {#hub-deleting}
The directory structure of `/hub/deleting` looks like:
```
/hub/deleting
├── baz:qux:1
| ├── children
| ├── deleting
| ├── component_type
| ├── id
| └── url
└── baz:qux:2
├── children
├── deleting
├── exec
├── component_type
├── id
└── url
```
Since deletion is not an atomic process, the deleting directory contains
information about children that are in the process of being deleted. When a
childs deletion is complete, it is removed from this directory and ceases to
exist in its parents hub. The full deletion process is explained later in this
document.
The hub gives a name to each childs hub directory based on this format:
- For static instances, the format is `<instance name>:<instance id>`
- For dynamic instances, the format is `<collection name>:<instance
name>:<instance id>`
Unlike the `/hub/children` directory, the instance ID is a part of the format
for the `/hub/deleting` directory because multiple versions of the same instance
may be in the process of deletion and the instance ID is used to distinguish
between them.
### `/hub/exec` {#hub-exec}
The directory structure of `/hub/exec` looks like:
```
/hub/exec
├── exposed
├── in
├── out
├── runtime
├── used
└── resolved_url
```
The `/hub/exec` directory is only visible when the instance is started since it
contains information about the current execution of the instance.
Directories:
- [`/hub/exec/exposed`](#hub-exec-exposed)
- [`/hub/exec/in`](#hub-exec-in)
- [`/hub/exec/out`](#hub-exec-out)
- [`/hub/exec/runtime`](#hub-exec-runtime)
- [`/hub/exec/used`](#hub-exec-used)
Files:
- `/hub/exec/resolved_url`: The component's resolved URL in text format.
### `/hub/exec/exposed` {#hub-exec-exposed}
The instance's exposed services as listed in its manifest file. A component can
connect directly to these services from the hub by opening the provided path.
### `/hub/exec/in` {#hub-exec-in}
The instance's incoming namespace, as supplied by the component manager. This
contains a listing of services and directories accessible to the given component
instance. A component can open the provided path to connect directly to these
services from the Hub.
### `/hub/exec/out/` {#hub-exec-out}
The instance's outgoing namespace, served by the instance itself. A component
can connect directly to these services from the hub by opening the provided
path.
### `/hub/exec/runtime` {#hub-exec-runtime}
The directory structure of `/hub/exec/runtime` looks like:
```
/hub/exec/runtime
└── elf
├── args
| ├── 0
| └── 1
├── process-id
└── job-id
```
If a component has an [ELF runtime](elf_runner.md), then it will have a
`/hub/exec/runtime/elf` directory.
Directories:
- `args`: command-line arguments, presented as a series of files from `0`
onward.
Files:
- `process-id`: the instance's process id in text format.
- `job-id`: the instance's job id in text format.
### `/hub/exec/used` {#hub-exec-used}
Lists services used at least once by the instance. A service is considered
"used" when it is requested by the instance and has been routed successfully by
the component manager.
## Advanced Routing {#advanced-routing}
### Offering parent hub
In this example, the parent component instance passes its view of the hub to
`hub_client`, which then maps it as `/parent_hub` in its namespace. `hub_client`
can inspect information about its parent and siblings through `/parent_hub`.
In the parent component manifest:
```
{
"offer": [
// Route the root hub to hub_client.
{
"directory": "/hub",
"from": "framework",
"to": "#hub_client",
},
// Route the ELF runner capability to hub_client.
{
"runner": "elf",
"from": "parent",
"to": "#hub_client",
},
],
"children": [
{
"name": "hub_client",
"url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client.cm",
"startup": "eager",
},
],
```
In `hub_client.cml`:
```
{
"program": {
"binary": "bin/hub_client",
},
"use": [
{ "runner": "elf" },
{
"directory": "/hub",
"from": "parent",
"as": "/parent_hub"
}
]
}
```
### Exposing a sibling hub
In this example, `hub_client_sibling` exposes its view of the hub to its parent.
The realm, in turn, offers that view of the hub as `/sibling_hub` to
`hub_client`. `hub_client` maps that view of the hub to its incoming namespace.
In `hub_client_sibling.cml`:
```
{
"program": {
"binary": "bin/hub_client_sibling",
},
"use": [
{ "runner": "elf" },
],
"expose": [
{
"directory": "/hub",
"from": "framework",
},
],
}
```
In the parent component manifest file:
```
{
// Route hub_client_sibling's view of the hub to hub_client.
"offer": [
{
"directory": "/hub",
"from": "#hub_client_sibling",
"to": "#hub_client",
"as": "/sibling_hub",
}
],
"children": [
{
"name": "hub_client_sibling",
"url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client_sibling.cm",
"startup": "eager",
},
{
"name": "hub_client",
"url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client.cm",
"startup": "eager",
},
],
}
```
In hub_client.cml:
```
{
"program": {
"binary": "bin/hub_client",
},
"use": [
{ "runner": "elf" },
{
"directory": "/sibling_hub", "from": "parent",
}
]
}
```
## Lifecycle of an instance
<!--
TODO: Avoid using `"startup": "eager"` in component manifests.
-->
To illustrate how the hub changes in response to lifecycle events, consider an
example involving 3 components named `A`, `B` and `C`. The manifest of each
component is given below:
- `A.cmx`
```
{
"program": {
"binary": "bin/A"
},
"use": [
{
"directory": "/hub",
"from": "framework",
},
]
"collections": [
{
"name": "coll",
"durability": "transient",
},
]
}
```
- `B.cmx`
```
{
"program": {
"binary": "bin/B"
},
"children": [
{
"name": "baz",
"url": "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
"startup": "eager",
}
]
}
```
- `C.cmx`
```
// C.cmx
{
"program": {
"binary": "bin/C"
}
}
```
Also consider 3 instances, one for each component:
- `A` -> `foo`
- `B` -> `bar`
- `C` -> `baz`
`foo` can perform various actions that modify the hub:
- [View the hub](#view-hub)
- [Connect to the Realm framework service](#connect-to-realm)
- [Create a dynamic child](#create-dynamic-child)
- [Start a dynamic child](#start-dynamic-child)
- [Destroy a dynamic child](#destroy-dynamic-child)
### View the hub {#view-hub}
`foo` can see its own hub by listing the contents of `/hub`. In Rust, this looks
like:
```
let entries = std::fs::read_dir("/hub")?;
for entry in entries {
println!("{}", entry?.path());
}
```
Without making any changes to the component hierarchy, the hub will look like
the following:
```
/hub
├── children
├── deleting
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
### Connect to the Realm framework service {#connect-to-realm}
A component must connect to the
[Realm framework service](realms.md#realm-framework-service) to manipulate its
dynamic children. In Rust, this looks like:
```
let realm = connect_to_service::<fsys::RealmMarker>()?;
```
### Create a dynamic child {#create-dynamic-child}
`foo` can define an instance of `B` called `bar` under the
[collection](realms.md#collections) `coll`. In Rust, this looks like:
```
// Define the child and the collection
let mut collection_ref = fsys::CollectionRef { name: String::from("coll") };
let child_decl = fsys::ChildDecl {
name: Some(String::from("bar")),
url: Some(String::from("fuchsia-pkg://fuchsia.com/example#meta/B.cm")),
startup: Some(fsys::StartupMode::Lazy),
};
// Create the child
realm.create_child(&mut collection_ref, child_decl).await??;
```
After executing this code, the hub changes to the following:
```
/hub
├── children
| └── coll:bar
| ├── children
| ├── deleting
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── deleting
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
Note: The instance `baz` (which is a child of `bar`) does not appear in the hub
yet.
Note: The instance `bar` has not been started, so it does not have an `exec`
directory.
### Start the dynamic child {#start-dynamic-child}
`foo` can now bind to the instance `bar` and start it. In Rust, this looks like
the following:
```
// Create a reference to the dynamic child
let mut child_ref = fsys::ChildRef {
name: "bar".to_string(),
collection: Some("coll".to_string()),
};
// Create a proxy for the exposed directory
let (dir_proxy, server_end) = create_proxy::<DirectoryMarker>()?;
// Bind to the child
realm.bind_child(&mut child_ref, server_end).await??;
```
After executing this code, the hub changes to the following:
```
/hub
├── children
| └── coll:bar
| ├── children
| | └── baz
| | ├── children
| | ├── deleting
| | ├── exec
| | | ├── exposed
| | | ├── in
| | | ├── out
| | | ├── runtime
| | | ├── used
| | | └── resolved_url
| | ├── component_type => "static"
| | ├── id => "0"
| | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
| ├── deleting
| ├── exec
| | ├── exposed
| | ├── in
| | ├── out
| | ├── runtime
| | ├── used
| | └── resolved_url
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── deleting
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
Note: The instance `baz` is automatically created and started as a static child
of `bar`.
Note: The instances `bar` and `baz` now have `exec` directories since they both
have been started.
### Destroy the dynamic child {#destroy-dynamic-child}
`foo` can destroy the instance `bar`. In Rust, this looks like the following:
```
// Create a reference to the dynamic child
let mut child_ref = fsys::ChildRef {
name: "bar".to_string(),
collection: Some("coll".to_string()),
};
// Destroy the child
realm.destroy_child(&mut child_ref).await??;
```
The above code begins the deletion process for `bar`. This process has several
stages, most of which occur asynchronously. As a result, the hubs structure
changes several times.
1. `bar` is marked for deletion. The hub changes to the following:
```
/hub
├── children
├── deleting
| └── coll:bar:1
| ├── children
| | └── baz
| | ├── children
| | ├── deleting
| | ├── exec
| | | ├── exposed
| | | ├── in
| | | ├── out
| | | ├── runtime
| | | ├── used
| | | └── resolved_url
| | ├── component_type => "static"
| | ├── id => "0"
| | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
| ├── deleting
| ├── exec
| | ├── exposed
| | ├── in
| | ├── out
| | ├── runtime
| | ├── used
| | └── resolved_url
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
1. `baz` is stopped. The hub changes to the following:
```
/hub
├── children
├── deleting
| └── coll:bar:1
| ├── children
| | └── baz
| | ├── children
| | ├── deleting
| | ├── component_type => "static"
| | ├── id => "0"
| | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
| ├── deleting
| ├── exec
| | ├── exposed
| | ├── in
| | ├── out
| | ├── runtime
| | ├── used
| | └── resolved_url
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
1. `bar` is stopped. The hub changes to the following:
```
/hub
├── children
├── deleting
| └── coll:bar
| ├── children
| | └── baz
| | ├── children
| | ├── deleting
| | ├── component_type => "static"
| | ├── id => "0"
| | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
| ├── deleting
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
1. `baz` is marked for deletion. The hub changes to the following:
```
/hub
├── children
├── deleting
| └── coll:bar:1
| ├── children
| ├── deleting
| | └── baz:0
| | ├── children
| | ├── deleting
| | ├── component_type => "static"
| | ├── id => "0"
| | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm"
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
1. `baz` is deleted. The hub changes to the following:
```
/hub
├── children
├── deleting
| └── coll:bar:1
| ├── children
| ├── deleting
| ├── component_type => "dynamic"
| ├── id => "1"
| └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm"
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```
1. `bar` is deleted. The hub changes to the following:
```
/hub
├── children
├── deleting
├── exec
| ├── exposed
| ├── in
| ├── out
| ├── runtime
| ├── used
| └── resolved_url
├── component_type => "static"
├── id => "0"
└── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
```