Skip to content

Configuration (v0.4.0)

J-Loudet edited this page Sep 9, 2022 · 2 revisions

Zenoh-Flow allows passing values from the different descriptor files to the node. These values are defined in the configuration part of the descriptor, each value being associated to a key. The main advantage of the configuration is that it allows modifying the behavior of a node without having to modify the code.

When coupled with the Composition (TBD Wiki + Link), it is possible to write different configurations at different levels that target the same keys.

This page aims at explaining (i) how to write a Configuration and (ii) the hierarchy Zenoh-Flow enforces when several configurations target the same keys.

Declaration

In YAML terms, a configuration section is an "object" and must follow this format:

configuration:
  key-1: value
  key-2: value
  key-3: value

The values accepted are listed here. In short, a value can be:

  • null,
  • a boolean,
  • a number,
  • a sequence (e.g. array),
  • a nested object,
  • an enum variant (through a tag).

Note that any and all configuration sections are optional.

A configuration section is accepted at the following places:

  • (global outer) in a data flow descriptor in a dedicated section,
  • (global inner) in a data flow descriptor under each node listing,
  • (composite outer) in a composite operator descriptor in a dedicated section,
  • (composite inner) in a composite operator descriptor under each operator listing,
  • (leaf) in a node declaration in a dedicated section.

Between parenthesis is the name we will be using in the rest of this page. The difference between outer and inner (regardless of the descriptor file) is the scope to which they apply: the outer configuration will apply to all nodes in the descriptor file, the inner configuration will apply only to the node it appears in.

Let us illustrate each section.

Data flow descriptor (global outer + inner)

flow: test

# "global outer" configuration section
configuration:
  foo: 1
  bar: "Hello World!"

sources:
  - id: source-1
    uri: file:///home/zenoh-flow/sources/source-1.so
    # "global inner" configuration section
    configuration:
      foo: 3.14

operators:
  - id: operator-1
    uri: file:///home/zenoh-flow/operators/operator-1.so
    # "global inner" configuration section
    configuration:
      bar: 3.14

sinks:
  - id: sink-1
    uri: file:///home/zenoh-flow/sinks/sink-1.so
    # the "global inner" configuration is optional.

links:
  # omitted for clarity

Composite operator descriptor (composite outer + inner)

id: operator-composite

# "composite outer" configuration section
configuration:
  foo: 2
  bar: "Hello Composite!"


inputs:
  - id: operator-composite-in
    node: sub-operator-1
    input: sub-operator-1-in

outputs:
  - id: operator-composite-out
    node: sub-operator-2
    output: sub-operator-2-out


operators:
  - id: sub-operator-1
    descriptor: ./src/model/dataflow/tests/sub-operator-1.yml
    # "composite inner" configuration section
    configuration:
      foo: null

  - id: sub-operator-2
    descriptor: ./src/model/dataflow/tests/sub-operator-2.yml


links:
  # omitted for clarity

Node descriptor (leaf)

id: source-1

# "leaf" configuration section
configuration:
  foo: "Hello Source!"

uri: file://source-1.so

outputs:
  - id: source-1-out
    type: _any_

tags: []

As one can see, the same keys are used in the different configuration sections. Then, what is the value that the node will eventually see when the data flow is instantiated?

Hierarchy

The hierarchy enforced by Zenoh-Flow is as follows:

global inner > global outer > composite inner > composite outer > leaf

There are two main "aspects" in this hierarchy:

  1. global > composite > leaf,
  2. inner > outer.

The rationale behind 1. is that nodes (the leaves) can be developed separately from the data flows they will be running in. What this means is that, potentially, the person creating the data flow will not have access to the descriptor of the node and thus will not be able to modify its configuration. Hence, the leaf must be at the bottom of the hierarchy. Similar reasoning leads to having the composite above the leaf and below the global.

The rationale behind 2. is the ability to apply a configuration to all nodes (through the "outer") and to specialize it to a restricted few (through the "inner").

Example

Let us illustrate this with an example:

flow: test

configuration:
  foo: "global-outer"
  bar: "global-outer"

operators:
  - id: operator-composite
    uri: file://./operator-composite.yml
    configuration:
      bar: "global-inner"

# the rest is omitted for clarity
id: operator-composite

configuration:
  foo: "composite-outer"
  bar: "composite-outer"
  baz: "composite-outer"

operators:
  - id: sub-operator
    descriptor: ./sub-operator.yml
    configuration:
      buzz: "composite-inner"

# the rest is omitted for clarity
id: sub-operator

# "leaf" configuration section
configuration:
  foo: "leaf"
  bar: "leaf"
  baz: "leaf"
  buzz: "leaf"
  quux: "leaf"

# the rest is omitted for clarity

The configuration that the sub-operator will see is:

foo: "global-outer"
bar: "global-inner"
baz: "composite-outer"
buzz: "composite-inner"
quux: "leaf"

Usage (TBD)

Rust

Python