diff --git a/sdk/src/Core/AWSXRayRecorder.Core.csproj b/sdk/src/Core/AWSXRayRecorder.Core.csproj index 760a603d..105d7ded 100644 --- a/sdk/src/Core/AWSXRayRecorder.Core.csproj +++ b/sdk/src/Core/AWSXRayRecorder.Core.csproj @@ -39,7 +39,6 @@ - diff --git a/sdk/src/Core/Sampling/DefaultSamplingStrategy.cs b/sdk/src/Core/Sampling/DefaultSamplingStrategy.cs index ea9e02e5..7fecca1b 100644 --- a/sdk/src/Core/Sampling/DefaultSamplingStrategy.cs +++ b/sdk/src/Core/Sampling/DefaultSamplingStrategy.cs @@ -43,9 +43,9 @@ public class DefaultSamplingStrategy : ISamplingStrategy public DaemonConfig DaemonCfg { get; private set; } /// - /// Instance of . + /// Instance of . /// - public AmazonXRayClient XRayClient = null; + public XRayConfig XRayConfig = null; /// /// Instance of . @@ -81,7 +81,7 @@ private void Start() { if (!_isPollerStarted) { - _connector = new ServiceConnector(DaemonCfg, XRayClient); + _connector = new ServiceConnector(DaemonCfg, XRayConfig); _rulePoller.Poll(_connector); _targetPoller.Poll(_connector); _isPollerStarted = true; diff --git a/sdk/src/Core/Sampling/GetSamplingRulesResponse.cs b/sdk/src/Core/Sampling/GetSamplingRulesResponse.cs index 624f6475..a9576622 100644 --- a/sdk/src/Core/Sampling/GetSamplingRulesResponse.cs +++ b/sdk/src/Core/Sampling/GetSamplingRulesResponse.cs @@ -19,7 +19,7 @@ namespace Amazon.XRay.Recorder.Core.Sampling { /// - /// Wrapper to API call response. + /// Class for keep list of sampling rules from x-ray backend. /// public class GetSamplingRulesResponse { diff --git a/sdk/src/Core/Sampling/GetSamplingTargetsResponse.cs b/sdk/src/Core/Sampling/GetSamplingTargetsResponse.cs index bf69a3f4..9fbba06a 100644 --- a/sdk/src/Core/Sampling/GetSamplingTargetsResponse.cs +++ b/sdk/src/Core/Sampling/GetSamplingTargetsResponse.cs @@ -19,7 +19,7 @@ namespace Amazon.XRay.Recorder.Core.Sampling { /// - /// Wrapper for API call response. + /// Class for keep last rule modification timestamp and list of sampling targets from x-ray backend. /// public class GetSamplingTargetsResponse { diff --git a/sdk/src/Core/Sampling/Model/SamplingRuleModel.cs b/sdk/src/Core/Sampling/Model/SamplingRuleModel.cs new file mode 100644 index 00000000..65ee367f --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingRuleModel.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class model for unmarshalling sampling rule from sampling rule response json. + /// + public class SamplingRuleModel + { + public string RuleName; + + public int? Priority; + + public double? FixedRate; + + public int? ReservoirSize; + + public string Host; + + public string ServiceName; + + public string HTTPMethod; + + public string URLPath; + + public string ServiceType; + + public string ResourceARN; + + public string RuleARN; + + public int? Version; + + public Dictionary Attributes; + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingRuleRecordsModel.cs b/sdk/src/Core/Sampling/Model/SamplingRuleRecordsModel.cs new file mode 100644 index 00000000..25f030df --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingRuleRecordsModel.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class model for unmarshalling sampling rule record from sampling rule response json. + /// + public class SamplingRuleRecordsModel + { + public double? CreatedAt; + + public double? ModifiedAt; + + public SamplingRuleModel SamplingRule; + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingRuleResponseModel.cs b/sdk/src/Core/Sampling/Model/SamplingRuleResponseModel.cs new file mode 100644 index 00000000..f805ad1e --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingRuleResponseModel.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class model for unmarshalling sampling rule response json. + /// + public class SamplingRuleResponseModel + { + public string NextToken; + + public List SamplingRuleRecords = new List(); + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingStatisticsDocumentModel.cs b/sdk/src/Core/Sampling/Model/SamplingStatisticsDocumentModel.cs new file mode 100644 index 00000000..3182db0b --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingStatisticsDocumentModel.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class for marshalling sampling statistics document json. + /// + public class SamplingStatisticsDocumentModel + { + public string ClientID { get; set; } + + public string RuleName { get; set; } + + public int? RequestCount { get; set; } + + public int? SampledCount { get; set; } + + public int? BorrowCount { get; set; } + + public double? Timestamp { get; set; } + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingStatisticsModel.cs b/sdk/src/Core/Sampling/Model/SamplingStatisticsModel.cs new file mode 100644 index 00000000..69f5917c --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingStatisticsModel.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class for marshalling sampling statistics document json. + /// + public class SamplingStatisticsModel + { + public List SamplingStatisticsDocuments { get; set; } = new List(); + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingTargetModel.cs b/sdk/src/Core/Sampling/Model/SamplingTargetModel.cs new file mode 100644 index 00000000..56c59c39 --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingTargetModel.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class model for unmarshalling sampling target from sampling target response json. + /// + public class SamplingTargetModel + { + public double? FixedRate; + + public int? ReservoirQuota; + + public double? ReservoirQuotaTTL; + + public string RuleName; + + public int? Interval; + } +} diff --git a/sdk/src/Core/Sampling/Model/SamplingTargetResponseModel.cs b/sdk/src/Core/Sampling/Model/SamplingTargetResponseModel.cs new file mode 100644 index 00000000..bc5e395d --- /dev/null +++ b/sdk/src/Core/Sampling/Model/SamplingTargetResponseModel.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class model for unmarshalling sampling target response json. + /// + public class SamplingTargetResponseModel + { + public double? LastRuleModification; + + public List SamplingTargetDocuments = new List(); + + public List UnprocessedStatistics = new List(); + } +} diff --git a/sdk/src/Core/Sampling/Model/UnprocessedStatisticsModel.cs b/sdk/src/Core/Sampling/Model/UnprocessedStatisticsModel.cs new file mode 100644 index 00000000..ec0ee8f6 --- /dev/null +++ b/sdk/src/Core/Sampling/Model/UnprocessedStatisticsModel.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +namespace Amazon.XRay.Recorder.Core.Sampling.Model +{ + /// + /// Class for unmarshalling unprocessed statistics from sampling target response json. + /// + public class UnprocessedStatisticsModel + { + public string RuleName; + + public string ErrorCode; + + public string Message; + } +} diff --git a/sdk/src/Core/Sampling/SamplingRule.cs b/sdk/src/Core/Sampling/SamplingRule.cs index 2f8843e6..02e0b15c 100644 --- a/sdk/src/Core/Sampling/SamplingRule.cs +++ b/sdk/src/Core/Sampling/SamplingRule.cs @@ -85,9 +85,9 @@ internal void IncrementSampledCount() /// /// Validates sampling rule. ResourceARN with "*" value is valid. SDK doesn't support Atrributes parameter with any value. /// - /// Instance of + /// Instance of /// True, if the rule is valid else false. - internal static bool IsValid(Model.SamplingRule rule) + internal static bool IsValid(Model.SamplingRuleModel rule) { if (!string.Equals(rule.ResourceARN, "*")) { diff --git a/sdk/src/Core/Sampling/ServiceConnector.cs b/sdk/src/Core/Sampling/ServiceConnector.cs index de783568..5ca4ebf9 100644 --- a/sdk/src/Core/Sampling/ServiceConnector.cs +++ b/sdk/src/Core/Sampling/ServiceConnector.cs @@ -14,13 +14,16 @@ // permissions and limitations under the License. // //----------------------------------------------------------------------------- + using System.Collections.Generic; -using Amazon.XRay.Model; using System.Threading.Tasks; using System; -using Amazon.Runtime; using Amazon.XRay.Recorder.Core.Internal.Utils; using Amazon.Runtime.Internal.Util; +using Amazon.XRay.Recorder.Core.Sampling.Model; +using ThirdParty.LitJson; +using System.Text; +using System.Net.Http; namespace Amazon.XRay.Recorder.Core.Sampling { @@ -32,19 +35,19 @@ namespace Amazon.XRay.Recorder.Core.Sampling class ServiceConnector : IConnector { private static readonly Logger _logger = Logger.GetLogger(typeof(ServiceConnector)); - private AmazonXRayClient _xrayClient; + private XRayConfig _xrayConfig; private readonly object _xrayClientLock = new object(); private const int Version = 1; - private readonly AmazonXRayConfig _config = new AmazonXRayConfig(); - private readonly AWSCredentials _credentials = new AnonymousAWSCredentials(); // sends unsigned requests to daemon endpoint private readonly DaemonConfig _daemonConfig; + private readonly DateTime EpochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly HttpClient _httpClient = new HttpClient(); /// /// Client id for the instance. Its 24 digit hex number. /// public string ClientID; - public ServiceConnector(DaemonConfig daemonConfig, AmazonXRayClient xrayClient) + public ServiceConnector(DaemonConfig daemonConfig, XRayConfig xrayConfig) { ClientID = ThreadSafeRandom.GenerateHexNumber(24); if (daemonConfig == null) @@ -53,61 +56,96 @@ public ServiceConnector(DaemonConfig daemonConfig, AmazonXRayClient xrayClient) } _daemonConfig = daemonConfig; - if (xrayClient == null) + if (xrayConfig == null) { - xrayClient = CreateXRayClient(); + xrayConfig = CreateXRayConfig(); } - _xrayClient = xrayClient; + _xrayConfig = xrayConfig; } - private AmazonXRayClient CreateXRayClient() + private XRayConfig CreateXRayConfig() { - _config.ServiceURL = $"http://{_daemonConfig.TCPEndpoint.Address}:{_daemonConfig.TCPEndpoint.Port}"; - return new AmazonXRayClient(_credentials,_config); + var config = new XRayConfig(); + config.ServiceURL = $"http://{_daemonConfig.TCPEndpoint.Address}:{_daemonConfig.TCPEndpoint.Port}"; + return config; } private void RefreshEndPoint() { var serviceUrlCandidate = $"http://{_daemonConfig.TCPEndpoint.Address}:{_daemonConfig.TCPEndpoint.Port}"; - - if (serviceUrlCandidate.Equals(_xrayClient.Config.ServiceURL)) return; // endpoint do not need refreshing - _config.ServiceURL = serviceUrlCandidate; - _xrayClient = new AmazonXRayClient(_credentials, _config); - _logger.DebugFormat($"ServiceConnector Endpoint refreshed to: {_xrayClient.Config.ServiceURL}"); + if (serviceUrlCandidate.Equals(_xrayConfig.ServiceURL)) return; // endpoint do not need refreshing + + _xrayConfig.ServiceURL = serviceUrlCandidate; + _logger.DebugFormat($"ServiceConnector Endpoint refreshed to: {_xrayConfig.ServiceURL}"); } /// - /// Use X-Ray client to get the sampling rules - /// from X-Ray service.The call is proxied and signed by X-Ray Daemon. + /// Get the sampling rules from X-Ray service.The call is proxied and signed by X-Ray Daemon. /// /// - public async Task GetSamplingRules() + public async Task GetSamplingRules() { - List newRules = new List(); - GetSamplingRulesRequest request = new GetSamplingRulesRequest(); - - Task responseTask; + Task responseTask; lock (_xrayClientLock) { RefreshEndPoint(); - responseTask = _xrayClient.GetSamplingRulesAsync(request); + responseTask = GetSamplingInfoAsync(_xrayConfig.ServiceURL + "/GetSamplingRules", string.Empty); } - var response = await responseTask; + var responseContent = await responseTask; + + List samplingRules = UnmarshallSamplingRuleResponse(responseContent); + + GetSamplingRulesResponse result = new GetSamplingRulesResponse(samplingRules); + return result; + } - foreach(var record in response.SamplingRuleRecords) + private async Task GetSamplingInfoAsync(string url, string content) + { + using (var stringContent = new StringContent(content, Encoding.UTF8, "application/json")) { - var rule = record.SamplingRule; - if (rule.Version == Version && SamplingRule.IsValid(rule)) // We currently only handle v1 sampling rules. + // Need to set header "ExpectContinue" as false for Daemon to sign properly. + // https://github.com/aws/aws-sdk-net/blob/master/sdk/src/Core/Amazon.Runtime/Internal/AmazonWebServiceRequest.cs#L41 + _httpClient.DefaultRequestHeaders.ExpectContinue = false; + using (var response = await _httpClient.PostAsync(url, stringContent)) { - var sampleRule = new SamplingRule(rule.RuleName, rule.Priority, rule.FixedRate, rule.ReservoirSize, rule.Host, rule.ServiceName, rule.HTTPMethod, rule.URLPath, rule.ServiceType, rule.ResourceARN, rule.Attributes); - newRules.Add(sampleRule); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadAsStringAsync(); } } + } - GetSamplingRulesResponse result = new GetSamplingRulesResponse(newRules); - return result; + private List UnmarshallSamplingRuleResponse(string responseContent) + { + List samplingRules = new List(); + + var samplingRuleResponse = JsonMapper.ToObject(responseContent); + + foreach (var samplingRuleRecord in samplingRuleResponse.SamplingRuleRecords) + { + var samplingRuleModel = samplingRuleRecord.SamplingRule; + if (samplingRuleModel.Version.GetValueOrDefault() == Version && SamplingRule.IsValid(samplingRuleModel)) + { + var samplingRule = new SamplingRule + ( + samplingRuleModel.RuleName, + samplingRuleModel.Priority.GetValueOrDefault(), + samplingRuleModel.FixedRate.GetValueOrDefault(), + samplingRuleModel.ReservoirSize.GetValueOrDefault(), + samplingRuleModel.Host, + samplingRuleModel.ServiceName, + samplingRuleModel.HTTPMethod, + samplingRuleModel.URLPath, + samplingRuleModel.ServiceType, + samplingRuleModel.ResourceARN, + samplingRuleModel.Attributes + ); + samplingRules.Add(samplingRule); + } + } + + return samplingRules; } /// @@ -119,47 +157,83 @@ public async Task GetSamplingRules() /// Instance of . public async Task GetSamplingTargets(List rules) { - GetSamplingTargetsRequest request = new GetSamplingTargetsRequest(); - IList newTargets = new List(); DateTime currentTime = TimeStamp.CurrentDateTime(); - List samplingStatisticsDocuments = GetSamplingStatisticsDocuments(rules, currentTime); - request.SamplingStatisticsDocuments = samplingStatisticsDocuments; - Task responseTask; + List samplingStatisticsDocumentModels = GetSamplingStatisticsDocuments(rules, currentTime); + var samplingStatisticsModel = new SamplingStatisticsModel(); + samplingStatisticsModel.SamplingStatisticsDocuments = samplingStatisticsDocumentModels; + + string requestContent = JsonMapper.ToJson(samplingStatisticsModel); // Marshall SamplingStatisticsDocument to json + + Task responseTask; lock (_xrayClientLock) { RefreshEndPoint(); - responseTask = _xrayClient.GetSamplingTargetsAsync(request); + responseTask = GetSamplingInfoAsync(_xrayConfig.ServiceURL + "/SamplingTargets", requestContent); } - var response = await responseTask; - foreach (var record in response.SamplingTargetDocuments) + var responseContent = await responseTask; + + var samplingTargetResponse = UnmarshallSamplingTargetResponse(responseContent); + + var targetList = ConvertTargetList(samplingTargetResponse.SamplingTargetDocuments); + + GetSamplingTargetsResponse result = new GetSamplingTargetsResponse(targetList); + result.RuleFreshness = new TimeStamp(ConvertDoubleToDateTime(samplingTargetResponse.LastRuleModification)); + return result; + } + + private List ConvertTargetList(List targetModels) + { + List result = new List(); + foreach (var targetModel in targetModels) { - Target t = new Target(record.RuleName, record.FixedRate, record.ReservoirQuota, record.ReservoirQuotaTTL, record.Interval); - newTargets.Add(t); + Target t = new Target + ( + targetModel.RuleName, + targetModel.FixedRate.GetValueOrDefault(), + targetModel.ReservoirQuota.GetValueOrDefault(), + ConvertDoubleToDateTime(targetModel.ReservoirQuotaTTL), + targetModel.Interval.GetValueOrDefault() + ); + result.Add(t); } - - GetSamplingTargetsResponse result = new GetSamplingTargetsResponse(newTargets); - result.RuleFreshness = new TimeStamp(response.LastRuleModification); return result; } - private List GetSamplingStatisticsDocuments(List rules, DateTime currentTime) + private SamplingTargetResponseModel UnmarshallSamplingTargetResponse(string responseContent) { - List samplingStatisticsDocuments = new List(); + var samplingTargetResponse = JsonMapper.ToObject(responseContent); + + return samplingTargetResponse; + } + + private List GetSamplingStatisticsDocuments(List rules, DateTime currentTime) + { + List samplingStatisticsDocumentModels = new List(); foreach (var rule in rules) { Statistics statistics = rule.SnapShotStatistics(); - SamplingStatisticsDocument doc = new SamplingStatisticsDocument(); - doc.ClientID = ClientID; - doc.RuleName = rule.RuleName; - doc.RequestCount = statistics.RequestCount; - doc.SampledCount = statistics.SampledCount; - doc.BorrowCount = statistics.BorrowCount; - doc.Timestamp = currentTime; - samplingStatisticsDocuments.Add(doc); + SamplingStatisticsDocumentModel item = new SamplingStatisticsDocumentModel(); + item.ClientID = ClientID; + item.RuleName = rule.RuleName; + item.RequestCount = statistics.RequestCount; + item.SampledCount = statistics.SampledCount; + item.BorrowCount = statistics.BorrowCount; + item.Timestamp = ConvertDateTimeToDouble(currentTime); + samplingStatisticsDocumentModels.Add(item); } - return samplingStatisticsDocuments; + return samplingStatisticsDocumentModels; + } + + private double ConvertDateTimeToDouble(DateTime currentTime) + { + var current = new TimeSpan(currentTime.ToUniversalTime().Ticks - EpochStart.Ticks); + return Math.Round(current.TotalMilliseconds, 0) / 1000.0; + } + + private DateTime ConvertDoubleToDateTime(double? seconds) + { + return seconds == null ? default(DateTime) : EpochStart.AddSeconds(seconds.GetValueOrDefault()); } } } - diff --git a/sdk/src/Core/Sampling/XRayConfig.cs b/sdk/src/Core/Sampling/XRayConfig.cs new file mode 100644 index 00000000..35795dde --- /dev/null +++ b/sdk/src/Core/Sampling/XRayConfig.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +namespace Amazon.XRay.Recorder.Core.Sampling +{ + /// + /// Class for xray configuration for getting sampling rules and sampling targets. + /// + public class XRayConfig + { + /// + /// Gets and sets of the ServiceURL property. + /// + public string ServiceURL { get; set; } + + /// + /// Default constructor. + /// + public XRayConfig() + { + } + } +} diff --git a/sdk/test/UnitTests/AWSXRayRecorder.UnitTests.csproj b/sdk/test/UnitTests/AWSXRayRecorder.UnitTests.csproj index 70129ece..c2e116cc 100644 --- a/sdk/test/UnitTests/AWSXRayRecorder.UnitTests.csproj +++ b/sdk/test/UnitTests/AWSXRayRecorder.UnitTests.csproj @@ -40,7 +40,6 @@ - diff --git a/sdk/test/UnitTests/JsonMapperTest.cs b/sdk/test/UnitTests/JsonMapperTest.cs new file mode 100644 index 00000000..65c16807 --- /dev/null +++ b/sdk/test/UnitTests/JsonMapperTest.cs @@ -0,0 +1,275 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// A copy of the License is located at +// +// http://aws.amazon.com/apache2.0 +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//----------------------------------------------------------------------------- + +using Amazon.XRay.Recorder.Core.Sampling.Model; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ThirdParty.LitJson; + +namespace Amazon.XRay.Recorder.UnitTests +{ + [TestClass] + public class JsonMapperTest + { + [TestMethod] + public void TestMarshallSamplingStatisticsDocuments() + { + var samplingStatisticsModel = new SamplingStatisticsModel(); + var samplingStatisticsDocumentModel = new SamplingStatisticsDocumentModel + { + ClientID = "07492221d7fd13a86e750de2", + RuleName = "Test", + RequestCount = 108, + SampledCount = 4, + BorrowCount = 6, + Timestamp = 1604297926.362 + }; + + samplingStatisticsModel.SamplingStatisticsDocuments.Add(samplingStatisticsDocumentModel); + + var expected = "{\"SamplingStatisticsDocuments\":[{\"ClientID\":\"07492221d7fd13a86e750de2\",\"RuleName\":\"Test\",\"RequestCount\":108,\"SampledCount\":4,\"BorrowCount\":6,\"Timestamp\":1604297926.362}]}"; + var actual = JsonMapper.ToJson(samplingStatisticsModel); + + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public void TestMarshallSamplingStatisticsDocumentsWithEmptyValues() + { + var samplingStatisticsModel = new SamplingStatisticsModel(); + var samplingStatisticsDocumentModel = new SamplingStatisticsDocumentModel(); + + samplingStatisticsModel.SamplingStatisticsDocuments.Add(samplingStatisticsDocumentModel); + + var expected = "{\"SamplingStatisticsDocuments\":[{\"ClientID\":null,\"RuleName\":null,\"RequestCount\":null,\"SampledCount\":null,\"BorrowCount\":null,\"Timestamp\":null}]}"; + var actual = JsonMapper.ToJson(samplingStatisticsModel); + + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public void TestMarshallSamplingStatisticsDocumentsWithEmptyItems() + { + var samplingStatisticsModel = new SamplingStatisticsModel(); + + var expected = "{\"SamplingStatisticsDocuments\":[]}"; + var actual = JsonMapper.ToJson(samplingStatisticsModel); + + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public void TestUnmarshallSamplingRuleResponse() + { + var samplingRuleResponseJson = "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.602621583E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.05,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":1,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-east-1:1234567:sampling-rule/Default\",\"RuleName\":\"Default\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}}]}"; + + var samplingRuleResponseModel = JsonMapper.ToObject(samplingRuleResponseJson); + + Assert.IsNull(samplingRuleResponseModel.NextToken); + Assert.IsTrue(samplingRuleResponseModel.SamplingRuleRecords.Count > 0); + + foreach (var samplingRuleRecord in samplingRuleResponseModel.SamplingRuleRecords) + { + Assert.AreEqual(0.0, samplingRuleRecord.CreatedAt.GetValueOrDefault()); + Assert.AreEqual(1.602621583E9, samplingRuleRecord.ModifiedAt.GetValueOrDefault()); + Assert.IsNotNull(samplingRuleRecord.SamplingRule); + Assert.IsTrue(samplingRuleRecord.SamplingRule.Attributes.Count == 0); + Assert.AreEqual(0.05, samplingRuleRecord.SamplingRule.FixedRate.GetValueOrDefault()); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.HTTPMethod); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.Host); + Assert.AreEqual(10000, samplingRuleRecord.SamplingRule.Priority.GetValueOrDefault()); + Assert.AreEqual(1, samplingRuleRecord.SamplingRule.ReservoirSize.GetValueOrDefault()); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.ResourceARN); + Assert.AreEqual("arn:aws:xray:us-east-1:1234567:sampling-rule/Default", samplingRuleRecord.SamplingRule.RuleARN); + Assert.AreEqual("Default", samplingRuleRecord.SamplingRule.RuleName); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.ServiceName); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.ServiceType); + Assert.AreEqual("*", samplingRuleRecord.SamplingRule.URLPath); + Assert.AreEqual(1, samplingRuleRecord.SamplingRule.Version.GetValueOrDefault()); + } + } + + [TestMethod] + public void TestUnmarshallSamplingRuleResponseWithEmptyValues() + { + var samplingRuleResponseJson = "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":null,\"ModifiedAt\":null,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":null,\"HTTPMethod\":null,\"Host\":null,\"Priority\":null,\"ReservoirSize\":null,\"ResourceARN\":null,\"RuleARN\":null,\"RuleName\":null,\"ServiceName\":null,\"ServiceType\":null,\"URLPath\":null,\"Version\":null}}]}"; + + var samplingRuleResponseModel = JsonMapper.ToObject(samplingRuleResponseJson); + + Assert.IsNull(samplingRuleResponseModel.NextToken); + Assert.IsTrue(samplingRuleResponseModel.SamplingRuleRecords.Count > 0); + + foreach (var samplingRuleRecord in samplingRuleResponseModel.SamplingRuleRecords) + { + Assert.AreEqual(0, samplingRuleRecord.CreatedAt.GetValueOrDefault()); + Assert.AreEqual(0, samplingRuleRecord.ModifiedAt.GetValueOrDefault()); + Assert.IsNotNull(samplingRuleRecord.SamplingRule); + Assert.IsTrue(samplingRuleRecord.SamplingRule.Attributes.Count == 0); + Assert.AreEqual(0, samplingRuleRecord.SamplingRule.FixedRate.GetValueOrDefault()); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.HTTPMethod); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.Host); + Assert.AreEqual(0, samplingRuleRecord.SamplingRule.Priority.GetValueOrDefault()); + Assert.AreEqual(0, samplingRuleRecord.SamplingRule.ReservoirSize.GetValueOrDefault()); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.ResourceARN); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.RuleARN); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.RuleName); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.ServiceName); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.ServiceType); + Assert.AreEqual(null, samplingRuleRecord.SamplingRule.URLPath); + Assert.AreEqual(0, samplingRuleRecord.SamplingRule.Version.GetValueOrDefault()); + } + } + + [TestMethod] + public void TestUnmarshallSamplingRuleResponseWithInvalidFormat() + { + var samplingRuleResponseJson = "{\"a\":null,\"b\":[{\"c\":0.0,\"d\":1.602621583E9,\"e\":{\"f\":{},\"g\":0.05,\"h\":\"*\",\"i\":\"*\",\"j\":10000,\"k\":1,\"l\":\"*\",\"m\":\"arn:aws:xray:us-east-1:1234567:sampling-rule/Default\",\"n\":\"Default\",\"o\":\"*\",\"p\":\"*\",\"q\":\"*\",\"r\":1}}]}"; + + var samplingRuleResponseModel = JsonMapper.ToObject(samplingRuleResponseJson); + + Assert.IsNull(samplingRuleResponseModel.NextToken); + Assert.IsTrue(samplingRuleResponseModel.SamplingRuleRecords.Count == 0); + } + + [TestMethod] + public void TestUnmarshallSamplingRuleResponseWithNull() + { + string samplingRuleResponseJson = ""; + + var samplingRuleResponseModel = JsonMapper.ToObject(samplingRuleResponseJson); + + Assert.IsNull(samplingRuleResponseModel); + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponse() + { + string samplingTargetResponseJson = "{\"LastRuleModification\":1.603923208E9,\"SamplingTargetDocuments\":[{\"FixedRate\":0.05,\"Interval\":2,\"ReservoirQuota\":1,\"ReservoirQuotaTTL\":1.5,\"RuleName\":\"Test\"}],\"UnprocessedStatistics\":[{\"ErrorCode\":\"400\",\"Message\":\"Unknown rule\",\"RuleName\":\"Fault\"}]}"; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.AreEqual(1.603923208E9, samplingTargetResponseModel.LastRuleModification); + Assert.IsTrue(samplingTargetResponseModel.SamplingTargetDocuments.Count > 0); + Assert.IsTrue(samplingTargetResponseModel.UnprocessedStatistics.Count > 0); + + foreach (var target in samplingTargetResponseModel.SamplingTargetDocuments) + { + Assert.AreEqual(0.05, target.FixedRate.GetValueOrDefault()); + Assert.AreEqual(2, target.Interval.GetValueOrDefault()); + Assert.AreEqual(1, target.ReservoirQuota.GetValueOrDefault()); + Assert.AreEqual(1.5, target.ReservoirQuotaTTL.GetValueOrDefault()); + Assert.AreEqual("Test", target.RuleName); + } + + foreach (var unprocessed in samplingTargetResponseModel.UnprocessedStatistics) + { + Assert.AreEqual("400", unprocessed.ErrorCode); + Assert.AreEqual("Unknown rule", unprocessed.Message); + Assert.AreEqual("Fault", unprocessed.RuleName); + } + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponseWithoutSamplingTargetDocuments() + { + string samplingTargetResponseJson = "{\"LastRuleModification\":1.603923208E9,\"SamplingTargetDocuments\":[],\"UnprocessedStatistics\":[{\"ErrorCode\":\"400\",\"Message\":\"Unknown rule\",\"RuleName\":\"Fault\"}]}"; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.AreEqual(1.603923208E9, samplingTargetResponseModel.LastRuleModification); + Assert.IsTrue(samplingTargetResponseModel.SamplingTargetDocuments.Count == 0); + Assert.IsTrue(samplingTargetResponseModel.UnprocessedStatistics.Count > 0); + + foreach (var unprocessed in samplingTargetResponseModel.UnprocessedStatistics) + { + Assert.AreEqual("400", unprocessed.ErrorCode); + Assert.AreEqual("Unknown rule", unprocessed.Message); + Assert.AreEqual("Fault", unprocessed.RuleName); + } + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponseWithoutUnprocessedStatistics() + { + string samplingTargetResponseJson = "{\"LastRuleModification\":1.603923208E9,\"SamplingTargetDocuments\":[{\"FixedRate\":0.05,\"Interval\":2,\"ReservoirQuota\":1,\"ReservoirQuotaTTL\":1.5,\"RuleName\":\"Test\"}],\"UnprocessedStatistics\":[]}"; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.AreEqual(1.603923208E9, samplingTargetResponseModel.LastRuleModification); + Assert.IsTrue(samplingTargetResponseModel.SamplingTargetDocuments.Count > 0); + Assert.IsTrue(samplingTargetResponseModel.UnprocessedStatistics.Count == 0); + + foreach (var target in samplingTargetResponseModel.SamplingTargetDocuments) + { + Assert.AreEqual(0.05, target.FixedRate.GetValueOrDefault()); + Assert.AreEqual(2, target.Interval.GetValueOrDefault()); + Assert.AreEqual(1, target.ReservoirQuota.GetValueOrDefault()); + Assert.AreEqual(1.5, target.ReservoirQuotaTTL.GetValueOrDefault()); + Assert.AreEqual("Test", target.RuleName); + } + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponseWithEmptyValues() + { + string samplingTargetResponseJson = "{\"LastRuleModification\":null,\"SamplingTargetDocuments\":[{\"FixedRate\":null,\"Interval\":null,\"ReservoirQuota\":null,\"ReservoirQuotaTTL\":null,\"RuleName\":null}],\"UnprocessedStatistics\":[{\"ErrorCode\":null,\"Message\":null,\"RuleName\":null}]}"; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.AreEqual(0, samplingTargetResponseModel.LastRuleModification.GetValueOrDefault()); + Assert.IsTrue(samplingTargetResponseModel.SamplingTargetDocuments.Count > 0); + Assert.IsTrue(samplingTargetResponseModel.UnprocessedStatistics.Count > 0); + + foreach (var target in samplingTargetResponseModel.SamplingTargetDocuments) + { + Assert.AreEqual(0, target.FixedRate.GetValueOrDefault()); + Assert.AreEqual(0, target.Interval.GetValueOrDefault()); + Assert.AreEqual(0, target.ReservoirQuota.GetValueOrDefault()); + Assert.AreEqual(0, target.ReservoirQuotaTTL.GetValueOrDefault()); + Assert.IsNull(target.RuleName); + } + + foreach (var unprocessed in samplingTargetResponseModel.UnprocessedStatistics) + { + Assert.IsNull(unprocessed.ErrorCode); + Assert.IsNull(unprocessed.Message); + Assert.IsNull(unprocessed.RuleName); + } + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponseWithInvalidFormat() + { + string samplingTargetResponseJson = "{\"a\":1.603923208E9,\"b\":[{\"c\":0.05,\"d\":2,\"e\":1,\"f\":1.5,\"g\":\"Test\"}],\"h\":[{\"i\":\"400\",\"j\":\"Unknown rule\",\"k\":\"Fault\"}]}"; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.IsNull(samplingTargetResponseModel.LastRuleModification); + Assert.IsTrue(samplingTargetResponseModel.SamplingTargetDocuments.Count == 0); + Assert.IsTrue(samplingTargetResponseModel.UnprocessedStatistics.Count == 0); + } + + [TestMethod] + public void TestUnmarshallSamplingTargetResponseWithNull() + { + string samplingTargetResponseJson = ""; + + var samplingTargetResponseModel = JsonMapper.ToObject(samplingTargetResponseJson); + + Assert.IsNull(samplingTargetResponseModel); + } + } +}