Skip to content

Commit

Permalink
Handle Return Code From Get Module Logs Failure (#5846)
Browse files Browse the repository at this point in the history
The goal of this PR is to handle return code correctly when calling get module logs. 

Earlier Behavior
1. A Request to Edgelet component with an incorrect argument, returned a Bad Request but was never handled and lead to Stream Parsing failure when trying to parse module logs , giving a cryptic error

New Behavior
1. Edge-Agent checks the correct HTTP Status code before trying to parse the Module Log Response

Testing
1. Added Test Case for both Happy-Path and Bad-Path in TestGetModuleLogs Test in E2E Test

## Azure IoT Edge PR checklist:
  • Loading branch information
nimanch authored Nov 24, 2021
1 parent 3c9759f commit 5015eca
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Devices.Edge.Agent.Edgelet.Versioning
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -108,6 +109,17 @@ public virtual async Task<Stream> GetModuleLogs(string module, bool follow, Opti
async () =>
{
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode)
{
switch (httpResponseMessage.StatusCode)
{
case HttpStatusCode.BadRequest:
throw new ArgumentException($"Request Returned Status Code {httpResponseMessage.StatusCode} with Message {await httpResponseMessage.Content.ReadAsStringAsync()}");
default:
throw new EdgeletCommunicationException(await httpResponseMessage.Content.ReadAsStringAsync(), (int)httpResponseMessage.StatusCode);
}
}
return await httpResponseMessage.Content.ReadAsStreamAsync();
},
$"Get logs for {module}");
Expand Down
26 changes: 14 additions & 12 deletions test/Microsoft.Azure.Devices.Edge.Test.Common/IotHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,17 @@ public async Task<Device> CreateEdgeDeviceIdentityAsync(string deviceId, Option<
string parentDeviceScope = parentDevice == null ? string.Empty : parentDevice.Scope;
Log.Verbose($"Parent scope: {parentDeviceScope}");
var result = new Device(deviceId)
{
Authentication = new AuthenticationMechanism()
{
Type = authType,
X509Thumbprint = x509Thumbprint
},
Capabilities = new DeviceCapabilities()
{
IotEdge = true
}
};
{
Authentication = new AuthenticationMechanism()
{
Type = authType,
X509Thumbprint = x509Thumbprint
},
Capabilities = new DeviceCapabilities()
{
IotEdge = true
}
};
result.ParentScopes.Add(parentDeviceScope);
return result;
},
Expand Down Expand Up @@ -184,7 +184,9 @@ public Task<CloudToDeviceMethodResult> InvokeMethodAsync(
{
Log.Verbose($"Method '{method.MethodName}' on '{deviceId}/{moduleId}' returned: " +
$"{result.Status}\n{result.GetPayloadAsJson()}");
return result.Status == 200;
// No Need to retry when server returns Bad Request.
return result.Status == 200 || result.Status == 400;
},
e =>
{
Expand Down
38 changes: 38 additions & 0 deletions test/Microsoft.Azure.Devices.Edge.Test/EdgeAgentDirectMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ await this.runtime.DeployConfigurationAsync(
Context.Current.NestedEdge);
await Task.Delay(30000);

// Verify RFC3339 Operation
string since = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd'T'HH:mm:ssZ");
string until = DateTime.Now.AddDays(+1).ToString("yyyy-MM-dd'T'HH:mm:ssZ");

Expand All @@ -68,6 +69,43 @@ await this.runtime.DeployConfigurationAsync(
string expected = string.Join('\n', Enumerable.Range(0, count)) + "\n";
LogResponse response = JsonConvert.DeserializeObject<LogResponse[]>(result.GetPayloadAsJson()).Single();
Assert.AreEqual(expected, response.Payload);

// Verify Unix Time Operation
since = DateTime.Now.AddDays(-1).ToUnixTimestamp().ToString();
until = DateTime.Now.AddDays(+1).ToUnixTimestamp().ToString();

request = new ModuleLogsRequest("1.0", new List<LogRequestItem> { new LogRequestItem(moduleName, new ModuleLogFilter(Option.Some(10), Option.Some(since), Option.Some(until), Option.None<int>(), Option.None<bool>(), Option.None<string>())) }, LogsContentEncoding.None, LogsContentType.Text);

result = await this.IotHub.InvokeMethodAsync(this.runtime.DeviceId, ConfigModuleName.EdgeAgent, new CloudToDeviceMethod("GetModuleLogs", TimeSpan.FromSeconds(300), TimeSpan.FromSeconds(300)).SetPayloadJson(JsonConvert.SerializeObject(request)), token);

Assert.AreEqual((int)HttpStatusCode.OK, result.Status);

expected = string.Join('\n', Enumerable.Range(0, count)) + "\n";
response = JsonConvert.DeserializeObject<LogResponse[]>(result.GetPayloadAsJson()).Single();
Assert.AreEqual(expected, response.Payload);

// Verify Human Readable Time Operation
since = "1 hour".ToString();
until = "1 second".ToString();

request = new ModuleLogsRequest("1.0", new List<LogRequestItem> { new LogRequestItem(moduleName, new ModuleLogFilter(Option.Some(10), Option.Some(since), Option.Some(until), Option.None<int>(), Option.None<bool>(), Option.None<string>())) }, LogsContentEncoding.None, LogsContentType.Text);

result = await this.IotHub.InvokeMethodAsync(this.runtime.DeviceId, ConfigModuleName.EdgeAgent, new CloudToDeviceMethod("GetModuleLogs", TimeSpan.FromSeconds(300), TimeSpan.FromSeconds(300)).SetPayloadJson(JsonConvert.SerializeObject(request)), token);

Assert.AreEqual((int)HttpStatusCode.OK, result.Status);

expected = string.Join('\n', Enumerable.Range(0, count)) + "\n";
response = JsonConvert.DeserializeObject<LogResponse[]>(result.GetPayloadAsJson()).Single();
Assert.AreEqual(expected, response.Payload);

// Verify Incorrect Timestamp gives correct error
since = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd'T'HH:mm");
until = DateTime.Now.AddDays(+1).ToString("yyyy-MM-dd'T'HH:mm");

request = new ModuleLogsRequest("1.0", new List<LogRequestItem> { new LogRequestItem(moduleName, new ModuleLogFilter(Option.Some(10), Option.Some(since), Option.Some(until), Option.None<int>(), Option.None<bool>(), Option.None<string>())) }, LogsContentEncoding.None, LogsContentType.Text);

result = await this.IotHub.InvokeMethodAsync(this.runtime.DeviceId, ConfigModuleName.EdgeAgent, new CloudToDeviceMethod("GetModuleLogs", TimeSpan.FromSeconds(300), TimeSpan.FromSeconds(300)).SetPayloadJson(JsonConvert.SerializeObject(request)), token);
Assert.AreEqual((int)HttpStatusCode.BadRequest, result.Status);
}

[Test]
Expand Down

0 comments on commit 5015eca

Please sign in to comment.