Skip to content

feat: inventory.json support for child devices#3755

Merged
albinsuresh merged 2 commits intothin-edge:mainfrom
albinsuresh:feat/3430/inventory-json-support-for-child-devices
Aug 21, 2025
Merged

feat: inventory.json support for child devices#3755
albinsuresh merged 2 commits intothin-edge:mainfrom
albinsuresh:feat/3430/inventory-json-support-for-child-devices

Conversation

@albinsuresh
Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh commented Aug 6, 2025

Proposed changes

Enable users to specify initial inventory data for child devices running tedge-agent using an inventory.json file.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
  • Documentation Update (if none of the other choices apply)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Paste Link to the issue

#3430

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA (in all commits with git commit -s. You can activate automatic signing by running just prepare-dev once)
  • I ran just format as mentioned in CODING_GUIDELINES
  • I used just check as mentioned in CODING_GUIDELINES
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

@codecov
Copy link
Copy Markdown

codecov bot commented Aug 6, 2025

Codecov Report

❌ Patch coverage is 78.18182% with 48 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/core/tedge_agent/src/twin_manager/actor.rs 57.44% 35 Missing and 5 partials ⚠️
...rates/core/tedge_agent/src/twin_manager/builder.rs 94.23% 3 Missing ⚠️
crates/core/tedge_api/src/mqtt_topics.rs 0.00% 3 Missing ⚠️
crates/core/tedge_agent/src/twin_manager/tests.rs 98.48% 0 Missing and 1 partial ⚠️
crates/extensions/c8y_mapper_ext/src/converter.rs 0.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Aug 6, 2025

Robot Results

✅ Passed ❌ Failed ⏭️ Skipped Total Pass % ⏱️ Duration
681 0 3 681 100 2h5m30.145423s

@albinsuresh albinsuresh changed the title Feat/3430/inventory json support for child devices feat: inventory.json support for child devices Aug 7, 2025
@albinsuresh albinsuresh temporarily deployed to Test Pull Request August 13, 2025 09:37 — with GitHub Actions Inactive
@albinsuresh albinsuresh temporarily deployed to Test Pull Request August 13, 2025 13:24 — with GitHub Actions Inactive
@albinsuresh albinsuresh marked this pull request as ready for review August 13, 2025 14:08
@Bravo555 Bravo555 self-assigned this Aug 14, 2025
@albinsuresh albinsuresh force-pushed the feat/3430/inventory-json-support-for-child-devices branch from 17ae364 to e59cf43 Compare August 14, 2025 09:34
@Bravo555 Bravo555 self-requested a review August 14, 2025 12:00
Copy link
Copy Markdown
Member

@Bravo555 Bravo555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One error message adjustment request and docs update, apart from that, looks good and will approve after these changes.

Comment on lines +95 to +106
format!("Failed to open inventory file: {}", err).into(),
));
}
};
let inventory_json: Value = serde_json::from_reader(file).map_err(|err| {
RuntimeError::ActorError(
format!("Failed to parse inventory file contents as JSON: {}", err).into(),
)
})?;
let Value::Object(twin_map) = inventory_json else {
return Err(RuntimeError::ActorError(
"Invalid inventory.json format: expected a JSON object".into(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: attach the filename to the error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by 8754d74

let device_id = self.config.device_topic_id.clone();
for (key, value) in inventory_map {
self.publish_twin_data(&device_id, key.clone(), value.clone())
.await;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: don't have to clone here

Suggested change
.await;
self.publish_twin_data(&device_id, key, value).await;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by 8754d74

Comment on lines +33 to +69
async fn run(mut self) -> Result<(), RuntimeError> {
let mut inventory_map = self.load_inventory_json()?;
// Wait until the very fist message is received (at least the agent health status is guaranteed)
if let Some(mut msg) = self.messages.recv().await {
loop {
if let Ok((_, Channel::EntityTwinData { fragment_key })) = self
.config
.mqtt_schema
.entity_channel_of(msg.topic.as_ref())
{
// If a twin data message for the same key is available,
// ignore the value in inventory JSON
inventory_map.remove(&fragment_key);
}

msg = match timeout(Duration::from_secs(1), self.messages.recv()).await {
Ok(Some(next)) => next,
_ => break,
};
}
}

let device_id = self.config.device_topic_id.clone();
for (key, value) in inventory_map {
self.publish_twin_data(&device_id, key.clone(), value.clone())
.await;
}

// This is to prevent the MQTT actor from crashing while trying to send a twin message to this actor later
// after this actor has finished and closed its message box, but the MQTT actor still holds the sender half of it.
while self.messages.recv().await.is_some() {
// Do nothing
}
Ok(())
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: So as I understand, basically what we do here is:

  1. Read the fragments from the inventory file
  2. Wait until the end of the "init phase", during which we remove fragments that are already published (our heuristic for "end of the init phase" being messages not being published for 1s)
  3. Publish all the remaining fragments
  4. Effectively exit, but we're forced to loop because other actors may send us messages despite us not wanting to process them anymore.

So the actor stays alive, despite not doing anything, which sucks a bit, but as the comment says, it's hard to exit without the MQTT actor possibly receiving failures after we exit.

Funnily enough, this usage pattern here could be easily handled by an in-memory broker: we could subscribe for a bit, listen to some messages, and exit without other actors even being aware of us, because sources and sinks would be decoupled, but the actor runtime works more on a peer-to-peer basis, so that's why it's hard.

Even still, perhaps upstream producers being coupled to their downstream consumers such that the producer (actor A) can't output after the consumer (actor B) exits isn't a nice property. Perhaps the runtime should simply remove the consumer message box from producer's sender list and let the producer proceed, only if A doesn't depend on B in any way. But there's also a pattern where A sends message to B and then waits for B's response, in which case it does depend on it and should know ASAP if B exits (it should then probably also exit with an error).

So we have both request/response and publish/subscribe patterns, in this case publish/susbcribe is a bit awkward.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even still, perhaps upstream producers being coupled to their downstream consumers such that the producer (actor A) can't output after the consumer (actor B) exits isn't a nice property.

Completely agree. I am really tempted to change the MQTT actor logic to not fail on send when a channel is closed, but just log and proceed (potentially removing that sender from its peer_senders list as well). I just wasn't fully sure if some other part of the code is expecting on the existing behaviour to handle things like shutdown requests. That's why I went with this not-so-pretty workaround. But, I'm open to do that change in a separate PR so that we can discuss the impact.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to accept this workaround. As for changing only the MQTT actor (because indeed actors themselves have a choice to exit or keep going if send fails!), also approve making the modification in a separate PR, I'm curious what the test results would be.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 4612f52

@reubenmiller reubenmiller added the theme:telemetry Theme: Telemetry data label Aug 18, 2025
Copy link
Copy Markdown
Member

@rina23q rina23q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Contributor

@reubenmiller reubenmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, just need to make the very minor adjustments to the system tests tagging

@albinsuresh albinsuresh force-pushed the feat/3430/inventory-json-support-for-child-devices branch from 9081003 to 9892efc Compare August 21, 2025 07:46
@albinsuresh albinsuresh temporarily deployed to Test Pull Request August 21, 2025 07:46 — with GitHub Actions Inactive
@albinsuresh albinsuresh force-pushed the feat/3430/inventory-json-support-for-child-devices branch from 9892efc to 6b57b14 Compare August 21, 2025 08:58
@albinsuresh albinsuresh temporarily deployed to Test Pull Request August 21, 2025 08:58 — with GitHub Actions Inactive
@albinsuresh albinsuresh enabled auto-merge August 21, 2025 09:32
@albinsuresh albinsuresh added this pull request to the merge queue Aug 21, 2025
Merged via the queue into thin-edge:main with commit 8437d42 Aug 21, 2025
34 checks passed
@albinsuresh albinsuresh deleted the feat/3430/inventory-json-support-for-child-devices branch August 25, 2025 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

theme:telemetry Theme: Telemetry data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants