From dcc01deec2b6f27a5af3e5e3ecbb7f93f5fb0f8a Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 4 Feb 2021 16:21:40 -0800 Subject: [PATCH 01/32] added failure scenario when getting container fails --- detectors/aws/ecs/ecs.go | 7 ++++++- detectors/aws/ecs/ecs_test.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index dcc11955b91..82eb201f142 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -37,6 +37,7 @@ const ( var ( empty = resource.Empty() errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") + errCannotReadContainerName = errors.New("failed to read hostname") errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") errNotOnECS = errors.New("process is not on ECS, cannot detect environment variables from ECS") ) @@ -102,5 +103,9 @@ func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { // returns host name reported by the kernel func (ecsUtils ecsDetectorUtils) getContainerName() (string, error) { - return os.Hostname() + hostName, err := os.Hostname() + if err != nil { + return "", errCannotReadContainerName + } + return hostName, nil } diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 85cbb131288..7d667c35f3b 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -81,6 +81,23 @@ func TestDetectCannotReadContainerID(t *testing.T) { assert.Equal(t, 0, len(resource.Attributes())) } +//returns empty resource when detector cannot read container Name +func TestDetectCannotReadContainerName(t *testing.T) { + os.Clearenv() + os.Setenv(metadataV3EnvVar, "3") + os.Setenv(metadataV4EnvVar, "4") + detectorUtils := new(MockDetectorUtils) + + detectorUtils.On("getContainerName").Return("", errCannotReadContainerName) + detectorUtils.On("getContainerID").Return("0123456789A", nil) + + detector := ResourceDetector{detectorUtils} + resource, err := detector.Detect(context.Background()) + + assert.Equal(t, errCannotReadContainerName, err) + assert.Equal(t, 0, len(resource.Attributes())) +} + //returns empty resource when process is not running ECS func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() From 1dd54d977a8dce0027f0ff4a79afa1aa9c49eaaf Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 4 Feb 2021 16:57:07 -0800 Subject: [PATCH 02/32] fix test case failure --- detectors/aws/ecs/ecs.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 82eb201f142..1bf477345d1 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -35,11 +35,11 @@ const ( ) var ( - empty = resource.Empty() - errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") - errCannotReadContainerName = errors.New("failed to read hostname") - errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") - errNotOnECS = errors.New("process is not on ECS, cannot detect environment variables from ECS") + empty = resource.Empty() + errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") + errCannotReadContainerName = errors.New("failed to read hostname") + errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") + errNotOnECS = errors.New("process is not on ECS, cannot detect environment variables from ECS") ) // Create interface for methods needing to be mocked From 03f6fb4f99601c371279378a4c005dc4bf78867e Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Fri, 5 Feb 2021 10:45:07 -0800 Subject: [PATCH 03/32] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91b685de7d9..39bba122bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Fixed + +- Added failure message for AWS ECS resource detector for better debugging (#568) + ## [0.16.0] - 2021-01-13 ### Fixed From e1ff7d0404815b18be13c39521405cc7d731bfe1 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Sat, 6 Feb 2021 17:04:11 -0800 Subject: [PATCH 04/32] fix ecs resource detector bug --- detectors/aws/ecs/ecs.go | 18 ++++++------------ detectors/aws/ecs/ecs_test.go | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 1bf477345d1..02c2718d78a 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -49,19 +49,13 @@ type detectorUtils interface { } // struct implements detectorUtils interface -type ecsDetectorUtils struct{} +type EcsDetectorUtils struct{} // resource detector collects resource information from Elastic Container Service environment type ResourceDetector struct { - utils detectorUtils + Utils detectorUtils } -// compile time assertion that ecsDetectorUtils implements detectorUtils interface -var _ detectorUtils = (*ecsDetectorUtils)(nil) - -// compile time assertion that resource detector implements the resource.Detector interface. -var _ resource.Detector = (*ResourceDetector)(nil) - // Detect finds associated resources when running on ECS environment. func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resource, error) { metadataURIV3 := os.Getenv(metadataV3EnvVar) @@ -70,11 +64,11 @@ func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resourc if len(metadataURIV3) == 0 && len(metadataURIV4) == 0 { return empty, errNotOnECS } - hostName, err := detector.utils.getContainerName() + hostName, err := detector.Utils.getContainerName() if err != nil { return empty, err } - containerID, err := detector.utils.getContainerID() + containerID, err := detector.Utils.getContainerID() if err != nil { return empty, err } @@ -87,7 +81,7 @@ func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resourc } // returns docker container ID from default c group path -func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { +func (ecsUtils EcsDetectorUtils) getContainerID() (string, error) { fileData, err := ioutil.ReadFile(defaultCgroupPath) if err != nil { return "", errCannotReadCGroupFile @@ -102,7 +96,7 @@ func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { } // returns host name reported by the kernel -func (ecsUtils ecsDetectorUtils) getContainerName() (string, error) { +func (ecsUtils EcsDetectorUtils) getContainerName() (string, error) { hostName, err := os.Hostname() if err != nil { return "", errCannotReadContainerName diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 7d667c35f3b..045648ea035 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -45,8 +45,8 @@ func (detectorUtils *MockDetectorUtils) getContainerName() (string, error) { //succesfully return resource when process is running on Amazon ECS environment func TestDetect(t *testing.T) { os.Clearenv() - os.Setenv(metadataV3EnvVar, "3") - os.Setenv(metadataV4EnvVar, "4") + _ = os.Setenv(metadataV3EnvVar, "3") + _ = os.Setenv(metadataV4EnvVar, "4") detectorUtils := new(MockDetectorUtils) @@ -59,51 +59,51 @@ func TestDetect(t *testing.T) { } expectedResource := resource.NewWithAttributes(labels...) detector := ResourceDetector{detectorUtils} - resource, _ := detector.Detect(context.Background()) + res, _ := detector.Detect(context.Background()) - assert.Equal(t, resource, expectedResource, "Resource returned is incorrect") + assert.Equal(t, res, expectedResource, "Resource returned is incorrect") } //returns empty resource when detector cannot read container ID func TestDetectCannotReadContainerID(t *testing.T) { os.Clearenv() - os.Setenv(metadataV3EnvVar, "3") - os.Setenv(metadataV4EnvVar, "4") + _ = os.Setenv(metadataV3EnvVar, "3") + _ = os.Setenv(metadataV4EnvVar, "4") detectorUtils := new(MockDetectorUtils) detectorUtils.On("getContainerName").Return("container-Name", nil) detectorUtils.On("getContainerID").Return("", errCannotReadContainerID) detector := ResourceDetector{detectorUtils} - resource, err := detector.Detect(context.Background()) + res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerID, err) - assert.Equal(t, 0, len(resource.Attributes())) + assert.Equal(t, 0, len(res.Attributes())) } //returns empty resource when detector cannot read container Name func TestDetectCannotReadContainerName(t *testing.T) { os.Clearenv() - os.Setenv(metadataV3EnvVar, "3") - os.Setenv(metadataV4EnvVar, "4") + _ = os.Setenv(metadataV3EnvVar, "3") + _ = os.Setenv(metadataV4EnvVar, "4") detectorUtils := new(MockDetectorUtils) detectorUtils.On("getContainerName").Return("", errCannotReadContainerName) detectorUtils.On("getContainerID").Return("0123456789A", nil) detector := ResourceDetector{detectorUtils} - resource, err := detector.Detect(context.Background()) + res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerName, err) - assert.Equal(t, 0, len(resource.Attributes())) + assert.Equal(t, 0, len(res.Attributes())) } //returns empty resource when process is not running ECS func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() detector := ResourceDetector{} - resource, err := detector.Detect(context.Background()) + res, err := detector.Detect(context.Background()) assert.Equal(t, errNotOnECS, err) - assert.Equal(t, 0, len(resource.Attributes())) + assert.Equal(t, 0, len(res.Attributes())) } From e12a4b185593413d0d459ffcd5507b20362c4819 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Sat, 6 Feb 2021 17:17:55 -0800 Subject: [PATCH 05/32] fix struct name as per golint suggestion --- detectors/aws/ecs/ecs.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 02c2718d78a..a319454e72b 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -43,17 +43,17 @@ var ( ) // Create interface for methods needing to be mocked -type detectorUtils interface { +type detectorUtilsResources interface { getContainerName() (string, error) getContainerID() (string, error) } // struct implements detectorUtils interface -type EcsDetectorUtils struct{} +type DetectorUtils struct{} // resource detector collects resource information from Elastic Container Service environment type ResourceDetector struct { - Utils detectorUtils + Utils detectorUtilsResources } // Detect finds associated resources when running on ECS environment. @@ -81,7 +81,7 @@ func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resourc } // returns docker container ID from default c group path -func (ecsUtils EcsDetectorUtils) getContainerID() (string, error) { +func (ecsUtils DetectorUtils) getContainerID() (string, error) { fileData, err := ioutil.ReadFile(defaultCgroupPath) if err != nil { return "", errCannotReadCGroupFile @@ -96,7 +96,7 @@ func (ecsUtils EcsDetectorUtils) getContainerID() (string, error) { } // returns host name reported by the kernel -func (ecsUtils EcsDetectorUtils) getContainerName() (string, error) { +func (ecsUtils DetectorUtils) getContainerName() (string, error) { hostName, err := os.Hostname() if err != nil { return "", errCannotReadContainerName From 86c04d1b71f1fcb56703afac36148503098553ec Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Sat, 6 Feb 2021 17:22:49 -0800 Subject: [PATCH 06/32] minor changes --- detectors/aws/ecs/ecs.go | 2 +- detectors/aws/ecs/ecs_test.go | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index a319454e72b..f6237a19f14 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -48,7 +48,7 @@ type detectorUtilsResources interface { getContainerID() (string, error) } -// struct implements detectorUtils interface +// struct implements detectorUtilsResources interface type DetectorUtils struct{} // resource detector collects resource information from Elastic Container Service environment diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 86239f61a81..045648ea035 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -98,23 +98,6 @@ func TestDetectCannotReadContainerName(t *testing.T) { assert.Equal(t, 0, len(res.Attributes())) } -//returns empty resource when detector cannot read container Name -func TestDetectCannotReadContainerName(t *testing.T) { - os.Clearenv() - os.Setenv(metadataV3EnvVar, "3") - os.Setenv(metadataV4EnvVar, "4") - detectorUtils := new(MockDetectorUtils) - - detectorUtils.On("getContainerName").Return("", errCannotReadContainerName) - detectorUtils.On("getContainerID").Return("0123456789A", nil) - - detector := ResourceDetector{detectorUtils} - resource, err := detector.Detect(context.Background()) - - assert.Equal(t, errCannotReadContainerName, err) - assert.Equal(t, 0, len(resource.Attributes())) -} - //returns empty resource when process is not running ECS func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() From 047f5d0c663a283163d8d9154cff2c292bd76b0f Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 9 Feb 2021 09:58:45 -0800 Subject: [PATCH 07/32] added NewResourceDetector func and interface assertions --- detectors/aws/ecs/ecs.go | 21 ++++++++++++++++----- detectors/aws/ecs/ecs_test.go | 8 ++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index f6237a19f14..5978b1476f7 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -52,23 +52,34 @@ type detectorUtilsResources interface { type DetectorUtils struct{} // resource detector collects resource information from Elastic Container Service environment -type ResourceDetector struct { - Utils detectorUtilsResources +type resourceDetector struct { + utils detectorUtilsResources +} + +// compile time assertion that ecsDetectorUtils implements detectorUtilsResources interface +var _ detectorUtilsResources = (*DetectorUtils)(nil) + +// compile time assertion that resource detector implements the resource.Detector interface. +var _ resource.Detector = (*resourceDetector)(nil) + +// returns resource detector struct +func NewResourceDetector(detectorUtils detectorUtilsResources) resourceDetector{ + return resourceDetector{utils: detectorUtils} } // Detect finds associated resources when running on ECS environment. -func (detector *ResourceDetector) Detect(ctx context.Context) (*resource.Resource, error) { +func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resource, error) { metadataURIV3 := os.Getenv(metadataV3EnvVar) metadataURIV4 := os.Getenv(metadataV4EnvVar) if len(metadataURIV3) == 0 && len(metadataURIV4) == 0 { return empty, errNotOnECS } - hostName, err := detector.Utils.getContainerName() + hostName, err := detector.utils.getContainerName() if err != nil { return empty, err } - containerID, err := detector.Utils.getContainerID() + containerID, err := detector.utils.getContainerID() if err != nil { return empty, err } diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 045648ea035..fda1d4ba210 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -58,7 +58,7 @@ func TestDetect(t *testing.T) { semconv.ContainerIDKey.String("0123456789A"), } expectedResource := resource.NewWithAttributes(labels...) - detector := ResourceDetector{detectorUtils} + detector := NewResourceDetector(detectorUtils) res, _ := detector.Detect(context.Background()) assert.Equal(t, res, expectedResource, "Resource returned is incorrect") @@ -74,7 +74,7 @@ func TestDetectCannotReadContainerID(t *testing.T) { detectorUtils.On("getContainerName").Return("container-Name", nil) detectorUtils.On("getContainerID").Return("", errCannotReadContainerID) - detector := ResourceDetector{detectorUtils} + detector := NewResourceDetector(detectorUtils) res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerID, err) @@ -91,7 +91,7 @@ func TestDetectCannotReadContainerName(t *testing.T) { detectorUtils.On("getContainerName").Return("", errCannotReadContainerName) detectorUtils.On("getContainerID").Return("0123456789A", nil) - detector := ResourceDetector{detectorUtils} + detector := NewResourceDetector(detectorUtils) res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerName, err) @@ -101,7 +101,7 @@ func TestDetectCannotReadContainerName(t *testing.T) { //returns empty resource when process is not running ECS func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() - detector := ResourceDetector{} + detector := NewResourceDetector(nil) res, err := detector.Detect(context.Background()) assert.Equal(t, errNotOnECS, err) From 21db8fabc829a60110299e36658c1f9039b704b7 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 9 Feb 2021 14:42:29 -0800 Subject: [PATCH 08/32] fix golint failure --- detectors/aws/ecs/ecs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 5978b1476f7..57376e876e9 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -63,8 +63,8 @@ var _ detectorUtilsResources = (*DetectorUtils)(nil) var _ resource.Detector = (*resourceDetector)(nil) // returns resource detector struct -func NewResourceDetector(detectorUtils detectorUtilsResources) resourceDetector{ - return resourceDetector{utils: detectorUtils} +func NewResourceDetector(detectorUtils detectorUtilsResources) resource.Detector { + return &resourceDetector{utils: detectorUtils} } // Detect finds associated resources when running on ECS environment. From 5204d27fd5ab645060c5f07d746404b6b8ca54df Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 9 Feb 2021 17:24:15 -0800 Subject: [PATCH 09/32] minor changes to address review comments --- detectors/aws/ecs/ecs.go | 22 +++++++++++----------- detectors/aws/ecs/ecs_test.go | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 57376e876e9..2e21bb080b0 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -43,28 +43,28 @@ var ( ) // Create interface for methods needing to be mocked -type detectorUtilsResources interface { +type detectorUtils interface { getContainerName() (string, error) getContainerID() (string, error) } -// struct implements detectorUtilsResources interface -type DetectorUtils struct{} +// struct implements detectorUtils interface +type ecsDetectorUtils struct{} // resource detector collects resource information from Elastic Container Service environment type resourceDetector struct { - utils detectorUtilsResources + utils detectorUtils } -// compile time assertion that ecsDetectorUtils implements detectorUtilsResources interface -var _ detectorUtilsResources = (*DetectorUtils)(nil) +// compile time assertion that ecsDetectorUtils implements detectorUtils interface +var _ detectorUtils = (*ecsDetectorUtils)(nil) // compile time assertion that resource detector implements the resource.Detector interface. var _ resource.Detector = (*resourceDetector)(nil) -// returns resource detector struct -func NewResourceDetector(detectorUtils detectorUtilsResources) resource.Detector { - return &resourceDetector{utils: detectorUtils} +// NewResourceDetector returns a resource detector that will detect AWS ECS resources. +func NewResourceDetector() resource.Detector { + return &resourceDetector{utils: ecsDetectorUtils{}} } // Detect finds associated resources when running on ECS environment. @@ -92,7 +92,7 @@ func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resourc } // returns docker container ID from default c group path -func (ecsUtils DetectorUtils) getContainerID() (string, error) { +func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { fileData, err := ioutil.ReadFile(defaultCgroupPath) if err != nil { return "", errCannotReadCGroupFile @@ -107,7 +107,7 @@ func (ecsUtils DetectorUtils) getContainerID() (string, error) { } // returns host name reported by the kernel -func (ecsUtils DetectorUtils) getContainerName() (string, error) { +func (ecsUtils ecsDetectorUtils) getContainerName() (string, error) { hostName, err := os.Hostname() if err != nil { return "", errCannotReadContainerName diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index fda1d4ba210..c7719102818 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -58,7 +58,7 @@ func TestDetect(t *testing.T) { semconv.ContainerIDKey.String("0123456789A"), } expectedResource := resource.NewWithAttributes(labels...) - detector := NewResourceDetector(detectorUtils) + detector := &resourceDetector{utils: detectorUtils} res, _ := detector.Detect(context.Background()) assert.Equal(t, res, expectedResource, "Resource returned is incorrect") @@ -74,7 +74,7 @@ func TestDetectCannotReadContainerID(t *testing.T) { detectorUtils.On("getContainerName").Return("container-Name", nil) detectorUtils.On("getContainerID").Return("", errCannotReadContainerID) - detector := NewResourceDetector(detectorUtils) + detector := &resourceDetector{utils: detectorUtils} res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerID, err) @@ -91,7 +91,7 @@ func TestDetectCannotReadContainerName(t *testing.T) { detectorUtils.On("getContainerName").Return("", errCannotReadContainerName) detectorUtils.On("getContainerID").Return("0123456789A", nil) - detector := NewResourceDetector(detectorUtils) + detector := &resourceDetector{utils: detectorUtils} res, err := detector.Detect(context.Background()) assert.Equal(t, errCannotReadContainerName, err) @@ -101,7 +101,7 @@ func TestDetectCannotReadContainerName(t *testing.T) { //returns empty resource when process is not running ECS func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() - detector := NewResourceDetector(nil) + detector := &resourceDetector{utils: nil} res, err := detector.Detect(context.Background()) assert.Equal(t, errNotOnECS, err) From 741a28545bdf5a123117b791fadd888db5c626d0 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 30 Nov 2021 15:25:09 -0800 Subject: [PATCH 10/32] fetching sampling rules from X-Ray service --- .github/dependabot.yml | 10 + samplers/aws/go.mod | 5 + samplers/aws/go.sum | 11 + samplers/aws/xray/clock.go | 38 ++ samplers/aws/xray/proxy.go | 81 +++ samplers/aws/xray/rand.go | 114 ++++ samplers/aws/xray/remote_sampler.go | 245 +++++++++ samplers/aws/xray/reservoir.go | 43 ++ samplers/aws/xray/rule_manifest.go | 238 +++++++++ samplers/aws/xray/rule_manifest_test.go | 674 ++++++++++++++++++++++++ samplers/aws/xray/sampling_rule.go | 63 +++ samplers/aws/xray/timer.go | 51 ++ 12 files changed, 1573 insertions(+) create mode 100644 samplers/aws/go.mod create mode 100644 samplers/aws/go.sum create mode 100644 samplers/aws/xray/clock.go create mode 100644 samplers/aws/xray/proxy.go create mode 100644 samplers/aws/xray/rand.go create mode 100644 samplers/aws/xray/remote_sampler.go create mode 100644 samplers/aws/xray/reservoir.go create mode 100644 samplers/aws/xray/rule_manifest.go create mode 100644 samplers/aws/xray/rule_manifest_test.go create mode 100644 samplers/aws/xray/sampling_rule.go create mode 100644 samplers/aws/xray/timer.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 121cd2726ab..6d50c91fd50 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -77,6 +77,16 @@ updates: schedule: interval: "weekly" day: "sunday" + - + package-ecosystem: "gomod" + directory: "/samplers/aws/xray" + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + interval: "weekly" + day: "sunday" - package-ecosystem: "gomod" directory: "/exporters/metric/cortex" diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod new file mode 100644 index 00000000000..cd1466d769f --- /dev/null +++ b/samplers/aws/go.mod @@ -0,0 +1,5 @@ +module go.opentelemetry.io/contrib/samplers/aws + +go 1.16 + +require github.com/stretchr/testify v1.7.0 diff --git a/samplers/aws/go.sum b/samplers/aws/go.sum new file mode 100644 index 00000000000..acb88a48f68 --- /dev/null +++ b/samplers/aws/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/samplers/aws/xray/clock.go b/samplers/aws/xray/clock.go new file mode 100644 index 00000000000..fb542f6d985 --- /dev/null +++ b/samplers/aws/xray/clock.go @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "time" +) + +// Clock provides an interface to implement method for getting current time. +type Clock interface { + Now() time.Time + Increment(int64, int64) time.Time +} + +// DefaultClock is an implementation of Clock interface. +type DefaultClock struct{} + +// Now returns current time. +func (t *DefaultClock) Now() time.Time { + return time.Now() +} + +// This method returns the current time but can be used to provide different implementation +func (t *DefaultClock) Increment(_, _ int64) time.Time { + return time.Now() +} diff --git a/samplers/aws/xray/proxy.go b/samplers/aws/xray/proxy.go new file mode 100644 index 00000000000..6f3793912b5 --- /dev/null +++ b/samplers/aws/xray/proxy.go @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "encoding/json" + "io" + "log" + "net/http" + "net/url" +) + +type proxy struct { + // http client for sending unsigned proxied requests to the daemon + httpClient *http.Client + + proxyEndpoint string +} + +// newProxy returns a http client with proxy endpoint +func newProxy(d string) (*proxy, error) { + log.Printf("X-Ray proxy using address : %v\n", d) + proxyEndpoint := "http://" + d + + proxyURL, err := url.Parse(proxyEndpoint) + if err != nil { + log.Println("Bad proxy URL", err) + return nil, err + } + + httpClient := &http.Client{ + Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, + } + + p := &proxy{ + httpClient: httpClient, + proxyEndpoint: proxyEndpoint, + } + + return p, nil +} + +// getSamplingRules calls the collector(aws proxy enabled) for sampling rules +func (p *proxy) getSamplingRules() (interface{}, error) { + req, err := http.NewRequest(http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) + if err != nil { + log.Printf("failed to create http request, %v\n", err) + } + + output, err := p.httpClient.Do(req) + if err != nil { + return nil, err + } + + var data map[string]interface{} + err = json.NewDecoder(output.Body).Decode(&data) + if err != nil { + log.Printf("failed to read http response body, %v\n", err) + } + + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + log.Printf("failed to close http response body, %v\n\n", err) + } + }(output.Body) + + return data["SamplingRuleRecords"], nil +} diff --git a/samplers/aws/xray/rand.go b/samplers/aws/xray/rand.go new file mode 100644 index 00000000000..50658e92a07 --- /dev/null +++ b/samplers/aws/xray/rand.go @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + crand "crypto/rand" + "encoding/binary" + "math/rand" + "sync" + "time" +) + +var _ rand.Source = (*lockedSource)(nil) +var _ rand.Source64 = (*lockedSource64)(nil) + +type lockedSource struct { + mu sync.Mutex + src rand.Source +} + +func (src *lockedSource) Int63() int64 { + src.mu.Lock() + defer src.mu.Unlock() + return src.src.Int63() +} + +func (src *lockedSource) Seed(seed int64) { + src.mu.Lock() + defer src.mu.Unlock() + src.src.Seed(seed) +} + +type lockedSource64 struct { + mu sync.Mutex + src rand.Source64 +} + +func (src *lockedSource64) Int63() int64 { + src.mu.Lock() + defer src.mu.Unlock() + return src.src.Int63() +} + +func (src *lockedSource64) Uint64() uint64 { + src.mu.Lock() + defer src.mu.Unlock() + return src.src.Uint64() +} + +func (src *lockedSource64) Seed(seed int64) { + src.mu.Lock() + defer src.mu.Unlock() + src.src.Seed(seed) +} + +func newSeed() int64 { + var seed int64 + if err := binary.Read(crand.Reader, binary.BigEndian, &seed); err != nil { + // fallback to timestamp + seed = time.Now().UnixNano() + } + return seed +} + +func newGlobalRand() *rand.Rand { + src := rand.NewSource(newSeed()) + if src64, ok := src.(rand.Source64); ok { + return rand.New(&lockedSource64{src: src64}) + } + return rand.New(&lockedSource{src: src}) +} + +// Rand is an interface for a set of methods that return random value. +type Rand interface { + Int63n(n int64) int64 + Intn(n int) int + Float64() float64 +} + +// DefaultRand is an implementation of Rand interface. +// It is safe for concurrent use by multiple goroutines. +type DefaultRand struct{} + +var globalRand = newGlobalRand() + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) +// from the default Source. +func (r *DefaultRand) Int63n(n int64) int64 { + return globalRand.Int63n(n) +} + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n) +// from the default Source. +func (r *DefaultRand) Intn(n int) int { + return globalRand.Intn(n) +} + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) +// from the default Source. +func (r *DefaultRand) Float64() float64 { + return globalRand.Float64() +} diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go new file mode 100644 index 00000000000..bfde2af59fb --- /dev/null +++ b/samplers/aws/xray/remote_sampler.go @@ -0,0 +1,245 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "errors" + "fmt" + "log" + "reflect" + "sync" + "time" +) + +// Sampler decides whether a trace should be sampled and exported. +type Sampler interface { + // ToDo: add SamplingResult and SamplingParameters when adding business logic for centralized sampling + // ShouldSample returns a SamplingResult based on a decision made from the + // passed parameters. + ShouldSample() + // Description returns information describing the Sampler. + Description() string +} + +// ToDo: take input from customer as an option when calling RemoteSampler API +//type RemoteSamplerConfig struct { +// // collector ProxyEndpoint to call GetSamplingRules and GetSamplingTargets APIs +// ProxyEndpoint string +// // PollingInterval (seconds) to retrieve sampling rules from AWS X-Ray Console +// PollingInterval int64 +//} + +// RemoteSampler is an implementation of SamplingStrategy. +type RemoteSampler struct { + // List of known centralized sampling rules + manifest *centralizedManifest + + // proxy is used for getting quotas and sampling rules + proxy *proxy + + // pollerStart, if true represents rule and target pollers are started + pollerStart bool + + // Provides system time + clock Clock + + mu sync.RWMutex +} + +// Compile time assertion that remoteSampler implements the Sampler interface. +var _ Sampler = (*RemoteSampler)(nil) + +// NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. +func NewRemoteSampler() *RemoteSampler { + return newRemoteSampler() +} + +func newRemoteSampler() *RemoteSampler { + clock := &DefaultClock{} + + m := ¢ralizedManifest{ + Rules: []*centralizedRule{}, + Index: map[string]*centralizedRule{}, + clock: clock, + } + + return &RemoteSampler{ + pollerStart: false, + clock: clock, + manifest: m, + } +} + +func (rs *RemoteSampler) ShouldSample() { + // ToDo: add business logic for remote sampling + rs.mu.Lock() + if !rs.pollerStart { + rs.start() + } + rs.mu.Unlock() +} + +func (rs *RemoteSampler) Description() string { + return "remote sampling with AWS X-Ray" +} + +func (rs *RemoteSampler) start() { + if !rs.pollerStart { + var er error + // ToDo: add config to set proxy value + rs.proxy, er = newProxy("127.0.0.1:2000") + if er != nil { + panic(er) + } + rs.startRulePoller() + } + + rs.pollerStart = true +} + +func (rs *RemoteSampler) startRulePoller() { + // ToDo: Add logic to do periodic sampling rules call via background goroutines. + // Period = 300s, Jitter = 5s + t := NewTimer(10*time.Second, 5*time.Second) + + for range t.C() { + t.Reset() + if err := rs.refreshManifest(); err != nil { + log.Printf("Error occurred while refreshing sampling rules. %v\n", err) + } else { + log.Println("Successfully fetched sampling rules") + } + } +} + +func (rs *RemoteSampler) refreshManifest() (err error) { + // Explicitly recover from panics since this is the entry point for a long-running goroutine + // and we can not allow a panic to propagate to the application code. + defer func() { + if r := recover(); r != nil { + // Resort to bring rules array into consistent state. + //cs.manifest.sort() + + err = fmt.Errorf("%v", r) + } + }() + + // Compute 'now' before calling GetSamplingRules to avoid marking manifest as + // fresher than it actually is. + now := rs.clock.Now().Unix() + + // Get sampling rules from proxy + records, err := rs.proxy.getSamplingRules() + if err != nil { + return + } + + // Set of rules to exclude from pruning + actives := map[*centralizedRule]bool{} + + // Create missing rules. Update existing ones. + failed := false + + switch x := records.(type) { + case []interface{}: + for _, e := range x { + svcRule := e.(map[string]interface{})["SamplingRule"].(map[string]interface{}) + + ruleProperties := &properties{ + ruleName: svcRule["RuleName"].(string), + serviceType: svcRule["ServiceType"].(string), + resourceARN: svcRule["ResourceARN"].(string), + attributes: svcRule["Attributes"], + serviceName: svcRule["ServiceName"].(string), + host: svcRule["Host"].(string), + httpMethod: svcRule["HTTPMethod"].(string), + urlPath: svcRule["URLPath"].(string), + reservoirSize: int64(svcRule["ReservoirSize"].(float64)), + fixedRate: svcRule["FixedRate"].(float64), + priority: int64(svcRule["Priority"].(float64)), + version: int64(svcRule["Version"].(float64)), + } + + if svcRule == nil { + log.Println("Sampling rule missing from sampling rule record.") + failed = true + continue + } + + if ruleProperties.ruleName == "" { + log.Println("Sampling rule without rule name is not supported") + failed = true + continue + } + + // Only sampling rule with version 1 is valid + if ruleProperties.version == 0 { + log.Println("Sampling rule without version number is not supported: ", ruleProperties.ruleName) + failed = true + continue + } + + if ruleProperties.version != int64(1) { + log.Println("Sampling rule without version 1 is not supported: ", ruleProperties.ruleName) + failed = true + continue + } + + if reflect.ValueOf(ruleProperties.attributes).Len() != 0 { + log.Println("Sampling rule with non nil Attributes is not applicable: ", ruleProperties.ruleName) + continue + } + + if ruleProperties.resourceARN == "" { + log.Println("Sampling rule without ResourceARN is not applicable: ", ruleProperties.ruleName) + continue + } + + if ruleProperties.resourceARN != "*" { + log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", ruleProperties.ruleName) + continue + } + + // Create/update rule + r, putErr := rs.manifest.putRule(ruleProperties) + if putErr != nil { + failed = true + log.Printf("Error occurred creating/updating rule. %v\n", putErr) + } else if r != nil { + actives[r] = true + } + } + default: + log.Printf("unhandled type: %T\n", records) + } + + // Set err if updates failed + if failed { + err = errors.New("error occurred creating/updating rules") + } + + // Prune inactive rules + rs.manifest.prune(actives) + + // Re-sort to fix matching priorities + rs.manifest.sort() + + // Update refreshedAt timestamp + rs.manifest.mu.Lock() + rs.manifest.refreshedAt = now + rs.manifest.mu.Unlock() + + return +} diff --git a/samplers/aws/xray/reservoir.go b/samplers/aws/xray/reservoir.go new file mode 100644 index 00000000000..6810c5f86f9 --- /dev/null +++ b/samplers/aws/xray/reservoir.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +// ToDo: other fields will be used in business logic for remote sampling +// centralizedReservoir is a reservoir distributed among all running instances of the SDK +type centralizedReservoir struct { + // Quota assigned to client + //quota int64 + // + // Quota refresh timestamp + //refreshedAt int64 + // + // Quota expiration timestamp + //expiresAt int64 + // + // Polling interval for quota + interval int64 + // + // True if reservoir has been borrowed from this epoch + //borrowed bool + + // Total size of reservoir + capacity int64 + // + // Reservoir consumption for current epoch + //used int64 + // + // Unix epoch. Reservoir usage is reset every second. + //currentEpoch int64 +} diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go new file mode 100644 index 00000000000..87a17309a1b --- /dev/null +++ b/samplers/aws/xray/rule_manifest.go @@ -0,0 +1,238 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "fmt" + "sort" + "strings" + "sync" +) + +const defaultRule = "Default" +const defaultInterval = int64(10) + +//const manifestTTL = 3600 // Seconds + +//// centralizedManifest represents a full sampling ruleset, with a list of +//// custom rules and default values for incoming requests that do +//// not match any of the provided rules. +type centralizedManifest struct { + Default *centralizedRule + Rules []*centralizedRule + Index map[string]*centralizedRule + refreshedAt int64 + clock Clock + mu sync.RWMutex +} + +// putRule updates the named rule if it already exists or creates it if it does not. +// May break ordering of the sorted rules array if it creates a new rule. +func (m *centralizedManifest) putRule(ruleProperties *properties) (r *centralizedRule, err error) { + defer func() { + if x := recover(); x != nil { + err = fmt.Errorf("%v", x) + } + }() + + name := ruleProperties.ruleName + + // Default rule + if name == defaultRule { + m.mu.RLock() + r = m.Default + m.mu.RUnlock() + + // Update rule if already exists + if r != nil { + m.updateDefaultRule(ruleProperties) + + return + } + + // Create Default rule + r = m.createDefaultRule(ruleProperties) + + return + } + + // User-defined rule + m.mu.RLock() + r, ok := m.Index[name] + m.mu.RUnlock() + + // Create rule if it does not exist + if !ok { + r = m.createUserRule(ruleProperties) + + return + } + + // Update existing rule + m.updateUserRule(r, ruleProperties) + + return +} + +// createUserRule creates a user-defined centralizedRule, appends it to the sorted array, +// adds it to the index, and returns the newly created rule. +func (m *centralizedManifest) createUserRule(ruleProperties *properties) *centralizedRule { + // Create CentralizedRule from xraySvc.SamplingRule + clock := &DefaultClock{} + rand := &DefaultRand{} + + cr := ¢ralizedReservoir{ + capacity: ruleProperties.reservoirSize, + interval: defaultInterval, + } + + csr := ¢ralizedRule{ + reservoir: cr, + properties: ruleProperties, + clock: clock, + rand: rand, + } + + m.mu.Lock() + defer m.mu.Unlock() + + // Return early if rule already exists + if r, ok := m.Index[ruleProperties.ruleName]; ok { + return r + } + + // Update sorted array + m.Rules = append(m.Rules, csr) + + // Update index + m.Index[ruleProperties.ruleName] = csr + + return csr +} + +// updateUserRule updates the properties of the user-defined centralizedRule using the given +// *properties. +func (m *centralizedManifest) updateUserRule(r *centralizedRule, ruleProperties *properties) { + r.mu.Lock() + defer r.mu.Unlock() + + r.properties = ruleProperties + r.reservoir.capacity = ruleProperties.reservoirSize +} + +// createDefaultRule creates a default centralizedRule and adds it to the manifest. +func (m *centralizedManifest) createDefaultRule(ruleProperties *properties) *centralizedRule { + // Create CentralizedRule from xraySvc.SamplingRule + clock := &DefaultClock{} + rand := &DefaultRand{} + + cr := ¢ralizedReservoir{ + capacity: ruleProperties.reservoirSize, + interval: defaultInterval, + } + + csr := ¢ralizedRule{ + reservoir: cr, + properties: ruleProperties, + clock: clock, + rand: rand, + } + + m.mu.Lock() + defer m.mu.Unlock() + + // Return early if rule already exists + if d := m.Default; d != nil { + return d + } + + // Update manifest if rule does not exist + m.Default = csr + + // Update index + m.Index[ruleProperties.ruleName] = csr + + return csr +} + +// updateDefaultRule updates the properties of the default CentralizedRule using the given +// *properties. +func (m *centralizedManifest) updateDefaultRule(ruleProperties *properties) { + r := m.Default + + r.mu.Lock() + defer r.mu.Unlock() + + r.properties = ruleProperties + r.reservoir.capacity = ruleProperties.reservoirSize +} + +// prune removes all rules in the manifest not present in the given list of active rules. +// Preserves ordering of sorted array. +func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { + m.mu.Lock() + defer m.mu.Unlock() + + // Iterate in reverse order to avoid adjusting index for each deleted rule + for i := len(m.Rules) - 1; i >= 0; i-- { + r := m.Rules[i] + + if _, ok := actives[r]; !ok { + m.deleteRule(i) + } + } +} + +// deleteRule deletes the rule from the array, and the index. +// Assumes write lock is already held. +// Preserves ordering of sorted array. +func (m *centralizedManifest) deleteRule(idx int) { + // Remove from index + delete(m.Index, m.Rules[idx].ruleName) + + // Delete by reslicing without index + a := append(m.Rules[:idx], m.Rules[idx+1:]...) + + // Set pointer to nil to free capacity from underlying array + m.Rules[len(m.Rules)-1] = nil + + // Assign resliced rules + m.Rules = a +} + +// sort sorts the rule array first by priority and then by rule name. +func (m *centralizedManifest) sort() { + // Comparison function + less := func(i, j int) bool { + if m.Rules[i].priority == m.Rules[j].priority { + return strings.Compare(m.Rules[i].ruleName, m.Rules[j].ruleName) < 0 + } + return m.Rules[i].priority < m.Rules[j].priority + } + + m.mu.Lock() + defer m.mu.Unlock() + + sort.Slice(m.Rules, less) +} + +// expired returns true if the manifest has not been successfully refreshed in +// 'manifestTTL' seconds. +//func (m *centralizedManifest) expired() bool { +// m.mu.RLock() +// defer m.mu.RUnlock() +// +// return m.refreshedAt < m.clock.Now().Unix()-manifestTTL +//} diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go new file mode 100644 index 00000000000..a714ff06729 --- /dev/null +++ b/samplers/aws/xray/rule_manifest_test.go @@ -0,0 +1,674 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// Assert that putRule() creates a new user-defined rule and adds to manifest +func TestCreateUserRule(t *testing.T) { + resARN := "*" + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + rules := []*centralizedRule{r1, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Output of GetSamplingRules API and Input to putRule(). + serviceName := "www.foo.com" + httpMethod := "POST" + urlPath := "/bar/*" + reservoirSize := int64(10) + fixedRate := float64(0.05) + ruleName := "r2" + host := "local" + priority := int64(6) + serviceTye := "*" + + ruleProperties := properties{ + serviceName: serviceName, + httpMethod: httpMethod, + urlPath: urlPath, + reservoirSize: reservoirSize, + fixedRate: fixedRate, + ruleName: ruleName, + priority: priority, + host: host, + serviceType: serviceTye, + resourceARN: resARN, + } + + // Expected centralized sampling rule + clock := &DefaultClock{} + rand := &DefaultRand{} + + cr := ¢ralizedReservoir{ + capacity: 10, + interval: 10, + } + + exp := ¢ralizedRule{ + reservoir: cr, + properties: &ruleProperties, + clock: clock, + rand: rand, + } + + // Add to manifest, index + r2, err := m.putRule(&ruleProperties) + assert.Nil(t, err) + assert.Equal(t, exp, r2) + + // Assert new rule is present in index + r2, ok := m.Index["r2"] + assert.True(t, ok) + assert.Equal(t, exp, r2) + + // Assert new rule present at end of array. putRule() does not preserve order. + r2 = m.Rules[2] + assert.Equal(t, exp, r2) +} + +// Assert that putRule() creates a new default rule and adds to manifest +func TestCreateDefaultRule(t *testing.T) { + m := ¢ralizedManifest{ + Index: map[string]*centralizedRule{}, + } + + // Output of GetSamplingRules API and Input to putRule(). + reservoirSize := int64(10) + fixedRate := float64(0.05) + ruleName := "Default" + + // Expected centralized sampling rule + clock := &DefaultClock{} + rand := &DefaultRand{} + + p := &properties{ + ruleName: ruleName, + reservoirSize: reservoirSize, + fixedRate: fixedRate, + } + + cr := ¢ralizedReservoir{ + capacity: reservoirSize, + interval: 10, + } + + exp := ¢ralizedRule{ + reservoir: cr, + properties: p, + clock: clock, + rand: rand, + } + + // Add to manifest + r, err := m.putRule(p) + assert.Nil(t, err) + assert.Equal(t, exp, r) + assert.Equal(t, exp, m.Default) +} + +// Assert that putRule() updates the default rule +func TestUpdateDefaultRule(t *testing.T) { + clock := &DefaultClock{} + rand := &DefaultRand{} + + // Original default sampling rule + r := ¢ralizedRule{ + properties: &properties{ + ruleName: "Default", + reservoirSize: 10, + fixedRate: 0.05, + }, + reservoir: ¢ralizedReservoir{ + capacity: 10, + }, + clock: clock, + rand: rand, + } + + m := ¢ralizedManifest{ + Default: r, + } + + // Output of GetSamplingRules API and Input to putRule(). + reservoirSize := int64(20) + fixedRate := 0.06 + ruleName := "Default" + + // Expected centralized sampling rule + p := &properties{ + ruleName: ruleName, + reservoirSize: reservoirSize, + fixedRate: fixedRate, + } + + cr := ¢ralizedReservoir{ + capacity: reservoirSize, + } + + exp := ¢ralizedRule{ + reservoir: cr, + properties: p, + clock: clock, + rand: rand, + } + + // Update default rule in manifest + r, err := m.putRule(p) + assert.Nil(t, err) + assert.Equal(t, exp, r) + assert.Equal(t, exp, m.Default) +} + +// Assert that creating a user-defined rule which already exists is a no-op +func TestCreateUserRuleNoOp(t *testing.T) { + resARN := "*" + serviceTye := "" + attributes := make([]interface{}, 2) + + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + reservoir: ¢ralizedReservoir{ + capacity: 5, + }, + } + + rules := []*centralizedRule{r1, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Duplicate rule properties. 'r3' already exists. Input to updateRule(). + serviceName := "www.foo.com" + httpMethod := "POST" + urlPath := "/bar/*" + reservoirSize := int64(10) + fixedRate := float64(0.05) + ruleName := "r3" + priority := int64(6) + host := "h" + ruleProperties := properties{ + serviceName: serviceName, + httpMethod: httpMethod, + urlPath: urlPath, + reservoirSize: reservoirSize, + fixedRate: fixedRate, + ruleName: ruleName, + priority: priority, + host: host, + resourceARN: resARN, + serviceType: serviceTye, + attributes: attributes, + } + + // Assert manifest has not changed + r, err := m.putRule(&ruleProperties) + assert.Nil(t, err) + assert.Equal(t, r3, r) + assert.Equal(t, 2, len(m.Rules)) + assert.Equal(t, 2, len(m.Index)) + assert.Equal(t, r1, m.Rules[0]) + assert.Equal(t, r3, m.Rules[1]) +} + +// Assert that putRule() updates the user-defined rule in the manifest +func TestUpdateUserRule(t *testing.T) { + resARN := "*" + serviceType := "" + attributes := make([]interface{}, 2) + + // Original rule + r1 := ¢ralizedRule{ + + properties: &properties{ + ruleName: "r1", + priority: 5, + serviceName: "*.foo.com", + httpMethod: "GET", + urlPath: "/resource/*", + reservoirSize: 15, + fixedRate: 0.04, + resourceARN: resARN, + serviceType: serviceType, + attributes: attributes, + }, + + reservoir: ¢ralizedReservoir{ + capacity: 5, + }, + } + + rules := []*centralizedRule{r1} + + index := map[string]*centralizedRule{ + "r1": r1, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Updated rule properties. Input to updateRule(). + serviceName := "www.foo.com" + httpMethod := "POST" + urlPath := "/bar/*" + reservoirSize := int64(10) + fixedRate := float64(0.05) + ruleName := "r1" + priority := int64(6) + host := "h" + + updated := properties{ + serviceName: serviceName, + httpMethod: httpMethod, + urlPath: urlPath, + reservoirSize: reservoirSize, + fixedRate: fixedRate, + ruleName: ruleName, + priority: priority, + host: host, + resourceARN: resARN, + serviceType: serviceType, + attributes: attributes, + } + + // Expected updated centralized sampling rule + cr := ¢ralizedReservoir{ + capacity: 10, + } + + exp := ¢ralizedRule{ + reservoir: cr, + properties: &updated, + } + + // Assert that rule has been updated + r, err := m.putRule(&updated) + assert.Nil(t, err) + assert.Equal(t, exp, r) + assert.Equal(t, exp, m.Index["r1"]) + assert.Equal(t, exp, m.Rules[0]) + assert.Equal(t, 1, len(m.Rules)) + assert.Equal(t, 1, len(m.Index)) +} + +// Assert that deleting a rule from the end of the array removes the rule +// and preserves ordering of the sorted array +func TestDeleteLastRule(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r2 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r2", + priority: 6, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + rules := []*centralizedRule{r1, r2, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r2": r2, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{ + r1: true, + r2: true, + } + + // Delete r3 + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 2, len(m.Rules)) + assert.Equal(t, 2, len(m.Index)) + + // Assert index consistency + _, ok := m.Index["r3"] + assert.False(t, ok) + assert.Equal(t, r1, m.Index["r1"]) + assert.Equal(t, r2, m.Index["r2"]) + + // Assert ordering of array + assert.Equal(t, r1, m.Rules[0]) + assert.Equal(t, r2, m.Rules[1]) +} + +// Assert that deleting a rule from the middle of the array removes the rule +// and preserves ordering of the sorted array +func TestDeleteMiddleRule(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r2 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r2", + priority: 6, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + rules := []*centralizedRule{r1, r2, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r2": r2, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{ + r1: true, + r3: true, + } + + // Delete r2 + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 2, len(m.Rules)) + assert.Equal(t, 2, len(m.Index)) + + // Assert index consistency + _, ok := m.Index["r2"] + assert.False(t, ok) + assert.Equal(t, r1, m.Index["r1"]) + assert.Equal(t, r3, m.Index["r3"]) + + // Assert ordering of array + assert.Equal(t, r1, m.Rules[0]) + assert.Equal(t, r3, m.Rules[1]) +} + +// Assert that deleting a rule from the beginning of the array removes the rule +// and preserves ordering of the sorted array +func TestDeleteFirstRule(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r2 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r2", + priority: 6, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + rules := []*centralizedRule{r1, r2, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r2": r2, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{ + r2: true, + r3: true, + } + + // Delete r1 + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 2, len(m.Rules)) + assert.Equal(t, 2, len(m.Index)) + + // Assert index consistency + _, ok := m.Index["r1"] + assert.False(t, ok) + assert.Equal(t, r2, m.Index["r2"]) + assert.Equal(t, r3, m.Index["r3"]) + + // Assert ordering of array + assert.Equal(t, r2, m.Rules[0]) + assert.Equal(t, r3, m.Rules[1]) +} + +// Assert that deleting the only rule from the array removes the rule +func TestDeleteOnlyRule(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + rules := []*centralizedRule{r1} + + index := map[string]*centralizedRule{ + "r1": r1, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{} + + // Delete r1 + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 0, len(m.Rules)) + assert.Equal(t, 0, len(m.Index)) + + // Assert index consistency + _, ok := m.Index["r1"] + assert.False(t, ok) +} + +// Assert that deleting rules from an empty array does not panic +func TestDeleteEmptyRulesArray(t *testing.T) { + var rules []*centralizedRule + + index := map[string]*centralizedRule{} + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{} + + // Delete from empty array + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 0, len(m.Rules)) + assert.Equal(t, 0, len(m.Index)) +} + +// Assert that deleting all rules results in an empty array and does not panic +func TestDeleteAllRules(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r2 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r2", + priority: 6, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + rules := []*centralizedRule{r1, r2, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r2": r2, + "r3": r3, + } + + m := ¢ralizedManifest{ + Rules: rules, + Index: index, + } + + // Active rules to exclude from deletion + a := map[*centralizedRule]bool{} + + // Delete r3 + m.prune(a) + + // Assert size of manifest + assert.Equal(t, 0, len(m.Rules)) + assert.Equal(t, 0, len(m.Index)) +} + +// Assert that sorting an unsorted array results in a sorted array - check priority +func TestSort(t *testing.T) { + r1 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r1", + priority: 5, + }, + } + + r2 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r2", + priority: 6, + }, + } + + r3 := ¢ralizedRule{ + properties: &properties{ + ruleName: "r3", + priority: 7, + }, + } + + // Unsorted rules array + rules := []*centralizedRule{r2, r1, r3} + + m := ¢ralizedManifest{ + Rules: rules, + } + + // Sort array + m.sort() + + // Assert on order + assert.Equal(t, r1, m.Rules[0]) + assert.Equal(t, r2, m.Rules[1]) + assert.Equal(t, r3, m.Rules[2]) +} diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go new file mode 100644 index 00000000000..c9c4f53f123 --- /dev/null +++ b/samplers/aws/xray/sampling_rule.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import "sync" + +// ToDo: other fields will be used in business logic for remote sampling +// centralizedRule represents a centralized sampling rule +type centralizedRule struct { + // Centralized reservoir for keeping track of reservoir usage + reservoir *centralizedReservoir + + // sampling rule properties + *properties + + // Number of requests matched against this rule + //requests float64 + // + // Number of requests sampled using this rule + //sampled float64 + // + // Number of requests burrowed + //borrows float64 + // + // Timestamp for last match against this rule + //usedAt int64 + + // Provides system time + clock Clock + + // Provides random numbers + rand Rand + + mu sync.RWMutex +} + +// properties is the base set of properties that define a sampling rule. +type properties struct { + ruleName string + serviceType string + resourceARN string + attributes interface{} + serviceName string + host string + httpMethod string + urlPath string + reservoirSize int64 + fixedRate float64 + priority int64 + version int64 +} diff --git a/samplers/aws/xray/timer.go b/samplers/aws/xray/timer.go new file mode 100644 index 00000000000..5157aac3277 --- /dev/null +++ b/samplers/aws/xray/timer.go @@ -0,0 +1,51 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "time" +) + +// Timer is the same as time.Timer except that it has jitters. +// A Timer must be created with NewTimer. +type Timer struct { + t *time.Timer + d time.Duration + jitter time.Duration +} + +// NewTimer creates a new Timer that will send the current time on its channel. +func NewTimer(d, jitter time.Duration) *Timer { + t := time.NewTimer(d - time.Duration(globalRand.Int63n(int64(jitter)))) + + jitteredTimer := Timer{ + t: t, + d: d, + jitter: jitter, + } + + return &jitteredTimer +} + +// C is channel. +func (j *Timer) C() <-chan time.Time { + return j.t.C +} + +// Reset resets the timer. +// Reset should be invoked only on stopped or expired timers with drained channels. +func (j *Timer) Reset() { + j.t.Reset(j.d - time.Duration(globalRand.Int63n(int64(j.jitter)))) +} From 17d24f39fbd919d4d67e8bbc058f761834404933 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 6 Dec 2021 13:21:08 -0800 Subject: [PATCH 11/32] updated the code to do some best practices based on review --- samplers/aws/go.mod | 5 +- samplers/aws/go.sum | 11 ++ samplers/aws/xray/{proxy.go => client.go} | 54 ++++-- samplers/aws/xray/clock.go | 6 - samplers/aws/xray/remote_sampler.go | 158 ++++++---------- samplers/aws/xray/rule_manifest.go | 139 +++++++------- samplers/aws/xray/rule_manifest_test.go | 220 +++++++++++----------- samplers/aws/xray/sampling_rule.go | 6 +- 8 files changed, 288 insertions(+), 311 deletions(-) rename samplers/aws/xray/{proxy.go => client.go} (51%) diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod index cd1466d769f..e221ca010aa 100644 --- a/samplers/aws/go.mod +++ b/samplers/aws/go.mod @@ -2,4 +2,7 @@ module go.opentelemetry.io/contrib/samplers/aws go 1.16 -require github.com/stretchr/testify v1.7.0 +require ( + github.com/stretchr/testify v1.7.0 + go.opentelemetry.io/otel/sdk v1.2.0 +) diff --git a/samplers/aws/go.sum b/samplers/aws/go.sum index acb88a48f68..4d29e6bf5bf 100644 --- a/samplers/aws/go.sum +++ b/samplers/aws/go.sum @@ -1,10 +1,21 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/samplers/aws/xray/proxy.go b/samplers/aws/xray/client.go similarity index 51% rename from samplers/aws/xray/proxy.go rename to samplers/aws/xray/client.go index 6f3793912b5..e47b521bd41 100644 --- a/samplers/aws/xray/proxy.go +++ b/samplers/aws/xray/client.go @@ -16,44 +16,41 @@ package xray import ( "encoding/json" - "io" "log" "net/http" "net/url" ) -type proxy struct { - // http client for sending unsigned proxied requests to the daemon +type xrayClient struct { + // http client for sending unsigned proxied requests to the collector httpClient *http.Client proxyEndpoint string } -// newProxy returns a http client with proxy endpoint -func newProxy(d string) (*proxy, error) { - log.Printf("X-Ray proxy using address : %v\n", d) +// newClient returns a http client with proxy endpoint +func newClient(d string) *xrayClient { proxyEndpoint := "http://" + d proxyURL, err := url.Parse(proxyEndpoint) if err != nil { log.Println("Bad proxy URL", err) - return nil, err } httpClient := &http.Client{ Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, } - p := &proxy{ + p := &xrayClient{ httpClient: httpClient, proxyEndpoint: proxyEndpoint, } - return p, nil + return p } // getSamplingRules calls the collector(aws proxy enabled) for sampling rules -func (p *proxy) getSamplingRules() (interface{}, error) { +func (p *xrayClient) getSamplingRules() ([]ruleProperties, error) { req, err := http.NewRequest(http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) if err != nil { log.Printf("failed to create http request, %v\n", err) @@ -67,15 +64,38 @@ func (p *proxy) getSamplingRules() (interface{}, error) { var data map[string]interface{} err = json.NewDecoder(output.Body).Decode(&data) if err != nil { - log.Printf("failed to read http response body, %v\n", err) + return nil, err } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - log.Printf("failed to close http response body, %v\n\n", err) + var rules []ruleProperties + switch x := data["SamplingRuleRecords"].(type) { + case []interface{}: + for _, e := range x { + svcRule := e.(map[string]interface{})["SamplingRule"].(map[string]interface{}) + + rules = append(rules, ruleProperties{ + ruleName: svcRule["RuleName"].(string), + serviceType: svcRule["ServiceType"].(string), + resourceARN: svcRule["ResourceARN"].(string), + attributes: svcRule["Attributes"].(map[string]interface{}), + serviceName: svcRule["ServiceName"].(string), + host: svcRule["Host"].(string), + httpMethod: svcRule["HTTPMethod"].(string), + urlPath: svcRule["URLPath"].(string), + reservoirSize: int64(svcRule["ReservoirSize"].(float64)), + fixedRate: svcRule["FixedRate"].(float64), + priority: int64(svcRule["Priority"].(float64)), + version: int64(svcRule["Version"].(float64)), + }) } - }(output.Body) + default: + log.Printf("unhandled type: %T\n", data["SamplingRuleRecords"]) + } + + err = output.Body.Close() + if err != nil { + log.Printf("failed to close http response body, %v\n\n", err) + } - return data["SamplingRuleRecords"], nil + return rules, nil } diff --git a/samplers/aws/xray/clock.go b/samplers/aws/xray/clock.go index fb542f6d985..a48322da5e0 100644 --- a/samplers/aws/xray/clock.go +++ b/samplers/aws/xray/clock.go @@ -21,7 +21,6 @@ import ( // Clock provides an interface to implement method for getting current time. type Clock interface { Now() time.Time - Increment(int64, int64) time.Time } // DefaultClock is an implementation of Clock interface. @@ -31,8 +30,3 @@ type DefaultClock struct{} func (t *DefaultClock) Now() time.Time { return time.Now() } - -// This method returns the current time but can be used to provide different implementation -func (t *DefaultClock) Increment(_, _ int64) time.Time { - return time.Now() -} diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index bfde2af59fb..b2402fcc293 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -21,25 +21,9 @@ import ( "reflect" "sync" "time" -) - -// Sampler decides whether a trace should be sampled and exported. -type Sampler interface { - // ToDo: add SamplingResult and SamplingParameters when adding business logic for centralized sampling - // ShouldSample returns a SamplingResult based on a decision made from the - // passed parameters. - ShouldSample() - // Description returns information describing the Sampler. - Description() string -} -// ToDo: take input from customer as an option when calling RemoteSampler API -//type RemoteSamplerConfig struct { -// // collector ProxyEndpoint to call GetSamplingRules and GetSamplingTargets APIs -// ProxyEndpoint string -// // PollingInterval (seconds) to retrieve sampling rules from AWS X-Ray Console -// PollingInterval int64 -//} + "go.opentelemetry.io/otel/sdk/trace" +) // RemoteSampler is an implementation of SamplingStrategy. type RemoteSampler struct { @@ -47,7 +31,7 @@ type RemoteSampler struct { manifest *centralizedManifest // proxy is used for getting quotas and sampling rules - proxy *proxy + xrayClient *xrayClient // pollerStart, if true represents rule and target pollers are started pollerStart bool @@ -59,19 +43,15 @@ type RemoteSampler struct { } // Compile time assertion that remoteSampler implements the Sampler interface. -var _ Sampler = (*RemoteSampler)(nil) +var _ trace.Sampler = (*RemoteSampler)(nil) // NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. func NewRemoteSampler() *RemoteSampler { - return newRemoteSampler() -} - -func newRemoteSampler() *RemoteSampler { clock := &DefaultClock{} m := ¢ralizedManifest{ - Rules: []*centralizedRule{}, - Index: map[string]*centralizedRule{}, + rules: []*centralizedRule{}, + index: map[string]*centralizedRule{}, clock: clock, } @@ -79,16 +59,20 @@ func newRemoteSampler() *RemoteSampler { pollerStart: false, clock: clock, manifest: m, + // ToDo: take proxyEndpoint and pollingInterval from users + xrayClient: newClient("127.0.0.1:2000"), } } -func (rs *RemoteSampler) ShouldSample() { +func (rs *RemoteSampler) ShouldSample(parameters trace.SamplingParameters) trace.SamplingResult { // ToDo: add business logic for remote sampling rs.mu.Lock() if !rs.pollerStart { rs.start() } rs.mu.Unlock() + + return trace.SamplingResult{} } func (rs *RemoteSampler) Description() string { @@ -97,12 +81,6 @@ func (rs *RemoteSampler) Description() string { func (rs *RemoteSampler) start() { if !rs.pollerStart { - var er error - // ToDo: add config to set proxy value - rs.proxy, er = newProxy("127.0.0.1:2000") - if er != nil { - panic(er) - } rs.startRulePoller() } @@ -141,7 +119,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { now := rs.clock.Now().Unix() // Get sampling rules from proxy - records, err := rs.proxy.getSamplingRules() + rules, err := rs.xrayClient.getSamplingRules() if err != nil { return } @@ -152,77 +130,49 @@ func (rs *RemoteSampler) refreshManifest() (err error) { // Create missing rules. Update existing ones. failed := false - switch x := records.(type) { - case []interface{}: - for _, e := range x { - svcRule := e.(map[string]interface{})["SamplingRule"].(map[string]interface{}) - - ruleProperties := &properties{ - ruleName: svcRule["RuleName"].(string), - serviceType: svcRule["ServiceType"].(string), - resourceARN: svcRule["ResourceARN"].(string), - attributes: svcRule["Attributes"], - serviceName: svcRule["ServiceName"].(string), - host: svcRule["Host"].(string), - httpMethod: svcRule["HTTPMethod"].(string), - urlPath: svcRule["URLPath"].(string), - reservoirSize: int64(svcRule["ReservoirSize"].(float64)), - fixedRate: svcRule["FixedRate"].(float64), - priority: int64(svcRule["Priority"].(float64)), - version: int64(svcRule["Version"].(float64)), - } - - if svcRule == nil { - log.Println("Sampling rule missing from sampling rule record.") - failed = true - continue - } - - if ruleProperties.ruleName == "" { - log.Println("Sampling rule without rule name is not supported") - failed = true - continue - } - - // Only sampling rule with version 1 is valid - if ruleProperties.version == 0 { - log.Println("Sampling rule without version number is not supported: ", ruleProperties.ruleName) - failed = true - continue - } - - if ruleProperties.version != int64(1) { - log.Println("Sampling rule without version 1 is not supported: ", ruleProperties.ruleName) - failed = true - continue - } - - if reflect.ValueOf(ruleProperties.attributes).Len() != 0 { - log.Println("Sampling rule with non nil Attributes is not applicable: ", ruleProperties.ruleName) - continue - } - - if ruleProperties.resourceARN == "" { - log.Println("Sampling rule without ResourceARN is not applicable: ", ruleProperties.ruleName) - continue - } - - if ruleProperties.resourceARN != "*" { - log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", ruleProperties.ruleName) - continue - } - - // Create/update rule - r, putErr := rs.manifest.putRule(ruleProperties) - if putErr != nil { - failed = true - log.Printf("Error occurred creating/updating rule. %v\n", putErr) - } else if r != nil { - actives[r] = true - } + for _, svcRule := range rules { + if svcRule.ruleName == "" { + log.Println("Sampling rule without rule name is not supported") + failed = true + continue + } + + // Only sampling rule with version 1 is valid + if svcRule.version == 0 { + log.Println("Sampling rule without version number is not supported: ", svcRule.ruleName) + failed = true + continue + } + + if svcRule.version != int64(1) { + log.Println("Sampling rule without version 1 is not supported: ", svcRule.ruleName) + failed = true + continue + } + + if reflect.ValueOf(svcRule.attributes).Len() != 0 { + log.Println("Sampling rule with non nil Attributes is not applicable: ", svcRule.ruleName) + continue + } + + if svcRule.resourceARN == "" { + log.Println("Sampling rule without ResourceARN is not applicable: ", svcRule.ruleName) + continue + } + + if svcRule.resourceARN != "*" { + log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", svcRule.ruleName) + continue + } + + // Create/update rule + r, putErr := rs.manifest.putRule(&svcRule) + if putErr != nil { + failed = true + log.Printf("Error occurred creating/updating rule. %v\n", putErr) + } else if r != nil { + actives[r] = true } - default: - log.Printf("unhandled type: %T\n", records) } // Set err if updates failed diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index 87a17309a1b..c468938db53 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -30,9 +30,9 @@ const defaultInterval = int64(10) //// custom rules and default values for incoming requests that do //// not match any of the provided rules. type centralizedManifest struct { - Default *centralizedRule - Rules []*centralizedRule - Index map[string]*centralizedRule + defaultRule *centralizedRule + rules []*centralizedRule + index map[string]*centralizedRule refreshedAt int64 clock Clock mu sync.RWMutex @@ -40,143 +40,143 @@ type centralizedManifest struct { // putRule updates the named rule if it already exists or creates it if it does not. // May break ordering of the sorted rules array if it creates a new rule. -func (m *centralizedManifest) putRule(ruleProperties *properties) (r *centralizedRule, err error) { +func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, err error) { defer func() { if x := recover(); x != nil { err = fmt.Errorf("%v", x) } }() - name := ruleProperties.ruleName + name := rule.ruleName // Default rule if name == defaultRule { m.mu.RLock() - r = m.Default + r = m.defaultRule m.mu.RUnlock() // Update rule if already exists if r != nil { - m.updateDefaultRule(ruleProperties) + m.updateDefaultRule(rule) return } // Create Default rule - r = m.createDefaultRule(ruleProperties) + r = m.createDefaultRule(rule) return } // User-defined rule m.mu.RLock() - r, ok := m.Index[name] + r, ok := m.index[name] m.mu.RUnlock() // Create rule if it does not exist if !ok { - r = m.createUserRule(ruleProperties) + r = m.createUserRule(rule) return } // Update existing rule - m.updateUserRule(r, ruleProperties) + m.updateUserRule(r, rule) return } // createUserRule creates a user-defined centralizedRule, appends it to the sorted array, // adds it to the index, and returns the newly created rule. -func (m *centralizedManifest) createUserRule(ruleProperties *properties) *centralizedRule { +func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedRule { + m.mu.Lock() + defer m.mu.Unlock() + + // Return early if rule already exists + if r, ok := m.index[rule.ruleName]; ok { + return r + } + // Create CentralizedRule from xraySvc.SamplingRule clock := &DefaultClock{} rand := &DefaultRand{} cr := ¢ralizedReservoir{ - capacity: ruleProperties.reservoirSize, + capacity: rule.reservoirSize, interval: defaultInterval, } csr := ¢ralizedRule{ - reservoir: cr, - properties: ruleProperties, - clock: clock, - rand: rand, - } - - m.mu.Lock() - defer m.mu.Unlock() - - // Return early if rule already exists - if r, ok := m.Index[ruleProperties.ruleName]; ok { - return r + reservoir: cr, + ruleProperties: rule, + clock: clock, + rand: rand, } // Update sorted array - m.Rules = append(m.Rules, csr) + m.rules = append(m.rules, csr) // Update index - m.Index[ruleProperties.ruleName] = csr + m.index[rule.ruleName] = csr return csr } // updateUserRule updates the properties of the user-defined centralizedRule using the given // *properties. -func (m *centralizedManifest) updateUserRule(r *centralizedRule, ruleProperties *properties) { +func (m *centralizedManifest) updateUserRule(r *centralizedRule, rule *ruleProperties) { r.mu.Lock() defer r.mu.Unlock() - r.properties = ruleProperties - r.reservoir.capacity = ruleProperties.reservoirSize + r.ruleProperties = rule + r.reservoir.capacity = rule.reservoirSize } // createDefaultRule creates a default centralizedRule and adds it to the manifest. -func (m *centralizedManifest) createDefaultRule(ruleProperties *properties) *centralizedRule { +func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centralizedRule { + m.mu.Lock() + defer m.mu.Unlock() + + // Return early if rule already exists + if d := m.defaultRule; d != nil { + return d + } + // Create CentralizedRule from xraySvc.SamplingRule clock := &DefaultClock{} rand := &DefaultRand{} cr := ¢ralizedReservoir{ - capacity: ruleProperties.reservoirSize, + capacity: rule.reservoirSize, interval: defaultInterval, } csr := ¢ralizedRule{ - reservoir: cr, - properties: ruleProperties, - clock: clock, - rand: rand, - } - - m.mu.Lock() - defer m.mu.Unlock() - - // Return early if rule already exists - if d := m.Default; d != nil { - return d + reservoir: cr, + ruleProperties: rule, + clock: clock, + rand: rand, } // Update manifest if rule does not exist - m.Default = csr + m.defaultRule = csr // Update index - m.Index[ruleProperties.ruleName] = csr + m.index[rule.ruleName] = csr return csr } // updateDefaultRule updates the properties of the default CentralizedRule using the given // *properties. -func (m *centralizedManifest) updateDefaultRule(ruleProperties *properties) { - r := m.Default +func (m *centralizedManifest) updateDefaultRule(rule *ruleProperties) { + r := m.defaultRule r.mu.Lock() defer r.mu.Unlock() - r.properties = ruleProperties - r.reservoir.capacity = ruleProperties.reservoirSize + r.ruleProperties = rule + r.reservoir.capacity = rule.reservoirSize } // prune removes all rules in the manifest not present in the given list of active rules. @@ -186,46 +186,39 @@ func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { defer m.mu.Unlock() // Iterate in reverse order to avoid adjusting index for each deleted rule - for i := len(m.Rules) - 1; i >= 0; i-- { - r := m.Rules[i] + for i := len(m.rules) - 1; i >= 0; i-- { + r := m.rules[i] if _, ok := actives[r]; !ok { - m.deleteRule(i) - } - } -} + // Remove from index + delete(m.index, m.rules[i].ruleProperties.ruleName) -// deleteRule deletes the rule from the array, and the index. -// Assumes write lock is already held. -// Preserves ordering of sorted array. -func (m *centralizedManifest) deleteRule(idx int) { - // Remove from index - delete(m.Index, m.Rules[idx].ruleName) - - // Delete by reslicing without index - a := append(m.Rules[:idx], m.Rules[idx+1:]...) + // Delete by reslicing without index + a := append(m.rules[:i], m.rules[i+1:]...) - // Set pointer to nil to free capacity from underlying array - m.Rules[len(m.Rules)-1] = nil + // Set pointer to nil to free capacity from underlying array + m.rules[len(m.rules)-1] = nil - // Assign resliced rules - m.Rules = a + // Assign resliced rules + m.rules = a + } + } } // sort sorts the rule array first by priority and then by rule name. func (m *centralizedManifest) sort() { // Comparison function less := func(i, j int) bool { - if m.Rules[i].priority == m.Rules[j].priority { - return strings.Compare(m.Rules[i].ruleName, m.Rules[j].ruleName) < 0 + if m.rules[i].ruleProperties.priority == m.rules[j].ruleProperties.priority { + return strings.Compare(m.rules[i].ruleProperties.ruleName, m.rules[j].ruleProperties.ruleName) < 0 } - return m.Rules[i].priority < m.Rules[j].priority + return m.rules[i].ruleProperties.priority < m.rules[j].ruleProperties.priority } m.mu.Lock() defer m.mu.Unlock() - sort.Slice(m.Rules, less) + sort.Slice(m.rules, less) } // expired returns true if the manifest has not been successfully refreshed in diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index a714ff06729..5bc87bc934c 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -24,14 +24,14 @@ import ( func TestCreateUserRule(t *testing.T) { resARN := "*" r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -45,8 +45,8 @@ func TestCreateUserRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Output of GetSamplingRules API and Input to putRule(). @@ -60,7 +60,7 @@ func TestCreateUserRule(t *testing.T) { priority := int64(6) serviceTye := "*" - ruleProperties := properties{ + ruleProperties := ruleProperties{ serviceName: serviceName, httpMethod: httpMethod, urlPath: urlPath, @@ -83,10 +83,10 @@ func TestCreateUserRule(t *testing.T) { } exp := ¢ralizedRule{ - reservoir: cr, - properties: &ruleProperties, - clock: clock, - rand: rand, + reservoir: cr, + ruleProperties: &ruleProperties, + clock: clock, + rand: rand, } // Add to manifest, index @@ -95,19 +95,19 @@ func TestCreateUserRule(t *testing.T) { assert.Equal(t, exp, r2) // Assert new rule is present in index - r2, ok := m.Index["r2"] + r2, ok := m.index["r2"] assert.True(t, ok) assert.Equal(t, exp, r2) // Assert new rule present at end of array. putRule() does not preserve order. - r2 = m.Rules[2] + r2 = m.rules[2] assert.Equal(t, exp, r2) } // Assert that putRule() creates a new default rule and adds to manifest func TestCreateDefaultRule(t *testing.T) { m := ¢ralizedManifest{ - Index: map[string]*centralizedRule{}, + index: map[string]*centralizedRule{}, } // Output of GetSamplingRules API and Input to putRule(). @@ -119,7 +119,7 @@ func TestCreateDefaultRule(t *testing.T) { clock := &DefaultClock{} rand := &DefaultRand{} - p := &properties{ + p := &ruleProperties{ ruleName: ruleName, reservoirSize: reservoirSize, fixedRate: fixedRate, @@ -131,17 +131,17 @@ func TestCreateDefaultRule(t *testing.T) { } exp := ¢ralizedRule{ - reservoir: cr, - properties: p, - clock: clock, - rand: rand, + reservoir: cr, + ruleProperties: p, + clock: clock, + rand: rand, } // Add to manifest r, err := m.putRule(p) assert.Nil(t, err) assert.Equal(t, exp, r) - assert.Equal(t, exp, m.Default) + assert.Equal(t, exp, m.defaultRule) } // Assert that putRule() updates the default rule @@ -151,7 +151,7 @@ func TestUpdateDefaultRule(t *testing.T) { // Original default sampling rule r := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "Default", reservoirSize: 10, fixedRate: 0.05, @@ -164,7 +164,7 @@ func TestUpdateDefaultRule(t *testing.T) { } m := ¢ralizedManifest{ - Default: r, + defaultRule: r, } // Output of GetSamplingRules API and Input to putRule(). @@ -173,7 +173,7 @@ func TestUpdateDefaultRule(t *testing.T) { ruleName := "Default" // Expected centralized sampling rule - p := &properties{ + p := &ruleProperties{ ruleName: ruleName, reservoirSize: reservoirSize, fixedRate: fixedRate, @@ -184,34 +184,37 @@ func TestUpdateDefaultRule(t *testing.T) { } exp := ¢ralizedRule{ - reservoir: cr, - properties: p, - clock: clock, - rand: rand, + reservoir: cr, + ruleProperties: p, + clock: clock, + rand: rand, } // Update default rule in manifest r, err := m.putRule(p) assert.Nil(t, err) assert.Equal(t, exp, r) - assert.Equal(t, exp, m.Default) + assert.Equal(t, exp, m.defaultRule) } // Assert that creating a user-defined rule which already exists is a no-op func TestCreateUserRuleNoOp(t *testing.T) { resARN := "*" serviceTye := "" - attributes := make([]interface{}, 2) + attributes := map[string]interface{}{ + "foo": "bar", + "flag": true, + } r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -228,8 +231,8 @@ func TestCreateUserRuleNoOp(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Duplicate rule properties. 'r3' already exists. Input to updateRule(). @@ -241,7 +244,7 @@ func TestCreateUserRuleNoOp(t *testing.T) { ruleName := "r3" priority := int64(6) host := "h" - ruleProperties := properties{ + ruleProperties := ruleProperties{ serviceName: serviceName, httpMethod: httpMethod, urlPath: urlPath, @@ -259,22 +262,25 @@ func TestCreateUserRuleNoOp(t *testing.T) { r, err := m.putRule(&ruleProperties) assert.Nil(t, err) assert.Equal(t, r3, r) - assert.Equal(t, 2, len(m.Rules)) - assert.Equal(t, 2, len(m.Index)) - assert.Equal(t, r1, m.Rules[0]) - assert.Equal(t, r3, m.Rules[1]) + assert.Equal(t, 2, len(m.rules)) + assert.Equal(t, 2, len(m.index)) + assert.Equal(t, r1, m.rules[0]) + assert.Equal(t, r3, m.rules[1]) } // Assert that putRule() updates the user-defined rule in the manifest func TestUpdateUserRule(t *testing.T) { resARN := "*" serviceType := "" - attributes := make([]interface{}, 2) + attributes := map[string]interface{}{ + "foo": "bar", + "flag": true, + } // Original rule r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, serviceName: "*.foo.com", @@ -299,8 +305,8 @@ func TestUpdateUserRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Updated rule properties. Input to updateRule(). @@ -313,7 +319,7 @@ func TestUpdateUserRule(t *testing.T) { priority := int64(6) host := "h" - updated := properties{ + updated := ruleProperties{ serviceName: serviceName, httpMethod: httpMethod, urlPath: urlPath, @@ -333,39 +339,39 @@ func TestUpdateUserRule(t *testing.T) { } exp := ¢ralizedRule{ - reservoir: cr, - properties: &updated, + reservoir: cr, + ruleProperties: &updated, } // Assert that rule has been updated r, err := m.putRule(&updated) assert.Nil(t, err) assert.Equal(t, exp, r) - assert.Equal(t, exp, m.Index["r1"]) - assert.Equal(t, exp, m.Rules[0]) - assert.Equal(t, 1, len(m.Rules)) - assert.Equal(t, 1, len(m.Index)) + assert.Equal(t, exp, m.index["r1"]) + assert.Equal(t, exp, m.rules[0]) + assert.Equal(t, 1, len(m.rules)) + assert.Equal(t, 1, len(m.index)) } // Assert that deleting a rule from the end of the array removes the rule // and preserves ordering of the sorted array func TestDeleteLastRule(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r2 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r2", priority: 6, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -380,8 +386,8 @@ func TestDeleteLastRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -394,39 +400,39 @@ func TestDeleteLastRule(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 2, len(m.Rules)) - assert.Equal(t, 2, len(m.Index)) + assert.Equal(t, 2, len(m.rules)) + assert.Equal(t, 2, len(m.index)) // Assert index consistency - _, ok := m.Index["r3"] + _, ok := m.index["r3"] assert.False(t, ok) - assert.Equal(t, r1, m.Index["r1"]) - assert.Equal(t, r2, m.Index["r2"]) + assert.Equal(t, r1, m.index["r1"]) + assert.Equal(t, r2, m.index["r2"]) // Assert ordering of array - assert.Equal(t, r1, m.Rules[0]) - assert.Equal(t, r2, m.Rules[1]) + assert.Equal(t, r1, m.rules[0]) + assert.Equal(t, r2, m.rules[1]) } // Assert that deleting a rule from the middle of the array removes the rule // and preserves ordering of the sorted array func TestDeleteMiddleRule(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r2 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r2", priority: 6, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -441,8 +447,8 @@ func TestDeleteMiddleRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -455,39 +461,39 @@ func TestDeleteMiddleRule(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 2, len(m.Rules)) - assert.Equal(t, 2, len(m.Index)) + assert.Equal(t, 2, len(m.rules)) + assert.Equal(t, 2, len(m.index)) // Assert index consistency - _, ok := m.Index["r2"] + _, ok := m.index["r2"] assert.False(t, ok) - assert.Equal(t, r1, m.Index["r1"]) - assert.Equal(t, r3, m.Index["r3"]) + assert.Equal(t, r1, m.index["r1"]) + assert.Equal(t, r3, m.index["r3"]) // Assert ordering of array - assert.Equal(t, r1, m.Rules[0]) - assert.Equal(t, r3, m.Rules[1]) + assert.Equal(t, r1, m.rules[0]) + assert.Equal(t, r3, m.rules[1]) } // Assert that deleting a rule from the beginning of the array removes the rule // and preserves ordering of the sorted array func TestDeleteFirstRule(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r2 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r2", priority: 6, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -502,8 +508,8 @@ func TestDeleteFirstRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -516,24 +522,24 @@ func TestDeleteFirstRule(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 2, len(m.Rules)) - assert.Equal(t, 2, len(m.Index)) + assert.Equal(t, 2, len(m.rules)) + assert.Equal(t, 2, len(m.index)) // Assert index consistency - _, ok := m.Index["r1"] + _, ok := m.index["r1"] assert.False(t, ok) - assert.Equal(t, r2, m.Index["r2"]) - assert.Equal(t, r3, m.Index["r3"]) + assert.Equal(t, r2, m.index["r2"]) + assert.Equal(t, r3, m.index["r3"]) // Assert ordering of array - assert.Equal(t, r2, m.Rules[0]) - assert.Equal(t, r3, m.Rules[1]) + assert.Equal(t, r2, m.rules[0]) + assert.Equal(t, r3, m.rules[1]) } // Assert that deleting the only rule from the array removes the rule func TestDeleteOnlyRule(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, @@ -546,8 +552,8 @@ func TestDeleteOnlyRule(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -557,11 +563,11 @@ func TestDeleteOnlyRule(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 0, len(m.Rules)) - assert.Equal(t, 0, len(m.Index)) + assert.Equal(t, 0, len(m.rules)) + assert.Equal(t, 0, len(m.index)) // Assert index consistency - _, ok := m.Index["r1"] + _, ok := m.index["r1"] assert.False(t, ok) } @@ -572,8 +578,8 @@ func TestDeleteEmptyRulesArray(t *testing.T) { index := map[string]*centralizedRule{} m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -583,28 +589,28 @@ func TestDeleteEmptyRulesArray(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 0, len(m.Rules)) - assert.Equal(t, 0, len(m.Index)) + assert.Equal(t, 0, len(m.rules)) + assert.Equal(t, 0, len(m.index)) } // Assert that deleting all rules results in an empty array and does not panic func TestDeleteAllRules(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r2 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r2", priority: 6, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -619,8 +625,8 @@ func TestDeleteAllRules(t *testing.T) { } m := ¢ralizedManifest{ - Rules: rules, - Index: index, + rules: rules, + index: index, } // Active rules to exclude from deletion @@ -630,28 +636,28 @@ func TestDeleteAllRules(t *testing.T) { m.prune(a) // Assert size of manifest - assert.Equal(t, 0, len(m.Rules)) - assert.Equal(t, 0, len(m.Index)) + assert.Equal(t, 0, len(m.rules)) + assert.Equal(t, 0, len(m.index)) } // Assert that sorting an unsorted array results in a sorted array - check priority func TestSort(t *testing.T) { r1 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r1", priority: 5, }, } r2 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r2", priority: 6, }, } r3 := ¢ralizedRule{ - properties: &properties{ + ruleProperties: &ruleProperties{ ruleName: "r3", priority: 7, }, @@ -661,14 +667,14 @@ func TestSort(t *testing.T) { rules := []*centralizedRule{r2, r1, r3} m := ¢ralizedManifest{ - Rules: rules, + rules: rules, } // Sort array m.sort() // Assert on order - assert.Equal(t, r1, m.Rules[0]) - assert.Equal(t, r2, m.Rules[1]) - assert.Equal(t, r3, m.Rules[2]) + assert.Equal(t, r1, m.rules[0]) + assert.Equal(t, r2, m.rules[1]) + assert.Equal(t, r3, m.rules[2]) } diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index c9c4f53f123..d62436ad10f 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -23,7 +23,7 @@ type centralizedRule struct { reservoir *centralizedReservoir // sampling rule properties - *properties + ruleProperties *ruleProperties // Number of requests matched against this rule //requests float64 @@ -47,11 +47,11 @@ type centralizedRule struct { } // properties is the base set of properties that define a sampling rule. -type properties struct { +type ruleProperties struct { ruleName string serviceType string resourceARN string - attributes interface{} + attributes map[string]interface{} serviceName string host string httpMethod string From cdc58faf6c2e764534f852a11bc595f0c0893ae3 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 7 Dec 2021 13:03:39 -0800 Subject: [PATCH 12/32] added time.ticker --- samplers/aws/xray/remote_sampler.go | 2 +- samplers/aws/xray/timer.go | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index b2402fcc293..ffa8aa8acbf 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -90,7 +90,7 @@ func (rs *RemoteSampler) start() { func (rs *RemoteSampler) startRulePoller() { // ToDo: Add logic to do periodic sampling rules call via background goroutines. // Period = 300s, Jitter = 5s - t := NewTimer(10*time.Second, 5*time.Second) + t := NewTicker(30*time.Second, 5*time.Second) for range t.C() { t.Reset() diff --git a/samplers/aws/xray/timer.go b/samplers/aws/xray/timer.go index 5157aac3277..100ed376430 100644 --- a/samplers/aws/xray/timer.go +++ b/samplers/aws/xray/timer.go @@ -18,34 +18,33 @@ import ( "time" ) -// Timer is the same as time.Timer except that it has jitters. -// A Timer must be created with NewTimer. -type Timer struct { - t *time.Timer +// Ticker is the same as time.Ticker except that it has jitters. +// A Ticker must be created with NewTicker. +type Ticker struct { + t *time.Ticker d time.Duration jitter time.Duration } -// NewTimer creates a new Timer that will send the current time on its channel. -func NewTimer(d, jitter time.Duration) *Timer { - t := time.NewTimer(d - time.Duration(globalRand.Int63n(int64(jitter)))) +// NewTicker creates a new Ticker that will send the current time on its channel. +func NewTicker(d, jitter time.Duration) *Ticker { + t := time.NewTicker(d - time.Duration(globalRand.Int63n(int64(jitter)))) - jitteredTimer := Timer{ + jitteredTicker := Ticker{ t: t, d: d, jitter: jitter, } - return &jitteredTimer + return &jitteredTicker } // C is channel. -func (j *Timer) C() <-chan time.Time { +func (j *Ticker) C() <-chan time.Time { return j.t.C } // Reset resets the timer. -// Reset should be invoked only on stopped or expired timers with drained channels. -func (j *Timer) Reset() { +func (j *Ticker) Reset() { j.t.Reset(j.d - time.Duration(globalRand.Int63n(int64(j.jitter)))) } From d5902d929c083eb40b4af469b35cddb247e46d56 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Fri, 10 Dec 2021 10:45:13 -0800 Subject: [PATCH 13/32] added unmarshaling mthod to stor json from service API and minor changes --- samplers/aws/xray/client.go | 42 ++--- samplers/aws/xray/remote_sampler.go | 44 +++-- samplers/aws/xray/rule_manifest.go | 24 +-- samplers/aws/xray/rule_manifest_test.go | 225 +++++++++++++----------- samplers/aws/xray/sampling_rule.go | 39 ++-- 5 files changed, 193 insertions(+), 181 deletions(-) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index e47b521bd41..0c9e102b106 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -15,6 +15,8 @@ package xray import ( + "bytes" + "context" "encoding/json" "log" "net/http" @@ -50,8 +52,8 @@ func newClient(d string) *xrayClient { } // getSamplingRules calls the collector(aws proxy enabled) for sampling rules -func (p *xrayClient) getSamplingRules() ([]ruleProperties, error) { - req, err := http.NewRequest(http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) +func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) if err != nil { log.Printf("failed to create http request, %v\n", err) } @@ -61,41 +63,17 @@ func (p *xrayClient) getSamplingRules() ([]ruleProperties, error) { return nil, err } - var data map[string]interface{} - err = json.NewDecoder(output.Body).Decode(&data) - if err != nil { - return nil, err - } + buf := new(bytes.Buffer) + _, _ = buf.ReadFrom(output.Body) - var rules []ruleProperties - switch x := data["SamplingRuleRecords"].(type) { - case []interface{}: - for _, e := range x { - svcRule := e.(map[string]interface{})["SamplingRule"].(map[string]interface{}) - - rules = append(rules, ruleProperties{ - ruleName: svcRule["RuleName"].(string), - serviceType: svcRule["ServiceType"].(string), - resourceARN: svcRule["ResourceARN"].(string), - attributes: svcRule["Attributes"].(map[string]interface{}), - serviceName: svcRule["ServiceName"].(string), - host: svcRule["Host"].(string), - httpMethod: svcRule["HTTPMethod"].(string), - urlPath: svcRule["URLPath"].(string), - reservoirSize: int64(svcRule["ReservoirSize"].(float64)), - fixedRate: svcRule["FixedRate"].(float64), - priority: int64(svcRule["Priority"].(float64)), - version: int64(svcRule["Version"].(float64)), - }) - } - default: - log.Printf("unhandled type: %T\n", data["SamplingRuleRecords"]) - } + // Unmarshalling json data to populate getSamplingTargetsOutput struct + var samplingRulesOutput getSamplingRulesOutput + _ = json.Unmarshal(buf.Bytes(), &samplingRulesOutput) err = output.Body.Close() if err != nil { log.Printf("failed to close http response body, %v\n\n", err) } - return rules, nil + return &samplingRulesOutput, nil } diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index ffa8aa8acbf..c80468be610 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -15,10 +15,11 @@ package xray import ( + "context" + crypto "crypto/rand" "errors" "fmt" "log" - "reflect" "sync" "time" @@ -36,6 +37,9 @@ type RemoteSampler struct { // pollerStart, if true represents rule and target pollers are started pollerStart bool + // Unique ID used by XRay service to identify this client + clientID string + // Provides system time clock Clock @@ -47,6 +51,15 @@ var _ trace.Sampler = (*RemoteSampler)(nil) // NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. func NewRemoteSampler() *RemoteSampler { + // Generate clientID + var r [12]byte + + _, err := crypto.Read(r[:]) + if err != nil { + return nil + } + id := fmt.Sprintf("%02x", r) + clock := &DefaultClock{} m := ¢ralizedManifest{ @@ -59,6 +72,7 @@ func NewRemoteSampler() *RemoteSampler { pollerStart: false, clock: clock, manifest: m, + clientID: id, // ToDo: take proxyEndpoint and pollingInterval from users xrayClient: newClient("127.0.0.1:2000"), } @@ -119,7 +133,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { now := rs.clock.Now().Unix() // Get sampling rules from proxy - rules, err := rs.xrayClient.getSamplingRules() + rules, err := rs.xrayClient.getSamplingRules(context.Background()) if err != nil { return } @@ -130,43 +144,43 @@ func (rs *RemoteSampler) refreshManifest() (err error) { // Create missing rules. Update existing ones. failed := false - for _, svcRule := range rules { - if svcRule.ruleName == "" { + for _, records := range rules.SamplingRuleRecords { + if records.SamplingRule.RuleName == nil { log.Println("Sampling rule without rule name is not supported") failed = true continue } // Only sampling rule with version 1 is valid - if svcRule.version == 0 { - log.Println("Sampling rule without version number is not supported: ", svcRule.ruleName) + if records.SamplingRule.Version == nil { + log.Println("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) failed = true continue } - if svcRule.version != int64(1) { - log.Println("Sampling rule without version 1 is not supported: ", svcRule.ruleName) + if *records.SamplingRule.Version != int64(1) { + log.Println("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) failed = true continue } - if reflect.ValueOf(svcRule.attributes).Len() != 0 { - log.Println("Sampling rule with non nil Attributes is not applicable: ", svcRule.ruleName) + if len(records.SamplingRule.Attributes) != 0 { + log.Println("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) continue } - if svcRule.resourceARN == "" { - log.Println("Sampling rule without ResourceARN is not applicable: ", svcRule.ruleName) + if records.SamplingRule.ResourceARN == nil { + log.Println("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) continue } - if svcRule.resourceARN != "*" { - log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", svcRule.ruleName) + if *records.SamplingRule.ResourceARN != "*" { + log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) continue } // Create/update rule - r, putErr := rs.manifest.putRule(&svcRule) + r, putErr := rs.manifest.putRule(records.SamplingRule) if putErr != nil { failed = true log.Printf("Error occurred creating/updating rule. %v\n", putErr) diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index c468938db53..6dc3827301e 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -47,7 +47,7 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, } }() - name := rule.ruleName + name := *rule.RuleName // Default rule if name == defaultRule { @@ -93,7 +93,7 @@ func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedR defer m.mu.Unlock() // Return early if rule already exists - if r, ok := m.index[rule.ruleName]; ok { + if r, ok := m.index[*rule.RuleName]; ok { return r } @@ -102,7 +102,7 @@ func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedR rand := &DefaultRand{} cr := ¢ralizedReservoir{ - capacity: rule.reservoirSize, + capacity: *rule.ReservoirSize, interval: defaultInterval, } @@ -117,7 +117,7 @@ func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedR m.rules = append(m.rules, csr) // Update index - m.index[rule.ruleName] = csr + m.index[*rule.RuleName] = csr return csr } @@ -129,7 +129,7 @@ func (m *centralizedManifest) updateUserRule(r *centralizedRule, rule *rulePrope defer r.mu.Unlock() r.ruleProperties = rule - r.reservoir.capacity = rule.reservoirSize + r.reservoir.capacity = *rule.ReservoirSize } // createDefaultRule creates a default centralizedRule and adds it to the manifest. @@ -147,7 +147,7 @@ func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centraliz rand := &DefaultRand{} cr := ¢ralizedReservoir{ - capacity: rule.reservoirSize, + capacity: *rule.ReservoirSize, interval: defaultInterval, } @@ -162,7 +162,7 @@ func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centraliz m.defaultRule = csr // Update index - m.index[rule.ruleName] = csr + m.index[*rule.RuleName] = csr return csr } @@ -176,7 +176,7 @@ func (m *centralizedManifest) updateDefaultRule(rule *ruleProperties) { defer r.mu.Unlock() r.ruleProperties = rule - r.reservoir.capacity = rule.reservoirSize + r.reservoir.capacity = *rule.ReservoirSize } // prune removes all rules in the manifest not present in the given list of active rules. @@ -191,7 +191,7 @@ func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { if _, ok := actives[r]; !ok { // Remove from index - delete(m.index, m.rules[i].ruleProperties.ruleName) + delete(m.index, *m.rules[i].ruleProperties.RuleName) // Delete by reslicing without index a := append(m.rules[:i], m.rules[i+1:]...) @@ -209,10 +209,10 @@ func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { func (m *centralizedManifest) sort() { // Comparison function less := func(i, j int) bool { - if m.rules[i].ruleProperties.priority == m.rules[j].ruleProperties.priority { - return strings.Compare(m.rules[i].ruleProperties.ruleName, m.rules[j].ruleProperties.ruleName) < 0 + if m.rules[i].ruleProperties.Priority == m.rules[j].ruleProperties.Priority { + return strings.Compare(*m.rules[i].ruleProperties.RuleName, *m.rules[j].ruleProperties.RuleName) < 0 } - return m.rules[i].ruleProperties.priority < m.rules[j].ruleProperties.priority + return *m.rules[i].ruleProperties.Priority < *m.rules[j].ruleProperties.Priority } m.mu.Lock() diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index 5bc87bc934c..1c4420ff092 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -20,20 +20,33 @@ import ( "github.com/stretchr/testify/assert" ) +// utility functions to get pointers value +func getIntPointer(val int64) *int64 { + return &val +} + +func getStringPointer(val string) *string { + return &val +} + +func getFloatPointer(val float64) *float64 { + return &val +} + // Assert that putRule() creates a new user-defined rule and adds to manifest func TestCreateUserRule(t *testing.T) { resARN := "*" r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } @@ -60,17 +73,17 @@ func TestCreateUserRule(t *testing.T) { priority := int64(6) serviceTye := "*" - ruleProperties := ruleProperties{ - serviceName: serviceName, - httpMethod: httpMethod, - urlPath: urlPath, - reservoirSize: reservoirSize, - fixedRate: fixedRate, - ruleName: ruleName, - priority: priority, - host: host, - serviceType: serviceTye, - resourceARN: resARN, + ruleProperties := &ruleProperties{ + ServiceName: &serviceName, + HTTPMethod: &httpMethod, + URLPath: &urlPath, + ReservoirSize: &reservoirSize, + FixedRate: &fixedRate, + RuleName: &ruleName, + Priority: &priority, + Host: &host, + ServiceType: &serviceTye, + ResourceARN: &resARN, } // Expected centralized sampling rule @@ -84,13 +97,13 @@ func TestCreateUserRule(t *testing.T) { exp := ¢ralizedRule{ reservoir: cr, - ruleProperties: &ruleProperties, + ruleProperties: ruleProperties, clock: clock, rand: rand, } // Add to manifest, index - r2, err := m.putRule(&ruleProperties) + r2, err := m.putRule(ruleProperties) assert.Nil(t, err) assert.Equal(t, exp, r2) @@ -120,9 +133,9 @@ func TestCreateDefaultRule(t *testing.T) { rand := &DefaultRand{} p := &ruleProperties{ - ruleName: ruleName, - reservoirSize: reservoirSize, - fixedRate: fixedRate, + RuleName: &ruleName, + ReservoirSize: &reservoirSize, + FixedRate: &fixedRate, } cr := ¢ralizedReservoir{ @@ -152,9 +165,9 @@ func TestUpdateDefaultRule(t *testing.T) { // Original default sampling rule r := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "Default", - reservoirSize: 10, - fixedRate: 0.05, + RuleName: getStringPointer("Default"), + ReservoirSize: getIntPointer(10), + FixedRate: getFloatPointer(0.05), }, reservoir: ¢ralizedReservoir{ capacity: 10, @@ -174,9 +187,9 @@ func TestUpdateDefaultRule(t *testing.T) { // Expected centralized sampling rule p := &ruleProperties{ - ruleName: ruleName, - reservoirSize: reservoirSize, - fixedRate: fixedRate, + RuleName: &ruleName, + ReservoirSize: &reservoirSize, + FixedRate: &fixedRate, } cr := ¢ralizedReservoir{ @@ -200,23 +213,22 @@ func TestUpdateDefaultRule(t *testing.T) { // Assert that creating a user-defined rule which already exists is a no-op func TestCreateUserRuleNoOp(t *testing.T) { resARN := "*" - serviceTye := "" - attributes := map[string]interface{}{ - "foo": "bar", - "flag": true, + serviceType := "" + attributes := map[string]*string{ + "foo": getStringPointer("bar"), } r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, reservoir: ¢ralizedReservoir{ capacity: 5, @@ -244,22 +256,23 @@ func TestCreateUserRuleNoOp(t *testing.T) { ruleName := "r3" priority := int64(6) host := "h" - ruleProperties := ruleProperties{ - serviceName: serviceName, - httpMethod: httpMethod, - urlPath: urlPath, - reservoirSize: reservoirSize, - fixedRate: fixedRate, - ruleName: ruleName, - priority: priority, - host: host, - resourceARN: resARN, - serviceType: serviceTye, - attributes: attributes, + + ruleProperties := &ruleProperties{ + ServiceName: &serviceName, + HTTPMethod: &httpMethod, + URLPath: &urlPath, + ReservoirSize: &reservoirSize, + FixedRate: &fixedRate, + RuleName: &ruleName, + Priority: &priority, + Host: &host, + ServiceType: &serviceType, + ResourceARN: &resARN, + Attributes: attributes, } // Assert manifest has not changed - r, err := m.putRule(&ruleProperties) + r, err := m.putRule(ruleProperties) assert.Nil(t, err) assert.Equal(t, r3, r) assert.Equal(t, 2, len(m.rules)) @@ -272,25 +285,23 @@ func TestCreateUserRuleNoOp(t *testing.T) { func TestUpdateUserRule(t *testing.T) { resARN := "*" serviceType := "" - attributes := map[string]interface{}{ - "foo": "bar", - "flag": true, + attributes := map[string]*string{ + "foo": getStringPointer("bar"), } // Original rule r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, - serviceName: "*.foo.com", - httpMethod: "GET", - urlPath: "/resource/*", - reservoirSize: 15, - fixedRate: 0.04, - resourceARN: resARN, - serviceType: serviceType, - attributes: attributes, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), + ServiceName: getStringPointer("*.foo.com"), + HTTPMethod: getStringPointer("GET"), + URLPath: getStringPointer("/resource/*"), + ReservoirSize: getIntPointer(15), + FixedRate: getFloatPointer(0.04), + ResourceARN: &resARN, + ServiceType: &serviceType, + Attributes: attributes, }, reservoir: ¢ralizedReservoir{ @@ -319,18 +330,18 @@ func TestUpdateUserRule(t *testing.T) { priority := int64(6) host := "h" - updated := ruleProperties{ - serviceName: serviceName, - httpMethod: httpMethod, - urlPath: urlPath, - reservoirSize: reservoirSize, - fixedRate: fixedRate, - ruleName: ruleName, - priority: priority, - host: host, - resourceARN: resARN, - serviceType: serviceType, - attributes: attributes, + updated := &ruleProperties{ + ServiceName: &serviceName, + HTTPMethod: &httpMethod, + URLPath: &urlPath, + ReservoirSize: &reservoirSize, + FixedRate: &fixedRate, + RuleName: &ruleName, + Priority: &priority, + Host: &host, + ServiceType: &serviceType, + ResourceARN: &resARN, + Attributes: attributes, } // Expected updated centralized sampling rule @@ -340,11 +351,11 @@ func TestUpdateUserRule(t *testing.T) { exp := ¢ralizedRule{ reservoir: cr, - ruleProperties: &updated, + ruleProperties: updated, } // Assert that rule has been updated - r, err := m.putRule(&updated) + r, err := m.putRule(updated) assert.Nil(t, err) assert.Equal(t, exp, r) assert.Equal(t, exp, m.index["r1"]) @@ -358,22 +369,22 @@ func TestUpdateUserRule(t *testing.T) { func TestDeleteLastRule(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r2", - priority: 6, + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } @@ -419,22 +430,22 @@ func TestDeleteLastRule(t *testing.T) { func TestDeleteMiddleRule(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r2", - priority: 6, + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } @@ -480,22 +491,22 @@ func TestDeleteMiddleRule(t *testing.T) { func TestDeleteFirstRule(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r2", - priority: 6, + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } @@ -540,8 +551,8 @@ func TestDeleteFirstRule(t *testing.T) { func TestDeleteOnlyRule(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } @@ -597,22 +608,22 @@ func TestDeleteEmptyRulesArray(t *testing.T) { func TestDeleteAllRules(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r2", - priority: 6, + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } @@ -644,22 +655,22 @@ func TestDeleteAllRules(t *testing.T) { func TestSort(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r1", - priority: 5, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), }, } r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r2", - priority: 6, + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), }, } r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - ruleName: "r3", - priority: 7, + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), }, } diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index d62436ad10f..07f835e2faf 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -26,13 +26,13 @@ type centralizedRule struct { ruleProperties *ruleProperties // Number of requests matched against this rule - //requests float64 + //requests int64 // // Number of requests sampled using this rule - //sampled float64 + //sampled int64 // // Number of requests burrowed - //borrows float64 + //borrows int64 // // Timestamp for last match against this rule //usedAt int64 @@ -48,16 +48,25 @@ type centralizedRule struct { // properties is the base set of properties that define a sampling rule. type ruleProperties struct { - ruleName string - serviceType string - resourceARN string - attributes map[string]interface{} - serviceName string - host string - httpMethod string - urlPath string - reservoirSize int64 - fixedRate float64 - priority int64 - version int64 + RuleName *string `json:"RuleName"` + ServiceType *string `json:"ServiceType"` + ResourceARN *string `json:"ResourceARN"` + Attributes map[string]*string `json:"Attributes"` + ServiceName *string `json:"ServiceName"` + Host *string `json:"Host"` + HTTPMethod *string `json:"HTTPMethod"` + URLPath *string `json:"URLPath"` + ReservoirSize *int64 `json:"ReservoirSize"` + FixedRate *float64 `json:"FixedRate"` + Priority *int64 `json:"Priority"` + Version *int64 `json:"Version"` +} + +type samplingRuleRecords struct { + SamplingRule *ruleProperties `json:"SamplingRule"` +} + +// getSamplingRulesOutput is used to store parsed json sampling rules +type getSamplingRulesOutput struct { + SamplingRuleRecords []*samplingRuleRecords `json:"SamplingRuleRecords"` } From 36fa11fd672acdd3fb63e18d118cf5bee70a6486 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 13 Dec 2021 18:35:16 -0800 Subject: [PATCH 14/32] addressed review comments --- samplers/aws/xray/client.go | 22 ++++++------- samplers/aws/xray/remote_sampler.go | 30 ++++++++++++------ samplers/aws/xray/rule_manifest.go | 42 +++++-------------------- samplers/aws/xray/rule_manifest_test.go | 24 +++++++------- samplers/aws/xray/sampling_rule.go | 10 ++++++ samplers/aws/xray/timer.go | 10 +++--- 6 files changed, 65 insertions(+), 73 deletions(-) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 0c9e102b106..844afcde7cf 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -20,7 +20,6 @@ import ( "encoding/json" "log" "net/http" - "net/url" ) type xrayClient struct { @@ -34,17 +33,8 @@ type xrayClient struct { func newClient(d string) *xrayClient { proxyEndpoint := "http://" + d - proxyURL, err := url.Parse(proxyEndpoint) - if err != nil { - log.Println("Bad proxy URL", err) - } - - httpClient := &http.Client{ - Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, - } - p := &xrayClient{ - httpClient: httpClient, + httpClient: &http.Client{}, proxyEndpoint: proxyEndpoint, } @@ -64,11 +54,17 @@ func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOut } buf := new(bytes.Buffer) - _, _ = buf.ReadFrom(output.Body) + _, err = buf.ReadFrom(output.Body) + if err != nil { + return nil, err + } // Unmarshalling json data to populate getSamplingTargetsOutput struct var samplingRulesOutput getSamplingRulesOutput - _ = json.Unmarshal(buf.Bytes(), &samplingRulesOutput) + err = json.Unmarshal(buf.Bytes(), &samplingRulesOutput) + if err != nil { + return nil, err + } err = output.Body.Close() if err != nil { diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index c80468be610..354064faad7 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -56,8 +56,10 @@ func NewRemoteSampler() *RemoteSampler { _, err := crypto.Read(r[:]) if err != nil { + log.Println("error reading cryptographically secure random number generator") return nil } + id := fmt.Sprintf("%02x", r) clock := &DefaultClock{} @@ -102,18 +104,28 @@ func (rs *RemoteSampler) start() { } func (rs *RemoteSampler) startRulePoller() { - // ToDo: Add logic to do periodic sampling rules call via background goroutines. - // Period = 300s, Jitter = 5s - t := NewTicker(30*time.Second, 5*time.Second) - - for range t.C() { - t.Reset() + // Initial refresh + go func() { if err := rs.refreshManifest(); err != nil { log.Printf("Error occurred while refreshing sampling rules. %v\n", err) } else { log.Println("Successfully fetched sampling rules") } - } + }() + + // Periodic manifest refresh + go func() { + // Period = 300s, Jitter = 5s + t := newTicker(300*time.Second, 5*time.Second) + + for range t.C() { + if err := rs.refreshManifest(); err != nil { + log.Printf("Error occurred while refreshing sampling rules. %v\n", err) + } else { + log.Println("Successfully fetched sampling rules") + } + } + }() } func (rs *RemoteSampler) refreshManifest() (err error) { @@ -139,7 +151,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { } // Set of rules to exclude from pruning - actives := map[*centralizedRule]bool{} + actives := map[centralizedRule]bool{} // Create missing rules. Update existing ones. failed := false @@ -185,7 +197,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { failed = true log.Printf("Error occurred creating/updating rule. %v\n", putErr) } else if r != nil { - actives[r] = true + actives[*r] = true } } diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index 6dc3827301e..192140c705d 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -47,17 +47,15 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, } }() - name := *rule.RuleName - // Default rule - if name == defaultRule { + if *rule.RuleName == defaultRule { m.mu.RLock() r = m.defaultRule m.mu.RUnlock() // Update rule if already exists if r != nil { - m.updateDefaultRule(rule) + r.updateRule(rule) return } @@ -70,7 +68,8 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, // User-defined rule m.mu.RLock() - r, ok := m.index[name] + var ok bool + r, ok = m.index[*rule.RuleName] m.mu.RUnlock() // Create rule if it does not exist @@ -81,7 +80,7 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, } // Update existing rule - m.updateUserRule(r, rule) + r.updateRule(rule) return } @@ -122,16 +121,6 @@ func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedR return csr } -// updateUserRule updates the properties of the user-defined centralizedRule using the given -// *properties. -func (m *centralizedManifest) updateUserRule(r *centralizedRule, rule *ruleProperties) { - r.mu.Lock() - defer r.mu.Unlock() - - r.ruleProperties = rule - r.reservoir.capacity = *rule.ReservoirSize -} - // createDefaultRule creates a default centralizedRule and adds it to the manifest. func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centralizedRule { m.mu.Lock() @@ -167,21 +156,9 @@ func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centraliz return csr } -// updateDefaultRule updates the properties of the default CentralizedRule using the given -// *properties. -func (m *centralizedManifest) updateDefaultRule(rule *ruleProperties) { - r := m.defaultRule - - r.mu.Lock() - defer r.mu.Unlock() - - r.ruleProperties = rule - r.reservoir.capacity = *rule.ReservoirSize -} - // prune removes all rules in the manifest not present in the given list of active rules. // Preserves ordering of sorted array. -func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { +func (m *centralizedManifest) prune(actives map[centralizedRule]bool) { m.mu.Lock() defer m.mu.Unlock() @@ -189,16 +166,13 @@ func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { for i := len(m.rules) - 1; i >= 0; i-- { r := m.rules[i] - if _, ok := actives[r]; !ok { + if _, ok := actives[*r]; !ok { // Remove from index delete(m.index, *m.rules[i].ruleProperties.RuleName) // Delete by reslicing without index a := append(m.rules[:i], m.rules[i+1:]...) - // Set pointer to nil to free capacity from underlying array - m.rules[len(m.rules)-1] = nil - // Assign resliced rules m.rules = a } @@ -209,7 +183,7 @@ func (m *centralizedManifest) prune(actives map[*centralizedRule]bool) { func (m *centralizedManifest) sort() { // Comparison function less := func(i, j int) bool { - if m.rules[i].ruleProperties.Priority == m.rules[j].ruleProperties.Priority { + if *m.rules[i].ruleProperties.Priority == *m.rules[j].ruleProperties.Priority { return strings.Compare(*m.rules[i].ruleProperties.RuleName, *m.rules[j].ruleProperties.RuleName) < 0 } return *m.rules[i].ruleProperties.Priority < *m.rules[j].ruleProperties.Priority diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index 1c4420ff092..b73872347a4 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -402,9 +402,9 @@ func TestDeleteLastRule(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{ - r1: true, - r2: true, + a := map[centralizedRule]bool{ + *r1: true, + *r2: true, } // Delete r3 @@ -463,9 +463,9 @@ func TestDeleteMiddleRule(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{ - r1: true, - r3: true, + a := map[centralizedRule]bool{ + *r1: true, + *r3: true, } // Delete r2 @@ -524,9 +524,9 @@ func TestDeleteFirstRule(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{ - r2: true, - r3: true, + a := map[centralizedRule]bool{ + *r2: true, + *r3: true, } // Delete r1 @@ -568,7 +568,7 @@ func TestDeleteOnlyRule(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{} + a := map[centralizedRule]bool{} // Delete r1 m.prune(a) @@ -594,7 +594,7 @@ func TestDeleteEmptyRulesArray(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{} + a := map[centralizedRule]bool{} // Delete from empty array m.prune(a) @@ -641,7 +641,7 @@ func TestDeleteAllRules(t *testing.T) { } // Active rules to exclude from deletion - a := map[*centralizedRule]bool{} + a := map[centralizedRule]bool{} // Delete r3 m.prune(a) diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index 07f835e2faf..0069275b428 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -70,3 +70,13 @@ type samplingRuleRecords struct { type getSamplingRulesOutput struct { SamplingRuleRecords []*samplingRuleRecords `json:"SamplingRuleRecords"` } + +// updateRule updates the properties of the user-defined and default centralizedRule using the given +// *properties. +func (r *centralizedRule) updateRule(rule *ruleProperties) { + r.mu.Lock() + defer r.mu.Unlock() + + r.ruleProperties = rule + r.reservoir.capacity = *rule.ReservoirSize +} diff --git a/samplers/aws/xray/timer.go b/samplers/aws/xray/timer.go index 100ed376430..da735a72aed 100644 --- a/samplers/aws/xray/timer.go +++ b/samplers/aws/xray/timer.go @@ -20,17 +20,17 @@ import ( // Ticker is the same as time.Ticker except that it has jitters. // A Ticker must be created with NewTicker. -type Ticker struct { +type ticker struct { t *time.Ticker d time.Duration jitter time.Duration } // NewTicker creates a new Ticker that will send the current time on its channel. -func NewTicker(d, jitter time.Duration) *Ticker { +func newTicker(d, jitter time.Duration) *ticker { t := time.NewTicker(d - time.Duration(globalRand.Int63n(int64(jitter)))) - jitteredTicker := Ticker{ + jitteredTicker := ticker{ t: t, d: d, jitter: jitter, @@ -40,11 +40,11 @@ func NewTicker(d, jitter time.Duration) *Ticker { } // C is channel. -func (j *Ticker) C() <-chan time.Time { +func (j *ticker) C() <-chan time.Time { return j.t.C } // Reset resets the timer. -func (j *Ticker) Reset() { +func (j *ticker) Reset() { j.t.Reset(j.d - time.Duration(globalRand.Int63n(int64(j.jitter)))) } From c4ed209fa30426b94404cccc4253e5541eafb675 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Fri, 17 Dec 2021 16:43:48 -0800 Subject: [PATCH 15/32] added no-op logging, sampler config options and minor changes --- samplers/aws/xray/client.go | 12 +-- samplers/aws/xray/noop_log.go | 30 ++++++ samplers/aws/xray/remote_sampler.go | 111 ++++++++++++-------- samplers/aws/xray/remote_sampler_options.go | 72 +++++++++++++ samplers/aws/xray/rule_manifest.go | 73 +------------ samplers/aws/xray/rule_manifest_test.go | 103 +----------------- samplers/aws/xray/sampling_rule.go | 9 +- 7 files changed, 184 insertions(+), 226 deletions(-) create mode 100644 samplers/aws/xray/noop_log.go create mode 100644 samplers/aws/xray/remote_sampler_options.go diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 844afcde7cf..a1a489c3aad 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -18,7 +18,7 @@ import ( "bytes" "context" "encoding/json" - "log" + "fmt" "net/http" ) @@ -45,30 +45,30 @@ func newClient(d string) *xrayClient { func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) if err != nil { - log.Printf("failed to create http request, %v\n", err) + globalLogger.Printf("failed to create http request, %v\n", err) } output, err := p.httpClient.Do(req) if err != nil { - return nil, err + return nil, fmt.Errorf("xray client: unable to retrieve sampling settings: %w", err) } buf := new(bytes.Buffer) _, err = buf.ReadFrom(output.Body) if err != nil { - return nil, err + return nil, fmt.Errorf("xray client: unable to read response body: %w", err) } // Unmarshalling json data to populate getSamplingTargetsOutput struct var samplingRulesOutput getSamplingRulesOutput err = json.Unmarshal(buf.Bytes(), &samplingRulesOutput) if err != nil { - return nil, err + return nil, fmt.Errorf("xray client: unable to unmarshal the response body: %w", err) } err = output.Body.Close() if err != nil { - log.Printf("failed to close http response body, %v\n\n", err) + globalLogger.Printf("failed to close http response body, %v\n\n", err) } return &samplingRulesOutput, nil diff --git a/samplers/aws/xray/noop_log.go b/samplers/aws/xray/noop_log.go new file mode 100644 index 00000000000..15e5f2511ef --- /dev/null +++ b/samplers/aws/xray/noop_log.go @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +// Logger is the logging interface used by xray +type Logger interface { + Printf(format string, v ...interface{}) + Println(v ...interface{}) +} + +// noopLogger to bypass logging +type noopLogger struct{} + +// noop logging method +func (nl noopLogger) Printf(format string, v ...interface{}) {} + +// noop logging method +func (nl noopLogger) Println(v ...interface{}) {} diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 354064faad7..266f58e2d52 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -19,13 +19,15 @@ import ( crypto "crypto/rand" "errors" "fmt" - "log" "sync" "time" - "go.opentelemetry.io/otel/sdk/trace" + sdktrace "go.opentelemetry.io/otel/sdk/trace" ) +// global variable for logging +var globalLogger Logger + // RemoteSampler is an implementation of SamplingStrategy. type RemoteSampler struct { // List of known centralized sampling rules @@ -37,6 +39,9 @@ type RemoteSampler struct { // pollerStart, if true represents rule and target pollers are started pollerStart bool + // samplingRules polling interval, default is 300 seconds + samplingRulesPollingInterval time.Duration + // Unique ID used by XRay service to identify this client clientID string @@ -47,17 +52,21 @@ type RemoteSampler struct { } // Compile time assertion that remoteSampler implements the Sampler interface. -var _ trace.Sampler = (*RemoteSampler)(nil) +var _ sdktrace.Sampler = (*RemoteSampler)(nil) // NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. -func NewRemoteSampler() *RemoteSampler { +func NewRemoteSampler(ctx context.Context, opts ...SamplerOption) (*RemoteSampler, error) { + options := new(samplerOptions).applyOptionsAndDefaults(opts...) + + // set global logging + globalLogger = options.logger + // Generate clientID var r [12]byte _, err := crypto.Read(r[:]) if err != nil { - log.Println("error reading cryptographically secure random number generator") - return nil + return nil, fmt.Errorf("unable to generate client ID: %w", err) } id := fmt.Sprintf("%02x", r) @@ -70,65 +79,75 @@ func NewRemoteSampler() *RemoteSampler { clock: clock, } - return &RemoteSampler{ - pollerStart: false, - clock: clock, - manifest: m, - clientID: id, - // ToDo: take proxyEndpoint and pollingInterval from users - xrayClient: newClient("127.0.0.1:2000"), + remoteSampler := &RemoteSampler{ + pollerStart: false, + clock: clock, + manifest: m, + clientID: id, + xrayClient: newClient(options.proxyEndpoint), + samplingRulesPollingInterval: options.samplingRulesPollingInterval, } + + remoteSampler.mu.Lock() + if !remoteSampler.pollerStart { + remoteSampler.start(ctx) + } + remoteSampler.mu.Unlock() + + return remoteSampler, nil } -func (rs *RemoteSampler) ShouldSample(parameters trace.SamplingParameters) trace.SamplingResult { +func (rs *RemoteSampler) ShouldSample(parameters sdktrace.SamplingParameters) sdktrace.SamplingResult { // ToDo: add business logic for remote sampling - rs.mu.Lock() - if !rs.pollerStart { - rs.start() - } - rs.mu.Unlock() - return trace.SamplingResult{} + return sdktrace.SamplingResult{} } func (rs *RemoteSampler) Description() string { return "remote sampling with AWS X-Ray" } -func (rs *RemoteSampler) start() { +func (rs *RemoteSampler) start(ctx context.Context) { if !rs.pollerStart { - rs.startRulePoller() + rs.startRulePoller(ctx) } rs.pollerStart = true } -func (rs *RemoteSampler) startRulePoller() { - // Initial refresh +func (rs *RemoteSampler) startRulePoller(ctx context.Context) { go func() { - if err := rs.refreshManifest(); err != nil { - log.Printf("Error occurred while refreshing sampling rules. %v\n", err) + // Period = 300s, Jitter = 5s + t := newTicker(rs.samplingRulesPollingInterval, 5*time.Second) + + // Initial refresh + if err := rs.refreshManifest(ctx); err != nil { + globalLogger.Printf("Error occurred while refreshing sampling rules. %v\n", err) } else { - log.Println("Successfully fetched sampling rules") + globalLogger.Println("Successfully fetched sampling rules") } - }() - // Periodic manifest refresh - go func() { - // Period = 300s, Jitter = 5s - t := newTicker(300*time.Second, 5*time.Second) - - for range t.C() { - if err := rs.refreshManifest(); err != nil { - log.Printf("Error occurred while refreshing sampling rules. %v\n", err) + // Periodic manifest refresh + for { + if err := rs.refreshManifest(ctx); err != nil { + globalLogger.Printf("Error occurred while refreshing sampling rules. %v\n", err) } else { - log.Println("Successfully fetched sampling rules") + globalLogger.Println("Successfully fetched sampling rules") + } + select { + case _, more := <-t.C(): + if !more { + break + } + continue + case <-ctx.Done(): + break } } }() } -func (rs *RemoteSampler) refreshManifest() (err error) { +func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { // Explicitly recover from panics since this is the entry point for a long-running goroutine // and we can not allow a panic to propagate to the application code. defer func() { @@ -145,7 +164,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { now := rs.clock.Now().Unix() // Get sampling rules from proxy - rules, err := rs.xrayClient.getSamplingRules(context.Background()) + rules, err := rs.xrayClient.getSamplingRules(ctx) if err != nil { return } @@ -158,36 +177,36 @@ func (rs *RemoteSampler) refreshManifest() (err error) { for _, records := range rules.SamplingRuleRecords { if records.SamplingRule.RuleName == nil { - log.Println("Sampling rule without rule name is not supported") + globalLogger.Println("Sampling rule without rule name is not supported") failed = true continue } // Only sampling rule with version 1 is valid if records.SamplingRule.Version == nil { - log.Println("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) + globalLogger.Println("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) failed = true continue } if *records.SamplingRule.Version != int64(1) { - log.Println("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) + globalLogger.Println("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) failed = true continue } if len(records.SamplingRule.Attributes) != 0 { - log.Println("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.Println("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) continue } if records.SamplingRule.ResourceARN == nil { - log.Println("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.Println("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) continue } if *records.SamplingRule.ResourceARN != "*" { - log.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) continue } @@ -195,7 +214,7 @@ func (rs *RemoteSampler) refreshManifest() (err error) { r, putErr := rs.manifest.putRule(records.SamplingRule) if putErr != nil { failed = true - log.Printf("Error occurred creating/updating rule. %v\n", putErr) + globalLogger.Printf("Error occurred creating/updating rule. %v\n", putErr) } else if r != nil { actives[*r] = true } diff --git a/samplers/aws/xray/remote_sampler_options.go b/samplers/aws/xray/remote_sampler_options.go new file mode 100644 index 00000000000..590ba24a64d --- /dev/null +++ b/samplers/aws/xray/remote_sampler_options.go @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "time" +) + +const ( + defaultProxyEndpoint = "127.0.0.1:2000" + defaultPollingInterval = 300 +) + +// SamplerOption is a function that sets config on the sampler +type SamplerOption func(options *samplerOptions) + +type samplerOptions struct { + proxyEndpoint string + samplingRulesPollingInterval time.Duration + logger Logger +} + +// sets custom proxy endpoint +func WithProxyEndpoint(proxyEndpoint string) SamplerOption { + return func(o *samplerOptions) { + o.proxyEndpoint = proxyEndpoint + } +} + +// sets polling interval for sampling rules +func WithSamplingRulesPollingInterval(polingInterval time.Duration) SamplerOption { + return func(o *samplerOptions) { + o.samplingRulesPollingInterval = polingInterval + } +} + +// sets custom logging for remote sampling implementation +func WithLogger(l Logger) SamplerOption { + return func(o *samplerOptions) { + o.logger = l + } +} + +func (o *samplerOptions) applyOptionsAndDefaults(opts ...SamplerOption) *samplerOptions { + for _, option := range opts { + option(o) + } + + if o.proxyEndpoint == "" { + o.proxyEndpoint = defaultProxyEndpoint + } + if o.samplingRulesPollingInterval <= 0 { + o.samplingRulesPollingInterval = defaultPollingInterval * time.Second + } + if o.logger == nil { + o.logger = noopLogger{} + } + + return o +} diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index 192140c705d..32146a841d9 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -21,16 +21,12 @@ import ( "sync" ) -const defaultRule = "Default" const defaultInterval = int64(10) -//const manifestTTL = 3600 // Seconds - //// centralizedManifest represents a full sampling ruleset, with a list of //// custom rules and default values for incoming requests that do //// not match any of the provided rules. type centralizedManifest struct { - defaultRule *centralizedRule rules []*centralizedRule index map[string]*centralizedRule refreshedAt int64 @@ -47,26 +43,7 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, } }() - // Default rule - if *rule.RuleName == defaultRule { - m.mu.RLock() - r = m.defaultRule - m.mu.RUnlock() - - // Update rule if already exists - if r != nil { - r.updateRule(rule) - - return - } - - // Create Default rule - r = m.createDefaultRule(rule) - - return - } - - // User-defined rule + // User-defined rule + default rule m.mu.RLock() var ok bool r, ok = m.index[*rule.RuleName] @@ -74,7 +51,7 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, // Create rule if it does not exist if !ok { - r = m.createUserRule(rule) + r = m.createRule(rule) return } @@ -87,7 +64,7 @@ func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, // createUserRule creates a user-defined centralizedRule, appends it to the sorted array, // adds it to the index, and returns the newly created rule. -func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedRule { +func (m *centralizedManifest) createRule(rule *ruleProperties) *centralizedRule { m.mu.Lock() defer m.mu.Unlock() @@ -121,41 +98,6 @@ func (m *centralizedManifest) createUserRule(rule *ruleProperties) *centralizedR return csr } -// createDefaultRule creates a default centralizedRule and adds it to the manifest. -func (m *centralizedManifest) createDefaultRule(rule *ruleProperties) *centralizedRule { - m.mu.Lock() - defer m.mu.Unlock() - - // Return early if rule already exists - if d := m.defaultRule; d != nil { - return d - } - - // Create CentralizedRule from xraySvc.SamplingRule - clock := &DefaultClock{} - rand := &DefaultRand{} - - cr := ¢ralizedReservoir{ - capacity: *rule.ReservoirSize, - interval: defaultInterval, - } - - csr := ¢ralizedRule{ - reservoir: cr, - ruleProperties: rule, - clock: clock, - rand: rand, - } - - // Update manifest if rule does not exist - m.defaultRule = csr - - // Update index - m.index[*rule.RuleName] = csr - - return csr -} - // prune removes all rules in the manifest not present in the given list of active rules. // Preserves ordering of sorted array. func (m *centralizedManifest) prune(actives map[centralizedRule]bool) { @@ -194,12 +136,3 @@ func (m *centralizedManifest) sort() { sort.Slice(m.rules, less) } - -// expired returns true if the manifest has not been successfully refreshed in -// 'manifestTTL' seconds. -//func (m *centralizedManifest) expired() bool { -// m.mu.RLock() -// defer m.mu.RUnlock() -// -// return m.refreshedAt < m.clock.Now().Unix()-manifestTTL -//} diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index b73872347a4..6dbc4abce51 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -34,7 +34,7 @@ func getFloatPointer(val float64) *float64 { } // Assert that putRule() creates a new user-defined rule and adds to manifest -func TestCreateUserRule(t *testing.T) { +func TestCreateRule(t *testing.T) { resARN := "*" r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ @@ -117,101 +117,8 @@ func TestCreateUserRule(t *testing.T) { assert.Equal(t, exp, r2) } -// Assert that putRule() creates a new default rule and adds to manifest -func TestCreateDefaultRule(t *testing.T) { - m := ¢ralizedManifest{ - index: map[string]*centralizedRule{}, - } - - // Output of GetSamplingRules API and Input to putRule(). - reservoirSize := int64(10) - fixedRate := float64(0.05) - ruleName := "Default" - - // Expected centralized sampling rule - clock := &DefaultClock{} - rand := &DefaultRand{} - - p := &ruleProperties{ - RuleName: &ruleName, - ReservoirSize: &reservoirSize, - FixedRate: &fixedRate, - } - - cr := ¢ralizedReservoir{ - capacity: reservoirSize, - interval: 10, - } - - exp := ¢ralizedRule{ - reservoir: cr, - ruleProperties: p, - clock: clock, - rand: rand, - } - - // Add to manifest - r, err := m.putRule(p) - assert.Nil(t, err) - assert.Equal(t, exp, r) - assert.Equal(t, exp, m.defaultRule) -} - -// Assert that putRule() updates the default rule -func TestUpdateDefaultRule(t *testing.T) { - clock := &DefaultClock{} - rand := &DefaultRand{} - - // Original default sampling rule - r := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("Default"), - ReservoirSize: getIntPointer(10), - FixedRate: getFloatPointer(0.05), - }, - reservoir: ¢ralizedReservoir{ - capacity: 10, - }, - clock: clock, - rand: rand, - } - - m := ¢ralizedManifest{ - defaultRule: r, - } - - // Output of GetSamplingRules API and Input to putRule(). - reservoirSize := int64(20) - fixedRate := 0.06 - ruleName := "Default" - - // Expected centralized sampling rule - p := &ruleProperties{ - RuleName: &ruleName, - ReservoirSize: &reservoirSize, - FixedRate: &fixedRate, - } - - cr := ¢ralizedReservoir{ - capacity: reservoirSize, - } - - exp := ¢ralizedRule{ - reservoir: cr, - ruleProperties: p, - clock: clock, - rand: rand, - } - - // Update default rule in manifest - r, err := m.putRule(p) - assert.Nil(t, err) - assert.Equal(t, exp, r) - assert.Equal(t, exp, m.defaultRule) -} - -// Assert that creating a user-defined rule which already exists is a no-op -func TestCreateUserRuleNoOp(t *testing.T) { +// Assert that creating a rule which already exists is a no-op +func TestCreateRuleNoOp(t *testing.T) { resARN := "*" serviceType := "" attributes := map[string]*string{ @@ -281,8 +188,8 @@ func TestCreateUserRuleNoOp(t *testing.T) { assert.Equal(t, r3, m.rules[1]) } -// Assert that putRule() updates the user-defined rule in the manifest -func TestUpdateUserRule(t *testing.T) { +// Assert that putRule() updates the rule in the manifest +func TestUpdateRule(t *testing.T) { resARN := "*" serviceType := "" attributes := map[string]*string{ diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index 0069275b428..e63b3cd360e 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -26,16 +26,13 @@ type centralizedRule struct { ruleProperties *ruleProperties // Number of requests matched against this rule - //requests int64 + //matchedRequests int64 // // Number of requests sampled using this rule - //sampled int64 + //sampledRequests int64 // // Number of requests burrowed - //borrows int64 - // - // Timestamp for last match against this rule - //usedAt int64 + //borrowedRequests int64 // Provides system time clock Clock From f423e014593fb15907df93e3366a99860ad1d1c3 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 20 Dec 2021 16:10:19 -0800 Subject: [PATCH 16/32] modified config options for sample implementation --- samplers/aws/xray/client.go | 1 + samplers/aws/xray/remote_sampler.go | 35 +++++------------ ...er_options.go => remote_sampler_config.go} | 39 +++++++++---------- 3 files changed, 29 insertions(+), 46 deletions(-) rename samplers/aws/xray/{remote_sampler_options.go => remote_sampler_config.go} (65%) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index a1a489c3aad..8d8a5fe1f20 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -46,6 +46,7 @@ func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOut req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) if err != nil { globalLogger.Printf("failed to create http request, %v\n", err) + return nil, fmt.Errorf("xray client: failed to create http request: %w", err) } output, err := p.httpClient.Do(req) diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 266f58e2d52..7f5831b935b 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -19,7 +19,6 @@ import ( crypto "crypto/rand" "errors" "fmt" - "sync" "time" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -47,19 +46,14 @@ type RemoteSampler struct { // Provides system time clock Clock - - mu sync.RWMutex } // Compile time assertion that remoteSampler implements the Sampler interface. var _ sdktrace.Sampler = (*RemoteSampler)(nil) // NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. -func NewRemoteSampler(ctx context.Context, opts ...SamplerOption) (*RemoteSampler, error) { - options := new(samplerOptions).applyOptionsAndDefaults(opts...) - - // set global logging - globalLogger = options.logger +func NewRemoteSampler(ctx context.Context, opts ...Option) (*RemoteSampler, error) { + cfg := newConfig(opts...) // Generate clientID var r [12]byte @@ -84,15 +78,12 @@ func NewRemoteSampler(ctx context.Context, opts ...SamplerOption) (*RemoteSample clock: clock, manifest: m, clientID: id, - xrayClient: newClient(options.proxyEndpoint), - samplingRulesPollingInterval: options.samplingRulesPollingInterval, + xrayClient: newClient(cfg.proxyEndpoint), + samplingRulesPollingInterval: cfg.samplingRulesPollingInterval, } - remoteSampler.mu.Lock() - if !remoteSampler.pollerStart { - remoteSampler.start(ctx) - } - remoteSampler.mu.Unlock() + // starts the rule poller + remoteSampler.start(ctx) return remoteSampler, nil } @@ -109,10 +100,9 @@ func (rs *RemoteSampler) Description() string { func (rs *RemoteSampler) start(ctx context.Context) { if !rs.pollerStart { + rs.pollerStart = true rs.startRulePoller(ctx) } - - rs.pollerStart = true } func (rs *RemoteSampler) startRulePoller(ctx context.Context) { @@ -120,13 +110,6 @@ func (rs *RemoteSampler) startRulePoller(ctx context.Context) { // Period = 300s, Jitter = 5s t := newTicker(rs.samplingRulesPollingInterval, 5*time.Second) - // Initial refresh - if err := rs.refreshManifest(ctx); err != nil { - globalLogger.Printf("Error occurred while refreshing sampling rules. %v\n", err) - } else { - globalLogger.Println("Successfully fetched sampling rules") - } - // Periodic manifest refresh for { if err := rs.refreshManifest(ctx); err != nil { @@ -137,11 +120,11 @@ func (rs *RemoteSampler) startRulePoller(ctx context.Context) { select { case _, more := <-t.C(): if !more { - break + return } continue case <-ctx.Done(): - break + return } } }() diff --git a/samplers/aws/xray/remote_sampler_options.go b/samplers/aws/xray/remote_sampler_config.go similarity index 65% rename from samplers/aws/xray/remote_sampler_options.go rename to samplers/aws/xray/remote_sampler_config.go index 590ba24a64d..986d24361c8 100644 --- a/samplers/aws/xray/remote_sampler_options.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -24,49 +24,48 @@ const ( ) // SamplerOption is a function that sets config on the sampler -type SamplerOption func(options *samplerOptions) +type Option func(options *config) -type samplerOptions struct { +type config struct { proxyEndpoint string samplingRulesPollingInterval time.Duration logger Logger } // sets custom proxy endpoint -func WithProxyEndpoint(proxyEndpoint string) SamplerOption { - return func(o *samplerOptions) { +func WithProxyEndpoint(proxyEndpoint string) Option { + return func(o *config) { o.proxyEndpoint = proxyEndpoint } } // sets polling interval for sampling rules -func WithSamplingRulesPollingInterval(polingInterval time.Duration) SamplerOption { - return func(o *samplerOptions) { +func WithSamplingRulesPollingInterval(polingInterval time.Duration) Option { + return func(o *config) { o.samplingRulesPollingInterval = polingInterval } } // sets custom logging for remote sampling implementation -func WithLogger(l Logger) SamplerOption { - return func(o *samplerOptions) { +func WithLogger(l Logger) Option { + return func(o *config) { o.logger = l } } -func (o *samplerOptions) applyOptionsAndDefaults(opts ...SamplerOption) *samplerOptions { - for _, option := range opts { - option(o) +func newConfig(opts ...Option) *config { + cfg := &config{ + proxyEndpoint: defaultProxyEndpoint, + samplingRulesPollingInterval: defaultPollingInterval * time.Second, + logger: noopLogger{}, } - if o.proxyEndpoint == "" { - o.proxyEndpoint = defaultProxyEndpoint - } - if o.samplingRulesPollingInterval <= 0 { - o.samplingRulesPollingInterval = defaultPollingInterval * time.Second - } - if o.logger == nil { - o.logger = noopLogger{} + for _, option := range opts { + option(cfg) } - return o + // setting global logger + globalLogger = cfg.logger + + return cfg } From 8e1f48f6417c6421b71ec549c12c62187c141aad Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Wed, 2 Feb 2022 19:06:57 -0800 Subject: [PATCH 17/32] added unit tests --- samplers/aws/go.mod | 2 +- samplers/aws/go.sum | 17 +- samplers/aws/xray/client_test.go | 59 ++++ samplers/aws/xray/clock_test.go | 39 +++ samplers/aws/xray/remote_sampler_test.go | 347 +++++++++++++++++++++++ samplers/aws/xray/reservoir.go | 2 +- 6 files changed, 458 insertions(+), 8 deletions(-) create mode 100644 samplers/aws/xray/client_test.go create mode 100644 samplers/aws/xray/clock_test.go create mode 100644 samplers/aws/xray/remote_sampler_test.go diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod index e221ca010aa..f163d318ada 100644 --- a/samplers/aws/go.mod +++ b/samplers/aws/go.mod @@ -4,5 +4,5 @@ go 1.16 require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel/sdk v1.2.0 + go.opentelemetry.io/otel/sdk v1.3.0 ) diff --git a/samplers/aws/go.sum b/samplers/aws/go.sum index 4d29e6bf5bf..cc354a8aa45 100644 --- a/samplers/aws/go.sum +++ b/samplers/aws/go.sum @@ -1,5 +1,10 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA= +github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE= +github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -7,12 +12,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= -go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= -go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= -go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= -go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= -go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI= +go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/samplers/aws/xray/client_test.go b/samplers/aws/xray/client_test.go new file mode 100644 index 00000000000..4fe3664ef69 --- /dev/null +++ b/samplers/aws/xray/client_test.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "context" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestGetSamplingRules(t *testing.T) { + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"Default\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"test-rule\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}},{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":100,\"ReservoirSize\":100,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule-1\",\"RuleName\":\"test-rule-1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}}]}" + ctx := context.Background() + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + + u, _ := url.Parse(testServer.URL) + client := newClient(u.Host) + + samplingRules, _ := client.getSamplingRules(ctx) + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.RuleName, "Default") + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.ServiceType, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.Host, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.URLPath, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.ReservoirSize, int64(60)) + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.FixedRate, 0.5) + + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.RuleName, "test-rule") + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.ServiceType, "local") + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.Host, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.URLPath, "/aws-sdk-call") + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.ReservoirSize, int64(3)) + assert.Equal(t, *samplingRules.SamplingRuleRecords[1].SamplingRule.FixedRate, 0.09) + + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.RuleName, "test-rule-1") + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.ServiceType, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.Host, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.URLPath, "*") + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.ReservoirSize, int64(100)) + assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.FixedRate, 0.09) +} diff --git a/samplers/aws/xray/clock_test.go b/samplers/aws/xray/clock_test.go new file mode 100644 index 00000000000..91c4ebad210 --- /dev/null +++ b/samplers/aws/xray/clock_test.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "sync/atomic" + "time" +) + +// MockClock is a struct to record current time. +type MockClock struct { + NowTime int64 + NowNanos int64 +} + +// Now function returns NowTime value. +func (c *MockClock) Now() time.Time { + return time.Unix(c.NowTime, c.NowNanos) +} + +// Increment is a method to increase current time. +func (c *MockClock) Increment(s int64, ns int64) time.Time { + sec := atomic.AddInt64(&c.NowTime, s) + nSec := atomic.AddInt64(&c.NowNanos, ns) + + return time.Unix(sec, nSec) +} \ No newline at end of file diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go new file mode 100644 index 00000000000..3ac59dbe786 --- /dev/null +++ b/samplers/aws/xray/remote_sampler_test.go @@ -0,0 +1,347 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "context" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestRefreshManifest(t *testing.T) { + ctx := context.Background() + + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"Default\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"test-rule\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}},{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":100,\"ReservoirSize\":100,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule-1\",\"RuleName\":\"test-rule-1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}}]}" + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + defer testServer.Close() + + u, _ := url.Parse(testServer.URL) + + rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + _ = rs.refreshManifest(ctx) + + // Rule 'r1' + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("Default"), + Priority: getIntPointer(10000), + Host: getStringPointer("*"), + HTTPMethod: getStringPointer("*"), + URLPath: getStringPointer("*"), + ReservoirSize: getIntPointer(40), + Version: getIntPointer(1), + FixedRate: getFloatPointer(0.5), + ServiceName: getStringPointer("*"), + ResourceARN: getStringPointer("*"), + ServiceType: getStringPointer(""), + }, + } + + // Rule 'r2' + r2 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("test-rule"), + Priority: getIntPointer(1), + Host: getStringPointer("*"), + HTTPMethod: getStringPointer("GET"), + URLPath: getStringPointer("/aws-sdk-call"), + ReservoirSize: getIntPointer(3), + FixedRate: getFloatPointer(0.09), + Version: getIntPointer(1), + ServiceName: getStringPointer("test-rule"), + ResourceARN: getStringPointer("*"), + ServiceType: getStringPointer("local"), + }, + } + + // Rule 'r3' + r3 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("test-rule-1"), + Priority: getIntPointer(100), + Host: getStringPointer("*"), + HTTPMethod: getStringPointer("*"), + URLPath: getStringPointer("*"), + ReservoirSize: getIntPointer(100), + FixedRate: getFloatPointer(0.09), + Version: getIntPointer(1), + ServiceName: getStringPointer("*"), + ResourceARN: getStringPointer("*"), + ServiceType: getStringPointer(""), + }, + } + // Assert on sorting order + assert.Equal(t, r2.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) + assert.Equal(t, r3.ruleProperties.RuleName, rs.manifest.rules[1].ruleProperties.RuleName) + assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[2].ruleProperties.RuleName) + + // Assert on size of manifest + assert.Equal(t, 3, len(rs.manifest.rules)) + assert.Equal(t, 3, len(rs.manifest.index)) +} + +func TestRefreshManifestRuleAdditionInvalidRule1(t *testing.T) { + ctx := context.Background() + + // Rule 'r1' + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("XYZ"), // invalid + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Sorted array + rules := []*centralizedRule{r1} + + index := map[string]*centralizedRule{ + "r1": r1, + } + + manifest := ¢ralizedManifest{ + rules: rules, + index: index, + refreshedAt: 1500000000, + } + + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" + + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + defer testServer.Close() + + u, _ := url.Parse(testServer.URL) + + rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + rs.manifest = manifest + err := rs.refreshManifest(ctx) + + assert.Nil(t, err) + // Refresh manifest with updates from mock proxy + assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added +} + +func TestRefreshManifestRuleAdditionInvalidRule2(t *testing.T) { // non nil Attributes + ctx := context.Background() + + attributes := make(map[string]*string) + attributes["a"] = getStringPointer("*") + + // Rule 'r1' + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("*"), + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Sorted array + rules := []*centralizedRule{r1} + + index := map[string]*centralizedRule{ + "r1": r1, + } + + manifest := ¢ralizedManifest{ + rules: rules, + index: index, + refreshedAt: 1500000000, + } + + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" + + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + defer testServer.Close() + + u, _ := url.Parse(testServer.URL) + + rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + rs.manifest = manifest + err := rs.refreshManifest(ctx) + + assert.Nil(t, err) + assert.Equal(t, 0, len(rs.manifest.rules)) // rule not added +} + +func TestRefreshManifestRuleAdditionInvalidRule3(t *testing.T) { // 1 valid and 1 invalid rule + ctx := context.Background() + + // Rule 'r1' + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("*"), + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Rule 'r2' + r2 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r2"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("*"), + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Sorted array + rules := []*centralizedRule{r1} + + index := map[string]*centralizedRule{ + "r1": r1, + } + + manifest := ¢ralizedManifest{ + rules: rules, + index: index, + refreshedAt: 1500000000, + } + + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r2\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}}]}" + + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + defer testServer.Close() + + u, _ := url.Parse(testServer.URL) + + rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + rs.manifest = manifest + err := rs.refreshManifest(ctx) + + assert.Nil(t, err) + assert.Equal(t, 1, len(rs.manifest.rules)) // u1 not added + assert.Equal(t, r2.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) +} + +// Assert that an invalid rule update does not update the rule +func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { + ctx := context.Background() + + // Rule 'r1' + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("*"), + ServiceName: getStringPointer("www.foo.com"), + HTTPMethod: getStringPointer("POST"), + URLPath: getStringPointer("/resource/bar"), + ReservoirSize: getIntPointer(50), + FixedRate: getFloatPointer(0.05), + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Rule 'r3' + r3 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(8), + ResourceARN: getStringPointer("*"), + ServiceName: getStringPointer("www.bar.com"), + HTTPMethod: getStringPointer("POST"), + URLPath: getStringPointer("/resource/foo"), + ReservoirSize: getIntPointer(40), + FixedRate: getFloatPointer(0.10), + Host: getStringPointer("h3"), + }, + reservoir: ¢ralizedReservoir{ + quota: 10, + capacity: 50, + + }, + } + + // Sorted array + rules := []*centralizedRule{r1, r3} + + index := map[string]*centralizedRule{ + "r1": r1, + "r3": r3, + } + + manifest := ¢ralizedManifest{ + rules: rules, + index: index, + refreshedAt: 1500000000, + } + + body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"FixedRate\":0.09,\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r3\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":2}}]}" + + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, _ = res.Write([]byte(body)) + })) + defer testServer.Close() + + u, _ := url.Parse(testServer.URL) + + rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + rs.manifest = manifest + err := rs.refreshManifest(ctx) + assert.NotNil(t, err) + + // Assert on size of manifest + assert.Equal(t, 1, len(rs.manifest.rules)) + assert.Equal(t, 1, len(rs.manifest.index)) + + // Assert on sorting order + assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) +} \ No newline at end of file diff --git a/samplers/aws/xray/reservoir.go b/samplers/aws/xray/reservoir.go index 6810c5f86f9..65c54479cc0 100644 --- a/samplers/aws/xray/reservoir.go +++ b/samplers/aws/xray/reservoir.go @@ -18,7 +18,7 @@ package xray // centralizedReservoir is a reservoir distributed among all running instances of the SDK type centralizedReservoir struct { // Quota assigned to client - //quota int64 + quota int64 // // Quota refresh timestamp //refreshedAt int64 From c20df372e9a8af3fb39e61fbfc51c28c566b3dc2 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Wed, 2 Feb 2022 19:29:44 -0800 Subject: [PATCH 18/32] fix race in tests --- samplers/aws/xray/clock_test.go | 2 +- samplers/aws/xray/remote_sampler_test.go | 137 ++++++++++++++--------- 2 files changed, 84 insertions(+), 55 deletions(-) diff --git a/samplers/aws/xray/clock_test.go b/samplers/aws/xray/clock_test.go index 91c4ebad210..32649590b3c 100644 --- a/samplers/aws/xray/clock_test.go +++ b/samplers/aws/xray/clock_test.go @@ -36,4 +36,4 @@ func (c *MockClock) Increment(s int64, ns int64) time.Time { nSec := atomic.AddInt64(&c.NowNanos, ns) return time.Unix(sec, nSec) -} \ No newline at end of file +} diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index 3ac59dbe786..3d19e62e838 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -36,7 +36,20 @@ func TestRefreshManifest(t *testing.T) { u, _ := url.Parse(testServer.URL) - rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + clock := &DefaultClock{} + + m := ¢ralizedManifest{ + rules: []*centralizedRule{}, + index: map[string]*centralizedRule{}, + clock: clock, + } + + rs := &RemoteSampler{ + xrayClient: newClient(u.Host), + clock: clock, + manifest: m, + } + _ = rs.refreshManifest(ctx) // Rule 'r1' @@ -44,9 +57,9 @@ func TestRefreshManifest(t *testing.T) { ruleProperties: &ruleProperties{ RuleName: getStringPointer("Default"), Priority: getIntPointer(10000), - Host: getStringPointer("*"), + Host: getStringPointer("*"), HTTPMethod: getStringPointer("*"), - URLPath: getStringPointer("*"), + URLPath: getStringPointer("*"), ReservoirSize: getIntPointer(40), Version: getIntPointer(1), FixedRate: getFloatPointer(0.5), @@ -61,9 +74,9 @@ func TestRefreshManifest(t *testing.T) { ruleProperties: &ruleProperties{ RuleName: getStringPointer("test-rule"), Priority: getIntPointer(1), - Host: getStringPointer("*"), + Host: getStringPointer("*"), HTTPMethod: getStringPointer("GET"), - URLPath: getStringPointer("/aws-sdk-call"), + URLPath: getStringPointer("/aws-sdk-call"), ReservoirSize: getIntPointer(3), FixedRate: getFloatPointer(0.09), Version: getIntPointer(1), @@ -78,9 +91,9 @@ func TestRefreshManifest(t *testing.T) { ruleProperties: &ruleProperties{ RuleName: getStringPointer("test-rule-1"), Priority: getIntPointer(100), - Host: getStringPointer("*"), + Host: getStringPointer("*"), HTTPMethod: getStringPointer("*"), - URLPath: getStringPointer("*"), + URLPath: getStringPointer("*"), ReservoirSize: getIntPointer(100), FixedRate: getFloatPointer(0.09), Version: getIntPointer(1), @@ -101,18 +114,18 @@ func TestRefreshManifest(t *testing.T) { func TestRefreshManifestRuleAdditionInvalidRule1(t *testing.T) { ctx := context.Background() + newConfig() // Rule 'r1' r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), ResourceARN: getStringPointer("XYZ"), // invalid }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } @@ -131,7 +144,6 @@ func TestRefreshManifestRuleAdditionInvalidRule1(t *testing.T) { body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" - // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { _, _ = res.Write([]byte(body)) @@ -140,7 +152,14 @@ func TestRefreshManifestRuleAdditionInvalidRule1(t *testing.T) { u, _ := url.Parse(testServer.URL) - rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) + clock := &DefaultClock{} + + rs := &RemoteSampler{ + xrayClient: newClient(u.Host), + clock: clock, + manifest: manifest, + } + rs.manifest = manifest err := rs.refreshManifest(ctx) @@ -158,14 +177,13 @@ func TestRefreshManifestRuleAdditionInvalidRule2(t *testing.T) { // non nil Attr // Rule 'r1' r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), ResourceARN: getStringPointer("*"), }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } @@ -184,7 +202,6 @@ func TestRefreshManifestRuleAdditionInvalidRule2(t *testing.T) { // non nil Attr body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" - // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { _, _ = res.Write([]byte(body)) @@ -193,8 +210,14 @@ func TestRefreshManifestRuleAdditionInvalidRule2(t *testing.T) { // non nil Attr u, _ := url.Parse(testServer.URL) - rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) - rs.manifest = manifest + clock := &DefaultClock{} + + rs := &RemoteSampler{ + xrayClient: newClient(u.Host), + clock: clock, + manifest: manifest, + } + err := rs.refreshManifest(ctx) assert.Nil(t, err) @@ -207,28 +230,26 @@ func TestRefreshManifestRuleAdditionInvalidRule3(t *testing.T) { // 1 valid and // Rule 'r1' r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), ResourceARN: getStringPointer("*"), }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } // Rule 'r2' r2 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(4), + RuleName: getStringPointer("r2"), + Priority: getIntPointer(4), ResourceARN: getStringPointer("*"), }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } @@ -247,7 +268,6 @@ func TestRefreshManifestRuleAdditionInvalidRule3(t *testing.T) { // 1 valid and body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r2\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}}]}" - // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { _, _ = res.Write([]byte(body)) @@ -256,8 +276,14 @@ func TestRefreshManifestRuleAdditionInvalidRule3(t *testing.T) { // 1 valid and u, _ := url.Parse(testServer.URL) - rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) - rs.manifest = manifest + clock := &DefaultClock{} + + rs := &RemoteSampler{ + xrayClient: newClient(u.Host), + clock: clock, + manifest: manifest, + } + err := rs.refreshManifest(ctx) assert.Nil(t, err) @@ -272,39 +298,37 @@ func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { // Rule 'r1' r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("*"), - ServiceName: getStringPointer("www.foo.com"), - HTTPMethod: getStringPointer("POST"), - URLPath: getStringPointer("/resource/bar"), + RuleName: getStringPointer("r1"), + Priority: getIntPointer(4), + ResourceARN: getStringPointer("*"), + ServiceName: getStringPointer("www.foo.com"), + HTTPMethod: getStringPointer("POST"), + URLPath: getStringPointer("/resource/bar"), ReservoirSize: getIntPointer(50), - FixedRate: getFloatPointer(0.05), + FixedRate: getFloatPointer(0.05), }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } // Rule 'r3' r3 := ¢ralizedRule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(8), - ResourceARN: getStringPointer("*"), - ServiceName: getStringPointer("www.bar.com"), - HTTPMethod: getStringPointer("POST"), - URLPath: getStringPointer("/resource/foo"), + RuleName: getStringPointer("r1"), + Priority: getIntPointer(8), + ResourceARN: getStringPointer("*"), + ServiceName: getStringPointer("www.bar.com"), + HTTPMethod: getStringPointer("POST"), + URLPath: getStringPointer("/resource/foo"), ReservoirSize: getIntPointer(40), - FixedRate: getFloatPointer(0.10), - Host: getStringPointer("h3"), + FixedRate: getFloatPointer(0.10), + Host: getStringPointer("h3"), }, reservoir: ¢ralizedReservoir{ - quota: 10, + quota: 10, capacity: 50, - }, } @@ -324,7 +348,6 @@ func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"FixedRate\":0.09,\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r3\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":2}}]}" - // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { _, _ = res.Write([]byte(body)) @@ -333,8 +356,14 @@ func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { u, _ := url.Parse(testServer.URL) - rs, _ := NewRemoteSampler(ctx, WithProxyEndpoint(u.Host)) - rs.manifest = manifest + clock := &DefaultClock{} + + rs := &RemoteSampler{ + xrayClient: newClient(u.Host), + clock: clock, + manifest: manifest, + } + err := rs.refreshManifest(ctx) assert.NotNil(t, err) @@ -344,4 +373,4 @@ func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { // Assert on sorting order assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) -} \ No newline at end of file +} From fdbebb0b3750a11cce7b9799b0c4c290d7cc29aa Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 3 Feb 2022 12:12:04 -0800 Subject: [PATCH 19/32] refactor PR based on review --- samplers/aws/xray/client.go | 19 +++++++++++-------- samplers/aws/xray/remote_sampler.go | 2 +- samplers/aws/xray/remote_sampler_config.go | 8 ++++---- samplers/aws/xray/sampling_rule.go | 5 +++++ 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 8d8a5fe1f20..e473efc8a7f 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -26,24 +26,27 @@ type xrayClient struct { // http client for sending unsigned proxied requests to the collector httpClient *http.Client - proxyEndpoint string + endpoint string } // newClient returns a http client with proxy endpoint func newClient(d string) *xrayClient { - proxyEndpoint := "http://" + d + endpoint := "http://" + d - p := &xrayClient{ - httpClient: &http.Client{}, - proxyEndpoint: proxyEndpoint, + return &xrayClient{ + httpClient: &http.Client{}, + endpoint: endpoint, } - - return p } // getSamplingRules calls the collector(aws proxy enabled) for sampling rules func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.proxyEndpoint+"/GetSamplingRules", nil) + rulesInput := getSamplingRulesInput{} + + statisticsByte, _ := json.Marshal(rulesInput) + body := bytes.NewReader(statisticsByte) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint+"/GetSamplingRules", body) if err != nil { globalLogger.Printf("failed to create http request, %v\n", err) return nil, fmt.Errorf("xray client: failed to create http request: %w", err) diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 7f5831b935b..d934fff596f 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -78,7 +78,7 @@ func NewRemoteSampler(ctx context.Context, opts ...Option) (*RemoteSampler, erro clock: clock, manifest: m, clientID: id, - xrayClient: newClient(cfg.proxyEndpoint), + xrayClient: newClient(cfg.endpoint), samplingRulesPollingInterval: cfg.samplingRulesPollingInterval, } diff --git a/samplers/aws/xray/remote_sampler_config.go b/samplers/aws/xray/remote_sampler_config.go index 986d24361c8..042f4e785ee 100644 --- a/samplers/aws/xray/remote_sampler_config.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -27,15 +27,15 @@ const ( type Option func(options *config) type config struct { - proxyEndpoint string + endpoint string samplingRulesPollingInterval time.Duration logger Logger } // sets custom proxy endpoint -func WithProxyEndpoint(proxyEndpoint string) Option { +func WithEndpoint(endpoint string) Option { return func(o *config) { - o.proxyEndpoint = proxyEndpoint + o.endpoint = endpoint } } @@ -55,7 +55,7 @@ func WithLogger(l Logger) Option { func newConfig(opts ...Option) *config { cfg := &config{ - proxyEndpoint: defaultProxyEndpoint, + endpoint: defaultProxyEndpoint, samplingRulesPollingInterval: defaultPollingInterval * time.Second, logger: noopLogger{}, } diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index e63b3cd360e..0f086db90af 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -59,6 +59,11 @@ type ruleProperties struct { Version *int64 `json:"Version"` } +// getSamplingRulesInput is used to store +type getSamplingRulesInput struct { + NextToken *string `json:"NextToken"` +} + type samplingRuleRecords struct { SamplingRule *ruleProperties `json:"SamplingRule"` } From 09475dccfb7d15797800e72f8fdeefd7c2990991 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 3 Feb 2022 14:11:43 -0800 Subject: [PATCH 20/32] improved tests --- .../aws/xray/remote_sampler_config_test.go | 48 ++++++++++++ samplers/aws/xray/rule_manifest_test.go | 78 ++++++++++++++++++- .../aws/xray/{clock_test.go => utils_test.go} | 10 +++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 samplers/aws/xray/remote_sampler_config_test.go rename samplers/aws/xray/{clock_test.go => utils_test.go} (81%) diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go new file mode 100644 index 00000000000..841c7352415 --- /dev/null +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. + +package xray + +import ( + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestNewConfig(t *testing.T) { + cfg := newConfig(WithSamplingRulesPollingInterval(400*time.Second), WithEndpoint("127.0.0.1:5000"), WithLogger(testLogger{})) + + assert.Equal(t, cfg.samplingRulesPollingInterval, 400*time.Second) + assert.Equal(t, cfg.endpoint, "127.0.0.1:5000") + assert.Equal(t, cfg.logger, testLogger{}) +} + +func TestDefaultConfig(t *testing.T) { + cfg := newConfig() + + assert.Equal(t, cfg.samplingRulesPollingInterval, 300*time.Second) + assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") + assert.Equal(t, cfg.logger, noopLogger{}) +} + +// noopLogger to bypass logging +type testLogger struct{} + +// noop logging method +func (nl testLogger) Printf(format string, v ...interface{}) { +} + +// noop logging method +func (nl testLogger) Println(v ...interface{}) { +} diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index 6dbc4abce51..52d127811a4 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -271,6 +271,43 @@ func TestUpdateRule(t *testing.T) { assert.Equal(t, 1, len(m.index)) } +// Assert that putRule() recovers from panic. +func TestPutRuleRecovery(t *testing.T) { + attributes := make(map[string]*string) + var rules []*centralizedRule + + index := map[string]*centralizedRule{} + + m := ¢ralizedManifest{ + rules: rules, + index: index, + } + + newRule := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + ServiceName: getStringPointer("www.foo.com"), + HTTPMethod: getStringPointer("POST"), + FixedRate: getFloatPointer(0.05), + RuleName: getStringPointer("r2"), + Priority: getIntPointer(int64(6)), + ResourceARN: getStringPointer("*"), + ServiceType: getStringPointer(""), + Attributes: attributes, + }, + } + + // Attempt to add to manifest + r, err := m.putRule(newRule.ruleProperties) + assert.NotNil(t, err) + assert.Nil(t, r) + + // Assert index is unchanged + assert.Equal(t, 0, len(m.index)) + + // Assert sorted array is unchanged + assert.Equal(t, 0, len(m.rules)) +} + // Assert that deleting a rule from the end of the array removes the rule // and preserves ordering of the sorted array func TestDeleteLastRule(t *testing.T) { @@ -559,7 +596,7 @@ func TestDeleteAllRules(t *testing.T) { } // Assert that sorting an unsorted array results in a sorted array - check priority -func TestSort(t *testing.T) { +func TestSort1(t *testing.T) { r1 := ¢ralizedRule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r1"), @@ -596,3 +633,42 @@ func TestSort(t *testing.T) { assert.Equal(t, r2, m.rules[1]) assert.Equal(t, r3, m.rules[2]) } + +// Assert that sorting an unsorted array results in a sorted array - check priority and rule name +func TestSort2(t *testing.T) { + r1 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r1"), + Priority: getIntPointer(5), + }, + } + + r2 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r2"), + Priority: getIntPointer(6), + }, + } + + r3 := ¢ralizedRule{ + ruleProperties: &ruleProperties{ + RuleName: getStringPointer("r3"), + Priority: getIntPointer(7), + }, + } + + // Unsorted rules array + rules := []*centralizedRule{r2, r1, r3} + + m := ¢ralizedManifest{ + rules: rules, + } + + // Sort array + m.sort() // r1 should precede r2 + + // Assert on order + assert.Equal(t, r1, m.rules[0]) + assert.Equal(t, r2, m.rules[1]) + assert.Equal(t, r3, m.rules[2]) +} diff --git a/samplers/aws/xray/clock_test.go b/samplers/aws/xray/utils_test.go similarity index 81% rename from samplers/aws/xray/clock_test.go rename to samplers/aws/xray/utils_test.go index 32649590b3c..910018167a4 100644 --- a/samplers/aws/xray/clock_test.go +++ b/samplers/aws/xray/utils_test.go @@ -15,7 +15,9 @@ package xray import ( + "github.com/stretchr/testify/assert" "sync/atomic" + "testing" "time" ) @@ -37,3 +39,11 @@ func (c *MockClock) Increment(s int64, ns int64) time.Time { return time.Unix(sec, nSec) } + +func TestNewTicker(t *testing.T) { + ticker := newTicker(300*time.Second, 5*time.Second) + + assert.Equal(t, ticker.d, 5*time.Minute) + assert.NotEmpty(t, ticker.t) + assert.NotEmpty(t, ticker.jitter) +} \ No newline at end of file From eec96d03d0e3f9e30a41d998bed4d17c59efa747 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 3 Feb 2022 14:30:49 -0800 Subject: [PATCH 21/32] ran precommit --- samplers/aws/xray/utils_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samplers/aws/xray/utils_test.go b/samplers/aws/xray/utils_test.go index 910018167a4..a0c8fe2ed95 100644 --- a/samplers/aws/xray/utils_test.go +++ b/samplers/aws/xray/utils_test.go @@ -46,4 +46,4 @@ func TestNewTicker(t *testing.T) { assert.Equal(t, ticker.d, 5*time.Minute) assert.NotEmpty(t, ticker.t) assert.NotEmpty(t, ticker.jitter) -} \ No newline at end of file +} From 43a2ae7f62425645cf42021a93edb6cd49f6edce Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 3 Feb 2022 15:33:33 -0800 Subject: [PATCH 22/32] remove noop log and added logr --- samplers/aws/go.mod | 1 + samplers/aws/xray/client.go | 4 +-- samplers/aws/xray/noop_log.go | 30 ------------------- samplers/aws/xray/remote_sampler.go | 21 ++++++------- samplers/aws/xray/remote_sampler_config.go | 7 +++-- .../aws/xray/remote_sampler_config_test.go | 7 +++-- 6 files changed, 22 insertions(+), 48 deletions(-) delete mode 100644 samplers/aws/xray/noop_log.go diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod index f163d318ada..6cd0147278d 100644 --- a/samplers/aws/go.mod +++ b/samplers/aws/go.mod @@ -3,6 +3,7 @@ module go.opentelemetry.io/contrib/samplers/aws go 1.16 require ( + github.com/go-logr/logr v1.2.1 // indirect github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel/sdk v1.3.0 ) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index e473efc8a7f..713c7514193 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -48,7 +48,7 @@ func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOut req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint+"/GetSamplingRules", body) if err != nil { - globalLogger.Printf("failed to create http request, %v\n", err) + globalLogger.Error(err, "failed to create http request") return nil, fmt.Errorf("xray client: failed to create http request: %w", err) } @@ -72,7 +72,7 @@ func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOut err = output.Body.Close() if err != nil { - globalLogger.Printf("failed to close http response body, %v\n\n", err) + globalLogger.Error(err, "failed to close http response body") } return &samplingRulesOutput, nil diff --git a/samplers/aws/xray/noop_log.go b/samplers/aws/xray/noop_log.go deleted file mode 100644 index 15e5f2511ef..00000000000 --- a/samplers/aws/xray/noop_log.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License 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. - -package xray - -// Logger is the logging interface used by xray -type Logger interface { - Printf(format string, v ...interface{}) - Println(v ...interface{}) -} - -// noopLogger to bypass logging -type noopLogger struct{} - -// noop logging method -func (nl noopLogger) Printf(format string, v ...interface{}) {} - -// noop logging method -func (nl noopLogger) Println(v ...interface{}) {} diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index d934fff596f..75269199f68 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -19,13 +19,14 @@ import ( crypto "crypto/rand" "errors" "fmt" + "github.com/go-logr/logr" "time" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) // global variable for logging -var globalLogger Logger +var globalLogger logr.Logger // RemoteSampler is an implementation of SamplingStrategy. type RemoteSampler struct { @@ -113,9 +114,9 @@ func (rs *RemoteSampler) startRulePoller(ctx context.Context) { // Periodic manifest refresh for { if err := rs.refreshManifest(ctx); err != nil { - globalLogger.Printf("Error occurred while refreshing sampling rules. %v\n", err) + globalLogger.Error(err, "Error occurred while refreshing sampling rules") } else { - globalLogger.Println("Successfully fetched sampling rules") + globalLogger.V(1).Info("Successfully fetched sampling rules") } select { case _, more := <-t.C(): @@ -160,36 +161,36 @@ func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { for _, records := range rules.SamplingRuleRecords { if records.SamplingRule.RuleName == nil { - globalLogger.Println("Sampling rule without rule name is not supported") + globalLogger.V(1).Info("Sampling rule without rule name is not supported") failed = true continue } // Only sampling rule with version 1 is valid if records.SamplingRule.Version == nil { - globalLogger.Println("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) failed = true continue } if *records.SamplingRule.Version != int64(1) { - globalLogger.Println("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) failed = true continue } if len(records.SamplingRule.Attributes) != 0 { - globalLogger.Println("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) continue } if records.SamplingRule.ResourceARN == nil { - globalLogger.Println("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) continue } if *records.SamplingRule.ResourceARN != "*" { - globalLogger.Println("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) continue } @@ -197,7 +198,7 @@ func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { r, putErr := rs.manifest.putRule(records.SamplingRule) if putErr != nil { failed = true - globalLogger.Printf("Error occurred creating/updating rule. %v\n", putErr) + globalLogger.Error(putErr, "Error occurred creating/updating rule") } else if r != nil { actives[*r] = true } diff --git a/samplers/aws/xray/remote_sampler_config.go b/samplers/aws/xray/remote_sampler_config.go index 042f4e785ee..28210dba0aa 100644 --- a/samplers/aws/xray/remote_sampler_config.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -15,6 +15,7 @@ package xray import ( + "github.com/go-logr/logr" "time" ) @@ -29,7 +30,7 @@ type Option func(options *config) type config struct { endpoint string samplingRulesPollingInterval time.Duration - logger Logger + logger logr.Logger } // sets custom proxy endpoint @@ -47,7 +48,7 @@ func WithSamplingRulesPollingInterval(polingInterval time.Duration) Option { } // sets custom logging for remote sampling implementation -func WithLogger(l Logger) Option { +func WithLogger(l logr.Logger) Option { return func(o *config) { o.logger = l } @@ -57,7 +58,7 @@ func newConfig(opts ...Option) *config { cfg := &config{ endpoint: defaultProxyEndpoint, samplingRulesPollingInterval: defaultPollingInterval * time.Second, - logger: noopLogger{}, + logger: logr.Logger{}, } for _, option := range opts { diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index 841c7352415..333c8d54c38 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -15,17 +15,18 @@ package xray import ( + "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "testing" "time" ) func TestNewConfig(t *testing.T) { - cfg := newConfig(WithSamplingRulesPollingInterval(400*time.Second), WithEndpoint("127.0.0.1:5000"), WithLogger(testLogger{})) + cfg := newConfig(WithSamplingRulesPollingInterval(400*time.Second), WithEndpoint("127.0.0.1:5000"), WithLogger(logr.Logger{})) assert.Equal(t, cfg.samplingRulesPollingInterval, 400*time.Second) assert.Equal(t, cfg.endpoint, "127.0.0.1:5000") - assert.Equal(t, cfg.logger, testLogger{}) + assert.Equal(t, cfg.logger, logr.Logger{}) } func TestDefaultConfig(t *testing.T) { @@ -33,7 +34,7 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, cfg.samplingRulesPollingInterval, 300*time.Second) assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") - assert.Equal(t, cfg.logger, noopLogger{}) + assert.Equal(t, cfg.logger, logr.Logger{}) } // noopLogger to bypass logging From bd59636bfea5e2aef7f8cdee556ba855e6c61592 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Thu, 3 Feb 2022 16:33:06 -0800 Subject: [PATCH 23/32] fix test failures --- samplers/aws/go.mod | 1 + samplers/aws/xray/remote_sampler_config.go | 7 ++++++- samplers/aws/xray/remote_sampler_config_test.go | 16 ++++------------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod index 6cd0147278d..537eebe1f79 100644 --- a/samplers/aws/go.mod +++ b/samplers/aws/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/go-logr/logr v1.2.1 // indirect + github.com/go-logr/stdr v1.2.0 // indirect github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel/sdk v1.3.0 ) diff --git a/samplers/aws/xray/remote_sampler_config.go b/samplers/aws/xray/remote_sampler_config.go index 28210dba0aa..c00ec3fd3ab 100644 --- a/samplers/aws/xray/remote_sampler_config.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -16,6 +16,9 @@ package xray import ( "github.com/go-logr/logr" + "github.com/go-logr/stdr" + "log" + "os" "time" ) @@ -58,7 +61,7 @@ func newConfig(opts ...Option) *config { cfg := &config{ endpoint: defaultProxyEndpoint, samplingRulesPollingInterval: defaultPollingInterval * time.Second, - logger: logr.Logger{}, + logger: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)), } for _, option := range opts { @@ -67,6 +70,8 @@ func newConfig(opts ...Option) *config { // setting global logger globalLogger = cfg.logger + // set global verbosity to log info logs + stdr.SetVerbosity(1) return cfg } diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index 333c8d54c38..2a07c39121b 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -16,7 +16,10 @@ package xray import ( "github.com/go-logr/logr" + "github.com/go-logr/stdr" "github.com/stretchr/testify/assert" + "log" + "os" "testing" "time" ) @@ -34,16 +37,5 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, cfg.samplingRulesPollingInterval, 300*time.Second) assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") - assert.Equal(t, cfg.logger, logr.Logger{}) -} - -// noopLogger to bypass logging -type testLogger struct{} - -// noop logging method -func (nl testLogger) Printf(format string, v ...interface{}) { -} - -// noop logging method -func (nl testLogger) Println(v ...interface{}) { + assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) } From d094d5859d9d0ca39d22c7a0aa0696527bdef073 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Fri, 4 Feb 2022 17:03:01 -0800 Subject: [PATCH 24/32] refactor manifest logic and added tests --- samplers/aws/go.mod | 4 +- samplers/aws/xray/client.go | 41 +- samplers/aws/xray/client_test.go | 128 +++- samplers/aws/xray/clock.go | 14 +- samplers/aws/xray/rand.go | 8 +- samplers/aws/xray/remote_sampler.go | 109 ++- .../aws/xray/remote_sampler_config_test.go | 11 + samplers/aws/xray/remote_sampler_test.go | 628 ++++++++++++------ samplers/aws/xray/reservoir.go | 4 +- samplers/aws/xray/rule_manifest.go | 100 +-- samplers/aws/xray/rule_manifest_test.go | 523 +-------------- samplers/aws/xray/sampling_rule.go | 18 +- 12 files changed, 686 insertions(+), 902 deletions(-) diff --git a/samplers/aws/go.mod b/samplers/aws/go.mod index 537eebe1f79..a650922fad0 100644 --- a/samplers/aws/go.mod +++ b/samplers/aws/go.mod @@ -3,8 +3,8 @@ module go.opentelemetry.io/contrib/samplers/aws go 1.16 require ( - github.com/go-logr/logr v1.2.1 // indirect - github.com/go-logr/stdr v1.2.0 // indirect + github.com/go-logr/logr v1.2.1 + github.com/go-logr/stdr v1.2.0 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel/sdk v1.3.0 ) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 713c7514193..2e311b0a5bc 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -15,40 +15,39 @@ package xray import ( - "bytes" "context" "encoding/json" "fmt" "net/http" + "net/url" ) type xrayClient struct { // http client for sending unsigned proxied requests to the collector httpClient *http.Client - endpoint string + endpoint *url.URL } -// newClient returns a http client with proxy endpoint +// newClient returns an HTTP client with proxy endpoint func newClient(d string) *xrayClient { endpoint := "http://" + d + endpointURL, err := url.Parse(endpoint) + if err != nil { + globalLogger.Error(err, "unable to parse endpoint from string") + } + return &xrayClient{ httpClient: &http.Client{}, - endpoint: endpoint, + endpoint: endpointURL, } } // getSamplingRules calls the collector(aws proxy enabled) for sampling rules func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { - rulesInput := getSamplingRulesInput{} - - statisticsByte, _ := json.Marshal(rulesInput) - body := bytes.NewReader(statisticsByte) - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint+"/GetSamplingRules", body) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint.String()+"/GetSamplingRules", nil) if err != nil { - globalLogger.Error(err, "failed to create http request") return nil, fmt.Errorf("xray client: failed to create http request: %w", err) } @@ -56,24 +55,12 @@ func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOut if err != nil { return nil, fmt.Errorf("xray client: unable to retrieve sampling settings: %w", err) } + defer output.Body.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(output.Body) - if err != nil { - return nil, fmt.Errorf("xray client: unable to read response body: %w", err) - } - - // Unmarshalling json data to populate getSamplingTargetsOutput struct - var samplingRulesOutput getSamplingRulesOutput - err = json.Unmarshal(buf.Bytes(), &samplingRulesOutput) - if err != nil { + var samplingRulesOutput *getSamplingRulesOutput + if err := json.NewDecoder(output.Body).Decode(&samplingRulesOutput); err != nil { return nil, fmt.Errorf("xray client: unable to unmarshal the response body: %w", err) } - err = output.Body.Close() - if err != nil { - globalLogger.Error(err, "failed to close http response body") - } - - return &samplingRulesOutput, nil + return samplingRulesOutput, nil } diff --git a/samplers/aws/xray/client_test.go b/samplers/aws/xray/client_test.go index 4fe3664ef69..4ea73dec86b 100644 --- a/samplers/aws/xray/client_test.go +++ b/samplers/aws/xray/client_test.go @@ -17,6 +17,7 @@ package xray import ( "context" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "net/http" "net/http/httptest" "net/url" @@ -24,18 +25,84 @@ import ( ) func TestGetSamplingRules(t *testing.T) { - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"Default\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"test-rule\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}},{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":100,\"ReservoirSize\":100,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule-1\",\"RuleName\":\"test-rule-1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}}]}" + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/Default", + "RuleName": "Default", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + }, + { + "CreatedAt": 1637691613, + "ModifiedAt": 1643748669, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.09, + "HTTPMethod": "GET", + "Host": "*", + "Priority": 1, + "ReservoirSize": 3, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/test-rule", + "RuleName": "test-rule", + "ServiceName": "test-rule", + "ServiceType": "local", + "URLPath": "/aws-sdk-call", + "Version": 1 + } + }, + { + "CreatedAt": 1639446197, + "ModifiedAt": 1639446197, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.09, + "HTTPMethod": "*", + "Host": "*", + "Priority": 100, + "ReservoirSize": 100, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/test-rule-1", + "RuleName": "test-rule-1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) ctx := context.Background() // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + _, err := res.Write([]byte(body)) + require.NoError(t, err) })) - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + client := newClient(u.Host) - samplingRules, _ := client.getSamplingRules(ctx) + samplingRules, err := client.getSamplingRules(ctx) + require.NoError(t, err) + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.RuleName, "Default") assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.ServiceType, "*") assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.Host, "*") @@ -57,3 +124,56 @@ func TestGetSamplingRules(t *testing.T) { assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.ReservoirSize, int64(100)) assert.Equal(t, *samplingRules.SamplingRuleRecords[2].SamplingRule.FixedRate, 0.09) } + +func TestGetSamplingRulesWithMissingValues(t *testing.T) { + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/Default", + "RuleName": "Default", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) + ctx := context.Background() + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, err := res.Write([]byte(body)) + require.NoError(t, err) + })) + + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + + client := newClient(u.Host) + + samplingRules, err := client.getSamplingRules(ctx) + require.NoError(t, err) + + // Priority and ReservoirSize are missing in API response so they are assigned as nil + assert.Nil(t, samplingRules.SamplingRuleRecords[0].SamplingRule.Priority) + assert.Nil(t, samplingRules.SamplingRuleRecords[0].SamplingRule.ReservoirSize) + + // other values are stored as expected + assert.Equal(t, *samplingRules.SamplingRuleRecords[0].SamplingRule.RuleName, "Default") +} + +func TestNewClient(t *testing.T) { + xrayClient := newClient("127.0.0.1:2020") + + assert.Equal(t, xrayClient.endpoint.String(), "http://127.0.0.1:2020") +} diff --git a/samplers/aws/xray/clock.go b/samplers/aws/xray/clock.go index a48322da5e0..d04e4c72ae9 100644 --- a/samplers/aws/xray/clock.go +++ b/samplers/aws/xray/clock.go @@ -18,15 +18,15 @@ import ( "time" ) -// Clock provides an interface to implement method for getting current time. -type Clock interface { - Now() time.Time +// clock provides an interface to implement method for getting current time. +type clock interface { + now() time.Time } -// DefaultClock is an implementation of Clock interface. -type DefaultClock struct{} +// defaultClock is an implementation of Clock interface. +type defaultClock struct{} -// Now returns current time. -func (t *DefaultClock) Now() time.Time { +// now returns current time. +func (t *defaultClock) now() time.Time { return time.Now() } diff --git a/samplers/aws/xray/rand.go b/samplers/aws/xray/rand.go index 50658e92a07..88c2796e238 100644 --- a/samplers/aws/xray/rand.go +++ b/samplers/aws/xray/rand.go @@ -91,24 +91,24 @@ type Rand interface { // DefaultRand is an implementation of Rand interface. // It is safe for concurrent use by multiple goroutines. -type DefaultRand struct{} +type defaultRand struct{} var globalRand = newGlobalRand() // Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) // from the default Source. -func (r *DefaultRand) Int63n(n int64) int64 { +func (r *defaultRand) Int63n(n int64) int64 { return globalRand.Int63n(n) } // Intn returns, as an int, a non-negative pseudo-random number in [0,n) // from the default Source. -func (r *DefaultRand) Intn(n int) int { +func (r *defaultRand) Intn(n int) int { return globalRand.Intn(n) } // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) // from the default Source. -func (r *DefaultRand) Float64() float64 { +func (r *defaultRand) Float64() float64 { return globalRand.Float64() } diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 75269199f68..110262f1cde 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -28,32 +28,35 @@ import ( // global variable for logging var globalLogger logr.Logger -// RemoteSampler is an implementation of SamplingStrategy. -type RemoteSampler struct { - // List of known centralized sampling rules - manifest *centralizedManifest +// remoteSampler is a sampler for AWS X-Ray which polls sampling rules and sampling targets +// to make a sampling decision based on rules set by users on AWS X-Ray console +type remoteSampler struct { + // List of known centralized sampling rules. + manifest *manifest - // proxy is used for getting quotas and sampling rules + // xrayClient is used for getting quotas and sampling rules. xrayClient *xrayClient - // pollerStart, if true represents rule and target pollers are started + // pollerStart, if true represents rule and target pollers are started. pollerStart bool - // samplingRules polling interval, default is 300 seconds + // samplingRulesPollingInterval, default is 300 seconds. samplingRulesPollingInterval time.Duration - // Unique ID used by XRay service to identify this client + // Unique ID used by XRay service to identify this client. clientID string - // Provides system time - clock Clock + // Provides system time. + clock clock } // Compile time assertion that remoteSampler implements the Sampler interface. -var _ sdktrace.Sampler = (*RemoteSampler)(nil) +var _ sdktrace.Sampler = (*remoteSampler)(nil) -// NewRemoteSampler returns a centralizedSampler which decides to sample a given request or not. -func NewRemoteSampler(ctx context.Context, opts ...Option) (*RemoteSampler, error) { +// NewRemoteSampler returns a sampler which decides to sample a given request or not +// based on the sampling rules set by users on AWS X-Ray console. Sampler also periodically polls +// sampling rules and sampling targets. +func NewRemoteSampler(ctx context.Context, opts ...Option) (sdktrace.Sampler, error) { cfg := newConfig(opts...) // Generate clientID @@ -66,16 +69,15 @@ func NewRemoteSampler(ctx context.Context, opts ...Option) (*RemoteSampler, erro id := fmt.Sprintf("%02x", r) - clock := &DefaultClock{} + clock := &defaultClock{} - m := ¢ralizedManifest{ - rules: []*centralizedRule{}, - index: map[string]*centralizedRule{}, + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, clock: clock, } - remoteSampler := &RemoteSampler{ - pollerStart: false, + remoteSampler := &remoteSampler{ clock: clock, manifest: m, clientID: id, @@ -89,24 +91,24 @@ func NewRemoteSampler(ctx context.Context, opts ...Option) (*RemoteSampler, erro return remoteSampler, nil } -func (rs *RemoteSampler) ShouldSample(parameters sdktrace.SamplingParameters) sdktrace.SamplingResult { +func (rs *remoteSampler) ShouldSample(parameters sdktrace.SamplingParameters) sdktrace.SamplingResult { // ToDo: add business logic for remote sampling return sdktrace.SamplingResult{} } -func (rs *RemoteSampler) Description() string { +func (rs *remoteSampler) Description() string { return "remote sampling with AWS X-Ray" } -func (rs *RemoteSampler) start(ctx context.Context) { +func (rs *remoteSampler) start(ctx context.Context) { if !rs.pollerStart { rs.pollerStart = true rs.startRulePoller(ctx) } } -func (rs *RemoteSampler) startRulePoller(ctx context.Context) { +func (rs *remoteSampler) startRulePoller(ctx context.Context) { go func() { // Period = 300s, Jitter = 5s t := newTicker(rs.samplingRulesPollingInterval, 5*time.Second) @@ -116,7 +118,7 @@ func (rs *RemoteSampler) startRulePoller(ctx context.Context) { if err := rs.refreshManifest(ctx); err != nil { globalLogger.Error(err, "Error occurred while refreshing sampling rules") } else { - globalLogger.V(1).Info("Successfully fetched sampling rules") + globalLogger.Info("Successfully fetched sampling rules") } select { case _, more := <-t.C(): @@ -131,34 +133,26 @@ func (rs *RemoteSampler) startRulePoller(ctx context.Context) { }() } -func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { - // Explicitly recover from panics since this is the entry point for a long-running goroutine - // and we can not allow a panic to propagate to the application code. - defer func() { - if r := recover(); r != nil { - // Resort to bring rules array into consistent state. - //cs.manifest.sort() - - err = fmt.Errorf("%v", r) - } - }() - +func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { // Compute 'now' before calling GetSamplingRules to avoid marking manifest as // fresher than it actually is. - now := rs.clock.Now().Unix() + now := rs.clock.now().Unix() - // Get sampling rules from proxy + // Get sampling rules from proxy. rules, err := rs.xrayClient.getSamplingRules(ctx) if err != nil { return } - // Set of rules to exclude from pruning - actives := map[centralizedRule]bool{} - - // Create missing rules. Update existing ones. failed := false + // temporary manifest declaration. + tempManifest := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: &defaultClock{}, + } + for _, records := range rules.SamplingRuleRecords { if records.SamplingRule.RuleName == nil { globalLogger.V(1).Info("Sampling rule without rule name is not supported") @@ -168,39 +162,37 @@ func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { // Only sampling rule with version 1 is valid if records.SamplingRule.Version == nil { - globalLogger.V(1).Info("Sampling rule without version number is not supported: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without version number is not supported", "RuleName", *records.SamplingRule.RuleName) failed = true continue } if *records.SamplingRule.Version != int64(1) { - globalLogger.V(1).Info("Sampling rule without version 1 is not supported: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without version 1 is not supported", "RuleName", *records.SamplingRule.RuleName) failed = true continue } if len(records.SamplingRule.Attributes) != 0 { - globalLogger.V(1).Info("Sampling rule with non nil Attributes is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule with non nil Attributes is not applicable", "RuleName", *records.SamplingRule.RuleName) continue } if records.SamplingRule.ResourceARN == nil { - globalLogger.V(1).Info("Sampling rule without ResourceARN is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule without ResourceARN is not applicable", "RuleName", *records.SamplingRule.RuleName) continue } if *records.SamplingRule.ResourceARN != "*" { - globalLogger.V(1).Info("Sampling rule with ResourceARN not equal to * is not applicable: ", *records.SamplingRule.RuleName) + globalLogger.V(1).Info("Sampling rule with ResourceARN not equal to * is not applicable", "RuleName", *records.SamplingRule.RuleName) continue } - // Create/update rule - r, putErr := rs.manifest.putRule(records.SamplingRule) - if putErr != nil { + // create rule and store it in temporary manifest to avoid locking issues. + createErr := tempManifest.createRule(records.SamplingRule) + if createErr != nil { failed = true - globalLogger.Error(putErr, "Error occurred creating/updating rule") - } else if r != nil { - actives[*r] = true + globalLogger.Error(createErr, "Error occurred creating/updating rule") } } @@ -209,14 +201,15 @@ func (rs *RemoteSampler) refreshManifest(ctx context.Context) (err error) { err = errors.New("error occurred creating/updating rules") } - // Prune inactive rules - rs.manifest.prune(actives) + // Re-sort to fix matching priorities. + tempManifest.sort() - // Re-sort to fix matching priorities - rs.manifest.sort() + // assign temp manifest to original copy/one sync refresh. + rs.manifest.mu.Lock() + rs.manifest.rules = tempManifest.rules + rs.manifest.index = tempManifest.index // Update refreshedAt timestamp - rs.manifest.mu.Lock() rs.manifest.refreshedAt = now rs.manifest.mu.Unlock() diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index 2a07c39121b..f771a7b48c5 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -24,6 +24,7 @@ import ( "time" ) +// assert that user provided values are tied to config func TestNewConfig(t *testing.T) { cfg := newConfig(WithSamplingRulesPollingInterval(400*time.Second), WithEndpoint("127.0.0.1:5000"), WithLogger(logr.Logger{})) @@ -32,6 +33,7 @@ func TestNewConfig(t *testing.T) { assert.Equal(t, cfg.logger, logr.Logger{}) } +// assert that when user did not provide values are then config would be picked up from default values func TestDefaultConfig(t *testing.T) { cfg := newConfig() @@ -39,3 +41,12 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) } + +// asset when some config is provided by user then other config will be picked up from default config +func TestPartialUserProvidedConfig(t *testing.T) { + cfg := newConfig(WithSamplingRulesPollingInterval(500 * time.Second)) + + assert.Equal(t, cfg.samplingRulesPollingInterval, 500*time.Second) + assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") + assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) +} diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index 3d19e62e838..ac08a1f46ab 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -17,62 +17,128 @@ package xray import ( "context" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "net/http" "net/http/httptest" "net/url" + "reflect" "testing" ) func TestRefreshManifest(t *testing.T) { ctx := context.Background() - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"Default\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"test-rule\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}},{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":100,\"ReservoirSize\":100,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule-1\",\"RuleName\":\"test-rule-1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}}]}" + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + }, + { + "CreatedAt": 1637691613, + "ModifiedAt": 1643748669, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.09, + "HTTPMethod": "GET", + "Host": "*", + "Priority": 1, + "ReservoirSize": 3, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r2", + "RuleName": "r2", + "ServiceName": "test-rule", + "ServiceType": "*", + "URLPath": "/aws-sdk-call", + "Version": 1 + } + }, + { + "CreatedAt": 1639446197, + "ModifiedAt": 1639446197, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.09, + "HTTPMethod": "*", + "Host": "*", + "Priority": 100, + "ReservoirSize": 100, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r3", + "RuleName": "r3", + "ServiceName": "*", + "ServiceType": "local", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + _, err := res.Write([]byte(body)) + require.NoError(t, err) })) defer testServer.Close() - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) - clock := &DefaultClock{} + clock := &defaultClock{} - m := ¢ralizedManifest{ - rules: []*centralizedRule{}, - index: map[string]*centralizedRule{}, + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, clock: clock, } - rs := &RemoteSampler{ + rs := &remoteSampler{ xrayClient: newClient(u.Host), clock: clock, manifest: m, } - _ = rs.refreshManifest(ctx) + err = rs.refreshManifest(ctx) + require.NoError(t, err) // Rule 'r1' - r1 := ¢ralizedRule{ + r1 := &rule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("Default"), + RuleName: getStringPointer("r1"), Priority: getIntPointer(10000), Host: getStringPointer("*"), HTTPMethod: getStringPointer("*"), URLPath: getStringPointer("*"), - ReservoirSize: getIntPointer(40), + ReservoirSize: getIntPointer(60), Version: getIntPointer(1), FixedRate: getFloatPointer(0.5), ServiceName: getStringPointer("*"), ResourceARN: getStringPointer("*"), - ServiceType: getStringPointer(""), + ServiceType: getStringPointer("*"), }, } // Rule 'r2' - r2 := ¢ralizedRule{ + r2 := &rule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("test-rule"), + RuleName: getStringPointer("r2"), Priority: getIntPointer(1), Host: getStringPointer("*"), HTTPMethod: getStringPointer("GET"), @@ -82,14 +148,14 @@ func TestRefreshManifest(t *testing.T) { Version: getIntPointer(1), ServiceName: getStringPointer("test-rule"), ResourceARN: getStringPointer("*"), - ServiceType: getStringPointer("local"), + ServiceType: getStringPointer("*"), }, } // Rule 'r3' - r3 := ¢ralizedRule{ + r3 := &rule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("test-rule-1"), + RuleName: getStringPointer("r3"), Priority: getIntPointer(100), Host: getStringPointer("*"), HTTPMethod: getStringPointer("*"), @@ -99,278 +165,438 @@ func TestRefreshManifest(t *testing.T) { Version: getIntPointer(1), ServiceName: getStringPointer("*"), ResourceARN: getStringPointer("*"), - ServiceType: getStringPointer(""), + ServiceType: getStringPointer("local"), }, } // Assert on sorting order assert.Equal(t, r2.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) + assert.Equal(t, r2.ruleProperties.Priority, rs.manifest.rules[0].ruleProperties.Priority) + assert.Equal(t, r2.ruleProperties.Host, rs.manifest.rules[0].ruleProperties.Host) + assert.Equal(t, r2.ruleProperties.HTTPMethod, rs.manifest.rules[0].ruleProperties.HTTPMethod) + assert.Equal(t, r2.ruleProperties.URLPath, rs.manifest.rules[0].ruleProperties.URLPath) + assert.Equal(t, r2.ruleProperties.ReservoirSize, rs.manifest.rules[0].ruleProperties.ReservoirSize) + assert.Equal(t, r2.ruleProperties.FixedRate, rs.manifest.rules[0].ruleProperties.FixedRate) + assert.Equal(t, r2.ruleProperties.Version, rs.manifest.rules[0].ruleProperties.Version) + assert.Equal(t, r2.ruleProperties.ServiceName, rs.manifest.rules[0].ruleProperties.ServiceName) + assert.Equal(t, r2.ruleProperties.ResourceARN, rs.manifest.rules[0].ruleProperties.ResourceARN) + assert.Equal(t, r2.ruleProperties.ServiceType, rs.manifest.rules[0].ruleProperties.ServiceType) + assert.Equal(t, r3.ruleProperties.RuleName, rs.manifest.rules[1].ruleProperties.RuleName) + assert.Equal(t, r3.ruleProperties.Priority, rs.manifest.rules[1].ruleProperties.Priority) + assert.Equal(t, r3.ruleProperties.Host, rs.manifest.rules[1].ruleProperties.Host) + assert.Equal(t, r3.ruleProperties.HTTPMethod, rs.manifest.rules[1].ruleProperties.HTTPMethod) + assert.Equal(t, r3.ruleProperties.URLPath, rs.manifest.rules[1].ruleProperties.URLPath) + assert.Equal(t, r3.ruleProperties.ReservoirSize, rs.manifest.rules[1].ruleProperties.ReservoirSize) + assert.Equal(t, r3.ruleProperties.FixedRate, rs.manifest.rules[1].ruleProperties.FixedRate) + assert.Equal(t, r3.ruleProperties.Version, rs.manifest.rules[1].ruleProperties.Version) + assert.Equal(t, r3.ruleProperties.ServiceName, rs.manifest.rules[1].ruleProperties.ServiceName) + assert.Equal(t, r3.ruleProperties.ResourceARN, rs.manifest.rules[1].ruleProperties.ResourceARN) + assert.Equal(t, r3.ruleProperties.ServiceType, rs.manifest.rules[1].ruleProperties.ServiceType) + assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[2].ruleProperties.RuleName) + assert.Equal(t, r1.ruleProperties.Priority, rs.manifest.rules[2].ruleProperties.Priority) + assert.Equal(t, r1.ruleProperties.Host, rs.manifest.rules[2].ruleProperties.Host) + assert.Equal(t, r1.ruleProperties.HTTPMethod, rs.manifest.rules[2].ruleProperties.HTTPMethod) + assert.Equal(t, r1.ruleProperties.URLPath, rs.manifest.rules[2].ruleProperties.URLPath) + assert.Equal(t, r1.ruleProperties.ReservoirSize, rs.manifest.rules[2].ruleProperties.ReservoirSize) + assert.Equal(t, r1.ruleProperties.FixedRate, rs.manifest.rules[2].ruleProperties.FixedRate) + assert.Equal(t, r1.ruleProperties.Version, rs.manifest.rules[2].ruleProperties.Version) + assert.Equal(t, r1.ruleProperties.ServiceName, rs.manifest.rules[2].ruleProperties.ServiceName) + assert.Equal(t, r1.ruleProperties.ResourceARN, rs.manifest.rules[2].ruleProperties.ResourceARN) + assert.Equal(t, r1.ruleProperties.ServiceType, rs.manifest.rules[2].ruleProperties.ServiceType) // Assert on size of manifest assert.Equal(t, 3, len(rs.manifest.rules)) assert.Equal(t, 3, len(rs.manifest.index)) } -func TestRefreshManifestRuleAdditionInvalidRule1(t *testing.T) { +// assert that invalid rule with ResourceARN other than "*" does not update to the manifest +func TestRefreshManifestAddInvalidRuleType1(t *testing.T) { ctx := context.Background() - newConfig() - - // Rule 'r1' - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("XYZ"), // invalid - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, - }, - } - - // Sorted array - rules := []*centralizedRule{r1} - index := map[string]*centralizedRule{ - "r1": r1, - } - - manifest := ¢ralizedManifest{ - rules: rules, - index: index, - refreshedAt: 1500000000, - } + // to enable logging + newConfig() - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" + // invalid rule due to ResourceARN + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "XYZ", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + _, err := res.Write([]byte(body)) + require.NoError(t, err) + })) defer testServer.Close() - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) - clock := &DefaultClock{} + clock := &defaultClock{} + + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } - rs := &RemoteSampler{ + rs := &remoteSampler{ xrayClient: newClient(u.Host), clock: clock, - manifest: manifest, + manifest: m, } - rs.manifest = manifest - err := rs.refreshManifest(ctx) + err = rs.refreshManifest(ctx) + require.NoError(t, err) - assert.Nil(t, err) // Refresh manifest with updates from mock proxy assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added } -func TestRefreshManifestRuleAdditionInvalidRule2(t *testing.T) { // non nil Attributes +// assert that invalid rule with attribute does not update to the manifest +func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { ctx := context.Background() - attributes := make(map[string]*string) - attributes["a"] = getStringPointer("*") - - // Rule 'r1' - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("*"), - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, - }, - } - - // Sorted array - rules := []*centralizedRule{r1} - - index := map[string]*centralizedRule{ - "r1": r1, - } - - manifest := ¢ralizedManifest{ - rules: rules, - index: index, - refreshedAt: 1500000000, - } + // to enable logging + newConfig() - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":1.639446197E9,\"ModifiedAt\":1.639446197E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.05,\"HTTPMethod\":\"POST\",\"Host\":\"*\",\"Priority\":4,\"ReservoirSize\":50,\"ResourceARN\":\"XYZ\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/r1\",\"RuleName\":\"r1\",\"ServiceName\":\"www.foo.com\",\"ServiceType\":\"\",\"URLPath\":\"/resource/bar\",\"Version\":1}}]}" + // invalid rule due to attributes + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {"a":"b"}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + _, err := res.Write([]byte(body)) + require.NoError(t, err) })) defer testServer.Close() - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) - clock := &DefaultClock{} + clock := &defaultClock{} - rs := &RemoteSampler{ + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } + + rs := &remoteSampler{ xrayClient: newClient(u.Host), clock: clock, - manifest: manifest, + manifest: m, } - err := rs.refreshManifest(ctx) + err = rs.refreshManifest(ctx) + require.NoError(t, err) - assert.Nil(t, err) assert.Equal(t, 0, len(rs.manifest.rules)) // rule not added } -func TestRefreshManifestRuleAdditionInvalidRule3(t *testing.T) { // 1 valid and 1 invalid rule +// assert that 1 valid and 1 invalid rule update only valid rule gets stored to the manifest +func TestRefreshManifestAddInvalidRule3(t *testing.T) { ctx := context.Background() - // Rule 'r1' - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("*"), - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, - }, - } + // to enable logging + newConfig() - // Rule 'r2' - r2 := ¢ralizedRule{ + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + }, + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {"a":"b"}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r2", + "RuleName": "r2", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) + // Rule 'r1' + r1 := &rule{ ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("*"), - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, + RuleName: getStringPointer("r1"), + Priority: getIntPointer(10000), + Host: getStringPointer("*"), + HTTPMethod: getStringPointer("*"), + URLPath: getStringPointer("*"), + ReservoirSize: getIntPointer(60), + FixedRate: getFloatPointer(0.5), + Version: getIntPointer(1), + ServiceName: getStringPointer("*"), + ResourceARN: getStringPointer("*"), + ServiceType: getStringPointer("*"), }, } - // Sorted array - rules := []*centralizedRule{r1} - - index := map[string]*centralizedRule{ - "r1": r1, - } - - manifest := ¢ralizedManifest{ - rules: rules, - index: index, - refreshedAt: 1500000000, - } - - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{\"a\":\"b\"},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r2\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":1}}]}" - // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + _, err := res.Write([]byte(body)) + require.NoError(t, err) })) defer testServer.Close() - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + + clock := &defaultClock{} - clock := &DefaultClock{} + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } - rs := &RemoteSampler{ + rs := &remoteSampler{ xrayClient: newClient(u.Host), clock: clock, - manifest: manifest, + manifest: m, } - err := rs.refreshManifest(ctx) + err = rs.refreshManifest(ctx) + require.NoError(t, err) - assert.Nil(t, err) - assert.Equal(t, 1, len(rs.manifest.rules)) // u1 not added - assert.Equal(t, r2.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) + assert.Equal(t, 1, len(rs.manifest.rules)) + + assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) + assert.Equal(t, r1.ruleProperties.Priority, rs.manifest.rules[0].ruleProperties.Priority) + assert.Equal(t, r1.ruleProperties.Host, rs.manifest.rules[0].ruleProperties.Host) + assert.Equal(t, r1.ruleProperties.HTTPMethod, rs.manifest.rules[0].ruleProperties.HTTPMethod) + assert.Equal(t, r1.ruleProperties.URLPath, rs.manifest.rules[0].ruleProperties.URLPath) + assert.Equal(t, r1.ruleProperties.ReservoirSize, rs.manifest.rules[0].ruleProperties.ReservoirSize) + assert.Equal(t, r1.ruleProperties.FixedRate, rs.manifest.rules[0].ruleProperties.FixedRate) + assert.Equal(t, r1.ruleProperties.Version, rs.manifest.rules[0].ruleProperties.Version) + assert.Equal(t, r1.ruleProperties.ServiceName, rs.manifest.rules[0].ruleProperties.ServiceName) + assert.Equal(t, r1.ruleProperties.ResourceARN, rs.manifest.rules[0].ruleProperties.ResourceARN) + assert.Equal(t, r1.ruleProperties.ServiceType, rs.manifest.rules[0].ruleProperties.ServiceType) } -// Assert that an invalid rule update does not update the rule -func TestRefreshManifestInvalidRuleUpdate(t *testing.T) { +// assert that manifest rules and index correctly updates from temporary manifest with each update +func TestManifestRulesAndIndexUpdate(t *testing.T) { ctx := context.Background() + count := 0 - // Rule 'r1' - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(4), - ResourceARN: getStringPointer("*"), - ServiceName: getStringPointer("www.foo.com"), - HTTPMethod: getStringPointer("POST"), - URLPath: getStringPointer("/resource/bar"), - ReservoirSize: getIntPointer(50), - FixedRate: getFloatPointer(0.05), - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, - }, - } - - // Rule 'r3' - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(8), - ResourceARN: getStringPointer("*"), - ServiceName: getStringPointer("www.bar.com"), - HTTPMethod: getStringPointer("POST"), - URLPath: getStringPointer("/resource/foo"), - ReservoirSize: getIntPointer(40), - FixedRate: getFloatPointer(0.10), - Host: getStringPointer("h3"), - }, - reservoir: ¢ralizedReservoir{ - quota: 10, - capacity: 50, - }, - } - - // Sorted array - rules := []*centralizedRule{r1, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r3": r3, - } - - manifest := ¢ralizedManifest{ - rules: rules, - index: index, - refreshedAt: 1500000000, - } + // to enable logging + newConfig() - body := "{\"NextToken\":null,\"SamplingRuleRecords\":[{\"CreatedAt\":0.0,\"ModifiedAt\":1.639517389E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.5,\"HTTPMethod\":\"*\",\"Host\":\"*\",\"Priority\":10000,\"ReservoirSize\":60,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/Default\",\"FixedRate\":0.09,\"RuleName\":\"r1\",\"ServiceName\":\"*\",\"ServiceType\":\"*\",\"URLPath\":\"*\",\"Version\":1}},{\"CreatedAt\":1.637691613E9,\"ModifiedAt\":1.643748669E9,\"SamplingRule\":{\"Attributes\":{},\"FixedRate\":0.09,\"HTTPMethod\":\"GET\",\"Host\":\"*\",\"Priority\":1,\"ReservoirSize\":3,\"ResourceARN\":\"*\",\"RuleARN\":\"arn:aws:xray:us-west-2:836082170990:sampling-rule/test-rule\",\"RuleName\":\"r3\",\"ServiceName\":\"test-rule\",\"ServiceType\":\"local\",\"URLPath\":\"/aws-sdk-call\",\"Version\":2}}]}" + // first update + body1 := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 100000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + }, + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r2", + "RuleName": "r2", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) + // second update + body2 := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 100000, + "ReservoirSize": 60, + "ResourceARN": "*", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "*", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) // generate a test server so we can capture and inspect the request testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - _, _ = res.Write([]byte(body)) + if count == 0 { + // first update + _, err := res.Write([]byte(body1)) + require.NoError(t, err) + } else { + // second update + _, err := res.Write([]byte(body2)) + require.NoError(t, err) + } })) defer testServer.Close() - u, _ := url.Parse(testServer.URL) + u, err := url.Parse(testServer.URL) + require.NoError(t, err) - clock := &DefaultClock{} + clock := &defaultClock{} - rs := &RemoteSampler{ + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } + + rs := &remoteSampler{ xrayClient: newClient(u.Host), clock: clock, - manifest: manifest, + manifest: m, } - err := rs.refreshManifest(ctx) - assert.NotNil(t, err) + err = rs.refreshManifest(ctx) + require.NoError(t, err) - // Assert on size of manifest + // assert that manifest has 2 rules and indexes currently + assert.Equal(t, 2, len(rs.manifest.rules)) + assert.Equal(t, 2, len(rs.manifest.index)) + + assert.Equal(t, rs.manifest.rules[0].ruleProperties.RuleName, getStringPointer("r2")) + assert.Equal(t, rs.manifest.rules[1].ruleProperties.RuleName, getStringPointer("r1")) + + // assert that both the rules are available in manifest index + _, okRule1 := rs.manifest.index[*rs.manifest.rules[0].ruleProperties.RuleName] + _, okRule2 := rs.manifest.index[*rs.manifest.rules[1].ruleProperties.RuleName] + + assert.True(t, okRule1) + assert.True(t, okRule2) + + // second update + count += 1 + err = rs.refreshManifest(ctx) + require.NoError(t, err) + + // assert that manifest has 1 "r1" rule and index currently assert.Equal(t, 1, len(rs.manifest.rules)) assert.Equal(t, 1, len(rs.manifest.index)) - // Assert on sorting order - assert.Equal(t, r1.ruleProperties.RuleName, rs.manifest.rules[0].ruleProperties.RuleName) + assert.Equal(t, rs.manifest.rules[0].ruleProperties.RuleName, getStringPointer("r1")) + + // assert that "r1" rule available in index + _, okRule := rs.manifest.index[*rs.manifest.rules[0].ruleProperties.RuleName] + assert.True(t, okRule) +} + +// assert that NewRemoteSampler returns a sampler with *xray.remoteSampler type +func TestNewRemoteSampler(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + rs, err := NewRemoteSampler(ctx) + require.NoError(t, err) + + s := &remoteSampler{} + assert.Equal(t, reflect.TypeOf(rs), reflect.TypeOf(s)) } diff --git a/samplers/aws/xray/reservoir.go b/samplers/aws/xray/reservoir.go index 65c54479cc0..9ae9b9906cd 100644 --- a/samplers/aws/xray/reservoir.go +++ b/samplers/aws/xray/reservoir.go @@ -15,8 +15,8 @@ package xray // ToDo: other fields will be used in business logic for remote sampling -// centralizedReservoir is a reservoir distributed among all running instances of the SDK -type centralizedReservoir struct { +// reservoir is a reservoir distributed among all running instances of the SDK +type reservoir struct { // Quota assigned to client quota int64 // diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index 32146a841d9..cf4c53e7467 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -15,7 +15,6 @@ package xray import ( - "fmt" "sort" "strings" "sync" @@ -23,68 +22,31 @@ import ( const defaultInterval = int64(10) -//// centralizedManifest represents a full sampling ruleset, with a list of -//// custom rules and default values for incoming requests that do -//// not match any of the provided rules. -type centralizedManifest struct { - rules []*centralizedRule - index map[string]*centralizedRule +// manifest represents a full sampling ruleset, with a list of +// custom rules and default values for incoming requests that do +// not match any of the provided rules. +type manifest struct { + rules []*rule + index map[string]*rule refreshedAt int64 - clock Clock + clock clock mu sync.RWMutex } -// putRule updates the named rule if it already exists or creates it if it does not. -// May break ordering of the sorted rules array if it creates a new rule. -func (m *centralizedManifest) putRule(rule *ruleProperties) (r *centralizedRule, err error) { - defer func() { - if x := recover(); x != nil { - err = fmt.Errorf("%v", x) - } - }() - - // User-defined rule + default rule - m.mu.RLock() - var ok bool - r, ok = m.index[*rule.RuleName] - m.mu.RUnlock() - - // Create rule if it does not exist - if !ok { - r = m.createRule(rule) - - return - } - - // Update existing rule - r.updateRule(rule) - - return -} - -// createUserRule creates a user-defined centralizedRule, appends it to the sorted array, +// createRule creates a user-defined rule, appends it to the sorted array, // adds it to the index, and returns the newly created rule. -func (m *centralizedManifest) createRule(rule *ruleProperties) *centralizedRule { - m.mu.Lock() - defer m.mu.Unlock() - - // Return early if rule already exists - if r, ok := m.index[*rule.RuleName]; ok { - return r - } +func (m *manifest) createRule(ruleProp *ruleProperties) (err error) { + clock := &defaultClock{} + rand := &defaultRand{} - // Create CentralizedRule from xraySvc.SamplingRule - clock := &DefaultClock{} - rand := &DefaultRand{} - - cr := ¢ralizedReservoir{ - capacity: *rule.ReservoirSize, + cr := &reservoir{ + capacity: *ruleProp.ReservoirSize, interval: defaultInterval, } - csr := ¢ralizedRule{ + csr := &rule{ reservoir: cr, - ruleProperties: rule, + ruleProperties: ruleProp, clock: clock, rand: rand, } @@ -93,36 +55,13 @@ func (m *centralizedManifest) createRule(rule *ruleProperties) *centralizedRule m.rules = append(m.rules, csr) // Update index - m.index[*rule.RuleName] = csr - - return csr -} - -// prune removes all rules in the manifest not present in the given list of active rules. -// Preserves ordering of sorted array. -func (m *centralizedManifest) prune(actives map[centralizedRule]bool) { - m.mu.Lock() - defer m.mu.Unlock() - - // Iterate in reverse order to avoid adjusting index for each deleted rule - for i := len(m.rules) - 1; i >= 0; i-- { - r := m.rules[i] - - if _, ok := actives[*r]; !ok { - // Remove from index - delete(m.index, *m.rules[i].ruleProperties.RuleName) + m.index[*ruleProp.RuleName] = csr - // Delete by reslicing without index - a := append(m.rules[:i], m.rules[i+1:]...) - - // Assign resliced rules - m.rules = a - } - } + return } // sort sorts the rule array first by priority and then by rule name. -func (m *centralizedManifest) sort() { +func (m *manifest) sort() { // Comparison function less := func(i, j int) bool { if *m.rules[i].ruleProperties.Priority == *m.rules[j].ruleProperties.Priority { @@ -131,8 +70,5 @@ func (m *centralizedManifest) sort() { return *m.rules[i].ruleProperties.Priority < *m.rules[j].ruleProperties.Priority } - m.mu.Lock() - defer m.mu.Unlock() - sort.Slice(m.rules, less) } diff --git a/samplers/aws/xray/rule_manifest_test.go b/samplers/aws/xray/rule_manifest_test.go index 52d127811a4..c10c7fce1b9 100644 --- a/samplers/aws/xray/rule_manifest_test.go +++ b/samplers/aws/xray/rule_manifest_test.go @@ -33,31 +33,31 @@ func getFloatPointer(val float64) *float64 { return &val } -// Assert that putRule() creates a new user-defined rule and adds to manifest +// Assert that createRule() creates a new rule and adds to manifest func TestCreateRule(t *testing.T) { resARN := "*" - r1 := ¢ralizedRule{ + r1 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r1"), Priority: getIntPointer(5), }, } - r3 := ¢ralizedRule{ + r3 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r3"), Priority: getIntPointer(7), }, } - rules := []*centralizedRule{r1, r3} + rules := []*rule{r1, r3} - index := map[string]*centralizedRule{ + index := map[string]*rule{ "r1": r1, "r3": r3, } - m := ¢ralizedManifest{ + m := &manifest{ rules: rules, index: index, } @@ -67,7 +67,7 @@ func TestCreateRule(t *testing.T) { httpMethod := "POST" urlPath := "/bar/*" reservoirSize := int64(10) - fixedRate := float64(0.05) + fixedRate := 0.05 ruleName := "r2" host := "local" priority := int64(6) @@ -87,15 +87,15 @@ func TestCreateRule(t *testing.T) { } // Expected centralized sampling rule - clock := &DefaultClock{} - rand := &DefaultRand{} + clock := &defaultClock{} + rand := &defaultRand{} - cr := ¢ralizedReservoir{ + cr := &reservoir{ capacity: 10, interval: 10, } - exp := ¢ralizedRule{ + exp := &rule{ reservoir: cr, ruleProperties: ruleProperties, clock: clock, @@ -103,9 +103,8 @@ func TestCreateRule(t *testing.T) { } // Add to manifest, index - r2, err := m.putRule(ruleProperties) + err := m.createRule(ruleProperties) assert.Nil(t, err) - assert.Equal(t, exp, r2) // Assert new rule is present in index r2, ok := m.index["r2"] @@ -117,501 +116,23 @@ func TestCreateRule(t *testing.T) { assert.Equal(t, exp, r2) } -// Assert that creating a rule which already exists is a no-op -func TestCreateRuleNoOp(t *testing.T) { - resARN := "*" - serviceType := "" - attributes := map[string]*string{ - "foo": getStringPointer("bar"), - } - - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r3"), - Priority: getIntPointer(7), - }, - reservoir: ¢ralizedReservoir{ - capacity: 5, - }, - } - - rules := []*centralizedRule{r1, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r3": r3, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Duplicate rule properties. 'r3' already exists. Input to updateRule(). - serviceName := "www.foo.com" - httpMethod := "POST" - urlPath := "/bar/*" - reservoirSize := int64(10) - fixedRate := float64(0.05) - ruleName := "r3" - priority := int64(6) - host := "h" - - ruleProperties := &ruleProperties{ - ServiceName: &serviceName, - HTTPMethod: &httpMethod, - URLPath: &urlPath, - ReservoirSize: &reservoirSize, - FixedRate: &fixedRate, - RuleName: &ruleName, - Priority: &priority, - Host: &host, - ServiceType: &serviceType, - ResourceARN: &resARN, - Attributes: attributes, - } - - // Assert manifest has not changed - r, err := m.putRule(ruleProperties) - assert.Nil(t, err) - assert.Equal(t, r3, r) - assert.Equal(t, 2, len(m.rules)) - assert.Equal(t, 2, len(m.index)) - assert.Equal(t, r1, m.rules[0]) - assert.Equal(t, r3, m.rules[1]) -} - -// Assert that putRule() updates the rule in the manifest -func TestUpdateRule(t *testing.T) { - resARN := "*" - serviceType := "" - attributes := map[string]*string{ - "foo": getStringPointer("bar"), - } - - // Original rule - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - ServiceName: getStringPointer("*.foo.com"), - HTTPMethod: getStringPointer("GET"), - URLPath: getStringPointer("/resource/*"), - ReservoirSize: getIntPointer(15), - FixedRate: getFloatPointer(0.04), - ResourceARN: &resARN, - ServiceType: &serviceType, - Attributes: attributes, - }, - - reservoir: ¢ralizedReservoir{ - capacity: 5, - }, - } - - rules := []*centralizedRule{r1} - - index := map[string]*centralizedRule{ - "r1": r1, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Updated rule properties. Input to updateRule(). - serviceName := "www.foo.com" - httpMethod := "POST" - urlPath := "/bar/*" - reservoirSize := int64(10) - fixedRate := float64(0.05) - ruleName := "r1" - priority := int64(6) - host := "h" - - updated := &ruleProperties{ - ServiceName: &serviceName, - HTTPMethod: &httpMethod, - URLPath: &urlPath, - ReservoirSize: &reservoirSize, - FixedRate: &fixedRate, - RuleName: &ruleName, - Priority: &priority, - Host: &host, - ServiceType: &serviceType, - ResourceARN: &resARN, - Attributes: attributes, - } - - // Expected updated centralized sampling rule - cr := ¢ralizedReservoir{ - capacity: 10, - } - - exp := ¢ralizedRule{ - reservoir: cr, - ruleProperties: updated, - } - - // Assert that rule has been updated - r, err := m.putRule(updated) - assert.Nil(t, err) - assert.Equal(t, exp, r) - assert.Equal(t, exp, m.index["r1"]) - assert.Equal(t, exp, m.rules[0]) - assert.Equal(t, 1, len(m.rules)) - assert.Equal(t, 1, len(m.index)) -} - -// Assert that putRule() recovers from panic. -func TestPutRuleRecovery(t *testing.T) { - attributes := make(map[string]*string) - var rules []*centralizedRule - - index := map[string]*centralizedRule{} - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - newRule := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - ServiceName: getStringPointer("www.foo.com"), - HTTPMethod: getStringPointer("POST"), - FixedRate: getFloatPointer(0.05), - RuleName: getStringPointer("r2"), - Priority: getIntPointer(int64(6)), - ResourceARN: getStringPointer("*"), - ServiceType: getStringPointer(""), - Attributes: attributes, - }, - } - - // Attempt to add to manifest - r, err := m.putRule(newRule.ruleProperties) - assert.NotNil(t, err) - assert.Nil(t, r) - - // Assert index is unchanged - assert.Equal(t, 0, len(m.index)) - - // Assert sorted array is unchanged - assert.Equal(t, 0, len(m.rules)) -} - -// Assert that deleting a rule from the end of the array removes the rule -// and preserves ordering of the sorted array -func TestDeleteLastRule(t *testing.T) { - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - r2 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(6), - }, - } - - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r3"), - Priority: getIntPointer(7), - }, - } - - rules := []*centralizedRule{r1, r2, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r2": r2, - "r3": r3, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{ - *r1: true, - *r2: true, - } - - // Delete r3 - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 2, len(m.rules)) - assert.Equal(t, 2, len(m.index)) - - // Assert index consistency - _, ok := m.index["r3"] - assert.False(t, ok) - assert.Equal(t, r1, m.index["r1"]) - assert.Equal(t, r2, m.index["r2"]) - - // Assert ordering of array - assert.Equal(t, r1, m.rules[0]) - assert.Equal(t, r2, m.rules[1]) -} - -// Assert that deleting a rule from the middle of the array removes the rule -// and preserves ordering of the sorted array -func TestDeleteMiddleRule(t *testing.T) { - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - r2 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(6), - }, - } - - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r3"), - Priority: getIntPointer(7), - }, - } - - rules := []*centralizedRule{r1, r2, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r2": r2, - "r3": r3, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{ - *r1: true, - *r3: true, - } - - // Delete r2 - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 2, len(m.rules)) - assert.Equal(t, 2, len(m.index)) - - // Assert index consistency - _, ok := m.index["r2"] - assert.False(t, ok) - assert.Equal(t, r1, m.index["r1"]) - assert.Equal(t, r3, m.index["r3"]) - - // Assert ordering of array - assert.Equal(t, r1, m.rules[0]) - assert.Equal(t, r3, m.rules[1]) -} - -// Assert that deleting a rule from the beginning of the array removes the rule -// and preserves ordering of the sorted array -func TestDeleteFirstRule(t *testing.T) { - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - r2 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(6), - }, - } - - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r3"), - Priority: getIntPointer(7), - }, - } - - rules := []*centralizedRule{r1, r2, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r2": r2, - "r3": r3, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{ - *r2: true, - *r3: true, - } - - // Delete r1 - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 2, len(m.rules)) - assert.Equal(t, 2, len(m.index)) - - // Assert index consistency - _, ok := m.index["r1"] - assert.False(t, ok) - assert.Equal(t, r2, m.index["r2"]) - assert.Equal(t, r3, m.index["r3"]) - - // Assert ordering of array - assert.Equal(t, r2, m.rules[0]) - assert.Equal(t, r3, m.rules[1]) -} - -// Assert that deleting the only rule from the array removes the rule -func TestDeleteOnlyRule(t *testing.T) { - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - rules := []*centralizedRule{r1} - - index := map[string]*centralizedRule{ - "r1": r1, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{} - - // Delete r1 - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 0, len(m.rules)) - assert.Equal(t, 0, len(m.index)) - - // Assert index consistency - _, ok := m.index["r1"] - assert.False(t, ok) -} - -// Assert that deleting rules from an empty array does not panic -func TestDeleteEmptyRulesArray(t *testing.T) { - var rules []*centralizedRule - - index := map[string]*centralizedRule{} - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{} - - // Delete from empty array - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 0, len(m.rules)) - assert.Equal(t, 0, len(m.index)) -} - -// Assert that deleting all rules results in an empty array and does not panic -func TestDeleteAllRules(t *testing.T) { - r1 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r1"), - Priority: getIntPointer(5), - }, - } - - r2 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r2"), - Priority: getIntPointer(6), - }, - } - - r3 := ¢ralizedRule{ - ruleProperties: &ruleProperties{ - RuleName: getStringPointer("r3"), - Priority: getIntPointer(7), - }, - } - - rules := []*centralizedRule{r1, r2, r3} - - index := map[string]*centralizedRule{ - "r1": r1, - "r2": r2, - "r3": r3, - } - - m := ¢ralizedManifest{ - rules: rules, - index: index, - } - - // Active rules to exclude from deletion - a := map[centralizedRule]bool{} - - // Delete r3 - m.prune(a) - - // Assert size of manifest - assert.Equal(t, 0, len(m.rules)) - assert.Equal(t, 0, len(m.index)) -} - // Assert that sorting an unsorted array results in a sorted array - check priority func TestSort1(t *testing.T) { - r1 := ¢ralizedRule{ + r1 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r1"), Priority: getIntPointer(5), }, } - r2 := ¢ralizedRule{ + r2 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r2"), Priority: getIntPointer(6), }, } - r3 := ¢ralizedRule{ + r3 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r3"), Priority: getIntPointer(7), @@ -619,9 +140,9 @@ func TestSort1(t *testing.T) { } // Unsorted rules array - rules := []*centralizedRule{r2, r1, r3} + rules := []*rule{r2, r1, r3} - m := ¢ralizedManifest{ + m := &manifest{ rules: rules, } @@ -636,21 +157,21 @@ func TestSort1(t *testing.T) { // Assert that sorting an unsorted array results in a sorted array - check priority and rule name func TestSort2(t *testing.T) { - r1 := ¢ralizedRule{ + r1 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r1"), Priority: getIntPointer(5), }, } - r2 := ¢ralizedRule{ + r2 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r2"), Priority: getIntPointer(6), }, } - r3 := ¢ralizedRule{ + r3 := &rule{ ruleProperties: &ruleProperties{ RuleName: getStringPointer("r3"), Priority: getIntPointer(7), @@ -658,9 +179,9 @@ func TestSort2(t *testing.T) { } // Unsorted rules array - rules := []*centralizedRule{r2, r1, r3} + rules := []*rule{r2, r1, r3} - m := ¢ralizedManifest{ + m := &manifest{ rules: rules, } diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index 0f086db90af..03e20c7638b 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -17,10 +17,10 @@ package xray import "sync" // ToDo: other fields will be used in business logic for remote sampling -// centralizedRule represents a centralized sampling rule -type centralizedRule struct { +// rule represents a centralized sampling rule +type rule struct { // Centralized reservoir for keeping track of reservoir usage - reservoir *centralizedReservoir + reservoir *reservoir // sampling rule properties ruleProperties *ruleProperties @@ -35,7 +35,7 @@ type centralizedRule struct { //borrowedRequests int64 // Provides system time - clock Clock + clock clock // Provides random numbers rand Rand @@ -72,13 +72,3 @@ type samplingRuleRecords struct { type getSamplingRulesOutput struct { SamplingRuleRecords []*samplingRuleRecords `json:"SamplingRuleRecords"` } - -// updateRule updates the properties of the user-defined and default centralizedRule using the given -// *properties. -func (r *centralizedRule) updateRule(rule *ruleProperties) { - r.mu.Lock() - defer r.mu.Unlock() - - r.ruleProperties = rule - r.reservoir.capacity = *rule.ReservoirSize -} From 01a89957ee2363ef2adee0c78d0048c451e5bd2b Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Fri, 4 Feb 2022 17:22:12 -0800 Subject: [PATCH 25/32] fix linter error --- Makefile | 16 ++++++++-------- samplers/aws/xray/client.go | 6 +++++- samplers/aws/xray/remote_sampler_test.go | 2 +- samplers/aws/xray/reservoir.go | 2 +- samplers/aws/xray/sampling_rule.go | 4 +--- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 58c69203272..b1087da691e 100644 --- a/Makefile +++ b/Makefile @@ -139,14 +139,14 @@ test-short: done .PHONY: lint -lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell lint-modules - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "golangci-lint in $${dir}"; \ - (cd "$${dir}" && \ - $(TOOLS_DIR)/golangci-lint run --fix && \ - $(TOOLS_DIR)/golangci-lint run); \ - done - $(TOOLS_DIR)/misspell -w $(ALL_DOCS) +# lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell lint-modules +# set -e; for dir in $(ALL_GO_MOD_DIRS); do \ +# echo "golangci-lint in $${dir}"; \ +# (cd "$${dir}" && \ +# $(TOOLS_DIR)/golangci-lint run --fix && \ +# $(TOOLS_DIR)/golangci-lint run); \ +# done +# $(TOOLS_DIR)/misspell -w $(ALL_DOCS) .PHONY: lint-modules lint-modules: diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 2e311b0a5bc..cb8c08c1125 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -15,6 +15,7 @@ package xray import ( + "bytes" "context" "encoding/json" "fmt" @@ -46,7 +47,10 @@ func newClient(d string) *xrayClient { // getSamplingRules calls the collector(aws proxy enabled) for sampling rules func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint.String()+"/GetSamplingRules", nil) + statisticsByte, _ := json.Marshal(getSamplingRulesInput{}) + body := bytes.NewReader(statisticsByte) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint.String()+"/GetSamplingRules", body) if err != nil { return nil, fmt.Errorf("xray client: failed to create http request: %w", err) } diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index ac08a1f46ab..98c84773aa8 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -574,7 +574,7 @@ func TestManifestRulesAndIndexUpdate(t *testing.T) { assert.True(t, okRule2) // second update - count += 1 + count++ err = rs.refreshManifest(ctx) require.NoError(t, err) diff --git a/samplers/aws/xray/reservoir.go b/samplers/aws/xray/reservoir.go index 9ae9b9906cd..c9848bc3b10 100644 --- a/samplers/aws/xray/reservoir.go +++ b/samplers/aws/xray/reservoir.go @@ -18,7 +18,7 @@ package xray // reservoir is a reservoir distributed among all running instances of the SDK type reservoir struct { // Quota assigned to client - quota int64 + //quota int64 // // Quota refresh timestamp //refreshedAt int64 diff --git a/samplers/aws/xray/sampling_rule.go b/samplers/aws/xray/sampling_rule.go index 03e20c7638b..400109c1519 100644 --- a/samplers/aws/xray/sampling_rule.go +++ b/samplers/aws/xray/sampling_rule.go @@ -14,8 +14,6 @@ package xray -import "sync" - // ToDo: other fields will be used in business logic for remote sampling // rule represents a centralized sampling rule type rule struct { @@ -40,7 +38,7 @@ type rule struct { // Provides random numbers rand Rand - mu sync.RWMutex + //mu sync.RWMutex } // properties is the base set of properties that define a sampling rule. From 7b1987e6806a328143f78577c09575f65fb8dd47 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 7 Feb 2022 10:45:04 -0800 Subject: [PATCH 26/32] fix golangci-lint --- Makefile | 16 +- samplers/aws/xray/client_test.go | 5 +- samplers/aws/xray/remote_sampler.go | 3 +- samplers/aws/xray/remote_sampler_config.go | 5 +- .../aws/xray/remote_sampler_config_test.go | 7 +- samplers/aws/xray/remote_sampler_test.go | 5 +- samplers/aws/xray/utils_test.go | 3 +- tools/go.mod | 4 +- tools/go.sum | 284 ++++++++++++------ 9 files changed, 222 insertions(+), 110 deletions(-) diff --git a/Makefile b/Makefile index b1087da691e..58c69203272 100644 --- a/Makefile +++ b/Makefile @@ -139,14 +139,14 @@ test-short: done .PHONY: lint -# lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell lint-modules -# set -e; for dir in $(ALL_GO_MOD_DIRS); do \ -# echo "golangci-lint in $${dir}"; \ -# (cd "$${dir}" && \ -# $(TOOLS_DIR)/golangci-lint run --fix && \ -# $(TOOLS_DIR)/golangci-lint run); \ -# done -# $(TOOLS_DIR)/misspell -w $(ALL_DOCS) +lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell lint-modules + set -e; for dir in $(ALL_GO_MOD_DIRS); do \ + echo "golangci-lint in $${dir}"; \ + (cd "$${dir}" && \ + $(TOOLS_DIR)/golangci-lint run --fix && \ + $(TOOLS_DIR)/golangci-lint run); \ + done + $(TOOLS_DIR)/misspell -w $(ALL_DOCS) .PHONY: lint-modules lint-modules: diff --git a/samplers/aws/xray/client_test.go b/samplers/aws/xray/client_test.go index 4ea73dec86b..048171db6f1 100644 --- a/samplers/aws/xray/client_test.go +++ b/samplers/aws/xray/client_test.go @@ -16,12 +16,13 @@ package xray import ( "context" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "net/http" "net/http/httptest" "net/url" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestGetSamplingRules(t *testing.T) { diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 110262f1cde..4873849ece4 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -19,9 +19,10 @@ import ( crypto "crypto/rand" "errors" "fmt" - "github.com/go-logr/logr" "time" + "github.com/go-logr/logr" + sdktrace "go.opentelemetry.io/otel/sdk/trace" ) diff --git a/samplers/aws/xray/remote_sampler_config.go b/samplers/aws/xray/remote_sampler_config.go index c00ec3fd3ab..5083145a121 100644 --- a/samplers/aws/xray/remote_sampler_config.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -15,11 +15,12 @@ package xray import ( - "github.com/go-logr/logr" - "github.com/go-logr/stdr" "log" "os" "time" + + "github.com/go-logr/logr" + "github.com/go-logr/stdr" ) const ( diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index f771a7b48c5..8768c66a000 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -15,13 +15,14 @@ package xray import ( - "github.com/go-logr/logr" - "github.com/go-logr/stdr" - "github.com/stretchr/testify/assert" "log" "os" "testing" "time" + + "github.com/go-logr/logr" + "github.com/go-logr/stdr" + "github.com/stretchr/testify/assert" ) // assert that user provided values are tied to config diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index 98c84773aa8..4922368d6d9 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -16,13 +16,14 @@ package xray import ( "context" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "net/http" "net/http/httptest" "net/url" "reflect" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRefreshManifest(t *testing.T) { diff --git a/samplers/aws/xray/utils_test.go b/samplers/aws/xray/utils_test.go index a0c8fe2ed95..64fa5e16cea 100644 --- a/samplers/aws/xray/utils_test.go +++ b/samplers/aws/xray/utils_test.go @@ -15,10 +15,11 @@ package xray import ( - "github.com/stretchr/testify/assert" "sync/atomic" "testing" "time" + + "github.com/stretchr/testify/assert" ) // MockClock is a struct to record current time. diff --git a/tools/go.mod b/tools/go.mod index c1d20b089c7..b83725196e7 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,8 +4,8 @@ go 1.13 require ( github.com/client9/misspell v0.3.4 - github.com/golangci/golangci-lint v1.43.0 + github.com/golangci/golangci-lint v1.44.0 github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad go.opentelemetry.io/build-tools/multimod v0.0.0-20210920164323-2ceabab23375 - golang.org/x/tools v0.1.8 + golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da ) diff --git a/tools/go.sum b/tools/go.sum index 8b811ba34a8..b3288f94cc0 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -27,6 +27,10 @@ cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSU cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -37,6 +41,7 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -55,9 +60,10 @@ github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCq github.com/Antonboom/nilnil v0.1.0 h1:DLDavmg0a6G/F4Lt9t7Enrbgb3Oph6LnDE6YVsmTt74= github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -70,11 +76,10 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/OpenPeeDeeP/depguard v1.1.0 h1:pjK9nLPS1FwQYGGpPxoMYpe7qACHOhAWQMQzV71i49o= +github.com/OpenPeeDeeP/depguard v1.1.0/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -93,14 +98,15 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ashanbrown/forbidigo v1.2.0 h1:RMlEFupPCxQ1IogYOQUnIQwGEUGK8g5vAPMRyJoSxbc= -github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= -github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde h1:YOsoVXsZQPA9aOTy1g0lAJv5VzZUvwQuZqug8XPeqfM= -github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= +github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBlWTShc= +github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= +github.com/ashanbrown/makezero v1.1.0 h1:b2FVq4dTlBpy9f6qxhbyWH+6zy56IETE9cFbBGtDqs8= +github.com/ashanbrown/makezero v1.1.0/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -112,19 +118,23 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= -github.com/blizzy78/varnamelen v0.3.0 h1:80mYO7Y5ppeEefg1Jzu+NBg16iwToOQVnDnNIoWSShs= -github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= +github.com/blizzy78/varnamelen v0.5.0 h1:v9LpMwxzTqAJC4lsD/jR7zWb8a66trcqhTEH4Mk6Fio= +github.com/blizzy78/varnamelen v0.5.0/go.mod h1:Mc0nLBKI1/FP0Ga4kqMOgBig0eS5QtR107JnMAb1Wuc= github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= -github.com/breml/bidichk v0.1.1 h1:Qpy8Rmgos9qdJxhka0K7ADEE5bQZX9PQUthkgggHpFM= -github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= +github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE= +github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= +github.com/breml/errchkjson v0.2.1 h1:QCToXnY9BNngrbJoW3qfCTt3BdtbnsI6wyP/WGrxxSE= +github.com/breml/errchkjson v0.2.1/go.mod h1:jZEATw/jF69cL1iy7//Yih8yp/mXp2CBoBr9GJwCAsY= github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI= @@ -132,12 +142,20 @@ github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3/ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -151,6 +169,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daixiang0/gci v0.2.9 h1:iwJvwQpBZmMg31w+QQ6jsyZ54KEATn6/nfARbBNW294= @@ -173,10 +192,13 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/esimonov/ifshort v1.0.3 h1:JD6x035opqGec5fZ0TLjXeROD2p5H7oLGn8MKfy9HTM= -github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= +github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -188,18 +210,20 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= -github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= -github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= +github.com/fzipp/gocyclo v0.4.0 h1:IykTnjwh2YLyYkGa0y92iTTEQcnyAz0r9zOo15EbJ7k= +github.com/fzipp/gocyclo v0.4.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-critic/go-critic v0.6.1 h1:lS8B9LH/VVsvQQP7Ao5TJyQqteVKVs3E4dXiHMyubtI= -github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= +github.com/go-critic/go-critic v0.6.2 h1:L5SDut1N4ZfsWZY0sH4DCrsHLHnhuuWak2wa165t9gs= +github.com/go-critic/go-critic v0.6.2/go.mod h1:td1s27kfmLpe5G/DPjlnFI7o1UCzePptwU7Az0V5iCM= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= @@ -216,7 +240,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -232,14 +255,12 @@ github.com/go-toolsmith/astequal v1.0.1 h1:JbSszi42Jiqu36Gnf363HWS9MTEAz67vTQLpo github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5 h1:eD9POs68PHkwrx7hAB78z1cb6PfGq/jyWn3wJywsH1o= +github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5/go.mod h1:3NAwwmD4uY/yggRxoEjk/S00MIV3A+H7rrE3i87eYxM= github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk= github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= @@ -259,6 +280,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -296,8 +318,8 @@ github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZB github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.43.0 h1:SLwZFEmDgopqZpfP495zCtV9REUf551JJlJ51Ql7NZA= -github.com/golangci/golangci-lint v1.43.0/go.mod h1:VIFlUqidx5ggxDfQagdvd9E67UjMXtTHBkBQ7sHoC5Q= +github.com/golangci/golangci-lint v1.44.0 h1:YJPouGNQEdK+x2KsCpWMIBy0q6MSuxHjkWMxJMNj/DU= +github.com/golangci/golangci-lint v1.44.0/go.mod h1:aBolpzNkmYogKPynGKdOWDCEc8LlwnxZC6w/SJ1TaEs= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= @@ -342,6 +364,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -355,11 +378,12 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254 h1:Nb2aRlC404yz7gQIfRZxX9/MLvQiqXyiBTJtgAy6yrI= -github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= +github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= +github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -374,8 +398,8 @@ github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnq github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5 h1:rx8127mFPqXXsfPSo8BwnIU97MKFZc89WHAHt8PwDVY= -github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= +github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70= +github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= @@ -390,18 +414,25 @@ github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -419,13 +450,17 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -454,15 +489,17 @@ github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a h1:8NZHLa6Gp0hW6 github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d h1:XeSMXURZPtUffuWAaq90o6kLgZdgu+QA8wk4MPC8ikI= -github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= +github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -482,14 +519,15 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kulti/thelper v0.4.0 h1:2Nx7XbdbE/BYZeoip2mURKUdtHQRuy6Ug+wR7K9ywNM= -github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= +github.com/kulti/thelper v0.5.0 h1:CiEKStgoG4K9bjf/zk3eNX0D0J2iFWzxEY+h9UXmlJg= +github.com/kulti/thelper v0.5.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= github.com/kunwardeep/paralleltest v1.0.3 h1:UdKIkImEAXjR1chUWLn+PNXqWUGs//7tzMeWuP7NhmI= github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= @@ -497,15 +535,18 @@ github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77 github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= github.com/ldez/gomoddirectives v0.2.2 h1:p9/sXuNFArS2RLc+UpYZSI4KQwGMEDWC/LbtF5OPFVg= github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.2.0 h1:693V8Bf1NdShJ8eu/s84QySA0J2VWBanVBa2WwXD/Wk= -github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= +github.com/ldez/tagliatelle v0.3.0 h1:Aubm2ZsrsjIGFvdxemMPJaXrSJ5Cys6VWyTQFt9k2dI= +github.com/ldez/tagliatelle v0.3.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= +github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg= +github.com/leonklingele/grouper v1.1.0/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -521,8 +562,8 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -537,18 +578,18 @@ github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= -github.com/mgechev/revive v1.1.2 h1:MiYA/o9M7REjvOF20QN43U8OtXDDHQFKLCtJnxLGLog= -github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= +github.com/mgechev/revive v1.1.3 h1:6tBZacs2/uv9UOpkBQhCtXh2NGgu2Ry97ZyjcN6uDCM= +github.com/mgechev/revive v1.1.3/go.mod h1:jMzDa13teAuv/KLeqgJw79NDe+1IT0ZO3Mht0vN1Yls= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -564,14 +605,16 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= @@ -585,8 +628,8 @@ github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4N github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.2.3 h1:+ANTMqRNrqwInnP9aszg/0jDo+zbXa4x66U19Bx/oTk= -github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= +github.com/nishanths/exhaustive v0.7.11 h1:xV/WU3Vdwh5BUH4N06JNUznb6d5zhRPOnlgCrpNYNKA= +github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI= github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= github.com/nishanths/predeclared v0.2.1 h1:1TXtjmy4f3YCFjTxRd8zcFHOmoUir+gp0ESzjFzG2sw= github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= @@ -603,10 +646,12 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= @@ -617,7 +662,7 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= @@ -625,6 +670,7 @@ github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -633,12 +679,14 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349 h1:Kq/3kL0k033ds3tyez5lFPrfQ74fNJ+OqCclRipubwA= -github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= +github.com/polyfloyd/go-errorlint v0.0.0-20211125173453-6d6d39c5bb8b h1:/BDyEJWLnDUYKGWdlNx/82qSaVu2bUok/EvPUtIGuvw= +github.com/polyfloyd/go-errorlint v0.0.0-20211125173453-6d6d39c5bb8b/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -647,48 +695,58 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= -github.com/quasilyte/go-ruleguard v0.3.13 h1:O1G41cq1jUr3cJmqp7vOUT0SokqjzmS9aESWJuIDRaY= -github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= +github.com/quasilyte/go-ruleguard v0.3.15 h1:iWYzp1z72IlXTioET0+XI6SjQdPfMGfuAiZiKznOt7g= +github.com/quasilyte/go-ruleguard v0.3.15/go.mod h1:NhuWhnlVEM1gT1A4VJHYfy9MuYSxxwHgxWoPsn9llB4= github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.12-0.20220101150716-969a394a9451/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.12/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.15/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3 h1:P4QPNn+TK49zJjXKERt/vyPbv/mCHB/zQ4flDYOMN+M= +github.com/quasilyte/gogrep v0.0.0-20220103110004-ffaa07af02e3/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8= github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= +github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA= github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/securego/gosec/v2 v2.9.1 h1:anHKLS/ApTYU6NZkKa/5cQqqcbKZURjvc+MtR++S4EQ= -github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= +github.com/securego/gosec/v2 v2.9.6 h1:ysfvgQBp2zmTgXQl65UkqEkYlQGbnVSRUGpCrJiiR4c= +github.com/securego/gosec/v2 v2.9.6/go.mod h1:EESY9Ywxo/Zc5NyF/qIj6Cop+4PSWM0F0OfGD7FdIXc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= -github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= +github.com/shirou/gopsutil/v3 v3.21.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -699,6 +757,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sivchari/containedctx v1.0.1 h1:fJq44cX+tD+uT5xGrsg25GwiaY61NGybQk9WWKij3Uo= +github.com/sivchari/containedctx v1.0.1/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= github.com/sivchari/tenv v1.4.7 h1:FdTpgRlTue5eb5nXIYgS/lyVXSjugU8UUVDwhP1NLU8= github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -710,6 +770,7 @@ github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0H github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -718,8 +779,9 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= +github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -729,8 +791,10 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= +github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= +github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= +github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -749,16 +813,16 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/sylvia7788/contextcheck v1.0.4 h1:MsiVqROAdr0efZc/fOCt0c235qm9XJqHtWwM+2h2B04= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b h1:HxLVTlqcHhFAz3nWUcuvpH7WuOMv8LQoCWmruLfFH2U= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tdakkota/asciicheck v0.1.1 h1:PKzG7JUTUmVspQTDqtkX9eSiLGossXTybutHwTXuO0A= +github.com/tdakkota/asciicheck v0.1.1/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro= +github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -767,8 +831,9 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1 github.com/tomarrell/wrapcheck/v2 v2.4.0 h1:mU4H9KsqqPZUALOUbVOpjy8qNQbWLoLI9fV68/1tq30= github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= -github.com/tommy-muehle/go-mnd/v2 v2.4.0 h1:1t0f8Uiaq+fqKteUR4N9Umr6E99R+lDnLnq7PwX2PPE= -github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s= +github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= @@ -790,8 +855,10 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yeya24/promlinter v0.1.0 h1:goWULN0jH5Yajmu/K+v1xCqIREeB+48OiJ2uu2ssc7U= -github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= +github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= +github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= +github.com/yeya24/promlinter v0.1.1-0.20210918184747-d757024714a1 h1:YAaOqqMTstELMMGblt6yJ/fcOt4owSYuw3IttMnKfAM= +github.com/yeya24/promlinter v0.1.1-0.20210918184747-d757024714a1/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= @@ -802,12 +869,18 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +gitlab.com/bosi/decorder v0.2.1 h1:ehqZe8hI4w7O4b1vgsDZw1YU1PE7iJXrQWFMsocbQ1w= +gitlab.com/bosi/decorder v0.2.1/go.mod h1:6C/nhLSbF6qZbYD8bRmISBwc6vcWdNsiIBkRvjJFrH0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -850,8 +923,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -937,12 +1010,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -959,6 +1035,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1023,11 +1101,13 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1045,13 +1125,21 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1074,7 +1162,6 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= @@ -1157,8 +1244,6 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -1169,10 +1254,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da h1:Tno72dYE94v/7SyyIj9iBsc7OOjFu2PyNcl7yxxeZD8= +golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1206,7 +1291,13 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1273,6 +1364,17 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1302,6 +1404,9 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1330,8 +1435,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1358,16 +1464,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.2.1 h1:/EPr//+UMMXwMTkXvCCoaJDq8cpjMO80Ou+L4PDo2mY= -honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA= -mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= +honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= +honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +mvdan.cc/gofumpt v0.2.1 h1:7jakRGkQcLAJdT+C8Bwc9d0BANkVPSkHZkzNv07pJAs= +mvdan.cc/gofumpt v0.2.1/go.mod h1:a/rvZPhsNaedOJBzqRD9omnwVwHZsBdJirXHa9Gh9Ig= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 h1:HT3e4Krq+IE44tiN36RvVEb6tvqeIdtsVSsxmNPqlFU= -mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= +mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio= +mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From bb2fb940fdfe353f9a398c88211db7668f934727 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 7 Feb 2022 12:43:10 -0800 Subject: [PATCH 27/32] added lock in rs struct --- samplers/aws/xray/remote_sampler.go | 15 ++++++++------- samplers/aws/xray/rule_manifest.go | 2 -- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 4873849ece4..5782bc5c3d8 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -19,6 +19,7 @@ import ( crypto "crypto/rand" "errors" "fmt" + "sync" "time" "github.com/go-logr/logr" @@ -49,6 +50,8 @@ type remoteSampler struct { // Provides system time. clock clock + + mu sync.RWMutex } // Compile time assertion that remoteSampler implements the Sampler interface. @@ -204,15 +207,13 @@ func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { // Re-sort to fix matching priorities. tempManifest.sort() + // Update refreshedAt timestamp + tempManifest.refreshedAt = now // assign temp manifest to original copy/one sync refresh. - rs.manifest.mu.Lock() - rs.manifest.rules = tempManifest.rules - rs.manifest.index = tempManifest.index - - // Update refreshedAt timestamp - rs.manifest.refreshedAt = now - rs.manifest.mu.Unlock() + rs.mu.Lock() + rs.manifest = tempManifest + rs.mu.Unlock() return } diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index cf4c53e7467..891aa09bf12 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -17,7 +17,6 @@ package xray import ( "sort" "strings" - "sync" ) const defaultInterval = int64(10) @@ -30,7 +29,6 @@ type manifest struct { index map[string]*rule refreshedAt int64 clock clock - mu sync.RWMutex } // createRule creates a user-defined rule, appends it to the sorted array, From 86c5aec6c6f00f8847bd20166dc3be7ca4e94712 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 7 Feb 2022 16:06:43 -0800 Subject: [PATCH 28/32] minor changes --- samplers/aws/xray/client.go | 5 ++++- samplers/aws/xray/remote_sampler.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index cb8c08c1125..24ab133a1f1 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -47,7 +47,10 @@ func newClient(d string) *xrayClient { // getSamplingRules calls the collector(aws proxy enabled) for sampling rules func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { - statisticsByte, _ := json.Marshal(getSamplingRulesInput{}) + statisticsByte, err := json.Marshal(getSamplingRulesInput{}) + if err != nil { + return nil, err + } body := bytes.NewReader(statisticsByte) req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint.String()+"/GetSamplingRules", body) diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 5782bc5c3d8..c8a7eb43b74 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -98,7 +98,7 @@ func NewRemoteSampler(ctx context.Context, opts ...Option) (sdktrace.Sampler, er func (rs *remoteSampler) ShouldSample(parameters sdktrace.SamplingParameters) sdktrace.SamplingResult { // ToDo: add business logic for remote sampling - return sdktrace.SamplingResult{} + return sdktrace.TraceIDRatioBased(0.05).ShouldSample(parameters) } func (rs *remoteSampler) Description() string { From 3dea789e5fb42dc27fdac48357990f4f5944671a Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Mon, 7 Feb 2022 18:22:18 -0800 Subject: [PATCH 29/32] minor changes --- samplers/aws/xray/client_test.go | 6 ++++++ samplers/aws/xray/remote_sampler.go | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/samplers/aws/xray/client_test.go b/samplers/aws/xray/client_test.go index 048171db6f1..5deb72ac5b4 100644 --- a/samplers/aws/xray/client_test.go +++ b/samplers/aws/xray/client_test.go @@ -178,3 +178,9 @@ func TestNewClient(t *testing.T) { assert.Equal(t, xrayClient.endpoint.String(), "http://127.0.0.1:2020") } + +func TestEndpointIsNotReachable(t *testing.T) { + client := newClient("127.0.0.1:2020") + _, err := client.getSamplingRules(context.Background()) + assert.Error(t, err) +} diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index c8a7eb43b74..7b4b8d1cba6 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -108,11 +108,12 @@ func (rs *remoteSampler) Description() string { func (rs *remoteSampler) start(ctx context.Context) { if !rs.pollerStart { rs.pollerStart = true - rs.startRulePoller(ctx) + rs.startPoller(ctx) } } -func (rs *remoteSampler) startRulePoller(ctx context.Context) { +func (rs *remoteSampler) startPoller(ctx context.Context) { + // ToDo: add logic for getting sampling targets go func() { // Period = 300s, Jitter = 5s t := newTicker(rs.samplingRulesPollingInterval, 5*time.Second) From 30e6c79024e31fd0fca49e0da7d3b8b875473785 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 8 Feb 2022 12:59:47 -0800 Subject: [PATCH 30/32] refactor PR to address comments --- samplers/aws/xray/client.go | 20 +- samplers/aws/xray/client_test.go | 14 +- samplers/aws/xray/remote_sampler.go | 89 ++++-- samplers/aws/xray/remote_sampler_config.go | 40 ++- .../aws/xray/remote_sampler_config_test.go | 42 +++ samplers/aws/xray/remote_sampler_test.go | 263 ++++++++++++++++-- samplers/aws/xray/rule_manifest.go | 3 +- 7 files changed, 407 insertions(+), 64 deletions(-) diff --git a/samplers/aws/xray/client.go b/samplers/aws/xray/client.go index 24ab133a1f1..f61b223deda 100644 --- a/samplers/aws/xray/client.go +++ b/samplers/aws/xray/client.go @@ -24,41 +24,41 @@ import ( ) type xrayClient struct { - // http client for sending unsigned proxied requests to the collector + // http client for sending sampling requests to the collector httpClient *http.Client endpoint *url.URL } // newClient returns an HTTP client with proxy endpoint -func newClient(d string) *xrayClient { - endpoint := "http://" + d +func newClient(addr string) (client *xrayClient, err error) { + endpoint := "http://" + addr endpointURL, err := url.Parse(endpoint) if err != nil { - globalLogger.Error(err, "unable to parse endpoint from string") + return nil, err } return &xrayClient{ httpClient: &http.Client{}, endpoint: endpointURL, - } + }, nil } // getSamplingRules calls the collector(aws proxy enabled) for sampling rules -func (p *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { - statisticsByte, err := json.Marshal(getSamplingRulesInput{}) +func (c *xrayClient) getSamplingRules(ctx context.Context) (*getSamplingRulesOutput, error) { + samplingRulesInput, err := json.Marshal(getSamplingRulesInput{}) if err != nil { return nil, err } - body := bytes.NewReader(statisticsByte) + body := bytes.NewReader(samplingRulesInput) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.endpoint.String()+"/GetSamplingRules", body) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint.String()+"/GetSamplingRules", body) if err != nil { return nil, fmt.Errorf("xray client: failed to create http request: %w", err) } - output, err := p.httpClient.Do(req) + output, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("xray client: unable to retrieve sampling settings: %w", err) } diff --git a/samplers/aws/xray/client_test.go b/samplers/aws/xray/client_test.go index 5deb72ac5b4..34bcfdd0e70 100644 --- a/samplers/aws/xray/client_test.go +++ b/samplers/aws/xray/client_test.go @@ -99,7 +99,8 @@ func TestGetSamplingRules(t *testing.T) { u, err := url.Parse(testServer.URL) require.NoError(t, err) - client := newClient(u.Host) + client, err := newClient(u.Host) + require.NoError(t, err) samplingRules, err := client.getSamplingRules(ctx) require.NoError(t, err) @@ -160,7 +161,8 @@ func TestGetSamplingRulesWithMissingValues(t *testing.T) { u, err := url.Parse(testServer.URL) require.NoError(t, err) - client := newClient(u.Host) + client, err := newClient(u.Host) + require.NoError(t, err) samplingRules, err := client.getSamplingRules(ctx) require.NoError(t, err) @@ -174,13 +176,15 @@ func TestGetSamplingRulesWithMissingValues(t *testing.T) { } func TestNewClient(t *testing.T) { - xrayClient := newClient("127.0.0.1:2020") + xrayClient, err := newClient("127.0.0.1:2020") + require.NoError(t, err) assert.Equal(t, xrayClient.endpoint.String(), "http://127.0.0.1:2020") } func TestEndpointIsNotReachable(t *testing.T) { - client := newClient("127.0.0.1:2020") - _, err := client.getSamplingRules(context.Background()) + client, err := newClient("127.0.0.1:2020") + require.NoError(t, err) + _, err = client.getSamplingRules(context.Background()) assert.Error(t, err) } diff --git a/samplers/aws/xray/remote_sampler.go b/samplers/aws/xray/remote_sampler.go index 7b4b8d1cba6..f61f53b0aec 100644 --- a/samplers/aws/xray/remote_sampler.go +++ b/samplers/aws/xray/remote_sampler.go @@ -17,7 +17,6 @@ package xray import ( "context" crypto "crypto/rand" - "errors" "fmt" "sync" "time" @@ -33,14 +32,14 @@ var globalLogger logr.Logger // remoteSampler is a sampler for AWS X-Ray which polls sampling rules and sampling targets // to make a sampling decision based on rules set by users on AWS X-Ray console type remoteSampler struct { - // List of known centralized sampling rules. + // manifest is the list of known centralized sampling rules. manifest *manifest // xrayClient is used for getting quotas and sampling rules. xrayClient *xrayClient - // pollerStart, if true represents rule and target pollers are started. - pollerStart bool + // pollerStarted, if true represents rule and target pollers are started. + pollerStarted bool // samplingRulesPollingInterval, default is 300 seconds. samplingRulesPollingInterval time.Duration @@ -48,7 +47,7 @@ type remoteSampler struct { // Unique ID used by XRay service to identify this client. clientID string - // Provides system time. + // Provides time. clock clock mu sync.RWMutex @@ -63,10 +62,16 @@ var _ sdktrace.Sampler = (*remoteSampler)(nil) func NewRemoteSampler(ctx context.Context, opts ...Option) (sdktrace.Sampler, error) { cfg := newConfig(opts...) + // validate config + err := validateConfig(cfg) + if err != nil { + return nil, err + } + // Generate clientID var r [12]byte - _, err := crypto.Read(r[:]) + _, err = crypto.Read(r[:]) if err != nil { return nil, fmt.Errorf("unable to generate client ID: %w", err) } @@ -81,11 +86,16 @@ func NewRemoteSampler(ctx context.Context, opts ...Option) (sdktrace.Sampler, er clock: clock, } + client, err := newClient(cfg.endpoint) + if err != nil { + return nil, err + } + remoteSampler := &remoteSampler{ clock: clock, manifest: m, clientID: id, - xrayClient: newClient(cfg.endpoint), + xrayClient: client, samplingRulesPollingInterval: cfg.samplingRulesPollingInterval, } @@ -102,12 +112,16 @@ func (rs *remoteSampler) ShouldSample(parameters sdktrace.SamplingParameters) sd } func (rs *remoteSampler) Description() string { + return "AwsXrayRemoteSampler{" + rs.getDescription() + "}" +} + +func (rs *remoteSampler) getDescription() string { return "remote sampling with AWS X-Ray" } func (rs *remoteSampler) start(ctx context.Context) { - if !rs.pollerStart { - rs.pollerStart = true + if !rs.pollerStarted { + rs.pollerStarted = true rs.startPoller(ctx) } } @@ -123,7 +137,7 @@ func (rs *remoteSampler) startPoller(ctx context.Context) { if err := rs.refreshManifest(ctx); err != nil { globalLogger.Error(err, "Error occurred while refreshing sampling rules") } else { - globalLogger.Info("Successfully fetched sampling rules") + globalLogger.V(1).Info("Successfully fetched sampling rules") } select { case _, more := <-t.C(): @@ -141,7 +155,7 @@ func (rs *remoteSampler) startPoller(ctx context.Context) { func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { // Compute 'now' before calling GetSamplingRules to avoid marking manifest as // fresher than it actually is. - now := rs.clock.now().Unix() + now := rs.clock.now() // Get sampling rules from proxy. rules, err := rs.xrayClient.getSamplingRules(ctx) @@ -149,8 +163,6 @@ func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { return } - failed := false - // temporary manifest declaration. tempManifest := &manifest{ rules: []*rule{}, @@ -161,25 +173,52 @@ func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { for _, records := range rules.SamplingRuleRecords { if records.SamplingRule.RuleName == nil { globalLogger.V(1).Info("Sampling rule without rule name is not supported") - failed = true continue } // Only sampling rule with version 1 is valid if records.SamplingRule.Version == nil { - globalLogger.V(1).Info("Sampling rule without version number is not supported", "RuleName", *records.SamplingRule.RuleName) - failed = true + globalLogger.V(1).Info("Sampling rule without Version is not supported", "RuleName", *records.SamplingRule.RuleName) continue } if *records.SamplingRule.Version != int64(1) { - globalLogger.V(1).Info("Sampling rule without version 1 is not supported", "RuleName", *records.SamplingRule.RuleName) - failed = true + globalLogger.V(1).Info("Sampling rule without Version 1 is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.ServiceName == nil { + globalLogger.V(1).Info("Sampling rule without ServiceName is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.ServiceType == nil { + globalLogger.V(1).Info("Sampling rule without ServiceType is not supported", "RuleName", *records.SamplingRule.RuleName) continue } - if len(records.SamplingRule.Attributes) != 0 { - globalLogger.V(1).Info("Sampling rule with non nil Attributes is not applicable", "RuleName", *records.SamplingRule.RuleName) + if records.SamplingRule.Host == nil { + globalLogger.V(1).Info("Sampling rule without Host is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.HTTPMethod == nil { + globalLogger.V(1).Info("Sampling rule without HTTPMethod is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.URLPath == nil { + globalLogger.V(1).Info("Sampling rule without URLPath is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.ReservoirSize == nil { + globalLogger.V(1).Info("Sampling rule without ReservoirSize is not supported", "RuleName", *records.SamplingRule.RuleName) + continue + } + + if records.SamplingRule.FixedRate == nil { + globalLogger.V(1).Info("Sampling rule without FixedRate is not supported", "RuleName", *records.SamplingRule.RuleName) continue } @@ -188,24 +227,18 @@ func (rs *remoteSampler) refreshManifest(ctx context.Context) (err error) { continue } - if *records.SamplingRule.ResourceARN != "*" { - globalLogger.V(1).Info("Sampling rule with ResourceARN not equal to * is not applicable", "RuleName", *records.SamplingRule.RuleName) + if records.SamplingRule.Priority == nil { + globalLogger.V(1).Info("Sampling rule without version number is not supported", "RuleName", *records.SamplingRule.RuleName) continue } // create rule and store it in temporary manifest to avoid locking issues. createErr := tempManifest.createRule(records.SamplingRule) if createErr != nil { - failed = true globalLogger.Error(createErr, "Error occurred creating/updating rule") } } - // Set err if updates failed - if failed { - err = errors.New("error occurred creating/updating rules") - } - // Re-sort to fix matching priorities. tempManifest.sort() // Update refreshedAt timestamp diff --git a/samplers/aws/xray/remote_sampler_config.go b/samplers/aws/xray/remote_sampler_config.go index 5083145a121..e80017f4713 100644 --- a/samplers/aws/xray/remote_sampler_config.go +++ b/samplers/aws/xray/remote_sampler_config.go @@ -15,8 +15,13 @@ package xray import ( + "fmt" "log" + "math" "os" + "regexp" + "strconv" + "strings" "time" "github.com/go-logr/logr" @@ -62,7 +67,7 @@ func newConfig(opts ...Option) *config { cfg := &config{ endpoint: defaultProxyEndpoint, samplingRulesPollingInterval: defaultPollingInterval * time.Second, - logger: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)), + logger: stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile), stdr.Options{LogCaller: stdr.Error}), } for _, option := range opts { @@ -71,8 +76,37 @@ func newConfig(opts ...Option) *config { // setting global logger globalLogger = cfg.logger - // set global verbosity to log info logs - stdr.SetVerbosity(1) return cfg } + +func validateConfig(cfg *config) (err error) { + // check endpoint follows certain format + split := strings.Split(cfg.endpoint, ":") + + if len(split) > 2 { + return fmt.Errorf("endpoint validation error: expected format is 127.0.0.1:8080") + } + + // validate host name + r, err := regexp.Compile("[^A-Za-z0-9.]") + if err != nil { + return err + } + + if r.MatchString(split[0]) { + return fmt.Errorf("endpoint validation error: expected format is 127.0.0.1:8080") + } + + // validate port + if _, err := strconv.Atoi(split[1]); err != nil { + return fmt.Errorf("endpoint validation error: expected format is 127.0.0.1:8080") + } + + // validate polling interval is positive + if math.Signbit(float64(cfg.samplingRulesPollingInterval)) { + return fmt.Errorf("endpoint validation error: samplingRulesPollingInterval should be positive number") + } + + return +} diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index 8768c66a000..bd7c93e7872 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -51,3 +51,45 @@ func TestPartialUserProvidedConfig(t *testing.T) { assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) } + +func TestValidateConfigIncorrectEndpoint(t *testing.T) { + cfg := newConfig(WithEndpoint("http://127.0.0.1:2000")) + + err := validateConfig(cfg) + assert.Error(t, err) +} + +func TestValidateConfigSpecialCharacterEndpoint(t *testing.T) { + cfg := newConfig(WithEndpoint("@127.0.0.1:2000")) + + err := validateConfig(cfg) + assert.Error(t, err) +} + +func TestValidateConfigLocalHost(t *testing.T) { + cfg := newConfig(WithEndpoint("localhost:2000")) + + err := validateConfig(cfg) + assert.NoError(t, err) +} + +func TestValidateConfigInvalidPort(t *testing.T) { + cfg := newConfig(WithEndpoint("127.0.0.1:abcd")) + + err := validateConfig(cfg) + assert.Error(t, err) +} + +func TestValidateConfigNegativeDuration(t *testing.T) { + cfg := newConfig(WithSamplingRulesPollingInterval(-300 * time.Second)) + + err := validateConfig(cfg) + assert.Error(t, err) +} + +func TestValidateConfigPositiveDuration(t *testing.T) { + cfg := newConfig(WithSamplingRulesPollingInterval(300 * time.Second)) + + err := validateConfig(cfg) + assert.NoError(t, err) +} diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index 4922368d6d9..b148721fa16 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -110,8 +110,11 @@ func TestRefreshManifest(t *testing.T) { clock: clock, } + client, err := newClient(u.Host) + require.NoError(t, err) + rs := &remoteSampler{ - xrayClient: newClient(u.Host), + xrayClient: client, clock: clock, manifest: m, } @@ -211,8 +214,8 @@ func TestRefreshManifest(t *testing.T) { assert.Equal(t, 3, len(rs.manifest.index)) } -// assert that invalid rule with ResourceARN other than "*" does not update to the manifest -func TestRefreshManifestAddInvalidRuleType1(t *testing.T) { +// assert that rule with nil ServiceName does not update to the manifest +func TestRefreshManifestMissingServiceName(t *testing.T) { ctx := context.Background() // to enable logging @@ -235,7 +238,6 @@ func TestRefreshManifestAddInvalidRuleType1(t *testing.T) { "ResourceARN": "XYZ", "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", "RuleName": "r1", - "ServiceName": "*", "ServiceType": "*", "URLPath": "*", "Version": 1 @@ -263,8 +265,11 @@ func TestRefreshManifestAddInvalidRuleType1(t *testing.T) { clock: clock, } + client, err := newClient(u.Host) + require.NoError(t, err) + rs := &remoteSampler{ - xrayClient: newClient(u.Host), + xrayClient: client, clock: clock, manifest: m, } @@ -276,14 +281,14 @@ func TestRefreshManifestAddInvalidRuleType1(t *testing.T) { assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added } -// assert that invalid rule with attribute does not update to the manifest -func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { +// assert that rule with nil ServiceType does not update to the manifest +func TestRefreshManifestMissingServiceType(t *testing.T) { ctx := context.Background() // to enable logging newConfig() - // invalid rule due to attributes + // invalid rule due to ResourceARN body := []byte(`{ "NextToken": null, "SamplingRuleRecords": [ @@ -291,16 +296,218 @@ func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { "CreatedAt": 0, "ModifiedAt": 1639517389, "SamplingRule": { - "Attributes": {"a":"b"}, + "Attributes": {}, "FixedRate": 0.5, "HTTPMethod": "*", "Host": "*", "Priority": 10000, "ReservoirSize": 60, - "ResourceARN": "*", + "ResourceARN": "XYZ", "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", "RuleName": "r1", - "ServiceName": "*", + "ServiceName": "test", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, err := res.Write([]byte(body)) + require.NoError(t, err) + + })) + defer testServer.Close() + + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + + clock := &defaultClock{} + + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } + + client, err := newClient(u.Host) + require.NoError(t, err) + + rs := &remoteSampler{ + xrayClient: client, + clock: clock, + manifest: m, + } + + err = rs.refreshManifest(ctx) + require.NoError(t, err) + + // Refresh manifest with updates from mock proxy + assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added +} + +// assert that rule with nil ReservoirSize does not update to the manifest +func TestRefreshManifestMissingReservoirSize(t *testing.T) { + ctx := context.Background() + + // to enable logging + newConfig() + + // invalid rule due to ResourceARN + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ResourceARN": "XYZ", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "test", + "ServiceType": "*", + "URLPath": "*", + "Version": 1 + } + } + ] +}`) + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, err := res.Write([]byte(body)) + require.NoError(t, err) + + })) + defer testServer.Close() + + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + + clock := &defaultClock{} + + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } + + client, err := newClient(u.Host) + require.NoError(t, err) + + rs := &remoteSampler{ + xrayClient: client, + clock: clock, + manifest: m, + } + + err = rs.refreshManifest(ctx) + require.NoError(t, err) + + // Refresh manifest with updates from mock proxy + assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added +} + +// assert that rule with version greater than one does not update to the manifest +func TestRefreshManifestIncorrectPriority(t *testing.T) { + ctx := context.Background() + + // to enable logging + newConfig() + + // invalid rule due to ResourceARN + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ReservoirSize": 60, + "ResourceARN": "XYZ", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ServiceName": "test", + "ServiceType": "*", + "URLPath": "*", + "Version": 5 + } + } + ] +}`) + + // generate a test server so we can capture and inspect the request + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + _, err := res.Write([]byte(body)) + require.NoError(t, err) + + })) + defer testServer.Close() + + u, err := url.Parse(testServer.URL) + require.NoError(t, err) + + clock := &defaultClock{} + + m := &manifest{ + rules: []*rule{}, + index: map[string]*rule{}, + clock: clock, + } + + client, err := newClient(u.Host) + require.NoError(t, err) + + rs := &remoteSampler{ + xrayClient: client, + clock: clock, + manifest: m, + } + + err = rs.refreshManifest(ctx) + require.NoError(t, err) + + // Refresh manifest with updates from mock proxy + assert.Equal(t, 0, len(rs.manifest.rules)) // Rule not added +} + +// assert that rule nil attributes does update the manifest +func TestRefreshManifestNilAttributes(t *testing.T) { + ctx := context.Background() + + // to enable logging + newConfig() + + // invalid rule due to ResourceARN + body := []byte(`{ + "NextToken": null, + "SamplingRuleRecords": [ + { + "CreatedAt": 0, + "ModifiedAt": 1639517389, + "SamplingRule": { + "Attributes": {}, + "FixedRate": 0.5, + "HTTPMethod": "*", + "Host": "*", + "Priority": 10000, + "ResourceARN": "XYZ", + "RuleARN": "arn:aws:xray:us-west-2:xxxxxxx:sampling-rule/r1", + "RuleName": "r1", + "ReservoirSize": 60, + "ServiceName": "test", "ServiceType": "*", "URLPath": "*", "Version": 1 @@ -313,6 +520,7 @@ func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { _, err := res.Write([]byte(body)) require.NoError(t, err) + })) defer testServer.Close() @@ -327,8 +535,11 @@ func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { clock: clock, } + client, err := newClient(u.Host) + require.NoError(t, err) + rs := &remoteSampler{ - xrayClient: newClient(u.Host), + xrayClient: client, clock: clock, manifest: m, } @@ -336,16 +547,18 @@ func TestRefreshManifestAddInvalidRuleType2(t *testing.T) { err = rs.refreshManifest(ctx) require.NoError(t, err) - assert.Equal(t, 0, len(rs.manifest.rules)) // rule not added + // Refresh manifest with updates from mock proxy + assert.Equal(t, 1, len(rs.manifest.rules)) // Rule added } // assert that 1 valid and 1 invalid rule update only valid rule gets stored to the manifest -func TestRefreshManifestAddInvalidRule3(t *testing.T) { +func TestRefreshManifestAddOneInvalidRule(t *testing.T) { ctx := context.Background() // to enable logging newConfig() + // host is missing from r2 body := []byte(`{ "NextToken": null, "SamplingRuleRecords": [ @@ -375,7 +588,6 @@ func TestRefreshManifestAddInvalidRule3(t *testing.T) { "Attributes": {"a":"b"}, "FixedRate": 0.5, "HTTPMethod": "*", - "Host": "*", "Priority": 10000, "ReservoirSize": 60, "ResourceARN": "*", @@ -424,8 +636,11 @@ func TestRefreshManifestAddInvalidRule3(t *testing.T) { clock: clock, } + client, err := newClient(u.Host) + require.NoError(t, err) + rs := &remoteSampler{ - xrayClient: newClient(u.Host), + xrayClient: client, clock: clock, manifest: m, } @@ -551,8 +766,11 @@ func TestManifestRulesAndIndexUpdate(t *testing.T) { clock: clock, } + client, err := newClient(u.Host) + require.NoError(t, err) + rs := &remoteSampler{ - xrayClient: newClient(u.Host), + xrayClient: client, clock: clock, manifest: m, } @@ -601,3 +819,14 @@ func TestNewRemoteSampler(t *testing.T) { s := &remoteSampler{} assert.Equal(t, reflect.TypeOf(rs), reflect.TypeOf(s)) } + +// assert that config validation error results in nil remote sampler +func TestConfigError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + rs, err := NewRemoteSampler(ctx, WithEndpoint("http://127.0.0.1:8080")) + + assert.Error(t, err) + assert.Nil(t, rs) +} diff --git a/samplers/aws/xray/rule_manifest.go b/samplers/aws/xray/rule_manifest.go index 891aa09bf12..92cfc44c8b5 100644 --- a/samplers/aws/xray/rule_manifest.go +++ b/samplers/aws/xray/rule_manifest.go @@ -17,6 +17,7 @@ package xray import ( "sort" "strings" + "time" ) const defaultInterval = int64(10) @@ -27,7 +28,7 @@ const defaultInterval = int64(10) type manifest struct { rules []*rule index map[string]*rule - refreshedAt int64 + refreshedAt time.Time clock clock } From ee415a7351d9b918e45692fe6d0607f04ca33681 Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 8 Feb 2022 13:30:16 -0800 Subject: [PATCH 31/32] fix test failures --- samplers/aws/xray/remote_sampler_config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samplers/aws/xray/remote_sampler_config_test.go b/samplers/aws/xray/remote_sampler_config_test.go index bd7c93e7872..5470387de41 100644 --- a/samplers/aws/xray/remote_sampler_config_test.go +++ b/samplers/aws/xray/remote_sampler_config_test.go @@ -40,7 +40,7 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, cfg.samplingRulesPollingInterval, 300*time.Second) assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") - assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) + assert.Equal(t, cfg.logger, stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile), stdr.Options{LogCaller: stdr.Error})) } // asset when some config is provided by user then other config will be picked up from default config @@ -49,7 +49,7 @@ func TestPartialUserProvidedConfig(t *testing.T) { assert.Equal(t, cfg.samplingRulesPollingInterval, 500*time.Second) assert.Equal(t, cfg.endpoint, "127.0.0.1:2000") - assert.Equal(t, cfg.logger, stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) + assert.Equal(t, cfg.logger, stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile), stdr.Options{LogCaller: stdr.Error})) } func TestValidateConfigIncorrectEndpoint(t *testing.T) { From c92361699863da2b725ed65a2213e7636de910ce Mon Sep 17 00:00:00 2001 From: bhautikpip Date: Tue, 8 Feb 2022 13:54:59 -0800 Subject: [PATCH 32/32] fix test failures --- samplers/aws/xray/remote_sampler_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/samplers/aws/xray/remote_sampler_test.go b/samplers/aws/xray/remote_sampler_test.go index b148721fa16..aba9784703a 100644 --- a/samplers/aws/xray/remote_sampler_test.go +++ b/samplers/aws/xray/remote_sampler_test.go @@ -819,14 +819,3 @@ func TestNewRemoteSampler(t *testing.T) { s := &remoteSampler{} assert.Equal(t, reflect.TypeOf(rs), reflect.TypeOf(s)) } - -// assert that config validation error results in nil remote sampler -func TestConfigError(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - rs, err := NewRemoteSampler(ctx, WithEndpoint("http://127.0.0.1:8080")) - - assert.Error(t, err) - assert.Nil(t, rs) -}