Azure / iot-hub-device-update

Device Update for IoT Hub agent
MIT License
53 stars 41 forks source link

Advisory for Debian 11 Based Distro Running on ARM64 SOM #484

Closed mathaou closed 1 year ago

mathaou commented 1 year ago

Hello!

Not a lot of info I can find for my specific situation, but I've tried following the following guides and associated issue threads even slightly related to my issue:

https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-agent-provisioning https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-raspberry-pi

I was able to get the identity service just fine, but I can't seem to install the deviceupdate-agent from any apt packages, and I really want to verify there's no other way before trying to compile myself. I've added the microsoft-prod.list for debian 11 and have also apt installed the packages-microsoft-prod.deb for debian 11. I've tried debian 10 and stretch/multiarch and even a couple of ubuntu images for fun. Not able to find any deviceupdate-agent. The microsoft-prod.list file has arm64 as a supported architecture.

Essentially, what are my options? Can't change distros or architectures, ideally this all runs on this debian 11 arm64 SOM.

eshashah-msft commented 1 year ago

Hi @mathaou, we have support for Debian 10 - ARM64 (Refer to this doc: Device Update for IoT Hub supported platforms . The package can be found here: https://packages.microsoft.com/debian/10/prod/pool/main/d/deviceupdate-agent/ Debian 11 is not supported out of the box but the agent can be built for Deb 11 after updating dependencies.

mathaou commented 1 year ago

Is there any guidance for this? Cross compilation steps? Regular build steps? Documentation or issues of this being done before?

jw-msft commented 1 year ago

A while back I was able to build adu and its dependencies on a Deb11 + RPi4 except I got stuck on building the DO local service--see #146. There are commits in the comments near the bottom of that issue. Also, as seen in that issue, Deb11 and raspian/rpios support will be added in an upcoming release.

Started work on cross-compilation to arm64 in this branch, but I am quite busy with feature work atm and won't be able to get back to it for some weeks. It is currently getting stuck where cmake is trying to include the cross-compiled/arm64 dependencies (such as azure iothub sdk and libdeliveryoptimization client SDK lib) that don't reside in the staging dir yet.

mathaou commented 1 year ago

@jw-msft I see that the xcomp is currently trying to use toolchains and the like. It's a sad state of modern software, but the most reliable and painless way that I've found to xcomp is with a docker container + qemu.

Regarding the Deb11 support, is there any sort of timetable for when the next release will be available? By or before Q4 of this year perhaps?

jw-msft commented 1 year ago

Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42

There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow

The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well.

For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.

mathaou commented 1 year ago

Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42

There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow

The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well.

For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.

Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.

jw-msft commented 1 year ago

IIRC from when I had tried a while back, one can build the core agent for Deb11 on device after commits https://github.com/Azure/iot-hub-device-update/commit/e283cf1a34dded4b7f59e66ad4b9b64679b7073d, https://github.com/Azure/iot-hub-device-update/commit/b80dac99b9200a9f43403851e631a30145abe993, and https://github.com/Azure/iot-hub-device-update/commit/150076b028f06d8374399ffd06033938fac39779 in develop branch and for azure-blob-storage-file-upload-utility repo commit https://github.com/Azure/azure-blob-storage-file-upload-utility/commit/ca52b773e3b62f1cfbeda551977fad80c98824d5. The next challenge is to build DO on Deb11 if using the DO content downloader (highly recommended for production device). If just evaluating for non-prod, then one could register the curl downloader - see https://github.com/Azure/iot-hub-device-update/blob/develop/packages/debian/postinst#L234 (use libcurl_content_downloader.so). The x-comp should hopefully land by oct timeframe.

jw-msft commented 1 year ago

Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42 There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well. For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.

Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.

@mathaou Ok, let me see if this week I can get you a dockerfile that will build it on ARM as a short-term workaround

mathaou commented 1 year ago

Thanks @mathaou. ACR + qemu in the build pipeline as can be seen here: https://github.com/Azure/iot-hub-device-update/blob/main/azurepipelines/build/templates/adu-docker-build-steps.yml#L42 There's also work in the backlog to check in a Dockerfile for it. Anyways, although it was straightforward to setup/maintain, we've found it to be quite slow The toolchain-based xcomp I'm doing is geared more for daily development with faster iterations, but should speedup the pipeline builds as well. For Deb11, the hope is that it should be by then modulo a smallish chance of reprioritization.

Sounds good. Yeah toolchain is always faster but an absolute nightmare of spinning plates these days. Very finicky. Some of my docker builds can take 10+ minutes but it is what it is.

@mathaou Ok, let me see if this week I can get you a dockerfile that will build it on ARM as a short-term workaround

I would really appreciate it thank you.

jw-msft commented 1 year ago

@mathaou This PR uses Hashicorp Packer to build a docker image that will contain the build artifacts for DU agent, its dependencies in /usr/local/lib/, and ability to run the unit tests.

It does not install DO (Delivery optimization) agent for downloading, so that would need to be done separately inside the running the container for now. Will see if I can get another iteration out Monday-ish to addres that.

mathaou commented 1 year ago

@mathaou This PR uses Hashicorp Packer to build a docker image that will contain the build artifacts for DU agent, its dependencies in /usr/local/lib/, and ability to run the unit tests.

It does not install DO (Delivery optimization) agent for downloading, so that would need to be done separately inside the running the container for now. Will see if I can get another iteration out Monday-ish to addres that.

Thank you very much for your work! I will try this out Monday-ish, then.

jw-msft commented 1 year ago

I've added another commit to that PR that updates the README.md.

To build deliveryoptimization packages, use build/docker/debian11/arm64/Dockerfile from this DO-client PR: https://github.com/microsoft/do-client/pull/174 Do open that Dockerfile and read the comments in there to docker build the image (first, git clone https://github.com/microsoft/do-client --branch develop) and once in the container interactively by following the Dockerfile comments, cd to /code and follow the steps in /code/README.md to build agent and sdk, namely:

cd /code
python3 build/build.py --project agent --package-for deb
python3 build/build.py --project sdk --package-for deb

resultant .deb will be:

matthewfarstad commented 1 year ago

I've followed all of the steps and I have my *.deb files. Getting this error:

root@imx8mn-var-som:~# apt install ./libdeliveryoptimization_1.0.0_arm64.deb 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'libdeliveryoptimization' instead of './libdeliveryoptimization_1.0.0_arm64.deb'
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
 libdeliveryoptimization : Depends: deliveryoptimization-agent but it is not installable
E: Unable to correct problems, you have held broken packages.

Am I missing something?

jw-msft commented 1 year ago

I believe your issue may be that you have to install deliveryoptimization-agent .deb before libdeliveryoptimization .deb.

What is the device where the .deb packages are being installed and which debian 11 bullseye OS image is being used? Are there any clues in /var/log/dpkg.log?

It worked for me using:

$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

Here's what I did:

Then I installed the .deb packages with the following:

sudo apt install .\deliveryoptimization-agent_1.0.0_arm64.deb
sudo apt install .\libdeliveryoptimization_1.0.0_arm64.deb
sudo apt install .\deviceupdate-agent_1.0.2_arm64.deb

Attached here are the /var/log/dpkg.log and /var/log/apt/term.log that show the above working. rpi4_var_log_apt_term.log rpi4_var_log_dpkg.log

Then I copied over a known-working du-config.json to /etc/adu/ and it ran fine, including downloading the update payload using DO-agent, installing the APT package using APT Handler, and reporting back 700 (Apply succeeded) in the twin: adu-shell.20230815-190556.log apt-handler.20230815-190556.log du-agent.20230815-190544.log

matthewfarstad commented 1 year ago

THANK YOU

It works fine!

jw-msft commented 1 year ago

Glad it worked for you, @matthewfarstad, and thanks for verifying it.

matthewfarstad commented 1 year ago

Reopening because I am now getting into a "Failed parse of JSON file: /etc/adu/du-config.json" loop for deviceupdate-agent.

Aug 18 20:18:05 imx8mn-var-som systemd[1]: Started Device Update Agent daemon.. Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2661Z 91683[91683] [E] Failed parse of JSON file: /etc/adu/du-config.json [ADUC_ConfigInfo_Init:360] Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2662Z 91683[91683] [E] Cannot initialize config info. [main:925] Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: WARNING: Unable to start file logger. (Log folder: /var/log/adu) Aug 18 20:18:05 imx8mn-var-som AducIotAgent[91683]: 2023-08-18T20:18:05.2662Z 91683[91683] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525] Aug 18 20:18:05 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Main process exited, code=exited, status=255/EXCEPTION Aug 18 20:18:05 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Failed with result 'exit-code'.

-rw-r--r-- 1 adu adu 507 Aug 18 20:04 /etc/adu/du-config.json

cat <<EOF | tee -a /etc/adu/du-config.json
{
   "schemaVersion": "1.0",
   "aduShellTrustedUsers": [
      "adu",
      "do"
   ],
   "manufacturer": "kappa",
   "model": "vacuum",
   "agents": [
      {
      "name": "main",
      "runas": "adu",
      "connectionSource": {
         "connectionType": "string",
         "connectionData": "HostName=${__HOSTNAME};DeviceId=${__MAC};SharedAccessKey=${__SHARED_ACCESS_KEY}"
      },
      "manufacturer": "kappa",
      "model": "vacuum"
      }
   ]
}
EOF

chown adu:adu /etc/adu/du-config.json
chown root:adu /usr/bin/adu-shell
chmod u=rxs /usr/bin/adu-shell

Documentation says:

Daemon When the Device Update Agent has been installed as a daemon, it will automatically start on boot. The Device Update Agent daemon reads the IoT Hub device connection string from a configuration file. Place your device connection string in /adu/adu-conf.txt.

So how do I get this to work? Some info says to use the .json file, older info says to use the conf.txt... I swear I just had it working but I was doing a million other things and of course didn't write down steps and just assumed it would stay that way...

matthewfarstad commented 1 year ago

I gave all files the permissions they needed and now I get this:

Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7089Z 117984[117984] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7097Z 117984[117984] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7097Z 117984[117984] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] Agent exited with code 1 [main:1101] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [W] Agent is shutting down. [ShutdownAgent:812] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7098Z 117984[117984] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] Aug 18 20:24:10 imx8mn-var-som AducIotAgent[117984]: 2023-08-18T20:24:10.7099Z 117984[117984] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525] Aug 18 20:24:10 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Main process exited, code=exited, status=1/FAILURE Aug 18 20:24:10 imx8mn-var-som systemd[1]: deviceupdate-agent.service: Failed with result 'exit-code'.

matthewfarstad commented 1 year ago

@jw-msft don't know how to reopen so pinging sorry

matthewfarstad commented 1 year ago

root@imx8mn-var-som:~# AducIotAgent 'device connection string' 2023-08-18T20:26:18.9768Z 127218[127218] [I] Agent (linux; 1.0.2) starting. [main:1021] 2023-08-18T20:26:18.9769Z 127218[127218] [I] Git Info: develop:e5e41f8a [main:1023] 2023-08-18T20:26:18.9769Z 127218[127218] [I] Supported Update Manifest version: min: 4, max: 5 [main:1025] 2023-08-18T20:26:18.9778Z 127218[127218] [I] Health check passed. [HealthCheck:632] 2023-08-18T20:26:18.9789Z 127218[127218] [I] Initializing PnP components. [ADUC_PnP_Components_Create:493] 2023-08-18T20:26:18.9789Z 127218[127218] [I] ADUC agent started. Using IoT Hub Client SDK 1.7.0 [AzureDeviceUpdateCoreInterface_Create:257] 2023-08-18T20:26:18.9790Z 127218[127218] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] 2023-08-18T20:26:18.9791Z 127218[127218] [E] Unable to initialize the diagnostic workflow data. [DiagnosticsInterface_Create:68] 2023-08-18T20:26:18.9793Z 127218[127218] [E] Failed to initialize PnP component 'diagnosticInformation'. [ADUC_PnP_Components_Create:503] 2023-08-18T20:26:18.9794Z 127218[127218] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:26:18.9794Z 127218[127218] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] 2023-08-18T20:26:18.9794Z 127218[127218] [E] ADUC_PnP_Components_Create failed [StartupAgent:760] 2023-08-18T20:26:18.9794Z 127218[127218] [I] Agent exited with code 1 [main:1101] 2023-08-18T20:26:18.9795Z 127218[127218] [W] Agent is shutting down. [ShutdownAgent:812] 2023-08-18T20:26:18.9795Z 127218[127218] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] 2023-08-18T20:26:18.9795Z 127218[127218] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:26:18.9796Z 127218[127218] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]

jw-msft commented 1 year ago

This is what the deb pkg runs, so you could run it manually like this:

wget https://raw.githubusercontent.com/Azure/iot-hub-device-update/develop/packages/debian/postinst
chmod 755 ./postinst
./postinst configure
matthewfarstad commented 1 year ago

Same error after running that:

2023-08-18T20:36:39.9617Z 172036[172036] [I] Agent (linux; 1.0.2) starting. [main:1021] 2023-08-18T20:36:39.9618Z 172036[172036] [I] Git Info: develop:e5e41f8a [main:1023] 2023-08-18T20:36:39.9618Z 172036[172036] [I] Supported Update Manifest version: min: 4, max: 5 [main:1025] 2023-08-18T20:36:39.9626Z 172036[172036] [I] Health check passed. [HealthCheck:632] 2023-08-18T20:36:39.9641Z 172036[172036] [I] Initializing PnP components. [ADUC_PnP_Components_Create:493] 2023-08-18T20:36:39.9643Z 172036[172036] [I] ADUC agent started. Using IoT Hub Client SDK 1.7.0 [AzureDeviceUpdateCoreInterface_Create:257] 2023-08-18T20:36:39.9643Z 172036[172036] [I] Calling ADUC_RegisterPlatformLayer [ADUC_MethodCall_Register:63] 2023-08-18T20:36:39.9644Z 172036[172036] [E] Unable to initialize the diagnostic workflow data. [DiagnosticsInterface_Create:68] 2023-08-18T20:36:39.9646Z 172036[172036] [E] Failed to initialize PnP component 'diagnosticInformation'. [ADUC_PnP_Components_Create:503] 2023-08-18T20:36:39.9647Z 172036[172036] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:36:39.9647Z 172036[172036] [I] Calling ADUC_Unregister [ADUC_MethodCall_Unregister:91] 2023-08-18T20:36:39.9647Z 172036[172036] [E] ADUC_PnP_Components_Create failed [StartupAgent:760] 2023-08-18T20:36:39.9647Z 172036[172036] [I] Agent exited with code 1 [main:1101] 2023-08-18T20:36:39.9648Z 172036[172036] [W] Agent is shutting down. [ShutdownAgent:812] 2023-08-18T20:36:39.9648Z 172036[172036] [I] De-initializing command listener thread [UninitializeCommandListenerThread:385] 2023-08-18T20:36:39.9648Z 172036[172036] [I] ADUC agent stopping [AzureDeviceUpdateCoreInterface_Destroy:308] 2023-08-18T20:36:39.9649Z 172036[172036] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]

jw-msft commented 1 year ago

Is /etc/adu/du-diagnostics-config.json existing and have adu:adu ownership?

i.e.

chown adu:adu /etc/adu/du-diagnostics-config.json
chmod 440 /etc/adu/du-diagnostics-config.json

It should have contents similar to the following, where each logComponent array element is a dir of files you want to ship to blob storage (when diagnostics retrieval is invoked in the azure portal):

{
    "logComponents":[
        {
            "componentName":"adu",
            "logPath":"/var/log/adu/"
        },
        {
            "componentName":"do",
            "logPath":"/var/log/deliveryoptimization-agent/"
        }
    ],
    "maxKilobytesToUploadPerLogPath":256
}

The [E] Unable to initialize the diagnostic workflow data. trace being seen corresponds to this line in DiagnosticsInterface_Create that calls DiagnosticsConfigUtils_InitFromFile.

DiagnosticsConfigUtils_InitFromFile fn def'n is here

matthewfarstad commented 1 year ago

I have my daemon running properly, but I thought my device status was supposed to change to connected or something...

matthewfarstad commented 1 year ago

Or I have this in my device update section, now. I think that is correct...

f8dc7a8b90f2    NA    NA    NA    Yes
matthewfarstad commented 1 year ago

Alright! I think I have everything in place knock on wood.

The final question I have is: what constitutes an update being available? My .importmanifest.json, .swu file, and /etc/adu-version all have this info available, but I can't seem to get an update available for my ADUGroup tag I'm targeting (there are no new updates for this group) after I import an update. Just thought I would ask if there was some hidden step.

I got an update to go through once on a fresh device, but it failed because I didn't have /etc/adu-version. I put 1.0.0 in there, so I hope that was correct.

matthewfarstad commented 1 year ago

Got it sorted. model was different.

jw-msft commented 1 year ago

I have my daemon running properly, but I thought my device status was supposed to change to connected or something...

  • If agent has an mqtt or mqtt/ws connection to the hub successfully it should show "Connected" value in the twin for the top-level "connectionState" property.
  • From the device running the daemon for the deviceupdate-agent.service, (if sudo systemctl status deviceupdate-agent shows it's running), doing: netstat -pant | grep ':8883' should show an ESTABLISHED tcp socket from device ip:port to the hub ip:8883 (secure mqtt) or ip:443 (mqtt over websocket), depending on du-config.json iotHubProtocol setting.

The final question I have is: what constitutes an update being available? My .importmanifest.json, .swu file, and /etc/adu-version all have this info available, but I can't seem to get an update available for my ADUGroup tag I'm targeting (there are no new updates for this group) after I import an update. Just thought I would ask if there was some hidden step.

Check for alignment of compatibility property in the import manifest and the following properties in du-config.json:

After publish of update, then the service will offer a link to Deploy the latest version of update for the ADUGroup device group (or for the Default group if none specified in twin) in portal ux under DeviceManagement -> Updates -> "Groups and Deployments" tab for the matching device group based on (by default) the model and manufacturer in du-config.json and update's compatibility properties.

I got an update to go through once on a fresh device, but it failed because I didn't have /etc/adu-version. I put 1.0.0 in there, so I hope that was correct.

Yes, the isInstalled check of the swupdate handlers uses the contents of /etc/adu-version will be compared with value of installedCriteria property under handlerProperties in import manifest.

matthewfarstad commented 1 year ago

Is there any way to have a range of that? Like "this update applies to anything 1.0.0 and higher"?

Or are the updates in azure kind of just automatically sandwiched together, i.e. it will find the update that moves 1.0.0 to 1.0.1, then find the one that moves 1.0.1 to 1.0.3 etc

jw-msft commented 1 year ago

Is there any way to have a range of that? Like "this update applies to anything 1.0.0 and higher"?

That is currently not provided by default by 1st party step handlers (which are based on matching installedCriteria string). One would have to encode it in the installedCriteria string or via custom handlerProperties and write a custom handler that interprets the handler properties to achieve that. e.g. one could add "minVersion" and "maxVersion" (either could have a +/- infinity) to the handlerProperties and implement the logic in the custom handler.

@eshashah-msft to provide link for feature voting or to track this feature request. Starting a github discussion would also be a good venue for discussing this further.

Or are the updates in azure kind of just automatically sandwiched together, i.e. it will find the update that moves 1.0.0 to 1.0.1, then find the one that moves 1.0.1 to 1.0.3 etc

This latter one. For the provider and name, one would add updates with incremented version. See https://learn.microsoft.com/en-us/azure/iot-hub-device-update/device-update-deployments for more info.

@josephmsft Could you elaborate further?

matthewfarstad commented 1 year ago

So I got a successful update, but /etc/adu-version didn't update to the correct version numbers. I'll run another update in morning and clear the logs of all my bad runs to verify whats going on.

matthewfarstad commented 1 year ago

Also I'm now trapped in an infinite update loop even though the IoT hub says this device is on latest update (reported update was successful, and then it switched to update in progress even though on latest). Was /etc/adu-version supposed to update? Because its the same.

Installing update.
0[TRACE] : SWUPDATE running :  [load_cert_chain] : failed: BIO_new_file(/adukey/public.pem)
0[ERROR] : SWUPDATE failed [0] ERROR : Error loading certificate chain from /adukey/public.pem
Error: Crypto cannot be initialized.
Applying update.

Do preinstall and postinstall scripts for swupdate handler not work? Or basically how do I get out of this loop, I want a post install script that triggers booting into another partition.


UPDATE: I see it is recommended to use v2 swupdate handler. The README.md says I need a handler file? Where is that for microsoft/swupdate:2?

Banking on it just being included in what I have already. Basing a script off of this example: https://github.com/Azure/iot-hub-device-update/tree/main/src/extensions/step_handlers/swupdate_handler_v2/tests/testdata/adu-yocto-ab-rootfs-update


2023-08-22T15:05:06.2716Z 453020[453159] [I] Loading handler for step #0 (handler: 'microsoft/swupdate:2') [StepsHandler_Download:606]
2023-08-22T15:05:06.2717Z 453020[453159] [I] Loading handler for 'microsoft/swupdate:2'. [LoadUpdateContentHandlerExtension:208]
2023-08-22T15:05:06.2719Z 453020[453159] [I] Loading extension 'microsoft/swupdate:2'. Reg file : /var/lib/adu/extensions/update_content_handlers/microsoft_swupdate_2/content_handler.json [LoadExtensionLibrary:85]
2023-08-22T15:05:06.2723Z 453020[453159] [I] Instantiating a Step Handler for 'microsoft/swupdate:2' [CreateUpdateContentHandlerExtension:30]
2023-08-22T15:05:06.2724Z 453020[453159] [I] Uninitializing config info. [ADUC_ConfigInfo_UnInit:525]
2023-08-22T15:05:06.2725Z 453020[453159] [I] SWUpdate handler v2 download task begin. [Download:326]
2023-08-22T15:05:06.2725Z 453020[453020] [I] Property osName changed to Debian GNU/Linux [RefreshDeviceInfoInterfaceData:131]
2023-08-22T15:05:06.2726Z 453020[453159] [I] SWUpdate_Handler download task end. [Download:389]
2023-08-22T15:05:06.2727Z 453020[453159] [I] Action 'Download' complete. Result: 0 (failed), 806355203 (0x30100103) [ADUC_Workflow_WorkCompletionCallback:896]
2023-08-22T15:05:06.2727Z 453020[453159] [I] WorkCompletionCallback: Download failed. Going to state Failed [ADUC_Workflow_WorkCompletionCallback:1021]
2023-08-22T15:05:06.2727Z 453020[453159] [I] Setting UpdateState to Failed [ADUC_Workflow_SetUpdateStateHelper:1093]

I'm trying this approach:

#!/bin/bash

# create importmanifest

__VERSION="1.0.1"

az iot du update init v5 \
    --update-provider test \
    --update-name one \
    --update-version $__VERSION \
    --compat manufacturer=test model=v1 \
    --step handler=microsoft/swupdate:2 properties="{\"installedCriteria\": \"$__VERSION\", \"scriptFileName\": \"update.sh\"}" \
    --file path=$1 > test_${__VERSION}.importmanifest.json

With this swdescription file

software =
{
    version = "1.0.1";
    stable = {
        A: {
            files: (
                {
                    filename = "test";
                    path = "/mnt/usr/bin/test";
                    device = "/dev/mmcblk2p2";
                    filesystem = "/mnt";
                    properties = {create-destination = "true"; atomic-install = "true";}
                }
            );
        }
        B: {
            files: (
                {
                    filename = "test";
                    path = "/mnt/usr/bin/test";
                    device = "/dev/mmcblk2p1";
                    filesystem = "/mnt";
                    properties = {create-destination = "true"; atomic-install = "true";}
                }
            );
        }
    };
}

With this update file. I tried following several things from the example. I know that there have to be the 5 supported cli ops (--action-...), but it seemed that there were a number of things being passed implicitly to the script. I included this script in the .swu archive (not sure if it has to exist on device)

Its unclear when this gets called and by what - I added the --swu-file and --installed-criteria cli params but I'm not sure that was correct

(https://github.com/Azure/iot-hub-device-update/blob/main/src/extensions/step_handlers/swupdate_handler_v2/README.md) tells me that the --installed-criteria gets passed.

#!/bin/bash

software_version_file="/etc/adu-version"
result_file=/adu/logs/swupdate.result.json
swupdate_log_file=/adu/logs/swupdate_log_file

current_partition=1 # assume we're in partition 1
update_partition=2
selection="stable,B"

image_file="" # for install task

rootfs_dev=$(mount | grep "on / type" | cut -d':' -f 2 | cut -d' ' -f 1)
if ! [[ $rootfs_dev == '/dev/mmcblk2p1' ]]; then # switch to assuming we're in partition 2
    current_partition=2
    update_partition=1
    selection="stable,A"
fi

#                            --action-download     : No-op. (No additional payloads)
#                            --action-install      : Invokes swupdate to install the specified 'swuFile'
#                            --action-apply        : Sets uboot environment variable to switch to the desired rootfs partition after rebooted.
#                                                    Returns a special (ADUC_Result) result code to instruct DU Agent to reboot the device.
#                            --action-cancel       : Restore uboot environment variable to the value before '--action-apply' is performed.
#                            --action-is-installed : Returns (ADUC_Result) result code that indicates whether the current rootfs contains
#                                                    software (image) version that match specified 'installedCriteria' string.

__ret_val=0

if [[ $0 != "${BASH_SOURCE[0]}" ]]; then
    ret='return'
else
    ret='exit'
fi

make_swupdate_handler_erc() {
    local base_erc=0x30101000
    local -n res=$2 # name reference
    res=$((base_erc + $1))
}

make_aduc_result_json() {
    local -n res=$4 # name reference
    res="{\"resultCode\":$1, \"extendedResultCode\":$2,\"resultDetails\":\"$3\"}"
}

print_usage() {
    echo "heyo"
}

# Usage: is_installed $installedCriteria $imageVersionFile <out resultCode> <out extendedResultCode> <out resultDetails>
is_installed() {
    local -n rc=$3  # name reference for resultCode
    local -n erc=$4 # name reference for extendedResultCode
    local -n rd=$5  # name reference for resultDetails

    __current_version=$(cat $software_version_file)
    if ! [[ -f $2 ]]; then
        rc=0
        make_swupdate_handler_erc 100 erc
        rd="Image version file name is empty."
    elif [[ $1 == "" ]]; then
        rc=0
        make_swupdate_handler_erc 101 erc
        rd="Installed criteria is empty."
    else
        if [ $__current_version == "$1" ]; then
            rc=900
        else
            rc=901
        fi
        erc=0
        rd=""
    fi
}

result() {
    echo "$@" > "$result_file"
}

check_is_installed_state() {
    local local_rc=2
    local local_erc=3
    local local_rd="4"
    local aduc_result=""

    # Evaluates installedCriteria string.
    is_installed "$1" "$software_version_file" local_rc local_erc local_rd

    make_aduc_result_json "$local_rc" "$local_erc" "$local_rd" aduc_result

    # Show output.
    echo "Result: $aduc_result"

    # Write ADUC_Result to result file.
    result "$aduc_result"
}

download_update_artifacts() {
    # Return ADUC_Result_Download_Success (500), no extended result code, no result details.
    local aduc_result=""
    make_aduc_result_json 500 0 "" aduc_result

    # Show output.
    echo "Result: $aduc_result"

    # Write ADUC_Result to result file.
    result "$aduc_result"

    $ret $__ret_val
}

install_update() {
    resultCode=0
    extendedResultCode=0
    resultDetails=""
    ret_val=1

    is_installed "$installed_criteria" "$software_version_file" resultCode extendedResultCode resultDetails

    is_installed_ret=$?
    if [[ $is_installed_ret -ne 0 ]]; then
        # is_installed functin failed to execute.
        resultCode=0
        make_swupdate_handler_erc "$is_installed_ret" extendedResultCode
        resultDetails="Internal error in 'is_installed' function."
    elif [[ $resultCode == 0 ]]; then
        # Failed to determine whether the update has been installed or not.
        # Return current ADUC_Result
        echo "Failed to determine wehther the update has been installed or not."
    elif [[ $resultCode -eq 901 ]]; then
        # Not installed.

        # install an update.
        echo "Installing update." >> "${log_file}"
        if [[ -f $image_file ]]; then

            # Note: Swupdate can use a public key to validate the signature of an image.
            #
            # Here is how we generated the private key for signing the image
            # and how we generated that public key file used to validate the image signature.
            #
            # Generated RSA private key with password using command:
            # openssl genrsa -aes256 -passout file:priv.pass -out priv.pem
            #
            # Generated RSA public key from private key using command:
            # openssl rsa -in ${WORKDIR}/priv.pem -out ${WORKDIR}/public.pem -outform PEM -pubout

            ret_val=1
            # get this working at some point @todo
            # if [[ ${public_key_file} -eq "" ]]; then
            #     # Call swupdate with the image file and no signature validations
            #     swupdate -v -i "${image_file}" -e ${selection} &>> "${swupdate_log_file}"
            #     ret_val=$?
            # else
            #     # Call swupdate with the image file and the public key for signature validation
            #     swupdate -v -i "${image_file}" -k "${public_key_file}" -e ${selection} &>> "${swupdate_log_file}"
            #     ret_val=$?
            # fi

            swupdate -v -i "${image_file}" -e ${selection} &>> "${swupdate_log_file}"
            ret_val=$?

            if [[ $ret_val -eq 0 ]]; then
                resultCode=600
                extendedResultCode=0
                resultDetails=""
            else
                resultCode=0
                make_swupdate_handler_erc "$ret_val" extendedResultCode
                resultDetails="SWUpdate command failed."
            fi
        else
            echo "Image file ${image_file} was not found." >> "${log_file}"
            resultCode=0
            # ADUC_ERC_SWUPDATE_HANDLER_INSTALL_FAILURE_IMAGE_FILE_NOT_FOUND (0x202)
            extendedResultCode=0x30100202
            resultDetails="Image file ${image_file} was not found."
        fi
        ret_val=0
    else
        # Installed.
        log_info "It appears that this component already installed the specified update."
        resultCode=603
        extendedResultCode=0
        resultDetails="Already installed."
        ret_val=0
    fi

    make_aduc_result_json "$resultCode" "$extendedResultCode" "$resultDetails" aduc_result

    # Show output.
    echo "Result: $aduc_result"

    # Write ADUC_Result to result file.
    result "$aduc_result"

    $ret $ret_val
}

apply_update() {
    ret_val=0
    resultCode=0
    extendedResultCode=0
    resultDetails=""

    fw_setenv mmcpart $update_partition
    ret_val=$?

    if [[ $ret_val -eq 0 ]]; then
        resultCode=705 # restart immediately
        extendedResultCode=0
        resultDetails=""
    else
        resultCode=0
        make_swupdate_handler_erc "$ret_val" extendedResultCode
        resultDetails="Cannot set rpipart value to $update_partition"
        echo "$resultDetails"
    fi
}

cancel_update() {
    echo "CancelUpdate called"

    resultCode=0
    extendedResultCode=0
    resultDetails=""
    ret_val=

    fw_setenv mmcpart $current_partition
    ret_val=$?

    if [[ $ret_val -eq 0 ]]; then
        resultCode=800
    else
        resultCode=801
        make_swupdate_handler_erc "$ret_val" extendedResultCode
        resultDetails="Failed to set rpipart to ${update_partition}"
        ret_val=0
    fi

    make_aduc_result_json "$resultCode" "$extendedResultCode" "$resultDetails" aduc_result
    echo "Result: $aduc_result"

    # Write ADUC_Result to result file.
    result "$aduc_result"

    $ret $ret_val
}

for arg in "$@"; do
  shift
  case "$arg" in
    '--action-download')   set -- "$@" '-d'   ;;
    '--action-install') set -- "$@" '-i'   ;;
    '--action-apply')   set -- "$@" '-a'   ;;
    '--action-cancel')     set -- "$@" '-c'   ;;
    '--action-is-installed')     set -- "$@" '-x'   ;;
    '--swu-file')     set -- "$@" '-f'   ;;
    '--installed-criteria')     set -- "$@" '-r'   ;;
    *)          set -- "$@" "$arg" ;;
  esac
done

installed_criteria=
check_is_installed=

while getopts "f:r:xcaid" opt
do
  case "$opt" in
    'd') download_update_artifacts; exit $__ret_val ;;
    'i') install_update; exit $__ret_val ;;
    'a') apply_update; exit $__ret_val ;;
    'c') cancel_update; exit $__ret_val ;;
    'x') check_is_installed=yes ;;
    'f') image_file=$OPTARG ;;
    'r') installed_criteria=$OPTARG ;;
    '?') print_usage >&2; exit 1 ;;
  esac
done

if [ -n "$check_is_installed" ]; then
    check_is_installed_state $installed_criteria
    exit $__ret_val
fi

$ret $__ret_val
matthewfarstad commented 1 year ago

I believe my script should be on the device since when I use the swupdate:1 handler I see function names that appear in that example. When I try to register swupdate:2 handler with this command I get bad output:

root@imx8mn-var-som:~# /usr/bin/AducIotAgent --update-type 'microsoft/swupdate:2' --register-content-handler /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so
Unsupported option '--update-type'. Try preceding with -- to separate options and additional args.

Attempting to just use the microsoft/swupdate:1 handler. I put my custom script in /usr/lib/adu/adu-swupdate.sh an update success - but my /etc/adu-version didn't update and my update logic didn't get triggered. Going to assume that the script I based mine off of was for the microsoft/swupdate:2 handler instead of microsoft/swupdate:1.

Basing a new script off the one that came with install:

#!/bin/bash

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Ensure that getopt starts from first option if ". <script.sh>" was used.
OPTIND=1

# Ensure we dont end the user's terminal session if invoked from source (".").
if [[ $0 != "${BASH_SOURCE[0]}" ]]; then
    ret='return'
else
    ret='exit'
fi

print_help() {
    echo "adu-swupdate.sh [-a] [-h] [-i image_file] [-l log_dir] [-r] "
    echo "-a                Applies the install by telling the bootloader to boot to the updated partition."
    echo "-h                Show this help message."
    echo "-i image_file     The update image file to install. For swupdate this is typically a .swu file"
    echo "-l log_dir        The folder where logs should be written."
    echo "-r                Reverts the apply by telling the bootloader to boot into the current partition."
}

action=""
num_actions=0
log_dir="/tmp"

while getopts "ahi:l:r" opt; do
    case "$opt" in
    a)
        action="apply"
        num_actions=$((num_actions + 1))
        ;;
    h)
        print_help
        $ret 0
        ;;
    i)
        action="install"
        image_file=$OPTARG
        num_actions=$((num_actions + 1))
        ;;
    l)
        log_dir=$OPTARG
        ;;
    r)
        action="revert"
        num_actions=$((num_actions + 1))
        ;;
    \?)
        print_help
        $ret 1
        ;;
    esac
done

# Check that an option was specified
if [[ $num_actions == 0 ]]; then
    echo "Must specify one action command line option."
    print_help
    $ret 1
fi

# Check that only one option was specified
if [[ $num_actions -gt 1 ]]; then
    echo "Must specify only one action command line option."
    print_help
    $ret 1
fi

# Make sure the log directory is created.
mkdir -p "$log_dir"

# SWUpdate doesn't support everything necessary for the dual-copy or A/B update strategy.
# Here we figure out the current OS partition and then set some environment variables
# that we use to tell swupdate which partition to target.
rootfs_dev=$(mount | grep "on / type" | cut -d':' -f 2 | cut -d' ' -f 1)
if [[ $rootfs_dev == '/dev/mmcblk0p2' ]]; then
    selection="stable,copy2"
    current_part=2
    update_part=3
else
    selection="stable,copy1"
    current_part=3
    update_part=2
fi

if [[ $action == "apply" ]]; then
    # Set the bootloader environment variable
    # to tell the bootloader to boot into the current partition
    # instead of the one that was updated.
    # rpipart variable is specific to our boot.scr script.
    echo "Applying update." >> "${log_dir}/swupdate.log"
    fw_setenv rpipart $update_part
    $ret $?
fi

if [[ $action == "revert" ]]; then
    # Set the bootloader environment variable
    # to tell the bootloader to boot into the current partition
    # instead of the one that was updated.
    # rpipart variable is specific to our boot.scr script.
    echo "Reverting update." >> "${log_dir}/swupdate.log"
    fw_setenv rpipart $current_part
    $ret $?
fi

if [[ $action == "install" ]]; then
    echo "Installing update." >> "${log_dir}/swupdate.log"
    if [[ -f $image_file ]]; then
        # Swupdate will use a public key to validate the signature of an image.
        # Here is how we generated the private key for signing the image
        # and how we generated that public key file used to validate the image signature.
        # Generated RSA private key with password using command:
        # openssl genrsa -aes256 -passout file:priv.pass -out priv.pem
        # Generated RSA public key from private key using command:
        # openssl rsa -in ${WORKDIR}/priv.pem -out ${WORKDIR}/public.pem -outform PEM -pubout

        # Call swupdate with the image file and the public key for signature validation
        swupdate -v -i "${image_file}" -k /adukey/public.pem -e ${selection} &>> "${log_dir}/swupdate.log"
        $ret $?
    else
        echo "Image file $image_file was not found." >> "${log_dir}/swupdate.log"
        $ret 1
    fi
fi
jw-msft commented 1 year ago

Also I'm now trapped in an infinite update loop even though the IoT hub says this device is on latest update (reported update was successful, and then it switched to update in progress even though on latest). Was /etc/adu-version supposed to update? Because its the same. Do preinstall and postinstall scripts for swupdate handler not work? Or basically how do I get out of this loop, I want a post install script that triggers booting into another partition.

I'd suggest temporarily stopping deviceupdate-agent service and manually running the swupdate cmd-line with strace to understand why Error: Crypto cannot be initialized. swupdate needs to use the public.pem to verify the sw-description signature, but since it's failing I would've expected exit code to be non-zero from swupdate and it should have led to an install failure as seen in the code here

Start by running swupdate manually with -v verbose flag and if need more info then run /usr/bin/swupdate again with strace (and maybe again with ltrace after that) on the front so we can see the syscalls and hopefully determine what is failing when trying to init crypto:

systemctl stop deviceupdate-agent
ps -efww | grep [A]duc
/usr/bin/swupdate -v -i ./adu-update-image-raspberrypi3-1.0.2-2.swu \
    -k /adukey/public.pem -e 'stable,B' | tee /tmp/swupdate.strace.log 2>&1
echo $?

Here's my result when running on RPi3 flashed with the wic and where I transferred over the .swu:

# sd-card flashed with adu-base-image-raspberrypi3-1.0.2.wic.gz inside 
# of https://github.com/Azure/iot-hub-device-update/releases/download/1.0.2/Tutorial_RaspberryPi.zip
# scp ./adu-update-image-raspberrypi3-1.0.2-2.swu to rpi first
$ /usr/bin/swupdate -v \
    -i ./adu-update-image-raspberrypi3-1.0.2-2.swu \
    -k /adukey/public.pem -e 'stable,copy2'
SWUpdate v

Licensed under GPLv2. See source distribution for detailed copyright notices.

[INFO ] : SWUPDATE running :  [print_registered_bootloaders] : Registered bootloaders:
[INFO ] : SWUPDATE running :  [print_registered_bootloaders] :  uboot   loaded.
[INFO ] : SWUPDATE running :  [print_registered_bootloaders] :  none    loaded.
[INFO ] : SWUPDATE running :  [main] : Using default bootloader interface: uboot
[INFO ] : SWUPDATE running :  [main] : Running on raspberrypi3 Revision 1.0
[INFO ] : SWUPDATE running :  [print_registered_handlers] : Registered handlers:
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     dummy
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     archive
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     tar
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     uboot
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     bootloader
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     lua
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     raw
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     rawfile
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     rawcopy
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     shellscript
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     preinstall
[INFO ] : SWUPDATE running :  [print_registered_handlers] :     postinstall
[INFO ] : SWUPDATE running :  [main] : software set: stable mode: copy2
[TRACE] : SWUPDATE running :  [listener_create] : creating socket at /tmp/swupdateprog
[TRACE] : SWUPDATE running :  [network_initializer] : Main loop daemon
[TRACE] : SWUPDATE running :  [listener_create] : creating socket at /tmp/sockinstctrl
[TRACE] : SWUPDATE running :  [network_thread] : Incoming network request: processing...
[INFO ] : SWUPDATE started :  Software Update started !
[TRACE] : SWUPDATE running :  [network_initializer] : Software update started
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] : Found file
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] :   filename sw-description
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] :   size 1007
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] : Found file
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] :   filename sw-description.sig
[TRACE] : SWUPDATE running :  [extract_file_to_tmp] :   size 256
[TRACE] : SWUPDATE running :  [swupdate_verify_file] : Verify signed image: Read 1007 bytes
[TRACE] : SWUPDATE running :  [swupdate_verify_file] : Verified OK
[TRACE] : SWUPDATE running :  [get_common_fields] : Version 1.0.2-2
[TRACE] : SWUPDATE running :  [parse_hw_compatibility] : Accepted Hw Revision : 1.0
[TRACE] : SWUPDATE running :  [_parse_images] : Found compressed Image: adu-base-image-raspberrypi3.ext4.gz in device : /dev/mmcblk0p3 for handler raw
[TRACE] : SWUPDATE running :  [check_hw_compatibility] : Hardware raspberrypi3 Revision: 1.0
[TRACE] : SWUPDATE running :  [check_hw_compatibility] : Hardware compatibility verified
[TRACE] : SWUPDATE running :  [extract_files] : Found file
[TRACE] : SWUPDATE running :  [extract_files] :         filename adu-base-image-raspberrypi3.ext4.gz
[TRACE] : SWUPDATE running :  [extract_files] :         size 124609949 required
[TRACE] : SWUPDATE running :  [extract_padding] : Expecting 484 padding bytes at end-of-file
[TRACE] : SWUPDATE running :  [network_initializer] : Valid image found: copying to FLASH
[INFO ] : SWUPDATE running :  Installation in progress
[TRACE] : SWUPDATE running :  [install_single_image] : Found installer for stream adu-base-image-raspberrypi3.ext4.gz raw
[INFO ] : SWUPDATE successful ! SWUPDATE successful !
[TRACE] : SWUPDATE running :  [network_initializer] : Main thread sleep again !
[INFO ] : No SWUPDATE running :  Waiting for requests...
[INFO ] : SWUPDATE running :  [endupdate] : SWUpdate was successful !

$ echo $?
0
jw-msft commented 1 year ago

I believe my script should be on the device since when I use the swupdate:1 handler I see function names that appear in that example. When I try to register swupdate:2 handler with this command I get bad output:

The cmd to register used that failed was using older cmdline switches:

/usr/bin/AducIotAgent --update-type 'microsoft/swupdate:2' \
    --register-content-handler /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so

Try this instead as per the newest deb pkg postinst script:

/usr/bin/AducIotAgent --extension-type updateContentHandler \
    --extension-id 'microsoft/swupdate:2' \
    --register-extension /var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so

However, it should already be registered as per its content_handler.json so running the registration likely won't change it:

$ cat /var/lib/adu/extensions/update_content_handlers/microsoft_swupdate_2/content_handler.json
{
    "fileName": "/var/lib/adu/extensions/sources/libmicrosoft_swupdate_2.so",
    "sizeInBytes": 198384,
    "hashes": {
        "sha256": "fjKbCUxqr75Ue1Bai34qQ4eOQqt/JhfcCqB81hYVdgg="
    },
    "handlerId": "microsoft/swupdate:2"
}
jw-msft commented 1 year ago

Why would there be no reported update available in azure?

Could *.importManifest.json that was uploaded also be provided? Also "deviceId" from the device twin would help for looking on the backend.

Did the netstat from https://github.com/Azure/iot-hub-device-update/issues/484#issuecomment-1686875288 show agent was connected to port 8883 and did "connectionStatus" in the twin change to "Connected"?

matthewfarstad commented 1 year ago

I think I got everything working. However, DU is saying that I am at 8/10 device classes for my tier. I see the next tier up only goes to 80, what is a device class and how I do I scale that to have hundreds of devices? I want to update remotely potentially thousands of devices, so is this the point I have to call someone at MSFT or what?

josephmsft commented 1 year ago

A device class can be thought of as a set of devices that are all compatible with the same updates. In practice this means they all have the same PnP modelId and compat properties. You can have any number of devices in a device class (up to the total limit of devices for your DU SKU tier).

matthewfarstad commented 1 year ago

A device class can be thought of as a set of devices that are all compatible with the same updates. In practice this means they all have the same PnP modelId and compat properties. You can have any number of devices in a device class (up to the total limit of devices for your DU SKU tier).

Ah I was giving each update a unique model. I'll stop doing that then.

jw-msft commented 1 year ago

@matthewfarstad Is the original issues resolved and this can be closed?

matthewfarstad commented 1 year ago

@matthewfarstad Is the original issues resolved and this can be closed?

Yes I ended up getting this working.