Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to restart the custom edge module while using the inbuilt "RestartModule" direct method of $edgeAgent from custom iot edge module. #3373

Open
malavvakharia opened this issue Sep 25, 2023 · 14 comments
Labels
bug Something isn't working. IoTSDK Tracks all IoT SDK issues across the board

Comments

@malavvakharia
Copy link

malavvakharia commented Sep 25, 2023

Expected Behavior:

We can restart the custom module by invoking the $edgeAgent "RestartModule" direct method.But as of now it will be giving the error.

image

Steps to Reproduce:

Provide a detailed set of steps to reproduce the bug.

  1. I am using the .NET sdk for invoking the inbuilt direct method of $edgeAgent.(Note: Calling the inbuilt direct method from the custom module not from $edgeAgent itself.)

  2. Code snippet (C#):

var DataAsJson = @"{
""schemaVersion"": ""1.0"",
""id"": ""edgemodule1""
}";
var methodRequest = new MethodRequest("RestartModule", Encoding.UTF8.GetBytes(DataAsJson), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));

         var result = await _moduleClient.InvokeMethodAsync("DeviceId","$edgeAgent", methodRequest);
  1. After deploying the module when we invoking the direct method calls it will give the error as shown in image.
@malavvakharia malavvakharia added the bug Something isn't working. label Sep 25, 2023
@github-actions github-actions bot added the IoTSDK Tracks all IoT SDK issues across the board label Sep 25, 2023
@malavvakharia malavvakharia changed the title [Bug Report] Not able to restart the custom edge module while using the inbuilt "RestartModule" direct method of $edgeAgent from custom iot edge module. Not able to restart the custom edge module while using the inbuilt "RestartModule" direct method of $edgeAgent from custom iot edge module. Sep 25, 2023
@malavvakharia
Copy link
Author

Hi @malavvakharia

To invoke a method on a module you should use the ModuleClient, from the picture seems you are using the DeviceClient.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.devices.client.moduleclient.invokemethodasync?view=azure-dotnet#microsoft-azure-devices-client-moduleclient-invokemethodasync(system-string-system-string-microsoft-azure-devices-client-methodrequest-system-threading-cancellationtoken)

Hello @rido-min ,

I am already using the module client. Maybe that's not the issue.

image

@rido-min
Copy link
Member

rido-min commented Sep 26, 2023

my apologies @malavvakharia

I'm able to reproduce the issue when invoking direct methods on $edgeAgent, however I'm able to invoke methods in other modules, so I suspect this might a limitation for $edgeAgent.

Let me review what's the expected behavior of $egeAgent and I'll update this ticket.

Thanks,
Rido

#region Assembly Microsoft.Azure.Devices.Client, Version=1.42.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
    _cancellationToken = cancellationToken;
    MqttTransportSettings mqttSetting = new(TransportType.Mqtt_Tcp_Only);
    ITransportSettings[] settings = { mqttSetting };

    // Open a connection to the Edge runtime
    _moduleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);

    // Reconnect is not implented because we'll let docker restart the process when the connection is lost
    _moduleClient.SetConnectionStatusChangesHandler((status, reason) =>
        _logger.LogWarning("Connection changed: Status: {status} Reason: {reason}", status, reason));

    await _moduleClient.OpenAsync(cancellationToken);

    _logger.LogInformation("IoT Hub module client initialized.");

    var iotEdgeDeviceId = Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");

    var echoResp = await _moduleClient.InvokeMethodAsync(iotEdgeDeviceId, 
        "module-sample", new MethodRequest("echo", Encoding.UTF8.GetBytes("\"hello\"")));

    _logger.LogInformation("Response from module-sample {r} ", echoResp.ResultAsJson);

    MethodRequest request = new(
        "RestartModule", 
        Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new
        {
            schemaVersion = "1.0",
            id = "SimulatedTemperatureSensor"
        })),
        TimeSpan.FromSeconds(15),
        TimeSpan.FromSeconds(5));

    string moduleId = "%24edgeAgent";
    
    _logger.LogInformation("Sending request to {d}-{m} method {n} with {p}", iotEdgeDeviceId, moduleId, request.Name, request.DataAsJson);

    var response = await _moduleClient.InvokeMethodAsync(iotEdgeDeviceId!, moduleId, request, cancellationToken);

    _logger.LogInformation("Response status: {status}, payload: {payload}", response.Status, response.ResultAsJson);
}

fails with

<4>EdgeAgentClient.ModuleBackgroundService[0] Connection changed: Status: Connected Reason: Connection_Ok
<6>EdgeAgentClient.ModuleBackgroundService[0] IoT Hub module client initialized.
<6>EdgeAgentClient.ModuleBackgroundService[0] Response from module-sample "hellohello" 
<6>EdgeAgentClient.ModuleBackgroundService[0] Sending request to tr-gateway-%24edgeAgent method RestartModule with {"schemaVersion":"1.0","id":"SimulatedTemperatureSensor"}
<3>Microsoft.Extensions.Hosting.Internal.Host[9] BackgroundService failed Microsoft.Azure.Devices.Client.Exceptions.DeviceNotFoundException: Device {"message":"Client tr-gateway/$edgeAgent not found"} not registered    at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.ExecuteAsync(HttpMethod httpMethod, Uri requestUri, Func`3 modifyRequestMessageAsync, Func`2 isSuccessful, Func`3 processResponseMessageAsync, IDictionary`2 errorMappingOverrides, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[T1,T2](Uri requestUri, T1 entity, IDictionary`2 errorMappingOverrides, IDictionary`2 customHeaders, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.Transport.HttpTransportHandler.InvokeMethodAsync(MethodInvokeRequest methodInvokeRequest, Uri uri, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri, MethodRequest methodRequest, CancellationToken cancellationToken)    at EdgeAgentClient.ModuleBackgroundService.ExecuteAsync(CancellationToken cancellationToken) in C:\code\temp\EdgeAgentClient\ModuleBackgroundService.cs:line 55    at Microsoft.Extensions.Hosting.Internal.Host.TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)
<2>Microsoft.Extensions.Hosting.Internal.Host[10] The HostOptions.BackgroundServiceExceptionBehavior is configured to StopHost. A BackgroundService has thrown an unhandled exception, and the IHost instance is stopping. To avoid this behavior, configure this to Ignore; however the BackgroundService will not be restarted. Microsoft.Azure.Devices.Client.Exceptions.DeviceNotFoundException: Device {"message":"Client tr-gateway/$edgeAgent not found"} not registered    at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.ExecuteAsync(HttpMethod httpMethod, Uri requestUri, Func`3 modifyRequestMessageAsync, Func`2 isSuccessful, Func`3 processResponseMessageAsync, IDictionary`2 errorMappingOverrides, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.PostAsync[T1,T2](Uri requestUri, T1 entity, IDictionary`2 errorMappingOverrides, IDictionary`2 customHeaders, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.Transport.HttpTransportHandler.InvokeMethodAsync(MethodInvokeRequest methodInvokeRequest, Uri uri, CancellationToken cancellationToken)    at Microsoft.Azure.Devices.Client.ModuleClient.InvokeMethodAsync(Uri uri, MethodRequest methodRequest, CancellationToken cancellationToken)    at EdgeAgentClient.ModuleBackgroundService.ExecuteAsync(CancellationToken cancellationToken) in C:\code\temp\EdgeAgentClient\ModuleBackgroundService.cs:line 55    at Microsoft.Extensions.Hosting.Internal.Host.TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)

@rido-min
Copy link
Member

reported in Azure/iotedge#7116

@rido-min
Copy link
Member

This sample invokes a method on a device, but should be straight forward to update to use the overload that accepts the moduleId

await InvokeMethodAsync(parameters.DeviceId, serviceClient);

@malavvakharia
Copy link
Author

malavvakharia commented Sep 27, 2023

This sample invokes a method on a device, but should be straight forward to update to use the overload that accepts the moduleId

await InvokeMethodAsync(parameters.DeviceId, serviceClient);

@rido-min,

That I have already tried just quick question here we have to pass which connection string here? (Custom module, $edgeAgentModule or Hub)

Reason I have already tried with the custom module connection string and as mentioned earlier It will give the authorization error.

If we have to use $edgeAgent connection string, then how to automatically fetch it in our custom module?

using var serviceClient = ServiceClient.CreateFromConnectionString(parameters.HubConnectionString);

@malavvakharia
Copy link
Author

This sample invokes a method on a device, but should be straight forward to update to use the overload that accepts the moduleId

await InvokeMethodAsync(parameters.DeviceId, serviceClient);

@rido-min,

That I have already tried just quick question here we have to pass which connection string here? (Custom module, $edgeAgentModule or Hub)

Reason I have already tried with the custom module connection string and as mentioned earlier It will give the authorization error.

If we have to use $edgeAgent connection string, then how to automatically fetch it in our custom module?

using var serviceClient = ServiceClient.CreateFromConnectionString(parameters.HubConnectionString);

image

@rido-min, Got the success but have just one doubt how to get the iot hub connection string from custom module environment variable? Do you have any idea?

For example, we get the device-Id like below:

string deviceId = Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID");

@rido-min
Copy link
Member

the iothub connection string is not available for devices, you will need (not recommended) to include it in your module deployment.

@malavvakharia
Copy link
Author

the iothub connection string is not available for devices, you will need (not recommended) to include it in your module deployment.

@rido-min ,
Then that is not best practice how to set the connection string ?

@rido-min
Copy link
Member

you can include the connection string in the configuration of your module, or as environment variable, but again this is strongly discouraged.

I'd like to understand your use case.. Why do you need to restart a module from another module?

@rido-min
Copy link
Member

rido-min commented Oct 4, 2023

@malavvakharia
this is a not supported feature, is there anything else we can do to help? otherwise please consider closing this issue

@malavvakharia
Copy link
Author

malavvakharia commented Oct 5, 2023

@rido-min ,
I can close ticket but as you mentioned "you can include the connection string in the configuration of your module, or as environment variable, but again this is strongly discouraged."(#3373 (comment)) so how to handle this situation? As you say there is not any inbuilt environment variable to fetch the device connection string then how to do that?

Use case: Need to restart the custom module when any twin change detection found.

@rido-min
Copy link
Member

rido-min commented Oct 5, 2023

[please dont do this] you can add any string to the environment variables section in the deployManifest.json.

I would try to avoid restarting the module, the SDK APIs should let you to observe any twin changes and process those without restarting

@malavvakharia
Copy link
Author

@rido-min ,

Agreed what you are saying regarding the restart module, in my use case I am using the broker details from twin and connect to it as well as use it's disconnect event to retry the connection now If I try to disconnect to that broker as twin changes it will try to retry the connection as per logic so In that case I have to restart the module or handle it on other way so it will not try to retry the connection again if that broker details removed from the twin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working. IoTSDK Tracks all IoT SDK issues across the board
Projects
None yet
Development

No branches or pull requests

2 participants