diff --git a/.changes/header.tpl.md b/.changes/header.tpl.md new file mode 100644 index 000000000..df8faa7b2 --- /dev/null +++ b/.changes/header.tpl.md @@ -0,0 +1,6 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and is generated by [Changie](https://github.com/miniscruff/changie). diff --git a/.changes/unreleased/.gitkeep b/.changes/unreleased/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.changes/v0.5.04.md b/.changes/v0.5.04.md new file mode 100644 index 000000000..dcf199aef --- /dev/null +++ b/.changes/v0.5.04.md @@ -0,0 +1,5 @@ +## v0.5.4 - 2023-05-24 +### Added +* HTTP provider middlewares +### Fixed +* Refactoring HTTP providers diff --git a/.changes/v0.5.05.md b/.changes/v0.5.05.md new file mode 100644 index 000000000..a4fcff794 --- /dev/null +++ b/.changes/v0.5.05.md @@ -0,0 +1,3 @@ +## v0.5.5 - 2023-06-14 +### Added +* resolver for property file diff --git a/.changes/v0.5.06.md b/.changes/v0.5.06.md new file mode 100644 index 000000000..06160628d --- /dev/null +++ b/.changes/v0.5.06.md @@ -0,0 +1,3 @@ +## v0.5.6 - 2023-07-13 +### Added +* preloaded http providers diff --git a/.changes/v0.5.07.md b/.changes/v0.5.07.md new file mode 100644 index 000000000..f19172848 --- /dev/null +++ b/.changes/v0.5.07.md @@ -0,0 +1,3 @@ +## v0.5.7 - 2023-07-13 +### Added +* grpc answ.log diff --git a/.changes/v0.5.08.md b/.changes/v0.5.08.md new file mode 100644 index 000000000..85c1c69f8 --- /dev/null +++ b/.changes/v0.5.08.md @@ -0,0 +1,3 @@ +## v0.5.8 - 2023-07-26 +### Added +* Dummy provider diff --git a/.changes/v0.5.09.md b/.changes/v0.5.09.md new file mode 100644 index 000000000..74b2a69f3 --- /dev/null +++ b/.changes/v0.5.09.md @@ -0,0 +1,3 @@ +## v0.5.9 - 2023-09-22 +### Added +* scenario generator diff --git a/.changes/v0.5.10.md b/.changes/v0.5.10.md new file mode 100644 index 000000000..d75f4d12a --- /dev/null +++ b/.changes/v0.5.10.md @@ -0,0 +1,3 @@ +## v0.5.10 - 2023-10-02 +### Fixed +* grpc generator bugs diff --git a/.changes/v0.5.11.md b/.changes/v0.5.11.md new file mode 100644 index 000000000..e8f63cafa --- /dev/null +++ b/.changes/v0.5.11.md @@ -0,0 +1,3 @@ +## v0.5.11 - 2023-10-12 +### Fixed +* update go.mod diff --git a/.changes/v0.5.12.md b/.changes/v0.5.12.md new file mode 100644 index 000000000..e7fc47bb6 --- /dev/null +++ b/.changes/v0.5.12.md @@ -0,0 +1,4 @@ +## v0.5.12 - 2023-10-19 +### Fixed +* grpc provider scan +* grpc reflect client depreceted diff --git a/.changes/v0.5.13.md b/.changes/v0.5.13.md new file mode 100644 index 000000000..d5b939a47 --- /dev/null +++ b/.changes/v0.5.13.md @@ -0,0 +1,3 @@ +## v0.5.13 - 2023-10-20 +### Fixed +* scenario variable source diff --git a/.changes/v0.5.14.md b/.changes/v0.5.14.md new file mode 100644 index 000000000..2f0aa1937 --- /dev/null +++ b/.changes/v0.5.14.md @@ -0,0 +1,6 @@ +## v0.5.14 - 2023-10-26 +### Added +* http provider benchmarks +### Fixed +* http provider missing error +* error messages diff --git a/.changes/v0.5.15.md b/.changes/v0.5.15.md new file mode 100644 index 000000000..cd9836ccd --- /dev/null +++ b/.changes/v0.5.15.md @@ -0,0 +1,5 @@ +## v0.5.15 - 2023-11-09 +### Added +* std json-decoder with array payload +### Fixed +* docs: bit misspells diff --git a/.changes/v0.5.16.md b/.changes/v0.5.16.md new file mode 100644 index 000000000..63b781d21 --- /dev/null +++ b/.changes/v0.5.16.md @@ -0,0 +1,3 @@ +## v0.5.16 - 2023-11-10 +### Changed +* yaml format for config without extension diff --git a/.changes/v0.5.17.md b/.changes/v0.5.17.md new file mode 100644 index 000000000..b7d03f303 --- /dev/null +++ b/.changes/v0.5.17.md @@ -0,0 +1,3 @@ +## v0.5.17 - 2024-11-24 +### Fixed +* Internal refactoring: GetMapValue() diff --git a/.changes/v0.5.18.md b/.changes/v0.5.18.md new file mode 100644 index 000000000..7cbee4d3e --- /dev/null +++ b/.changes/v0.5.18.md @@ -0,0 +1,5 @@ +## v0.5.18 - 2024-01-15 +### Fixed +* refactoring http scenario gun +* fix acceptance tests +* fix AnswLog init diff --git a/.changes/v0.5.19.md b/.changes/v0.5.19.md new file mode 100644 index 000000000..1b844aa14 --- /dev/null +++ b/.changes/v0.5.19.md @@ -0,0 +1,3 @@ +## v0.5.19 - 2024-01-25 +### Added +* grpc scenario generator diff --git a/.changes/v0.5.20.md b/.changes/v0.5.20.md new file mode 100644 index 000000000..41a3cc397 --- /dev/null +++ b/.changes/v0.5.20.md @@ -0,0 +1,3 @@ +## v0.5.20 - 2024-01-29 +### Fixed +* http config: fix limit and passes diff --git a/.changes/v0.5.21.md b/.changes/v0.5.21.md new file mode 100644 index 000000000..9ded51bc7 --- /dev/null +++ b/.changes/v0.5.21.md @@ -0,0 +1,4 @@ +## v0.5.21 - 2024-02-02 +### Added +* parameterizable grpc reflection port +* changelog diff --git a/.changes/v0.5.22.md b/.changes/v0.5.22.md new file mode 100644 index 000000000..6e0aba2eb --- /dev/null +++ b/.changes/v0.5.22.md @@ -0,0 +1,5 @@ +## v0.5.22 - 2024-03-26 +### Added +* `shared-client` for gRPC and HTTP generators +### Changed +* Refactoring HTTP generators. Now they use common components diff --git a/.changie.yaml b/.changie.yaml new file mode 100644 index 000000000..906d49559 --- /dev/null +++ b/.changie.yaml @@ -0,0 +1,26 @@ +changesDir: .changes +unreleasedDir: unreleased +headerPath: header.tpl.md +changelogPath: CHANGELOG.md +versionExt: md +versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}' +kindFormat: '### {{.Kind}}' +changeFormat: '* {{.Body}}' +kinds: +- label: Added + auto: minor +- label: Changed + auto: major +- label: Deprecated + auto: minor +- label: Removed + auto: major +- label: Fixed + auto: patch +- label: Security + auto: patch +newlines: + afterChangelogHeader: 1 + beforeChangelogVersion: 1 + endOfVersion: 1 +envPrefix: CHANGIE_ diff --git a/.mapping.json b/.mapping.json index 86c812964..66bdb00d8 100644 --- a/.mapping.json +++ b/.mapping.json @@ -1,39 +1,60 @@ { + ".changes/header.tpl.md":"load/projects/pandora/.changes/header.tpl.md", + ".changes/unreleased/.gitkeep":"load/projects/pandora/.changes/unreleased/.gitkeep", + ".changes/v0.5.04.md":"load/projects/pandora/.changes/v0.5.04.md", + ".changes/v0.5.05.md":"load/projects/pandora/.changes/v0.5.05.md", + ".changes/v0.5.06.md":"load/projects/pandora/.changes/v0.5.06.md", + ".changes/v0.5.07.md":"load/projects/pandora/.changes/v0.5.07.md", + ".changes/v0.5.08.md":"load/projects/pandora/.changes/v0.5.08.md", + ".changes/v0.5.09.md":"load/projects/pandora/.changes/v0.5.09.md", + ".changes/v0.5.10.md":"load/projects/pandora/.changes/v0.5.10.md", + ".changes/v0.5.11.md":"load/projects/pandora/.changes/v0.5.11.md", + ".changes/v0.5.12.md":"load/projects/pandora/.changes/v0.5.12.md", + ".changes/v0.5.13.md":"load/projects/pandora/.changes/v0.5.13.md", + ".changes/v0.5.14.md":"load/projects/pandora/.changes/v0.5.14.md", + ".changes/v0.5.15.md":"load/projects/pandora/.changes/v0.5.15.md", + ".changes/v0.5.16.md":"load/projects/pandora/.changes/v0.5.16.md", + ".changes/v0.5.17.md":"load/projects/pandora/.changes/v0.5.17.md", + ".changes/v0.5.18.md":"load/projects/pandora/.changes/v0.5.18.md", + ".changes/v0.5.19.md":"load/projects/pandora/.changes/v0.5.19.md", + ".changes/v0.5.20.md":"load/projects/pandora/.changes/v0.5.20.md", + ".changes/v0.5.21.md":"load/projects/pandora/.changes/v0.5.21.md", + ".changes/v0.5.22.md":"load/projects/pandora/.changes/v0.5.22.md", + ".changie.yaml":"load/projects/pandora/.changie.yaml", ".github/workflows/release.yml":"load/projects/pandora/.github/workflows/release.yml", ".github/workflows/test.yml":"load/projects/pandora/.github/workflows/test.yml", ".gitignore":"load/projects/pandora/.gitignore", ".goxc.json":"load/projects/pandora/.goxc.json", ".travis.yml":"load/projects/pandora/.travis.yml", "AUTHORS":"load/projects/pandora/AUTHORS", + "CHANGELOG.md":"load/projects/pandora/CHANGELOG.md", "LICENSE":"load/projects/pandora/LICENSE", "Makefile":"load/projects/pandora/Makefile", "README.md":"load/projects/pandora/README.md", - "acceptance_tests/acceptance_suite_test.go":"load/projects/pandora/acceptance_tests/acceptance_suite_test.go", - "acceptance_tests/http_test.go":"load/projects/pandora/acceptance_tests/http_test.go", "cli/cli.go":"load/projects/pandora/cli/cli.go", "cli/expvar.go":"load/projects/pandora/cli/expvar.go", "components/grpc/import/import.go":"load/projects/pandora/components/grpc/import/import.go", "components/guns/grpc/core.go":"load/projects/pandora/components/guns/grpc/core.go", + "components/guns/grpc/core_test.go":"load/projects/pandora/components/guns/grpc/core_test.go", "components/guns/grpc/scenario/ammo.go":"load/projects/pandora/components/guns/grpc/scenario/ammo.go", "components/guns/grpc/scenario/core.go":"load/projects/pandora/components/guns/grpc/scenario/core.go", "components/guns/grpc/scenario/templater.go":"load/projects/pandora/components/guns/grpc/scenario/templater.go", "components/guns/grpc/scenario/templater_text.go":"load/projects/pandora/components/guns/grpc/scenario/templater_text.go", "components/guns/grpc/scenario/templater_text_test.go":"load/projects/pandora/components/guns/grpc/scenario/templater_text_test.go", + "components/guns/grpc/shared_deps.go":"load/projects/pandora/components/guns/grpc/shared_deps.go", "components/guns/http/base.go":"load/projects/pandora/components/guns/http/base.go", "components/guns/http/base_test.go":"load/projects/pandora/components/guns/http/base_test.go", "components/guns/http/client.go":"load/projects/pandora/components/guns/http/client.go", "components/guns/http/connect.go":"load/projects/pandora/components/guns/http/connect.go", "components/guns/http/connect_test.go":"load/projects/pandora/components/guns/http/connect_test.go", - "components/guns/http/core.go":"load/projects/pandora/components/guns/http/core.go", "components/guns/http/doc.go":"load/projects/pandora/components/guns/http/doc.go", "components/guns/http/http.go":"load/projects/pandora/components/guns/http/http.go", "components/guns/http/http_test.go":"load/projects/pandora/components/guns/http/http_test.go", "components/guns/http/mock_client_test.go":"load/projects/pandora/components/guns/http/mock_client_test.go", "components/guns/http/mocks/ammo.go":"load/projects/pandora/components/guns/http/mocks/ammo.go", - "components/guns/http/phttp_suite_test.go":"load/projects/pandora/components/guns/http/phttp_suite_test.go", "components/guns/http/trace.go":"load/projects/pandora/components/guns/http/trace.go", + "components/guns/http/wrapper.go":"load/projects/pandora/components/guns/http/wrapper.go", "components/guns/http_scenario/ammo.go":"load/projects/pandora/components/guns/http_scenario/ammo.go", - "components/guns/http_scenario/client.go":"load/projects/pandora/components/guns/http_scenario/client.go", "components/guns/http_scenario/gun.go":"load/projects/pandora/components/guns/http_scenario/gun.go", "components/guns/http_scenario/gun_test.go":"load/projects/pandora/components/guns/http_scenario/gun_test.go", "components/guns/http_scenario/import.go":"load/projects/pandora/components/guns/http_scenario/import.go", @@ -137,7 +158,6 @@ "core/aggregator/mocks/sample_encoder.go":"load/projects/pandora/core/aggregator/mocks/sample_encoder.go", "core/aggregator/netsample/aggregator.go":"load/projects/pandora/core/aggregator/netsample/aggregator.go", "core/aggregator/netsample/mock_aggregator.go":"load/projects/pandora/core/aggregator/netsample/mock_aggregator.go", - "core/aggregator/netsample/netsample_suite_test.go":"load/projects/pandora/core/aggregator/netsample/netsample_suite_test.go", "core/aggregator/netsample/phout.go":"load/projects/pandora/core/aggregator/netsample/phout.go", "core/aggregator/netsample/phout_test.go":"load/projects/pandora/core/aggregator/netsample/phout_test.go", "core/aggregator/netsample/sample.go":"load/projects/pandora/core/aggregator/netsample/sample.go", @@ -146,6 +166,7 @@ "core/aggregator/reporter.go":"load/projects/pandora/core/aggregator/reporter.go", "core/aggregator/reporter_test.go":"load/projects/pandora/core/aggregator/reporter_test.go", "core/aggregator/test.go":"load/projects/pandora/core/aggregator/test.go", + "core/clientpool/pool.go":"load/projects/pandora/core/clientpool/pool.go", "core/config/config.go":"load/projects/pandora/core/config/config.go", "core/config/config_test.go":"load/projects/pandora/core/config/config_test.go", "core/config/doc.go":"load/projects/pandora/core/config/doc.go", @@ -181,7 +202,7 @@ "core/engine/instance.go":"load/projects/pandora/core/engine/instance.go", "core/engine/instance_test.go":"load/projects/pandora/core/engine/instance_test.go", "core/import/import.go":"load/projects/pandora/core/import/import.go", - "core/import/import_suite_test.go":"load/projects/pandora/core/import/import_suite_test.go", + "core/import/import_test.go":"load/projects/pandora/core/import/import_test.go", "core/mocks/aggregator.go":"load/projects/pandora/core/mocks/aggregator.go", "core/mocks/borrowed_sample.go":"load/projects/pandora/core/mocks/borrowed_sample.go", "core/mocks/data_sink.go":"load/projects/pandora/core/mocks/data_sink.go", @@ -195,7 +216,6 @@ "core/plugin/doc.go":"load/projects/pandora/core/plugin/doc.go", "core/plugin/example_test.go":"load/projects/pandora/core/plugin/example_test.go", "core/plugin/plugin.go":"load/projects/pandora/core/plugin/plugin.go", - "core/plugin/plugin_suite_test.go":"load/projects/pandora/core/plugin/plugin_suite_test.go", "core/plugin/plugin_test.go":"load/projects/pandora/core/plugin/plugin_test.go", "core/plugin/pluginconfig/hooks.go":"load/projects/pandora/core/plugin/pluginconfig/hooks.go", "core/plugin/pluginconfig/hooks_test.go":"load/projects/pandora/core/plugin/pluginconfig/hooks_test.go", @@ -209,7 +229,6 @@ "core/provider/json_test.go":"load/projects/pandora/core/provider/json_test.go", "core/provider/num.go":"load/projects/pandora/core/provider/num.go", "core/provider/num_test.go":"load/projects/pandora/core/provider/num_test.go", - "core/provider/provider_suite_test.go":"load/projects/pandora/core/provider/provider_suite_test.go", "core/provider/queue.go":"load/projects/pandora/core/provider/queue.go", "core/register/register.go":"load/projects/pandora/core/register/register.go", "core/schedule/composite.go":"load/projects/pandora/core/schedule/composite.go", @@ -219,7 +238,7 @@ "core/schedule/instance_step.go":"load/projects/pandora/core/schedule/instance_step.go", "core/schedule/line.go":"load/projects/pandora/core/schedule/line.go", "core/schedule/once.go":"load/projects/pandora/core/schedule/once.go", - "core/schedule/schedule_suite_test.go":"load/projects/pandora/core/schedule/schedule_suite_test.go", + "core/schedule/schedule_test.go":"load/projects/pandora/core/schedule/schedule_test.go", "core/schedule/start_sync.go":"load/projects/pandora/core/schedule/start_sync.go", "core/schedule/step.go":"load/projects/pandora/core/schedule/step.go", "core/schedule/unlilmited.go":"load/projects/pandora/core/schedule/unlilmited.go", @@ -232,6 +251,9 @@ "debian/rules":"load/projects/pandora/debian/rules", "debian/source/format":"load/projects/pandora/debian/source/format", "docs/eng/architecture.md":"load/projects/pandora/docs/eng/architecture.md", + "docs/eng/best-practices.md":"load/projects/pandora/docs/eng/best-practices.md", + "docs/eng/best_practices/rps-per-instance.md":"load/projects/pandora/docs/eng/best_practices/rps-per-instance.md", + "docs/eng/best_practices/shared-client.md":"load/projects/pandora/docs/eng/best_practices/shared-client.md", "docs/eng/config.md":"load/projects/pandora/docs/eng/config.md", "docs/eng/custom.md":"load/projects/pandora/docs/eng/custom.md", "docs/eng/grpc-generator.md":"load/projects/pandora/docs/eng/grpc-generator.md", @@ -243,7 +265,8 @@ "docs/eng/scenario-grpc-generator.md":"load/projects/pandora/docs/eng/scenario-grpc-generator.md", "docs/eng/scenario-http-generator.md":"load/projects/pandora/docs/eng/scenario-http-generator.md", "docs/eng/scenario/variable_source.md":"load/projects/pandora/docs/eng/scenario/variable_source.md", - "docs/eng/tuturial.md":"load/projects/pandora/docs/eng/tuturial.md", + "docs/eng/startup.md":"load/projects/pandora/docs/eng/startup.md", + "docs/eng/tutorial.md":"load/projects/pandora/docs/eng/tutorial.md", "docs/images/architecture.graphml":"load/projects/pandora/docs/images/architecture.graphml", "docs/images/architecture.png":"load/projects/pandora/docs/images/architecture.png", "docs/images/http_100kb_net.png":"load/projects/pandora/docs/images/http_100kb_net.png", @@ -255,6 +278,10 @@ "docs/images/scn_cases.png":"load/projects/pandora/docs/images/scn_cases.png", "docs/index.md":"load/projects/pandora/docs/index.md", "docs/rus/architecture.md":"load/projects/pandora/docs/rus/architecture.md", + "docs/rus/best-practices.md":"load/projects/pandora/docs/rus/best-practices.md", + "docs/rus/best_practices.md":"load/projects/pandora/docs/rus/best_practices.md", + "docs/rus/best_practices/rps-per-instance.md":"load/projects/pandora/docs/rus/best_practices/rps-per-instance.md", + "docs/rus/best_practices/shared-client.md":"load/projects/pandora/docs/rus/best_practices/shared-client.md", "docs/rus/config.md":"load/projects/pandora/docs/rus/config.md", "docs/rus/custom.md":"load/projects/pandora/docs/rus/custom.md", "docs/rus/grpc-generator.md":"load/projects/pandora/docs/rus/grpc-generator.md", @@ -267,7 +294,8 @@ "docs/rus/scenario-grpc-generator.md":"load/projects/pandora/docs/rus/scenario-grpc-generator.md", "docs/rus/scenario-http-generator.md":"load/projects/pandora/docs/rus/scenario-http-generator.md", "docs/rus/scenario/variable_source.md":"load/projects/pandora/docs/rus/scenario/variable_source.md", - "docs/rus/tuturial.md":"load/projects/pandora/docs/rus/tuturial.md", + "docs/rus/startup.md":"load/projects/pandora/docs/rus/startup.md", + "docs/rus/tutorial.md":"load/projects/pandora/docs/rus/tutorial.md", "examples/connect.yaml":"load/projects/pandora/examples/connect.yaml", "examples/custom_pandora/custom.yaml":"load/projects/pandora/examples/custom_pandora/custom.yaml", "examples/custom_pandora/custom_main.go":"load/projects/pandora/examples/custom_pandora/custom_main.go", @@ -297,10 +325,7 @@ "lib/confutil/property_var_resolver.go":"load/projects/pandora/lib/confutil/property_var_resolver.go", "lib/confutil/property_var_resolver_test.go":"load/projects/pandora/lib/confutil/property_var_resolver_test.go", "lib/errutil/errutil.go":"load/projects/pandora/lib/errutil/errutil.go", - "lib/errutil/errutil_suite_test.go":"load/projects/pandora/lib/errutil/errutil_suite_test.go", "lib/errutil/errutil_test.go":"load/projects/pandora/lib/errutil/errutil_test.go", - "lib/ginkgoutil/ginkgo.go":"load/projects/pandora/lib/ginkgoutil/ginkgo.go", - "lib/ginkgoutil/matchers.go":"load/projects/pandora/lib/ginkgoutil/matchers.go", "lib/ioutil2/closer.go":"load/projects/pandora/lib/ioutil2/closer.go", "lib/ioutil2/funcs.go":"load/projects/pandora/lib/ioutil2/funcs.go", "lib/ioutil2/io_mocks_test.go":"load/projects/pandora/lib/ioutil2/io_mocks_test.go", @@ -321,7 +346,7 @@ "lib/netutil/mocks/conn.go":"load/projects/pandora/lib/netutil/mocks/conn.go", "lib/netutil/mocks/dialer.go":"load/projects/pandora/lib/netutil/mocks/dialer.go", "lib/netutil/mocks/dns_cache.go":"load/projects/pandora/lib/netutil/mocks/dns_cache.go", - "lib/netutil/netutil_suite_test.go":"load/projects/pandora/lib/netutil/netutil_suite_test.go", + "lib/netutil/netutil_test.go":"load/projects/pandora/lib/netutil/netutil_test.go", "lib/netutil/validator.go":"load/projects/pandora/lib/netutil/validator.go", "lib/pointer/pointer.go":"load/projects/pandora/lib/pointer/pointer.go", "lib/str/format.go":"load/projects/pandora/lib/str/format.go", @@ -338,18 +363,36 @@ "lib/testutil/util.go":"load/projects/pandora/lib/testutil/util.go", "lib/zaputil/stack_extract_core.go":"load/projects/pandora/lib/zaputil/stack_extract_core.go", "lib/zaputil/stack_extract_core_test.go":"load/projects/pandora/lib/zaputil/stack_extract_core_test.go", - "lib/zaputil/zaputil_suite_test.go":"load/projects/pandora/lib/zaputil/zaputil_suite_test.go", "main.go":"load/projects/pandora/main.go", "script/checkfmt.sh":"load/projects/pandora/script/checkfmt.sh", "script/coverage.sh":"load/projects/pandora/script/coverage.sh", + "tests/acceptance/common.go":"load/projects/pandora/tests/acceptance/common.go", + "tests/acceptance/config_model.go":"load/projects/pandora/tests/acceptance/config_model.go", + "tests/acceptance/connect_test.go":"load/projects/pandora/tests/acceptance/connect_test.go", + "tests/acceptance/grpc_test.go":"load/projects/pandora/tests/acceptance/grpc_test.go", + "tests/acceptance/http_scenario_test.go":"load/projects/pandora/tests/acceptance/http_scenario_test.go", "tests/acceptance/http_test.go":"load/projects/pandora/tests/acceptance/http_test.go", + "tests/acceptance/testdata/connect/connect-check-limit.yaml":"load/projects/pandora/tests/acceptance/testdata/connect/connect-check-limit.yaml", + "tests/acceptance/testdata/connect/connect-check-passes.yaml":"load/projects/pandora/tests/acceptance/testdata/connect/connect-check-passes.yaml", + "tests/acceptance/testdata/connect/connect-shared-client.yaml":"load/projects/pandora/tests/acceptance/testdata/connect/connect-shared-client.yaml", + "tests/acceptance/testdata/connect/connect-ssl.yaml":"load/projects/pandora/tests/acceptance/testdata/connect/connect-ssl.yaml", + "tests/acceptance/testdata/connect/connect.yaml":"load/projects/pandora/tests/acceptance/testdata/connect/connect.yaml", + "tests/acceptance/testdata/connect/payload.uri":"load/projects/pandora/tests/acceptance/testdata/connect/payload.uri", + "tests/acceptance/testdata/connect/payload5.uri":"load/projects/pandora/tests/acceptance/testdata/connect/payload5.uri", + "tests/acceptance/testdata/grpc/base.yaml":"load/projects/pandora/tests/acceptance/testdata/grpc/base.yaml", + "tests/acceptance/testdata/grpc/grpc.payload":"load/projects/pandora/tests/acceptance/testdata/grpc/grpc.payload", "tests/acceptance/testdata/http/http-check-limit.yaml":"load/projects/pandora/tests/acceptance/testdata/http/http-check-limit.yaml", "tests/acceptance/testdata/http/http-check-passes.yaml":"load/projects/pandora/tests/acceptance/testdata/http/http-check-passes.yaml", "tests/acceptance/testdata/http/http.yaml":"load/projects/pandora/tests/acceptance/testdata/http/http.yaml", + "tests/acceptance/testdata/http/http2-shared-client.yaml":"load/projects/pandora/tests/acceptance/testdata/http/http2-shared-client.yaml", "tests/acceptance/testdata/http/http2.yaml":"load/projects/pandora/tests/acceptance/testdata/http/http2.yaml", "tests/acceptance/testdata/http/https.yaml":"load/projects/pandora/tests/acceptance/testdata/http/https.yaml", "tests/acceptance/testdata/http/payload.uri":"load/projects/pandora/tests/acceptance/testdata/http/payload.uri", "tests/acceptance/testdata/http/payload5.uri":"load/projects/pandora/tests/acceptance/testdata/http/payload5.uri", + "tests/acceptance/testdata/http_scenario/filter.json":"load/projects/pandora/tests/acceptance/testdata/http_scenario/filter.json", + "tests/acceptance/testdata/http_scenario/http_payload.hcl":"load/projects/pandora/tests/acceptance/testdata/http_scenario/http_payload.hcl", + "tests/acceptance/testdata/http_scenario/scenario.yaml":"load/projects/pandora/tests/acceptance/testdata/http_scenario/scenario.yaml", + "tests/acceptance/testdata/http_scenario/users.csv":"load/projects/pandora/tests/acceptance/testdata/http_scenario/users.csv", "tests/grpc_scenario/main_test.go":"load/projects/pandora/tests/grpc_scenario/main_test.go", "tests/grpc_scenario/testdata/filter.json":"load/projects/pandora/tests/grpc_scenario/testdata/filter.json", "tests/grpc_scenario/testdata/grpc_payload.hcl":"load/projects/pandora/tests/grpc_scenario/testdata/grpc_payload.hcl", diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..f4b279a46 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,96 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and is generated by [Changie](https://github.com/miniscruff/changie). + + +## v0.5.22 - 2024-03-26 +### Added +* `shared-client` for gRPC and HTTP generators +### Changed +* Refactoring HTTP generators. Now they use common components + +## v0.5.21 - 2024-02-02 +### Added +* parameterizable grpc reflection port +* changelog + +## v0.5.20 - 2024-01-29 +### Fixed +* http config: fix limit and passes + +## v0.5.19 - 2024-01-25 +### Added +* grpc scenario generator + +## v0.5.18 - 2024-01-15 +### Fixed +* refactoring http scenario gun +* fix acceptance tests +* fix AnswLog init + +## v0.5.17 - 2024-11-24 +### Fixed +* Internal refactoring: GetMapValue() + +## v0.5.16 - 2023-11-10 +### Changed +* yaml format for config without extension + +## v0.5.15 - 2023-11-09 +### Added +* std json-decoder with array payload +### Fixed +* docs: bit misspells + +## v0.5.14 - 2023-10-26 +### Added +* http provider benchmarks +### Fixed +* http provider missing error +* error messages + +## v0.5.13 - 2023-10-20 +### Fixed +* scenario variable source + +## v0.5.12 - 2023-10-19 +### Fixed +* grpc provider scan +* grpc reflect client depreceted + +## v0.5.11 - 2023-10-12 +### Fixed +* update go.mod + +## v0.5.10 - 2023-10-02 +### Fixed +* grpc generator bugs + +## v0.5.9 - 2023-09-22 +### Added +* scenario generator + +## v0.5.8 - 2023-07-26 +### Added +* Dummy provider + +## v0.5.7 - 2023-07-13 +### Added +* grpc answ.log + +## v0.5.6 - 2023-07-13 +### Added +* preloaded http providers + +## v0.5.5 - 2023-06-14 +### Added +* resolver for property file + +## v0.5.4 - 2023-05-24 +### Added +* HTTP provider middlewares +### Fixed +* Refactoring HTTP providers diff --git a/README.md b/README.md index ccb4a9072..4a3cd77fc 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,30 @@ pandora myconfig.yaml Or use Pandora with [Yandex.Tank](https://yandextank.readthedocs.io/en/latest/core_and_modules.html#pandora) and [Overload](https://overload.yandex.net). -### Documentation +## Changelog + +Install https://github.com/miniscruff/changie + +You can add changie completion to you favorite shell https://changie.dev/cli/changie_completion/ + +### Using + +See https://changie.dev/guide/quick-start/ + +Show current version `changie latest` + +Show next minor version `changie next minor` + +Add new comments - `changie new` - and follow interface + +Create changelog release file - `changie batch v0.5.21` + +Same for next version - `changie batch $(changie next patch)` + +Merge to main CHANGELOG.md file - `changie merge` + +## Documentation [Documentation](https://yandex.github.io/pandora/) -### Old Documentation +## Old Documentation [ReadTheDocs](https://yandexpandora.readthedocs.io/) diff --git a/acceptance_tests/acceptance_suite_test.go b/acceptance_tests/acceptance_suite_test.go deleted file mode 100644 index ac88799c7..000000000 --- a/acceptance_tests/acceptance_suite_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package acceptance - -import ( - "encoding/json" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "testing" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/onsi/gomega/gbytes" - "github.com/onsi/gomega/gexec" - "github.com/yandex/pandora/lib/ginkgoutil" - "github.com/yandex/pandora/lib/tag" - "go.uber.org/zap" - "gopkg.in/yaml.v2" -) - -var pandoraBin string - -func TestAcceptanceTests(t *testing.T) { - ginkgoutil.SetupSuite() - var args []string - if tag.Race { - zap.L().Debug("Building with race detector") - args = append(args, "-race") - } - if tag.Debug { - zap.L().Debug("Building with debug tag") - args = append(args, "-tags", "debug") - } - var err error - pandoraBin, err = gexec.Build("github.com/yandex/pandora", args...) - if err != nil { - t.Fatal(err) - } - defer gexec.CleanupBuildArtifacts() - RunSpecs(t, "AcceptanceTests Suite") -} - -type TestConfig struct { - // Default way to pass config to pandora. - PandoraConfig - // RawConfig overrides Pandora. - RawConfig string - ConfigName string // Without extension. "load" by default. - UseJSON bool // Using YAML by default. - CmdArgs []string // Nothing by default. - Files map[string]string // Extra files to put in dir. Ammo, etc. -} - -func NewTestConfig() *TestConfig { - return &TestConfig{ - PandoraConfig: PandoraConfig{ - Pool: []*InstancePoolConfig{NewInstansePoolConfig()}, - }, - Files: map[string]string{}, - } -} - -type PandoraConfig struct { - Pool []*InstancePoolConfig `yaml:"pools" json:"pools"` - LogConfig `yaml:"log,omitempty" json:"log,omitempty"` - MonitoringConfig `yaml:"monitoring,omitempty" json:"monitoring,omitempty"` -} - -type LogConfig struct { - Level string `yaml:"level,omitempty" json:"level,omitempty"` - File string `yaml:"file,omitempty" json:"file,omitempty"` -} - -type MonitoringConfig struct { - Expvar *expvarConfig `yaml:"Expvar"` - CPUProfile *cpuprofileConfig `yaml:"CPUProfile"` - MemProfile *memprofileConfig `yaml:"MemProfile"` -} - -type expvarConfig struct { - Enabled bool `yaml:"enabled" json:"enabled"` - Port int `yaml:"port" json:"port"` -} - -type cpuprofileConfig struct { - Enabled bool `yaml:"enabled" json:"enabled"` - File string `yaml:"file" json:"file"` -} - -type memprofileConfig struct { - Enabled bool `yaml:"enabled" json:"enabled"` - File string `yaml:"file" json:"file"` -} - -func (pc *PandoraConfig) Append(ipc *InstancePoolConfig) { - pc.Pool = append(pc.Pool, ipc) -} - -func NewInstansePoolConfig() *InstancePoolConfig { - return &InstancePoolConfig{ - Provider: map[string]interface{}{}, - Aggregator: map[string]interface{}{}, - Gun: map[string]interface{}{}, - RPSSchedule: map[string]interface{}{}, - StartupSchedule: map[string]interface{}{}, - } - -} - -type InstancePoolConfig struct { - ID string - Provider map[string]interface{} `yaml:"ammo" json:"ammo"` - Aggregator map[string]interface{} `yaml:"result" json:"result"` - Gun map[string]interface{} `yaml:"gun" json:"gun"` - RPSPerInstance bool `yaml:"rps-per-instance" json:"rps-per-instance"` - RPSSchedule interface{} `yaml:"rps" json:"rps"` - StartupSchedule interface{} `yaml:"startup" json:"startup"` -} - -type PandoraTester struct { - *gexec.Session - // TestDir is working dir of launched pandora. - // It contains config and ammo files, and will be removed after test execution. - // All files created during a test should created in this dir. - TestDir string - Config *TestConfig -} - -func NewTester(conf *TestConfig) *PandoraTester { - testDir, err := ioutil.TempDir("", "pandora_acceptance_") - Expect(err).ToNot(HaveOccurred()) - if conf.ConfigName == "" { - conf.ConfigName = "load" - } - extension := "yaml" - if conf.UseJSON { - extension = "json" - } - var confData []byte - - if conf.RawConfig != "" { - confData = []byte(conf.RawConfig) - } else { - if conf.UseJSON { - confData, err = json.Marshal(conf.PandoraConfig) - } else { - confData, err = yaml.Marshal(conf.PandoraConfig) - } - Expect(err).ToNot(HaveOccurred()) - } - confAbsName := filepath.Join(testDir, conf.ConfigName+"."+extension) - err = ioutil.WriteFile(confAbsName, confData, 0644) - Expect(err).ToNot(HaveOccurred()) - - for file, data := range conf.Files { - fileAbsName := filepath.Join(testDir, file) - err = ioutil.WriteFile(fileAbsName, []byte(data), 0644) - Expect(err).ToNot(HaveOccurred()) - } - - command := exec.Command(pandoraBin, conf.CmdArgs...) - command.Dir = testDir - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).ToNot(HaveOccurred()) - - tt := &PandoraTester{ - Session: session, - TestDir: testDir, - Config: conf, - } - return tt -} - -func (pt *PandoraTester) ShouldSay(pattern string) { - EventuallyWithOffset(1, pt.Out, 3*time.Second).Should(gbytes.Say(pattern)) -} - -func (pt *PandoraTester) ExitCode() int { - return pt.Session.Wait(5).ExitCode() -} - -func (pt *PandoraTester) Close() { - pt.Terminate() - _ = os.RemoveAll(pt.TestDir) -} diff --git a/acceptance_tests/http_test.go b/acceptance_tests/http_test.go deleted file mode 100644 index fea9dc757..000000000 --- a/acceptance_tests/http_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package acceptance - -import ( - "net/http" - "net/http/httptest" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "go.uber.org/atomic" - "golang.org/x/net/http2" -) - -var _ = Describe("http", func() { - var ( - server *httptest.Server - conf *TestConfig - tester *PandoraTester - ) - ServerEndpoint := func() string { - Expect(server).NotTo(BeNil()) - return server.Listener.Addr().String() - } - BeforeEach(func() { - conf = NewTestConfig() - tester = nil - }) - JustBeforeEach(func() { - if server != nil { - Expect(server.URL).NotTo(BeEmpty(), "Please start server manually") - } - tester = NewTester(conf) - }) - AfterEach(func() { - tester.Close() - if server != nil { - server.Close() - server = nil - } - }) - - Context("uri ammo", func() { - var ( - requetsCount atomic.Int64 // Request served by test server. - gunConfig map[string]interface{} // gunConfig config section. - ) - const ( - Requests = 4 - Instances = 2 - OutFile = "out.log" - ) - BeforeEach(func() { - requetsCount.Store(0) - server = httptest.NewUnstartedServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - requetsCount.Inc() - rw.WriteHeader(http.StatusOK) - })) - - conf.Pool[0].Gun = map[string]interface{}{ - // Set type in test. - "target": ServerEndpoint(), - } - const ammoFile = "ammo.uri" - conf.Pool[0].Provider = map[string]interface{}{ - "type": "uri", - "file": ammoFile, - } - conf.Files[ammoFile] = "/" - conf.Pool[0].Aggregator = map[string]interface{}{ - "type": "phout", - "destination": OutFile, - } - conf.Pool[0].RPSSchedule = []map[string]interface{}{ - {"type": "once", "times": Requests / 2}, - {"type": "const", "ops": Requests, "duration": "0.5s"}, - } - conf.Pool[0].StartupSchedule = []map[string]interface{}{ - {"type": "once", "times": Instances}, - } - gunConfig = conf.Pool[0].Gun - conf.Level = "debug" - }) - itOk := func() { - It("ok", func() { - exitCode := tester.ExitCode() - Expect(exitCode).To(BeZero(), "Pandora finish execution with non zero code") - Expect(requetsCount.Load()).To(BeEquivalentTo(Requests)) - // TODO(skipor): parse and check phout output - }) - } - - Context("http", func() { - BeforeEach(func() { - server.Start() - gunConfig["type"] = "http" - }) - itOk() - }) - - Context("https", func() { - BeforeEach(func() { - server.StartTLS() - gunConfig["type"] = "http" - gunConfig["ssl"] = true - }) - itOk() - }) - - Context("http2", func() { - Context("target support HTTP/2", func() { - BeforeEach(func() { - startHTTP2(server) - gunConfig["type"] = "http2" - }) - itOk() - }) - Context("target DOESN'T support HTTP/2", func() { - BeforeEach(func() { - server.StartTLS() - gunConfig["type"] = "http2" - }) - It("ok", func() { - exitCode := tester.ExitCode() - Expect(exitCode).NotTo(BeZero(), "Pandora should fail") - }) - - }) - }) - - }) -}) - -func startHTTP2(server *httptest.Server) { - _ = http2.ConfigureServer(server.Config, nil) - server.TLS = server.Config.TLSConfig - server.StartTLS() -} diff --git a/cli/cli.go b/cli/cli.go index ac8cd1670..196f485ab 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -25,7 +25,7 @@ import ( "go.uber.org/zap/zapcore" ) -const Version = "0.5.20" +const Version = "0.5.22" const defaultConfigFile = "load" const stdinConfigSelector = "-" diff --git a/components/grpc/import/import.go b/components/grpc/import/import.go index c84cd3b9a..a587b9011 100644 --- a/components/grpc/import/import.go +++ b/components/grpc/import/import.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package example import ( diff --git a/components/guns/grpc/core.go b/components/guns/grpc/core.go index b773e3a08..251f8958b 100644 --- a/components/guns/grpc/core.go +++ b/components/guns/grpc/core.go @@ -4,7 +4,10 @@ import ( "context" "crypto/tls" "encoding/json" + "errors" "fmt" + "strconv" + "strings" "time" "github.com/golang/protobuf/proto" @@ -15,6 +18,7 @@ import ( ammo "github.com/yandex/pandora/components/providers/grpc" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/clientpool" "github.com/yandex/pandora/core/warmup" "github.com/yandex/pandora/lib/answlog" "go.uber.org/zap" @@ -39,11 +43,16 @@ type GrpcDialOptions struct { } type GunConfig struct { - Target string `validate:"required"` - Timeout time.Duration `config:"timeout"` // grpc request timeout - TLS bool `config:"tls"` - DialOptions GrpcDialOptions `config:"dial_options"` - AnswLog AnswLogConfig `config:"answlog"` + Target string `validate:"required"` + ReflectPort int64 `config:"reflect_port"` + Timeout time.Duration `config:"timeout"` // grpc request timeout + TLS bool `config:"tls"` + DialOptions GrpcDialOptions `config:"dial_options"` + AnswLog AnswLogConfig `config:"answlog"` + SharedClient struct { + ClientNumber int `config:"client-number,omitempty"` + Enabled bool `config:"enabled"` + } `config:"shared-client,omitempty"` } type AnswLogConfig struct { @@ -54,7 +63,6 @@ type AnswLogConfig struct { type Gun struct { DebugLog bool - Client *grpc.ClientConn Conf GunConfig Aggr core.Aggregator core.GunDeps @@ -76,8 +84,27 @@ func DefaultGunConfig() GunConfig { } } -func (g *Gun) WarmUp(opts *warmup.Options) (interface{}, error) { - conn, err := MakeGRPCConnect(g.Conf.Target, g.Conf.TLS, g.Conf.DialOptions) +func (g *Gun) WarmUp(opts *warmup.Options) (any, error) { + return g.createSharedDeps(opts) +} + +func (g *Gun) createSharedDeps(opts *warmup.Options) (*SharedDeps, error) { + services, err := g.prepareMethodList(opts) + if err != nil { + return nil, err + } + clientPool, err := g.prepareClientPool() + if err != nil { + return nil, err + } + return &SharedDeps{ + services: services, + clientPool: clientPool, + }, nil +} + +func (g *Gun) prepareMethodList(opts *warmup.Options) (map[string]desc.MethodDescriptor, error) { + conn, err := g.makeReflectionConnect() if err != nil { return nil, fmt.Errorf("failed to connect to target: %w", err) } @@ -109,13 +136,25 @@ func (g *Gun) WarmUp(opts *warmup.Options) (interface{}, error) { return services, nil } -func (g *Gun) AcceptWarmUpResult(i interface{}) error { - services, ok := i.(map[string]desc.MethodDescriptor) - if !ok { - return fmt.Errorf("grpc WarmUp result should be services: map[string]desc.MethodDescriptor") +func (g *Gun) prepareClientPool() (*clientpool.Pool[grpcdynamic.Stub], error) { + if !g.Conf.SharedClient.Enabled { + return nil, nil } - g.Services = services - return nil + if g.Conf.SharedClient.ClientNumber < 1 { + g.Conf.SharedClient.ClientNumber = 1 + } + clientPool, err := clientpool.New[grpcdynamic.Stub](g.Conf.SharedClient.ClientNumber) + if err != nil { + return nil, fmt.Errorf("create clientpool err: %w", err) + } + for i := 0; i < g.Conf.SharedClient.ClientNumber; i++ { + conn, err := g.makeConnect() + if err != nil { + return nil, fmt.Errorf("makeGRPCConnect fail %w", err) + } + clientPool.Add(grpcdynamic.NewStub(conn)) + } + return clientPool, nil } func NewGun(conf GunConfig) *Gun { @@ -124,14 +163,23 @@ func NewGun(conf GunConfig) *Gun { } func (g *Gun) Bind(aggr core.Aggregator, deps core.GunDeps) error { - conn, err := MakeGRPCConnect(g.Conf.Target, g.Conf.TLS, g.Conf.DialOptions) - if err != nil { - return fmt.Errorf("makeGRPCConnect fail %w", err) + sharedDeps, ok := deps.Shared.(*SharedDeps) + if !ok { + return errors.New("grpc WarmUp result should be struct: *SharedDeps") + } + g.Services = sharedDeps.services + if sharedDeps.clientPool != nil { + g.Stub = sharedDeps.clientPool.Next() + } else { + conn, err := g.makeConnect() + if err != nil { + return fmt.Errorf("makeGRPCConnect fail %w", err) + } + g.Stub = grpcdynamic.NewStub(conn) } - g.Client = conn + g.Aggr = aggr g.GunDeps = deps - g.Stub = grpcdynamic.NewStub(conn) if ent := deps.Log.Check(zap.DebugLevel, "Gun bind"); ent != nil { deps.Log.Warn("Deprecation Warning: log level: debug doesn't produce request/response logs anymore. Please use AnswLog option instead:\nanswlog:\n enabled: true\n filter: all|warning|error\n path: answ.log") @@ -213,6 +261,15 @@ func (g *Gun) AnswLogging(logger *zap.Logger, method *desc.MethodDescriptor, req logger.Debug("Response:", zap.Stringer("resp", response), zap.Error(grpcErr)) } +func (g *Gun) makeConnect() (conn *grpc.ClientConn, err error) { + return MakeGRPCConnect(g.Conf.Target, g.Conf.TLS, g.Conf.DialOptions) +} + +func (g *Gun) makeReflectionConnect() (conn *grpc.ClientConn, err error) { + target := replacePort(g.Conf.Target, g.Conf.ReflectPort) + return MakeGRPCConnect(target, g.Conf.TLS, g.Conf.DialOptions) +} + func MakeGRPCConnect(target string, isTLS bool, dialOptions GrpcDialOptions) (conn *grpc.ClientConn, err error) { opts := []grpc.DialOption{} if isTLS { @@ -272,4 +329,22 @@ func ConvertGrpcStatus(err error) int { } } +func replacePort(host string, port int64) string { + if port == 0 { + return host + } + split := strings.Split(host, ":") + if len(split) == 1 { + return host + ":" + strconv.FormatInt(port, 10) + } + + oldPort := split[len(split)-1] + if _, err := strconv.ParseInt(oldPort, 10, 64); err != nil { + return host + ":" + strconv.FormatInt(port, 10) + } + + split[len(split)-1] = strconv.FormatInt(port, 10) + return strings.Join(split, ":") +} + var _ warmup.WarmedUp = (*Gun)(nil) diff --git a/components/guns/grpc/core_test.go b/components/guns/grpc/core_test.go new file mode 100644 index 000000000..ce1c2e7d5 --- /dev/null +++ b/components/guns/grpc/core_test.go @@ -0,0 +1,59 @@ +package grpc + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_replacePort(t *testing.T) { + tests := []struct { + name string + host string + port int64 + want string + }{ + { + name: "zero port", + host: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]:8888", + port: 0, + want: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]:8888", + }, + { + name: "replace ipv6", + host: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]:8888", + port: 9999, + want: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]:9999", + }, + { + name: "add port to ipv6", + host: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]", + port: 9999, + want: "[2a02:6b8:c02:901:0:fc5f:9a6c:4]:9999", + }, + { + name: "replace ipv4", + host: "127.0.0.1:8888", + port: 9999, + want: "127.0.0.1:9999", + }, + { + name: "replace host", + host: "localhost:8888", + port: 9999, + want: "localhost:9999", + }, + { + name: "add port", + host: "localhost", + port: 9999, + want: "localhost:9999", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := replacePort(tt.host, tt.port) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/components/guns/grpc/scenario/ammo.go b/components/guns/grpc/scenario/ammo.go index fe6787219..a259cc471 100644 --- a/components/guns/grpc/scenario/ammo.go +++ b/components/guns/grpc/scenario/ammo.go @@ -4,6 +4,7 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/yandex/pandora/components/providers/scenario" ) type SourceStorage interface { @@ -22,6 +23,15 @@ func (a *Scenario) SetID(id uint64) { a.id = id } +func (a *Scenario) Clone() scenario.ProvAmmo { + return &Scenario{ + Calls: a.Calls, + Name: a.Name, + MinWaitingTime: a.MinWaitingTime, + VariableStorage: a.VariableStorage, + } +} + type Call struct { Name string Preprocessors []Preprocessor diff --git a/components/guns/grpc/scenario/core.go b/components/guns/grpc/scenario/core.go index db86e1242..68e078ac0 100644 --- a/components/guns/grpc/scenario/core.go +++ b/components/guns/grpc/scenario/core.go @@ -22,6 +22,7 @@ const defaultTimeout = time.Second * 15 type GunConfig struct { Target string `validate:"required"` + ReflectPort int64 `config:"reflect_port"` Timeout time.Duration `config:"timeout"` // grpc request timeout TLS bool `config:"tls"` DialOptions GrpcDialOptions `config:"dial_options"` @@ -56,9 +57,10 @@ func NewGun(conf GunConfig) *Gun { return &Gun{ templ: NewTextTemplater(), gun: &grpcgun.Gun{Conf: grpcgun.GunConfig{ - Target: conf.Target, - Timeout: conf.Timeout, - TLS: conf.TLS, + Target: conf.Target, + ReflectPort: conf.ReflectPort, + Timeout: conf.Timeout, + TLS: conf.TLS, DialOptions: grpcgun.GrpcDialOptions{ Authority: conf.DialOptions.Authority, Timeout: conf.DialOptions.Timeout, @@ -84,10 +86,6 @@ func (g *Gun) WarmUp(opts *warmup.Options) (interface{}, error) { return g.gun.WarmUp(opts) } -func (g *Gun) AcceptWarmUpResult(i interface{}) error { - return g.gun.AcceptWarmUpResult(i) -} - func (g *Gun) Bind(aggr core.Aggregator, deps core.GunDeps) error { return g.gun.Bind(aggr, deps) } diff --git a/components/guns/grpc/shared_deps.go b/components/guns/grpc/shared_deps.go new file mode 100644 index 000000000..441b8b6ee --- /dev/null +++ b/components/guns/grpc/shared_deps.go @@ -0,0 +1,12 @@ +package grpc + +import ( + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic/grpcdynamic" + "github.com/yandex/pandora/core/clientpool" +) + +type SharedDeps struct { + services map[string]desc.MethodDescriptor + clientPool *clientpool.Pool[grpcdynamic.Stub] +} diff --git a/components/guns/http/base.go b/components/guns/http/base.go index 50cd433de..62f4651b4 100644 --- a/components/guns/http/base.go +++ b/components/guns/http/base.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package phttp import ( @@ -11,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httptrace" "net/http/httputil" @@ -19,6 +15,9 @@ import ( "github.com/pkg/errors" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/clientpool" + "github.com/yandex/pandora/core/warmup" + "github.com/yandex/pandora/lib/netutil" "go.uber.org/zap" ) @@ -27,9 +26,13 @@ const ( ) type BaseGunConfig struct { - AutoTag AutoTagConfig `config:"auto-tag"` - AnswLog AnswLogConfig `config:"answlog"` - HTTPTrace HTTPTraceConfig `config:"httptrace"` + AutoTag AutoTagConfig `config:"auto-tag"` + AnswLog AnswLogConfig `config:"answlog"` + HTTPTrace HTTPTraceConfig `config:"httptrace"` + SharedClient struct { + ClientNumber int `config:"client-number,omitempty"` + Enabled bool `config:"enabled"` + } `config:"shared-client,omitempty"` } // AutoTagConfig configure automatic tags generation based on ammo URI. First AutoTag URI path elements becomes tag. @@ -53,43 +56,98 @@ type HTTPTraceConfig struct { func DefaultBaseGunConfig() BaseGunConfig { return BaseGunConfig{ - AutoTagConfig{ + AutoTag: AutoTagConfig{ Enabled: false, URIElements: 2, NoTagOnly: true, }, - AnswLogConfig{ + AnswLog: AnswLogConfig{ Enabled: false, Path: "answ.log", Filter: "error", }, - HTTPTraceConfig{ + HTTPTrace: HTTPTraceConfig{ DumpEnabled: false, TraceEnabled: false, }, } } +func NewBaseGun(clientConstructor ClientConstructor, cfg HTTPGunConfig, answLog *zap.Logger) *BaseGun { + client := clientConstructor(cfg.Client, cfg.Target) + return &BaseGun{ + Config: cfg.Base, + OnClose: func() error { + client.CloseIdleConnections() + return nil + }, + AnswLog: answLog, + Client: client, + ClientConstructor: func() Client { + return clientConstructor(cfg.Client, cfg.Target) + }, + } +} + type BaseGun struct { - DebugLog bool // Automaticaly set in Bind if Log accepts debug messages. - Config BaseGunConfig - Do func(r *http.Request) (*http.Response, error) // Required. - Connect func(ctx context.Context) error // Optional hook. - OnClose func() error // Optional. Called on Close(). - Aggregator netsample.Aggregator // Lazy set via BindResultTo. - AnswLog *zap.Logger + DebugLog bool // Automaticaly set in Bind if Log accepts debug messages. + Config BaseGunConfig + Connect func(ctx context.Context) error // Optional hook. + OnClose func() error // Optional. Called on Close(). + Aggregator netsample.Aggregator // Lazy set via BindResultTo. + AnswLog *zap.Logger + Client Client + ClientConstructor func() Client + core.GunDeps } var _ Gun = (*BaseGun)(nil) var _ io.Closer = (*BaseGun)(nil) +type SharedDeps struct { + clientPool *clientpool.Pool[Client] +} + +func (b *BaseGun) WarmUp(opts *warmup.Options) (any, error) { + return b.createSharedDeps(opts) +} + +func (b *BaseGun) createSharedDeps(opts *warmup.Options) (*SharedDeps, error) { + clientPool, err := b.prepareClientPool() + if err != nil { + return nil, err + } + return &SharedDeps{ + clientPool: clientPool, + }, nil +} + +func (b *BaseGun) prepareClientPool() (*clientpool.Pool[Client], error) { + if !b.Config.SharedClient.Enabled { + return nil, nil + } + if b.Config.SharedClient.ClientNumber < 1 { + b.Config.SharedClient.ClientNumber = 1 + } + clientPool, _ := clientpool.New[Client](b.Config.SharedClient.ClientNumber) + for i := 0; i < b.Config.SharedClient.ClientNumber; i++ { + client := b.ClientConstructor() + clientPool.Add(client) + } + return clientPool, nil +} + func (b *BaseGun) Bind(aggregator netsample.Aggregator, deps core.GunDeps) error { log := deps.Log if ent := log.Check(zap.DebugLevel, "Gun bind"); ent != nil { // Enable debug level logging during shooting. Creating log entries isn't free. b.DebugLog = true } + extraDeps, ok := deps.Shared.(*SharedDeps) + if ok && extraDeps.clientPool != nil { + b.Client = extraDeps.clientPool.Next() + } if b.Aggregator != nil { log.Panic("already binded") @@ -162,7 +220,7 @@ func (b *BaseGun) Shoot(ammo Ammo) { } } var res *http.Response - res, err = b.Do(req) + res, err = b.Client.Do(req) if b.Config.HTTPTrace.TraceEnabled && timings != nil { sample.SetReceiveTime(timings.GetReceiveTime()) } @@ -299,3 +357,34 @@ func GetBody(req *http.Request) []byte { return nil } + +// DNS resolve optimisation. +// When DNSCache turned off - do nothing extra, host will be resolved on every shoot. +// When using resolved target, don't use DNS caching logic - it is useless. +// If we can resolve accessible target addr - use it as target, not use caching. +// Otherwise just use DNS cache - we should not fail shooting, we should try to +// connect on every shoot. DNS cache will save resolved addr after first successful connect. +func PreResolveTargetAddr(clientConf *ClientConfig, target string) (string, error) { + if !clientConf.Dialer.DNSCache { + return target, nil + } + if endpointIsResolved(target) { + clientConf.Dialer.DNSCache = false + return target, nil + } + resolved, err := netutil.LookupReachable(target, clientConf.Dialer.Timeout) + if err != nil { + zap.L().Warn("DNS target pre resolve failed", zap.String("target", target), zap.Error(err)) + return target, err + } + clientConf.Dialer.DNSCache = false + return resolved, nil +} + +func endpointIsResolved(endpoint string) bool { + host, _, err := net.SplitHostPort(endpoint) + if err != nil { + return false + } + return net.ParseIP(host) != nil +} diff --git a/components/guns/http/base_test.go b/components/guns/http/base_test.go index 3d5deb136..5608f78e3 100644 --- a/components/guns/http/base_test.go +++ b/components/guns/http/base_test.go @@ -1,7 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor package phttp import ( @@ -13,202 +9,341 @@ import ( "net/http/httptest" "net/url" "strings" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" ammomock "github.com/yandex/pandora/components/guns/http/mocks" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" "github.com/yandex/pandora/core/coretest" - "github.com/yandex/pandora/lib/ginkgoutil" + "github.com/yandex/pandora/core/engine" + "github.com/yandex/pandora/lib/monitoring" + "github.com/yandex/pandora/lib/testutil" "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) -func testDeps() core.GunDeps { - return core.GunDeps{ - Log: ginkgoutil.NewLogger(), - Ctx: context.Background(), +func newLogger() *zap.Logger { + zapConf := zap.NewDevelopmentConfig() + zapConf.Level.SetLevel(zapcore.DebugLevel) + log, err := zapConf.Build(zap.AddCaller()) + if err != nil { + zap.L().Fatal("Logger build failed", zap.Error(err)) } + return log } -var _ = Describe("BaseGun", func() { +func newEngineMetrics(prefix string) engine.Metrics { + return engine.Metrics{ + Request: monitoring.NewCounter(prefix + "_Requests"), + Response: monitoring.NewCounter(prefix + "_Responses"), + InstanceStart: monitoring.NewCounter(prefix + "_UsersStarted"), + InstanceFinish: monitoring.NewCounter(prefix + "_UsersFinished"), + } +} - var ( - base BaseGun - ) - BeforeEach(func() { - base = BaseGun{Config: DefaultBaseGunConfig()} - }) +func TestGunSuite(t *testing.T) { + suite.Run(t, new(BaseGunSuite)) +} + +type BaseGunSuite struct { + suite.Suite + fs afero.Fs + log *zap.Logger + metrics engine.Metrics + base BaseGun + gunDeps core.GunDeps +} + +func (s *BaseGunSuite) SetupSuite() { + s.log = testutil.NewLogger() + s.metrics = newEngineMetrics("http_suite") +} - Context("BindResultTo", func() { - It("nil panics", func() { - Expect(func() { - _ = base.Bind(nil, testDeps()) - }).To(Panic()) +func (s *BaseGunSuite) SetupTest() { + s.base = BaseGun{Config: DefaultBaseGunConfig()} +} + +func (s *BaseGunSuite) Test_BindResultTo_Panics() { + s.Run("nil panic", func() { + s.Panics(func() { + _ = s.base.Bind(nil, testDeps()) }) - It("second time panics", func() { - res := &netsample.TestAggregator{} - _ = base.Bind(res, testDeps()) - Expect(base.Aggregator).To(Equal(res)) - Expect(func() { - _ = base.Bind(&netsample.TestAggregator{}, testDeps()) - }).To(Panic()) + }) + s.Run("nil panic", func() { + res := &netsample.TestAggregator{} + _ = s.base.Bind(res, testDeps()) + s.Require().Equal(res, s.base.Aggregator) + s.Panics(func() { + _ = s.base.Bind(&netsample.TestAggregator{}, testDeps()) }) }) +} + +type ammoMock struct { + requestCallCnt int + idCallCnt int + isInvalidCallCnt int +} + +func (a *ammoMock) Request() (*http.Request, *netsample.Sample) { + a.requestCallCnt++ + return nil, nil +} + +func (a *ammoMock) ID() uint64 { + a.idCallCnt++ + return 0 +} - It("Shoot before bind panics", func() { - base.Do = func(*http.Request) (_ *http.Response, _ error) { - Fail("should not be called") - return +func (a *ammoMock) IsInvalid() bool { + a.isInvalidCallCnt++ + return false +} + +type testDecoratedClient struct { + client Client + t *testing.T + before func(req *http.Request) + after func(req *http.Request, res *http.Response, err error) + returnRes *http.Response + returnErr error +} + +func (c *testDecoratedClient) Do(req *http.Request) (*http.Response, error) { + if c.before != nil { + c.before(req) + } + if c.client == nil { + return c.returnRes, c.returnErr + } + res, err := c.client.Do(req) + if c.after != nil { + c.after(req, res, err) + } + return res, err +} + +func (c *testDecoratedClient) CloseIdleConnections() { + c.client.CloseIdleConnections() +} + +func (s *BaseGunSuite) Test_Shoot_BeforeBindPanics() { + s.base.Client = &testDecoratedClient{ + client: s.base.Client, + before: func(req *http.Request) { panic("should not be called\"") }, + after: nil, + } + am := &ammoMock{} + + s.Panics(func() { + s.base.Shoot(am) + }) +} + +func (s *BaseGunSuite) Test_Shoot() { + var ( + body io.ReadCloser + + am *ammomock.Ammo + req *http.Request + tag string + res *http.Response + sample *netsample.Sample + results *netsample.TestAggregator + shootErr error + ) + beforeEach := func() { + am = ammomock.NewAmmo(s.T()) + am.On("IsInvalid").Return(false).Maybe() + req = httptest.NewRequest("GET", "/1/2/3/4", nil) + tag = "" + results = &netsample.TestAggregator{} + _ = s.base.Bind(results, testDeps()) + } + + justBeforeEach := func() { + sample = netsample.Acquire(tag) + am.On("Request").Return(req, sample).Maybe() + res = &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(body), + Request: req, } - am := ammomock.NewAmmo(GinkgoT()) - am.On("Request").Return(nil, nil).Run( - func(mock.Arguments) { - Fail("should not be called") - }) - am.On("IsInvalid").Return(false) - Expect(func() { - base.Shoot(am) - }).To(Panic()) - }, 1) - - Context("Shoot", func() { - var ( - body io.ReadCloser - - am *ammomock.Ammo - req *http.Request - tag string - res *http.Response - sample *netsample.Sample - results *netsample.TestAggregator - shootErr error - ) - BeforeEach(func() { - am = ammomock.NewAmmo(GinkgoT()) - am.On("IsInvalid").Return(false) - req = httptest.NewRequest("GET", "/1/2/3/4", nil) - tag = "" - results = &netsample.TestAggregator{} - _ = base.Bind(results, testDeps()) - }) + s.base.Shoot(am) + s.Require().Len(results.Samples, 1) + shootErr = results.Samples[0].Err() + } - JustBeforeEach(func() { - sample = netsample.Acquire(tag) - am.On("Request").Return(req, sample) - res = &http.Response{ - StatusCode: http.StatusNotFound, - Body: ioutil.NopCloser(body), - Request: req, + s.Run("Do ok", func() { + beforeEachDoOk := func() { + body = ioutil.NopCloser(strings.NewReader("aaaaaaa")) + s.base.AnswLog = zap.NewNop() + s.base.Client = &testDecoratedClient{ + before: func(doReq *http.Request) { + s.Require().Equal(req, doReq) + }, + returnRes: &http.Response{ + StatusCode: http.StatusNotFound, + Body: ioutil.NopCloser(body), + Request: req, + }, } - base.Shoot(am) - Expect(results.Samples).To(HaveLen(1)) - shootErr = results.Samples[0].Err() + } + s.Run("ammo sample sent to results", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + justBeforeEach() + + s.Assert().Len(results.Samples, 1) + s.Assert().Equal(sample, results.Samples[0]) + s.Assert().Equal("__EMPTY__", sample.Tags()) + s.Assert().Equal(res.StatusCode, sample.ProtoCode()) + _ = shootErr }) - Context("Do ok", func() { - BeforeEach(func() { - body = ioutil.NopCloser(strings.NewReader("aaaaaaa")) - base.AnswLog = zap.NewNop() - base.Do = func(doReq *http.Request) (*http.Response, error) { - Expect(doReq).To(Equal(req)) - return res, nil - } - }) + s.Run("body read well", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + justBeforeEach() - It("ammo sample sent to results", func() { - Expect(results.Samples).To(HaveLen(1)) - Expect(results.Samples[0]).To(Equal(sample)) - Expect(sample.Tags()).To(Equal("__EMPTY__")) - Expect(sample.ProtoCode()).To(Equal(res.StatusCode)) - }) - It("body read well", func() { - Expect(shootErr).To(BeNil()) - _, err := body.Read([]byte{0}) - Expect(err).To(Equal(io.EOF), "body should be read fully") + s.Assert().NoError(shootErr) + _, err := body.Read([]byte{0}) + s.Assert().ErrorIs(err, io.EOF, "body should be read fully") + }) + + s.Run("autotag options is set", func() { + beforeEacAautotag := (func() { s.base.Config.AutoTag.Enabled = true }) + + s.Run("autotagged", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + beforeEacAautotag() + justBeforeEach() + + s.Assert().Equal("/1/2", sample.Tags()) }) - Context("autotag options is set", func() { - BeforeEach(func() { base.Config.AutoTag.Enabled = true }) - It("autotagged", func() { - Expect(sample.Tags()).To(Equal("/1/2")) + s.Run("tag is already set", func() { + const presetTag = "TAG" + beforeEachTagIsAlreadySet := func() { tag = presetTag } + s.Run("no tag added", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + beforeEacAautotag() + beforeEachTagIsAlreadySet() + justBeforeEach() + + s.Assert().Equal(presetTag, sample.Tags()) }) - Context("tag is already set", func() { - const presetTag = "TAG" - BeforeEach(func() { tag = presetTag }) - It("no tag added", func() { - Expect(sample.Tags()).To(Equal(presetTag)) - }) + s.Run("no-tag-only set to false", func() { + beforeEachNoTagOnly := func() { s.base.Config.AutoTag.NoTagOnly = false } + s.Run("autotag added", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + beforeEacAautotag() + beforeEachTagIsAlreadySet() + beforeEachNoTagOnly() + justBeforeEach() - Context("no-tag-only set to false", func() { - BeforeEach(func() { base.Config.AutoTag.NoTagOnly = false }) - It("autotag added", func() { - Expect(sample.Tags()).To(Equal(presetTag + "|/1/2")) - }) + s.Assert().Equal(presetTag+"|/1/2", sample.Tags()) }) }) }) + }) + + s.Run("Connect set", func() { + var connectCalled, doCalled bool + beforeEachConnectSet := func() { + s.base.Connect = func(ctx context.Context) error { + connectCalled = true + return nil + } - Context("Connect set", func() { - var connectCalled, doCalled bool - BeforeEach(func() { - base.Connect = func(ctx context.Context) error { - connectCalled = true - return nil - } - oldDo := base.Do - base.Do = func(r *http.Request) (*http.Response, error) { + s.base.Client = &testDecoratedClient{ + client: s.base.Client, + before: func(doReq *http.Request) { doCalled = true - return oldDo(r) - } - }) - It("Connect called", func() { - Expect(shootErr).To(BeNil()) - Expect(connectCalled).To(BeTrue()) - Expect(doCalled).To(BeTrue()) - }) + }, + } + } + s.Run("Connect called", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + beforeEachConnectSet() + justBeforeEach() + + s.Assert().NoError(shootErr) + s.Assert().True(connectCalled) + s.Assert().True(doCalled) }) - Context("Connect failed", func() { - connectErr := errors.New("connect error") - BeforeEach(func() { - base.Connect = func(ctx context.Context) error { - // Connect should report fail in sample itself. - s := netsample.Acquire("") - s.SetErr(connectErr) - results.Report(s) - return connectErr - } - }) - It("Shoot failed", func() { - Expect(shootErr).NotTo(BeNil()) - Expect(shootErr).To(Equal(connectErr)) - }) + }) + s.Run("Connect failed", func() { + connectErr := errors.New("connect error") + beforeEachConnectFailed := func() { + s.base.Connect = func(ctx context.Context) error { + // Connect should report fail in sample itself. + s := netsample.Acquire("") + s.SetErr(connectErr) + results.Report(s) + return connectErr + } + } + s.Run("Shoot failed", func() { + s.SetupTest() + beforeEach() + beforeEachDoOk() + beforeEachConnectFailed() + justBeforeEach() + + s.Assert().Error(shootErr) + s.Assert().ErrorIs(shootErr, connectErr) }) }) }) +} - DescribeTable("autotag", - func(path string, depth int, tag string) { - URL := &url.URL{Path: path} - Expect(autotag(depth, URL)).To(Equal(tag)) - }, - Entry("empty", "", 2, ""), - Entry("root", "/", 2, "/"), - Entry("exact depth", "/1/2", 2, "/1/2"), - Entry("more depth", "/1/2", 3, "/1/2"), - Entry("less depth", "/1/2", 1, "/1"), - ) +func Test_Autotag(t *testing.T) { + tests := []struct { + name string + path string + depth int + tag string + }{ + {"empty", "", 2, ""}, + {"root", "/", 2, "/"}, + {"exact depth", "/1/2", 2, "/1/2"}, + {"more depth", "/1/2", 3, "/1/2"}, + {"less depth", "/1/2", 1, "/1"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + URL := &url.URL{Path: tt.path} + got := autotag(tt.depth, URL) + assert.Equal(t, got, tt.tag) + }) + } +} - It("config decode", func() { - var conf BaseGunConfig - coretest.DecodeAndValidate(` +func Test_ConfigDecode(t *testing.T) { + var conf BaseGunConfig + coretest.DecodeAndValidateT(t, ` auto-tag: enabled: true uri-elements: 3 no-tag-only: false `, &conf) - }) -}) +} + +func testDeps() core.GunDeps { + return core.GunDeps{Log: testutil.NewLogger(), Ctx: context.Background()} +} diff --git a/components/guns/http/client.go b/components/guns/http/client.go index e42787fd2..64f5fab0b 100644 --- a/components/guns/http/client.go +++ b/components/guns/http/client.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package phttp import ( @@ -26,11 +21,14 @@ type Client interface { } type ClientConfig struct { - Redirect bool // When true, follow HTTP redirects. - Dialer DialerConfig `config:"dial"` - Transport TransportConfig `config:",squash"` + Redirect bool // When true, follow HTTP redirects. + Dialer DialerConfig `config:"dial"` + Transport TransportConfig `config:",squash"` + ConnectSSL bool `config:"connect-ssl"` // Defines if tunnel encrypted. } +type ClientConstructor func(clientConfig ClientConfig, target string) Client + func DefaultClientConfig() ClientConfig { return ClientConfig{ Transport: DefaultTransportConfig(), @@ -132,7 +130,7 @@ func NewHTTP2Transport(conf TransportConfig, dial netutil.DialerFunc, target str return tr } -func newClient(tr *http.Transport, redirect bool) Client { +func NewRedirectingClient(tr *http.Transport, redirect bool) Client { if redirect { return redirectClient{&http.Client{Transport: tr}} } @@ -175,6 +173,43 @@ func (c *panicOnHTTP1Client) Do(req *http.Request) (*http.Response, error) { return res, nil } +func WrapClientHostResolving(client Client, cfg HTTPGunConfig, targetResolved string) Client { + hostname := getHostWithoutPort(cfg.Target) + scheme := "http" + if cfg.SSL { + scheme = "https" + } + return &httpDecoratedClient{ + client: client, + scheme: scheme, + hostname: hostname, + targetResolved: targetResolved, + } +} + +type httpDecoratedClient struct { + client Client + scheme string + hostname string + targetResolved string +} + +func (c *httpDecoratedClient) Do(req *http.Request) (*http.Response, error) { + if req.Host == "" { + req.Host = c.hostname + } + + if c.targetResolved != "" { + req.URL.Host = c.targetResolved + } + req.URL.Scheme = c.scheme + return c.client.Do(req) +} + +func (c *httpDecoratedClient) CloseIdleConnections() { + c.client.CloseIdleConnections() +} + func checkHTTP2(state *tls.ConnectionState) error { if state == nil { return errors.New("http2: non TLS connection") diff --git a/components/guns/http/connect.go b/components/guns/http/connect.go index 64f439b3e..e102646f7 100644 --- a/components/guns/http/connect.go +++ b/components/guns/http/connect.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package phttp import ( @@ -19,68 +14,46 @@ import ( "go.uber.org/zap" ) -type ConnectGunConfig struct { - Target string `validate:"endpoint,required"` - ConnectSSL bool `config:"connect-ssl"` // Defines if tunnel encrypted. - SSL bool // As in HTTP gun, defines scheme for http requests. - Client ClientConfig `config:",squash"` - BaseGunConfig `config:",squash"` -} - -func NewConnectGun(conf ConnectGunConfig, answLog *zap.Logger) *ConnectGun { - scheme := "http" - if conf.SSL { - scheme = "https" - } - client := newConnectClient(conf) - var g ConnectGun - g = ConnectGun{ - BaseGun: BaseGun{ - Config: conf.BaseGunConfig, - Do: g.Do, - OnClose: func() error { - client.CloseIdleConnections() - return nil - }, - AnswLog: answLog, - }, - scheme: scheme, - client: client, +func NewConnectGun(cfg HTTPGunConfig, answLog *zap.Logger) *BaseGun { + var wrappedConstructor = func(clientConfig ClientConfig, target string) Client { + scheme := "http" + if cfg.SSL { + scheme = "https" + } + client := newConnectClient(cfg.Client, cfg.Target) + return &httpDecoratedClient{ + client: client, + hostname: "", + targetResolved: cfg.Target, + scheme: scheme, + } } - return &g -} -type ConnectGun struct { - BaseGun - scheme string - client Client + return NewBaseGun(wrappedConstructor, cfg, answLog) } -var _ Gun = (*ConnectGun)(nil) - -func (g *ConnectGun) Do(req *http.Request) (*http.Response, error) { - req.URL.Scheme = g.scheme - return g.client.Do(req) -} - -func DefaultConnectGunConfig() ConnectGunConfig { - return ConnectGunConfig{ - SSL: false, - ConnectSSL: false, - Client: DefaultClientConfig(), +func DefaultConnectGunConfig() HTTPGunConfig { + return HTTPGunConfig{ + SSL: false, + Client: DefaultClientConfig(), + Base: DefaultBaseGunConfig(), } } -func newConnectClient(conf ConnectGunConfig) Client { - transport := NewTransport(conf.Client.Transport, +func newConnectClient(conf ClientConfig, target string) Client { + transport := NewTransport( + conf.Transport, newConnectDialFunc( - conf.Target, + target, conf.ConnectSSL, - NewDialer(conf.Client.Dialer), - ), conf.Target) - return newClient(transport, conf.Client.Redirect) + NewDialer(conf.Dialer), + ), + target) + return NewRedirectingClient(transport, conf.Redirect) } +var _ ClientConstructor = newConnectClient + func newConnectDialFunc(target string, connectSSL bool, dialer netutil.Dialer) netutil.DialerFunc { return func(ctx context.Context, network, address string) (conn net.Conn, err error) { // TODO(skipor): make connect sample. diff --git a/components/guns/http/connect_test.go b/components/guns/http/connect_test.go index 139779e6d..3f5ec6ad2 100644 --- a/components/guns/http/connect_test.go +++ b/components/guns/http/connect_test.go @@ -1,7 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. - package phttp import ( @@ -18,12 +14,14 @@ import ( "go.uber.org/zap" ) -var tunnelHandler = func(t *testing.T, originURL string) http.Handler { +var tunnelHandler = func(t *testing.T, originURL string, compareURI bool) http.Handler { u, err := url.Parse(originURL) require.NoError(t, err) originHost := u.Host return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, originHost, r.RequestURI) + if compareURI { + require.Equal(t, originHost, r.RequestURI) + } toOrigin, err := net.Dial("tcp", originHost) require.NoError(t, err) @@ -64,9 +62,9 @@ func TestDo(t *testing.T) { var proxy *httptest.Server if tunnelSSL { - proxy = httptest.NewTLSServer(tunnelHandler(t, origin.URL)) + proxy = httptest.NewTLSServer(tunnelHandler(t, origin.URL, true)) } else { - proxy = httptest.NewServer(tunnelHandler(t, origin.URL)) + proxy = httptest.NewServer(tunnelHandler(t, origin.URL, true)) } defer proxy.Close() @@ -74,14 +72,14 @@ func TestDo(t *testing.T) { require.NoError(t, err) conf := DefaultConnectGunConfig() - conf.ConnectSSL = tunnelSSL + conf.Client.ConnectSSL = tunnelSSL scheme := "http://" if tunnelSSL { scheme = "https://" } conf.Target = strings.TrimPrefix(proxy.URL, scheme) - client := newConnectClient(conf) + client := newConnectClient(conf.Client, conf.Target) res, err := client.Do(req) require.NoError(t, err) @@ -91,12 +89,11 @@ func TestDo(t *testing.T) { } func TestNewConnectGun(t *testing.T) { - origin := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) })) defer origin.Close() - proxy := httptest.NewServer(tunnelHandler(t, origin.URL)) + proxy := httptest.NewServer(tunnelHandler(t, origin.URL, false)) defer proxy.Close() log := zap.NewNop() diff --git a/components/guns/http/doc.go b/components/guns/http/doc.go index 067925107..c79547468 100644 --- a/components/guns/http/doc.go +++ b/components/guns/http/doc.go @@ -1,7 +1,2 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // package phttp (pandora http) contains pandora extension points for HTTP related protocols. package phttp diff --git a/components/guns/http/http.go b/components/guns/http/http.go index 4924f9a31..7fcc890e5 100644 --- a/components/guns/http/http.go +++ b/components/guns/http/http.go @@ -1,115 +1,73 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package phttp import ( - "net/http" - "github.com/pkg/errors" "go.uber.org/zap" ) -type ClientGunConfig struct { - Target string `validate:"endpoint,required"` - SSL bool +type HTTPGunConfig struct { Base BaseGunConfig `config:",squash"` + Client ClientConfig `config:",squash"` + Target string `validate:"endpoint,required"` + SSL bool } -type HTTPGunConfig struct { - Gun ClientGunConfig `config:",squash"` - Client ClientConfig `config:",squash"` +func NewHTTP1Gun(cfg HTTPGunConfig, answLog *zap.Logger, targetResolved string) *BaseGun { + var wrappedConstructor = func(clientConfig ClientConfig, target string) Client { + return WrapClientHostResolving( + HTTP1ClientConstructor(cfg.Client, cfg.Target), + cfg, + targetResolved, + ) + } + return NewBaseGun(wrappedConstructor, cfg, answLog) } -type HTTP2GunConfig struct { - Gun ClientGunConfig `config:",squash"` - Client ClientConfig `config:",squash"` +func HTTP1ClientConstructor(clientConfig ClientConfig, target string) Client { + transport := NewTransport(clientConfig.Transport, NewDialer(clientConfig.Dialer).DialContext, target) + client := NewRedirectingClient(transport, clientConfig.Redirect) + return client } -func NewHTTPGun(conf HTTPGunConfig, answLog *zap.Logger, targetResolved string) *HTTPGun { - transport := NewTransport(conf.Client.Transport, NewDialer(conf.Client.Dialer).DialContext, conf.Gun.Target) - client := newClient(transport, conf.Client.Redirect) - return NewClientGun(client, conf.Gun, answLog, targetResolved) -} +var _ ClientConstructor = HTTP1ClientConstructor // NewHTTP2Gun return simple HTTP/2 gun that can shoot sequentially through one connection. -func NewHTTP2Gun(conf HTTP2GunConfig, answLog *zap.Logger, targetResolved string) (*HTTPGun, error) { - if !conf.Gun.SSL { +func NewHTTP2Gun(cfg HTTPGunConfig, answLog *zap.Logger, targetResolved string) (*BaseGun, error) { + if !cfg.SSL { // Open issue on github if you really need this feature. return nil, errors.New("HTTP/2.0 over TCP is not supported. Please leave SSL option true by default.") } - transport := NewHTTP2Transport(conf.Client.Transport, NewDialer(conf.Client.Dialer).DialContext, conf.Gun.Target) - client := newClient(transport, conf.Client.Redirect) - // Will panic and cancel shooting whet target doesn't support HTTP/2. - client = &panicOnHTTP1Client{client} - return NewClientGun(client, conf.Gun, answLog, targetResolved), nil -} - -func NewClientGun(client Client, conf ClientGunConfig, answLog *zap.Logger, targetResolved string) *HTTPGun { - scheme := "http" - if conf.SSL { - scheme = "https" + var wrappedConstructor = func(clientConfig ClientConfig, target string) Client { + return WrapClientHostResolving( + HTTP2ClientConstructor(cfg.Client, cfg.Target), + cfg, + targetResolved, + ) } - var g HTTPGun - g = HTTPGun{ - BaseGun: BaseGun{ - Config: conf.Base, - Do: g.Do, - OnClose: func() error { - client.CloseIdleConnections() - return nil - }, - AnswLog: answLog, - }, - scheme: scheme, - hostname: getHostWithoutPort(conf.Target), - targetResolved: targetResolved, - client: client, - } - return &g + return NewBaseGun(wrappedConstructor, cfg, answLog), nil } -type HTTPGun struct { - BaseGun - scheme string - hostname string - targetResolved string - client Client +func HTTP2ClientConstructor(clientConfig ClientConfig, target string) Client { + transport := NewHTTP2Transport(clientConfig.Transport, NewDialer(clientConfig.Dialer).DialContext, target) + client := NewRedirectingClient(transport, clientConfig.Redirect) + // Will panic and cancel shooting whet target doesn't support HTTP/2. + return &panicOnHTTP1Client{Client: client} } -var _ Gun = (*HTTPGun)(nil) - -func (g *HTTPGun) Do(req *http.Request) (*http.Response, error) { - if req.Host == "" { - req.Host = g.hostname - } - - req.URL.Host = g.targetResolved - req.URL.Scheme = g.scheme - return g.client.Do(req) -} +var _ ClientConstructor = HTTP2ClientConstructor func DefaultHTTPGunConfig() HTTPGunConfig { return HTTPGunConfig{ - Gun: DefaultClientGunConfig(), + SSL: false, + Base: DefaultBaseGunConfig(), Client: DefaultClientConfig(), } } -func DefaultHTTP2GunConfig() HTTP2GunConfig { - conf := HTTP2GunConfig{ +func DefaultHTTP2GunConfig() HTTPGunConfig { + return HTTPGunConfig{ Client: DefaultClientConfig(), - Gun: DefaultClientGunConfig(), - } - conf.Gun.SSL = true - return conf -} - -func DefaultClientGunConfig() ClientGunConfig { - return ClientGunConfig{ - SSL: false, - Base: DefaultBaseGunConfig(), + Base: DefaultBaseGunConfig(), + SSL: true, } } diff --git a/components/guns/http/http_test.go b/components/guns/http/http_test.go index 5f06b1d2d..907801252 100644 --- a/components/guns/http/http_test.go +++ b/components/guns/http/http_test.go @@ -1,7 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. - package phttp import ( @@ -43,10 +39,10 @@ func TestBaseGun_integration(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTPGunConfig() - conf.Gun.Target = host + ":80" + conf.Target = host + ":80" targetResolved := strings.TrimPrefix(server.URL, "http://") results := &netsample.TestAggregator{} - httpGun := NewHTTPGun(conf, log, targetResolved) + httpGun := NewHTTP1Gun(conf, log, targetResolved) _ = httpGun.Bind(results, testDeps()) am := newAmmoReq(t, expectedReq) @@ -92,9 +88,9 @@ func TestHTTP(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTPGunConfig() - conf.Gun.Target = server.Listener.Addr().String() - conf.Gun.SSL = tt.https - gun := NewHTTPGun(conf, log, conf.Gun.Target) + conf.Target = server.Listener.Addr().String() + conf.SSL = tt.https + gun := NewHTTP1Gun(conf, log, conf.Target) var aggr netsample.TestAggregator _ = gun.Bind(&aggr, testDeps()) gun.Shoot(newAmmoURL(t, "/")) @@ -133,9 +129,9 @@ func TestHTTP_Redirect(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTPGunConfig() - conf.Gun.Target = server.Listener.Addr().String() + conf.Target = server.Listener.Addr().String() conf.Client.Redirect = tt.redirect - gun := NewHTTPGun(conf, log, conf.Gun.Target) + gun := NewHTTP1Gun(conf, log, conf.Target) var aggr netsample.TestAggregator _ = gun.Bind(&aggr, testDeps()) gun.Shoot(newAmmoURL(t, "/redirect")) @@ -171,9 +167,9 @@ func TestHTTP_notSupportHTTP2(t *testing.T) { log := zap.NewNop() conf := DefaultHTTPGunConfig() - conf.Gun.Target = server.Listener.Addr().String() - conf.Gun.SSL = true - gun := NewHTTPGun(conf, log, conf.Gun.Target) + conf.Target = server.Listener.Addr().String() + conf.SSL = true + gun := NewHTTP1Gun(conf, log, conf.Target) var results netsample.TestAggregator _ = gun.Bind(&results, testDeps()) gun.Shoot(newAmmoURL(t, "/")) @@ -194,8 +190,8 @@ func TestHTTP2(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTP2GunConfig() - conf.Gun.Target = server.Listener.Addr().String() - gun, _ := NewHTTP2Gun(conf, log, conf.Gun.Target) + conf.Target = server.Listener.Addr().String() + gun, _ := NewHTTP2Gun(conf, log, conf.Target) var results netsample.TestAggregator _ = gun.Bind(&results, testDeps()) gun.Shoot(newAmmoURL(t, "/")) @@ -209,8 +205,8 @@ func TestHTTP2(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTP2GunConfig() - conf.Gun.Target = server.Listener.Addr().String() - gun, _ := NewHTTP2Gun(conf, log, conf.Gun.Target) + conf.Target = server.Listener.Addr().String() + gun, _ := NewHTTP2Gun(conf, log, conf.Target) var results netsample.TestAggregator _ = gun.Bind(&results, testDeps()) var r interface{} @@ -231,9 +227,9 @@ func TestHTTP2(t *testing.T) { defer server.Close() log := zap.NewNop() conf := DefaultHTTP2GunConfig() - conf.Gun.Target = server.Listener.Addr().String() - conf.Gun.SSL = false - _, err := NewHTTP2Gun(conf, log, conf.Gun.Target) + conf.Target = server.Listener.Addr().String() + conf.SSL = false + _, err := NewHTTP2Gun(conf, log, conf.Target) require.Error(t, err) }) } diff --git a/components/guns/http/phttp_suite_test.go b/components/guns/http/phttp_suite_test.go deleted file mode 100644 index 2dc671a62..000000000 --- a/components/guns/http/phttp_suite_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - -package phttp - -import ( - "testing" - - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestPhttp(t *testing.T) { - ginkgoutil.RunSuite(t, "HTTP Suite") -} diff --git a/components/guns/http/core.go b/components/guns/http/wrapper.go similarity index 84% rename from components/guns/http/core.go rename to components/guns/http/wrapper.go index 072d91607..0ddcad5b5 100644 --- a/components/guns/http/core.go +++ b/components/guns/http/wrapper.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package phttp import ( @@ -10,6 +5,7 @@ import ( "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/warmup" ) //go:generate mockery --name=Ammo --case=underscore --outpkg=ammomock @@ -29,6 +25,7 @@ type Ammo interface { type Gun interface { Shoot(ammo Ammo) Bind(sample netsample.Aggregator, deps core.GunDeps) error + WarmUp(opts *warmup.Options) (any, error) } func WrapGun(g Gun) core.Gun { @@ -47,3 +44,7 @@ func (g *gunWrapper) Shoot(ammo core.Ammo) { func (g *gunWrapper) Bind(a core.Aggregator, deps core.GunDeps) error { return g.Gun.Bind(netsample.UnwrapAggregator(a), deps) } + +func (g *gunWrapper) WarmUp(opts *warmup.Options) (any, error) { + return g.Gun.WarmUp(opts) +} diff --git a/components/guns/http_scenario/ammo.go b/components/guns/http_scenario/ammo.go index 06eabf6d6..3b6ea0d90 100644 --- a/components/guns/http_scenario/ammo.go +++ b/components/guns/http_scenario/ammo.go @@ -4,6 +4,8 @@ import ( "io" "net/http" "time" + + "github.com/yandex/pandora/components/providers/scenario" ) type SourceStorage interface { @@ -22,6 +24,15 @@ func (a *Scenario) SetID(id uint64) { a.ID = id } +func (a *Scenario) Clone() scenario.ProvAmmo { + return &Scenario{ + Requests: a.Requests, + Name: a.Name, + MinWaitingTime: a.MinWaitingTime, + VariableStorage: a.VariableStorage, + } +} + type Request struct { Method string Headers map[string]string diff --git a/components/guns/http_scenario/client.go b/components/guns/http_scenario/client.go deleted file mode 100644 index 2225c9971..000000000 --- a/components/guns/http_scenario/client.go +++ /dev/null @@ -1,76 +0,0 @@ -package httpscenario - -import ( - "crypto/tls" - "errors" - "fmt" - "net" - "net/http" - "strings" - - "go.uber.org/zap" - "golang.org/x/net/http2" -) - -//go:generate go run github.com/vektra/mockery/v2@v2.22.1 --inpackage --name=Client --filename=mock_client.go - -type Client interface { - Do(req *http.Request) (*http.Response, error) - CloseIdleConnections() // We should close idle conns after gun close. -} - -func newClient(tr *http.Transport, redirect bool) Client { - if redirect { - return redirectClient{Client: &http.Client{Transport: tr}} - } - return noRedirectClient{Transport: tr} -} - -type redirectClient struct{ *http.Client } - -func (c redirectClient) CloseIdleConnections() { - c.Transport.(*http.Transport).CloseIdleConnections() -} - -type noRedirectClient struct{ *http.Transport } - -func (c noRedirectClient) Do(req *http.Request) (*http.Response, error) { - return c.Transport.RoundTrip(req) -} - -// Used to cancel shooting in HTTP/2 gun, when target doesn't support HTTP/2 -type panicOnHTTP1Client struct { - Client -} - -const notHTTP2PanicMsg = "Non HTTP/2 connection established. Seems that target doesn't support HTTP/2." - -func (c *panicOnHTTP1Client) Do(req *http.Request) (*http.Response, error) { - res, err := c.Client.Do(req) - if err != nil { - var opError *net.OpError - // Unfortunately, Go doesn't expose tls.alert (https://github.com/golang/go/issues/35234), so we make decisions based on the error message - if errors.As(err, &opError) && opError.Op == "remote error" && strings.Contains(err.Error(), "no application protocol") { - zap.L().Panic(notHTTP2PanicMsg, zap.Error(err)) - } - return nil, err - } - err = checkHTTP2(res.TLS) - if err != nil { - zap.L().Panic(notHTTP2PanicMsg, zap.Error(err)) - } - return res, nil -} - -func checkHTTP2(state *tls.ConnectionState) error { - if state == nil { - return errors.New("http2: non TLS connection") - } - if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { - return fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, http2.NextProtoTLS) - } - if !state.NegotiatedProtocolIsMutual { - return errors.New("http2: could not negotiate protocol mutually") - } - return nil -} diff --git a/components/guns/http_scenario/gun.go b/components/guns/http_scenario/gun.go index 22b689329..cbf7a021b 100644 --- a/components/guns/http_scenario/gun.go +++ b/components/guns/http_scenario/gun.go @@ -2,7 +2,6 @@ package httpscenario import ( "bytes" - "context" "fmt" "io" "math/rand" @@ -16,63 +15,44 @@ import ( phttp "github.com/yandex/pandora/components/guns/http" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/warmup" "go.uber.org/zap" ) type Gun interface { Shoot(ammo *Scenario) Bind(sample netsample.Aggregator, deps core.GunDeps) error + WarmUp(opts *warmup.Options) (any, error) } const ( EmptyTag = "__EMPTY__" ) -type BaseGun struct { - DebugLog bool // Automaticaly set in Bind if Log accepts debug messages. - Config phttp.BaseGunConfig - Connect func(ctx context.Context) error // Optional hook. - OnClose func() error // Optional. Called on Close(). - Aggregator netsample.Aggregator // Lazy set via BindResultTo. - AnswLog *zap.Logger - core.GunDeps - scheme string - hostname string - targetResolved string - client Client +type ScenarioGun struct { + base *phttp.BaseGun } -var _ Gun = (*BaseGun)(nil) -var _ io.Closer = (*BaseGun)(nil) +var _ Gun = (*ScenarioGun)(nil) +var _ io.Closer = (*ScenarioGun)(nil) -func (g *BaseGun) Bind(aggregator netsample.Aggregator, deps core.GunDeps) error { - log := deps.Log - if ent := log.Check(zap.DebugLevel, "Gun bind"); ent != nil { - // Enable debug level logging during shooting. Creating log entries isn't free. - g.DebugLog = true - } - - if g.Aggregator != nil { - log.Panic("already binded") - } - if aggregator == nil { - log.Panic("nil aggregator") - } - g.Aggregator = aggregator - g.GunDeps = deps +func (g *ScenarioGun) WarmUp(opts *warmup.Options) (any, error) { + return g.base.WarmUp(opts) +} - return nil +func (g *ScenarioGun) Bind(aggregator netsample.Aggregator, deps core.GunDeps) error { + return g.base.Bind(aggregator, deps) } -// Shoot is thread safe iff Do and Connect hooks are thread safe. -func (g *BaseGun) Shoot(ammo *Scenario) { - if g.Aggregator == nil { +// Shoot is thread safe if Do and Connect hooks are thread safe. +func (g *ScenarioGun) Shoot(ammo *Scenario) { + if g.base.Aggregator == nil { zap.L().Panic("must bind before shoot") } - if g.Connect != nil { - err := g.Connect(g.Ctx) + if g.base.Connect != nil { + err := g.base.Connect(g.base.Ctx) if err != nil { - g.Log.Warn("Connect fail", zap.Error(err)) + g.base.Log.Warn("Connect fail", zap.Error(err)) return } } @@ -83,23 +63,23 @@ func (g *BaseGun) Shoot(ammo *Scenario) { err := g.shoot(ammo, templateVars) if err != nil { - g.Log.Warn("Invalid ammo", zap.Uint64("request", ammo.ID), zap.Error(err)) + g.base.Log.Warn("Invalid ammo", zap.Uint64("request", ammo.ID), zap.Error(err)) return } } -func (g *BaseGun) Do(req *http.Request) (*http.Response, error) { - return g.client.Do(req) +func (g *ScenarioGun) Do(req *http.Request) (*http.Response, error) { + return g.base.Client.Do(req) } -func (g *BaseGun) Close() error { - if g.OnClose != nil { - return g.OnClose() +func (g *ScenarioGun) Close() error { + if g.base.OnClose != nil { + return g.base.OnClose() } return nil } -func (g *BaseGun) shoot(ammo *Scenario, templateVars map[string]any) error { +func (g *ScenarioGun) shoot(ammo *Scenario, templateVars map[string]any) error { if templateVars == nil { templateVars = map[string]any{} } @@ -128,7 +108,7 @@ func (g *BaseGun) shoot(ammo *Scenario, templateVars map[string]any) error { return nil } -func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName string, templateVars map[string]any, requestVars map[string]any, stepLogID string) error { +func (g *ScenarioGun) shootStep(step Request, sample *netsample.Sample, ammoName string, templateVars map[string]any, requestVars map[string]any, stepLogID string) error { const op = "base_gun.shootStep" stepVars := map[string]any{} @@ -141,8 +121,8 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str return fmt.Errorf("%s preProcessor %w", op, err) } stepVars["preprocessor"] = preProcVars - if g.DebugLog { - g.GunDeps.Log.Debug("Preprocessor variables", zap.Any(fmt.Sprintf(".request.%s.preprocessor", step.Name), preProcVars)) + if g.base.DebugLog { + g.base.GunDeps.Log.Debug("Preprocessor variables", zap.Any(fmt.Sprintf(".request.%s.preprocessor", step.Name), preProcVars)) } } @@ -166,17 +146,17 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str } var reqBytes []byte - if g.Config.AnswLog.Enabled { + if g.base.Config.AnswLog.Enabled { var dumpErr error reqBytes, dumpErr = httputil.DumpRequestOut(req, true) if dumpErr != nil { - g.Log.Error("Error dumping request: %s", zap.Error(dumpErr)) + g.base.Log.Error("Error dumping request: %s", zap.Error(dumpErr)) } } timings, req := g.initTracing(req, sample) - resp, err := g.Do(req) + resp, err := g.base.Client.Do(req) g.saveTrace(timings, sample, resp) @@ -188,7 +168,7 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str processors := step.Postprocessors var respBody *bytes.Reader var respBodyBytes []byte - if g.Config.AnswLog.Enabled || g.DebugLog || len(processors) > 0 { + if g.base.Config.AnswLog.Enabled || g.base.DebugLog || len(processors) > 0 { respBodyBytes, err = io.ReadAll(resp.Body) if err == nil { respBody = bytes.NewReader(respBodyBytes) @@ -202,14 +182,14 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str defer func() { closeErr := resp.Body.Close() if closeErr != nil { - g.GunDeps.Log.Error("resp.Body.Close", zap.Error(closeErr)) + g.base.GunDeps.Log.Error("resp.Body.Close", zap.Error(closeErr)) } }() - if g.DebugLog { + if g.base.DebugLog { g.verboseLogging(resp, reqBytes, respBodyBytes) } - if g.Config.AnswLog.Enabled { + if g.base.Config.AnswLog.Enabled { g.answReqRespLogging(reqBytes, resp, respBodyBytes, stepLogID) } @@ -232,10 +212,10 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str stepVars["postprocessor"] = postprocessorVars sample.SetProtoCode(resp.StatusCode) - g.Aggregator.Report(sample) + g.base.Aggregator.Report(sample) - if g.DebugLog { - g.GunDeps.Log.Debug("Postprocessor variables", zap.Any(fmt.Sprintf(".request.%s.postprocessor", step.Name), postprocessorVars)) + if g.base.DebugLog { + g.base.GunDeps.Log.Debug("Postprocessor variables", zap.Any(fmt.Sprintf(".request.%s.postprocessor", step.Name), postprocessorVars)) } if step.Sleep > 0 { @@ -244,7 +224,7 @@ func (g *BaseGun) shootStep(step Request, sample *netsample.Sample, ammoName str return nil } -func (g *BaseGun) buildLogID(idBuilder *strings.Builder, tag string, ammoID uint64, rnd string) { +func (g *ScenarioGun) buildLogID(idBuilder *strings.Builder, tag string, ammoID uint64, rnd string) { idBuilder.Reset() idBuilder.WriteString(tag) idBuilder.WriteByte('.') @@ -253,7 +233,7 @@ func (g *BaseGun) buildLogID(idBuilder *strings.Builder, tag string, ammoID uint idBuilder.WriteString(strconv.Itoa(int(ammoID))) } -func (g *BaseGun) prepareRequest(reqParts RequestParts) (*http.Request, error) { +func (g *ScenarioGun) prepareRequest(reqParts RequestParts) (*http.Request, error) { const op = "base_gun.prepareRequest" var reader io.Reader @@ -268,26 +248,21 @@ func (g *BaseGun) prepareRequest(reqParts RequestParts) (*http.Request, error) { for k, v := range reqParts.Headers { req.Header.Set(k, v) } - if req.Host == "" { - req.Host = g.hostname - } - req.URL.Host = g.targetResolved - req.URL.Scheme = g.scheme return req, err } -func (g *BaseGun) initTracing(req *http.Request, sample *netsample.Sample) (*phttp.TraceTimings, *http.Request) { +func (g *ScenarioGun) initTracing(req *http.Request, sample *netsample.Sample) (*phttp.TraceTimings, *http.Request) { var timings *phttp.TraceTimings - if g.Config.HTTPTrace.TraceEnabled { + if g.base.Config.HTTPTrace.TraceEnabled { var clientTracer *httptrace.ClientTrace clientTracer, timings = phttp.CreateHTTPTrace() req = req.WithContext(httptrace.WithClientTrace(req.Context(), clientTracer)) } - if g.Config.HTTPTrace.DumpEnabled { + if g.base.Config.HTTPTrace.DumpEnabled { requestDump, err := httputil.DumpRequest(req, true) if err != nil { - g.Log.Error("DumpRequest error", zap.Error(err)) + g.base.Log.Error("DumpRequest error", zap.Error(err)) } else { sample.SetRequestBytes(len(requestDump)) } @@ -295,28 +270,28 @@ func (g *BaseGun) initTracing(req *http.Request, sample *netsample.Sample) (*pht return timings, req } -func (g *BaseGun) saveTrace(timings *phttp.TraceTimings, sample *netsample.Sample, resp *http.Response) { - if g.Config.HTTPTrace.TraceEnabled && timings != nil { +func (g *ScenarioGun) saveTrace(timings *phttp.TraceTimings, sample *netsample.Sample, resp *http.Response) { + if g.base.Config.HTTPTrace.TraceEnabled && timings != nil { sample.SetReceiveTime(timings.GetReceiveTime()) } - if g.Config.HTTPTrace.DumpEnabled && resp != nil { + if g.base.Config.HTTPTrace.DumpEnabled && resp != nil { responseDump, e := httputil.DumpResponse(resp, true) if e != nil { - g.Log.Error("DumpResponse error", zap.Error(e)) + g.base.Log.Error("DumpResponse error", zap.Error(e)) } else { sample.SetResponseBytes(len(responseDump)) } } - if g.Config.HTTPTrace.TraceEnabled && timings != nil { + if g.base.Config.HTTPTrace.TraceEnabled && timings != nil { sample.SetConnectTime(timings.GetConnectTime()) sample.SetSendTime(timings.GetSendTime()) sample.SetLatency(timings.GetLatency()) } } -func (g *BaseGun) verboseLogging(resp *http.Response, reqBody, respBody []byte) { +func (g *ScenarioGun) verboseLogging(resp *http.Response, reqBody, respBody []byte) { if resp == nil { - g.Log.Error("Response is nil") + g.base.Log.Error("Response is nil") return } fields := make([]zap.Field, 0, 4) @@ -326,7 +301,7 @@ func (g *BaseGun) verboseLogging(resp *http.Response, reqBody, respBody []byte) if reqBody != nil { fields = append(fields, zap.ByteString("Body", reqBody)) } - g.Log.Debug("Request debug info", fields...) + g.base.Log.Debug("Request debug info", fields...) fields = fields[:0] fields = append(fields, zap.Int("Status Code", resp.StatusCode)) @@ -335,12 +310,12 @@ func (g *BaseGun) verboseLogging(resp *http.Response, reqBody, respBody []byte) if reqBody != nil { fields = append(fields, zap.ByteString("Body", respBody)) } - g.Log.Debug("Response debug info", fields...) + g.base.Log.Debug("Response debug info", fields...) } -func (g *BaseGun) answLogging(bodyBytes []byte, resp *http.Response, respBytes []byte, stepName string) { +func (g *ScenarioGun) answLogging(bodyBytes []byte, resp *http.Response, respBytes []byte, stepName string) { msg := fmt.Sprintf("REQUEST[%s]:\n%s\n", stepName, string(bodyBytes)) - g.AnswLog.Debug(msg) + g.base.AnswLog.Debug(msg) headers := "" var writer bytes.Buffer @@ -348,15 +323,15 @@ func (g *BaseGun) answLogging(bodyBytes []byte, resp *http.Response, respBytes [ if err == nil { headers = writer.String() } else { - g.AnswLog.Error("error writing header", zap.Error(err)) + g.base.AnswLog.Error("error writing header", zap.Error(err)) } msg = fmt.Sprintf("RESPONSE[%s]:\n%s %s\n%s\n%s\n", stepName, resp.Proto, resp.Status, headers, string(respBytes)) - g.AnswLog.Debug(msg) + g.base.AnswLog.Debug(msg) } -func (g *BaseGun) answReqRespLogging(reqBytes []byte, resp *http.Response, respBytes []byte, stepName string) { - switch g.Config.AnswLog.Filter { +func (g *ScenarioGun) answReqRespLogging(reqBytes []byte, resp *http.Response, respBytes []byte, stepName string) { + switch g.base.Config.AnswLog.Filter { case "all": g.answLogging(reqBytes, resp, respBytes, stepName) case "warning": @@ -370,12 +345,12 @@ func (g *BaseGun) answReqRespLogging(reqBytes []byte, resp *http.Response, respB } } -func (g *BaseGun) reportErr(sample *netsample.Sample, err error) { +func (g *ScenarioGun) reportErr(sample *netsample.Sample, err error) { if err == nil { return } sample.AddTag(EmptyTag) sample.SetProtoCode(0) sample.SetErr(err) - g.Aggregator.Report(sample) + g.base.Aggregator.Report(sample) } diff --git a/components/guns/http_scenario/gun_test.go b/components/guns/http_scenario/gun_test.go index 1de5ce685..73bc0ffe3 100644 --- a/components/guns/http_scenario/gun_test.go +++ b/components/guns/http_scenario/gun_test.go @@ -29,7 +29,7 @@ func TestBaseGun_shoot(t *testing.T) { scheme string hostname string targetResolved string - client Client + client phttp.Client } tests := []struct { @@ -188,7 +188,7 @@ func TestBaseGun_shoot(t *testing.T) { aggregator := netsample.NewMockAggregator(t) aggregator.On("Report", mock.Anything) - g := &BaseGun{Aggregator: aggregator, client: client} + g := &ScenarioGun{base: &phttp.BaseGun{Aggregator: aggregator, Client: client}} tt.wantErr(t, g.shoot(tt.ammoMock, tt.templateVars), fmt.Sprintf("shoot(%v)", tt.ammoMock)) require.Equal(t, tt.wantTempateVars, tt.templateVars) diff --git a/components/guns/http_scenario/import.go b/components/guns/http_scenario/import.go index fec37771c..fb87ad1e6 100644 --- a/components/guns/http_scenario/import.go +++ b/components/guns/http_scenario/import.go @@ -1,16 +1,12 @@ package httpscenario import ( - "net" - "github.com/spf13/afero" phttp "github.com/yandex/pandora/components/guns/http" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" "github.com/yandex/pandora/core/register" "github.com/yandex/pandora/lib/answlog" - "github.com/yandex/pandora/lib/netutil" - "go.uber.org/zap" ) func WrapGun(g Gun) core.Gun { @@ -34,51 +30,20 @@ func (g *gunWrapper) Bind(a core.Aggregator, deps core.GunDeps) error { func Import(fs afero.Fs) { register.Gun("http/scenario", func(conf phttp.HTTPGunConfig) func() core.Gun { - targetResolved, _ := PreResolveTargetAddr(&conf.Client, conf.Gun.Target) - answLog := answlog.Init(conf.Gun.Base.AnswLog.Path, conf.Gun.Base.AnswLog.Enabled) + targetResolved, _ := phttp.PreResolveTargetAddr(&conf.Client, conf.Target) + answLog := answlog.Init(conf.Base.AnswLog.Path, conf.Base.AnswLog.Enabled) return func() core.Gun { gun := NewHTTPGun(conf, answLog, targetResolved) return WrapGun(gun) } }, phttp.DefaultHTTPGunConfig) - register.Gun("http2/scenario", func(conf phttp.HTTP2GunConfig) func() (core.Gun, error) { - targetResolved, _ := PreResolveTargetAddr(&conf.Client, conf.Gun.Target) - answLog := answlog.Init(conf.Gun.Base.AnswLog.Path, conf.Gun.Base.AnswLog.Enabled) + register.Gun("http2/scenario", func(conf phttp.HTTPGunConfig) func() (core.Gun, error) { + targetResolved, _ := phttp.PreResolveTargetAddr(&conf.Client, conf.Target) + answLog := answlog.Init(conf.Base.AnswLog.Path, conf.Base.AnswLog.Enabled) return func() (core.Gun, error) { gun, err := NewHTTP2Gun(conf, answLog, targetResolved) return WrapGun(gun), err } }, phttp.DefaultHTTP2GunConfig) } - -// DNS resolve optimisation. -// When DNSCache turned off - do nothing extra, host will be resolved on every shoot. -// When using resolved target, don't use DNS caching logic - it is useless. -// If we can resolve accessible target addr - use it as target, not use caching. -// Otherwise just use DNS cache - we should not fail shooting, we should try to -// connect on every shoot. DNS cache will save resolved addr after first successful connect. -func PreResolveTargetAddr(clientConf *phttp.ClientConfig, target string) (string, error) { - if !clientConf.Dialer.DNSCache { - return target, nil - } - if endpointIsResolved(target) { - clientConf.Dialer.DNSCache = false - return target, nil - } - resolved, err := netutil.LookupReachable(target, clientConf.Dialer.Timeout) - if err != nil { - zap.L().Warn("DNS target pre resolve failed", zap.String("target", target), zap.Error(err)) - return target, err - } - clientConf.Dialer.DNSCache = false - return resolved, nil -} - -func endpointIsResolved(endpoint string) bool { - host, _, err := net.SplitHostPort(endpoint) - if err != nil { - return false - } - return net.ParseIP(host) != nil -} diff --git a/components/guns/http_scenario/new.go b/components/guns/http_scenario/new.go index e65fbcdbf..8e47ee5aa 100644 --- a/components/guns/http_scenario/new.go +++ b/components/guns/http_scenario/new.go @@ -2,54 +2,34 @@ package httpscenario import ( "errors" - "net" phttp "github.com/yandex/pandora/components/guns/http" "go.uber.org/zap" ) -func NewHTTPGun(conf phttp.HTTPGunConfig, answLog *zap.Logger, targetResolved string) *BaseGun { - transport := phttp.NewTransport(conf.Client.Transport, phttp.NewDialer(conf.Client.Dialer).DialContext, conf.Gun.Target) - client := newClient(transport, conf.Client.Redirect) - return NewClientGun(client, conf.Gun, answLog, targetResolved) +func NewHTTPGun(conf phttp.HTTPGunConfig, answLog *zap.Logger, targetResolved string) *ScenarioGun { + return newScenarioGun(phttp.HTTP1ClientConstructor, conf, answLog, targetResolved) } // NewHTTP2Gun return simple HTTP/2 gun that can shoot sequentially through one connection. -func NewHTTP2Gun(conf phttp.HTTP2GunConfig, answLog *zap.Logger, targetResolved string) (*BaseGun, error) { - if !conf.Gun.SSL { +func NewHTTP2Gun(conf phttp.HTTPGunConfig, answLog *zap.Logger, targetResolved string) (*ScenarioGun, error) { + if !conf.SSL { // Open issue on github if you really need this feature. return nil, errors.New("HTTP/2.0 over TCP is not supported. Please leave SSL option true by default") } - transport := phttp.NewHTTP2Transport(conf.Client.Transport, phttp.NewDialer(conf.Client.Dialer).DialContext, conf.Gun.Target) - client := newClient(transport, conf.Client.Redirect) - // Will panic and cancel shooting whet target doesn't support HTTP/2. - client = &panicOnHTTP1Client{client} - return NewClientGun(client, conf.Gun, answLog, targetResolved), nil + return newScenarioGun(phttp.HTTP2ClientConstructor, conf, answLog, targetResolved), nil } -func NewClientGun(client Client, conf phttp.ClientGunConfig, answLog *zap.Logger, targetResolved string) *BaseGun { - scheme := "http" - if conf.SSL { - scheme = "https" +func newScenarioGun(clientConstructor phttp.ClientConstructor, cfg phttp.HTTPGunConfig, answLog *zap.Logger, targetResolved string) *ScenarioGun { + var wrappedConstructor = func(clientConfig phttp.ClientConfig, target string) phttp.Client { + return phttp.WrapClientHostResolving( + clientConstructor(cfg.Client, cfg.Target), + cfg, + targetResolved, + ) } - return &BaseGun{ - Config: conf.Base, - OnClose: func() error { - client.CloseIdleConnections() - return nil - }, - AnswLog: answLog, - scheme: scheme, - hostname: getHostWithoutPort(conf.Target), - targetResolved: targetResolved, - client: client, - } -} -func getHostWithoutPort(target string) string { - host, _, err := net.SplitHostPort(target) - if err != nil { - host = target + return &ScenarioGun{ + base: phttp.NewBaseGun(wrappedConstructor, cfg, answLog), } - return host } diff --git a/components/phttp/import/import.go b/components/phttp/import/import.go index ff1316810..2a9140b77 100644 --- a/components/phttp/import/import.go +++ b/components/phttp/import/import.go @@ -1,8 +1,6 @@ package phttp import ( - "net" - "github.com/spf13/afero" phttp "github.com/yandex/pandora/components/guns/http" scenarioGun "github.com/yandex/pandora/components/guns/http_scenario" @@ -11,8 +9,6 @@ import ( "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/register" "github.com/yandex/pandora/lib/answlog" - "github.com/yandex/pandora/lib/netutil" - "go.uber.org/zap" ) func Import(fs afero.Fs) { @@ -21,56 +17,25 @@ func Import(fs afero.Fs) { scenarioProvider.Import(fs) register.Gun("http", func(conf phttp.HTTPGunConfig) func() core.Gun { - targetResolved, _ := PreResolveTargetAddr(&conf.Client, conf.Gun.Target) - answLog := answlog.Init(conf.Gun.Base.AnswLog.Path, conf.Gun.Base.AnswLog.Enabled) - return func() core.Gun { return phttp.WrapGun(phttp.NewHTTPGun(conf, answLog, targetResolved)) } + targetResolved, _ := phttp.PreResolveTargetAddr(&conf.Client, conf.Target) + answLog := answlog.Init(conf.Base.AnswLog.Path, conf.Base.AnswLog.Enabled) + return func() core.Gun { return phttp.WrapGun(phttp.NewHTTP1Gun(conf, answLog, targetResolved)) } }, phttp.DefaultHTTPGunConfig) - register.Gun("http2", func(conf phttp.HTTP2GunConfig) func() (core.Gun, error) { - targetResolved, _ := PreResolveTargetAddr(&conf.Client, conf.Gun.Target) - answLog := answlog.Init(conf.Gun.Base.AnswLog.Path, conf.Gun.Base.AnswLog.Enabled) + register.Gun("http2", func(conf phttp.HTTPGunConfig) func() (core.Gun, error) { + targetResolved, _ := phttp.PreResolveTargetAddr(&conf.Client, conf.Target) + answLog := answlog.Init(conf.Base.AnswLog.Path, conf.Base.AnswLog.Enabled) return func() (core.Gun, error) { gun, err := phttp.NewHTTP2Gun(conf, answLog, targetResolved) return phttp.WrapGun(gun), err } }, phttp.DefaultHTTP2GunConfig) - register.Gun("connect", func(conf phttp.ConnectGunConfig) func() core.Gun { - conf.Target, _ = PreResolveTargetAddr(&conf.Client, conf.Target) - answLog := answlog.Init(conf.BaseGunConfig.AnswLog.Path, conf.BaseGunConfig.AnswLog.Enabled) + register.Gun("connect", func(conf phttp.HTTPGunConfig) func() core.Gun { + conf.Target, _ = phttp.PreResolveTargetAddr(&conf.Client, conf.Target) + answLog := answlog.Init(conf.Base.AnswLog.Path, conf.Base.AnswLog.Enabled) return func() core.Gun { return phttp.WrapGun(phttp.NewConnectGun(conf, answLog)) } }, phttp.DefaultConnectGunConfig) } - -// DNS resolve optimisation. -// When DNSCache turned off - do nothing extra, host will be resolved on every shoot. -// When using resolved target, don't use DNS caching logic - it is useless. -// If we can resolve accessible target addr - use it as target, not use caching. -// Otherwise just use DNS cache - we should not fail shooting, we should try to -// connect on every shoot. DNS cache will save resolved addr after first successful connect. -func PreResolveTargetAddr(clientConf *phttp.ClientConfig, target string) (string, error) { - if !clientConf.Dialer.DNSCache { - return target, nil - } - if endpointIsResolved(target) { - clientConf.Dialer.DNSCache = false - return target, nil - } - resolved, err := netutil.LookupReachable(target, clientConf.Dialer.Timeout) - if err != nil { - zap.L().Warn("DNS target pre resolve failed", zap.String("target", target), zap.Error(err)) - return target, err - } - clientConf.Dialer.DNSCache = false - return resolved, nil -} - -func endpointIsResolved(endpoint string) bool { - host, _, err := net.SplitHostPort(endpoint) - if err != nil { - return false - } - return net.ParseIP(host) != nil -} diff --git a/components/phttp/import/import_test.go b/components/phttp/import/import_test.go index 5c058af48..201d6816a 100644 --- a/components/phttp/import/import_test.go +++ b/components/phttp/import/import_test.go @@ -59,7 +59,7 @@ func Test_preResolveTargetAddr(t *testing.T) { conf := &phttp.ClientConfig{} conf.Dialer.DNSCache = true - got, err := PreResolveTargetAddr(conf, tt.target) + got, err := phttp.PreResolveTargetAddr(conf, tt.target) if tt.wantErr { require.Error(t, err) } else { diff --git a/components/providers/grpc/ammo.go b/components/providers/grpc/ammo.go index 5433ba124..1f0058cb0 100644 --- a/components/providers/grpc/ammo.go +++ b/components/providers/grpc/ammo.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ammo type Ammo struct { diff --git a/components/providers/grpc/grpcjson/provider.go b/components/providers/grpc/grpcjson/provider.go index 8fefd1c0f..82359f4b6 100644 --- a/components/providers/grpc/grpcjson/provider.go +++ b/components/providers/grpc/grpcjson/provider.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package grpcjson import ( diff --git a/components/providers/grpc/provider.go b/components/providers/grpc/provider.go index 57f0b5bfd..08594efcb 100644 --- a/components/providers/grpc/provider.go +++ b/components/providers/grpc/provider.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ammo import ( diff --git a/components/providers/http/ammo/ammo.go b/components/providers/http/ammo/ammo.go index bdabdc133..6ac91081d 100644 --- a/components/providers/http/ammo/ammo.go +++ b/components/providers/http/ammo/ammo.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ammo import ( diff --git a/components/providers/http/provider.go b/components/providers/http/provider.go index e52232b8b..0c38f4eb2 100644 --- a/components/providers/http/provider.go +++ b/components/providers/http/provider.go @@ -1,7 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. - package http import ( diff --git a/components/providers/scenario/provider.go b/components/providers/scenario/provider.go index 406b2170d..633d3cd53 100644 --- a/components/providers/scenario/provider.go +++ b/components/providers/scenario/provider.go @@ -20,6 +20,7 @@ type ProviderConfig struct { type ProvAmmo interface { SetID(id uint64) + Clone() ProvAmmo } type Provider[A ProvAmmo] struct { @@ -87,8 +88,9 @@ func (p *Provider[A]) Acquire() (core.Ammo, bool) { if !ok { return nil, false } - ammo.SetID(p.NextID()) - return ammo, true + clone := ammo.Clone() + clone.SetID(p.NextID()) + return clone, true } func (p *Provider[A]) Release(_ core.Ammo) { diff --git a/core/aggregator/discard.go b/core/aggregator/discard.go index 6dae8647f..d2f3a891f 100644 --- a/core/aggregator/discard.go +++ b/core/aggregator/discard.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/encoder.go b/core/aggregator/encoder.go index 246948070..06ed2a975 100644 --- a/core/aggregator/encoder.go +++ b/core/aggregator/encoder.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/encoder_test.go b/core/aggregator/encoder_test.go index 7588364c3..d8f3b3cfb 100644 --- a/core/aggregator/encoder_test.go +++ b/core/aggregator/encoder_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/jsonlines.go b/core/aggregator/jsonlines.go index a66e26eeb..6d1117c13 100644 --- a/core/aggregator/jsonlines.go +++ b/core/aggregator/jsonlines.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/jsonlines_test.go b/core/aggregator/jsonlines_test.go index 0ca60fbf9..afc1d6d9b 100644 --- a/core/aggregator/jsonlines_test.go +++ b/core/aggregator/jsonlines_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/log.go b/core/aggregator/log.go index 681dca3ee..3f79f719e 100644 --- a/core/aggregator/log.go +++ b/core/aggregator/log.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/netsample/mock_aggregator.go b/core/aggregator/netsample/mock_aggregator.go index 2b60051a1..7cd54acb9 100644 --- a/core/aggregator/netsample/mock_aggregator.go +++ b/core/aggregator/netsample/mock_aggregator.go @@ -5,8 +5,8 @@ package netsample import ( context "context" - mock "github.com/stretchr/testify/mock" core "github.com/yandex/pandora/core" + mock "github.com/stretchr/testify/mock" ) // MockAggregator is an autogenerated mock type for the Aggregator type diff --git a/core/aggregator/netsample/netsample_suite_test.go b/core/aggregator/netsample/netsample_suite_test.go deleted file mode 100644 index a243696ba..000000000 --- a/core/aggregator/netsample/netsample_suite_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package netsample - -import ( - "testing" - - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestNetsample(t *testing.T) { - ginkgoutil.RunSuite(t, "Netsample Suite") -} diff --git a/core/aggregator/netsample/phout_test.go b/core/aggregator/netsample/phout_test.go index 33f6a7bea..cc19fd153 100644 --- a/core/aggregator/netsample/phout_test.go +++ b/core/aggregator/netsample/phout_test.go @@ -3,66 +3,70 @@ package netsample import ( "context" "strings" + "testing" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/yandex/pandora/core" ) -var _ = Describe("Phout", func() { +func TestPhout(t *testing.T) { const fileName = "out.txt" - var ( - fs afero.Fs - conf PhoutConfig - testee Aggregator - ctx context.Context - cancel context.CancelFunc - runErr chan error - ) - getOutput := func() string { - data, err := afero.ReadFile(fs, fileName) - Expect(err).NotTo(HaveOccurred()) - return string(data) + + tests := []struct { + name string + resetConf func(cfg *PhoutConfig) + reportCnt int + want string + }{ + { + name: "no id by default", + reportCnt: 2, + want: strings.Repeat(testSampleNoIDPhout+"\n", 2), + }, + { + name: "id option set", + resetConf: func(cfg *PhoutConfig) { + cfg.ID = true + }, + reportCnt: 1, + want: testSamplePhout + "\n", + }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := afero.NewMemMapFs() + conf := DefaultPhoutConfig() + conf.Destination = fileName + if tt.resetConf != nil { + tt.resetConf(&conf) + } + ctx, cancel := context.WithCancel(context.Background()) - BeforeEach(func() { - fs = afero.NewMemMapFs() - conf = DefaultPhoutConfig() - conf.Destination = fileName - ctx, cancel = context.WithCancel(context.Background()) - }) - JustBeforeEach(func() { - var err error - testee, err = NewPhout(fs, conf) - Expect(err).NotTo(HaveOccurred()) - runErr = make(chan error) - go func() { - runErr <- testee.Run(ctx, core.AggregatorDeps{}) - }() - }) - It("no id by default", func() { - testee.Report(newTestSample()) - testee.Report(newTestSample()) - cancel() - Expect(<-runErr).NotTo(HaveOccurred()) - Expect(getOutput()).To(Equal(strings.Repeat(testSampleNoIDPhout+"\n", 2))) - }, 1) - Context("id option set", func() { - BeforeEach(func() { - conf.ID = true - }) - It("id printed", func() { - testee.Report(newTestSample()) + var err error + testee, err := NewPhout(fs, conf) + require.NoError(t, err) + runErr := make(chan error) + go func() { + runErr <- testee.Run(ctx, core.AggregatorDeps{}) + }() + + for i := 0; i < tt.reportCnt; i++ { + testee.Report(newTestSample()) + } cancel() - Expect(<-runErr).NotTo(HaveOccurred()) - Expect(getOutput()).To(Equal(testSamplePhout + "\n")) - }, 1) + err = <-runErr + assert.NoError(t, err) - }) + data, err := afero.ReadFile(fs, fileName) + require.NoError(t, err) -}) + assert.Equal(t, tt.want, string(data)) + }) + } +} const ( testSamplePhout = "1484660999.002 tag1|tag2#42 333333 0 0 0 0 0 0 0 13 999" diff --git a/core/aggregator/netsample/sample.go b/core/aggregator/netsample/sample.go index 14de66af0..48b634930 100644 --- a/core/aggregator/netsample/sample.go +++ b/core/aggregator/netsample/sample.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package netsample import ( diff --git a/core/aggregator/netsample/sample_test.go b/core/aggregator/netsample/sample_test.go index 94cdeef53..2c705388f 100644 --- a/core/aggregator/netsample/sample_test.go +++ b/core/aggregator/netsample/sample_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package netsample import ( diff --git a/core/aggregator/netsample/test.go b/core/aggregator/netsample/test.go index 268bdc146..72b094a58 100644 --- a/core/aggregator/netsample/test.go +++ b/core/aggregator/netsample/test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package netsample import ( diff --git a/core/aggregator/reporter.go b/core/aggregator/reporter.go index c7809e897..5a03971a7 100644 --- a/core/aggregator/reporter.go +++ b/core/aggregator/reporter.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/reporter_test.go b/core/aggregator/reporter_test.go index a1f950fec..0f00db51f 100644 --- a/core/aggregator/reporter_test.go +++ b/core/aggregator/reporter_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/aggregator/test.go b/core/aggregator/test.go index a57b667f7..930906545 100644 --- a/core/aggregator/test.go +++ b/core/aggregator/test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package aggregator import ( diff --git a/core/clientpool/pool.go b/core/clientpool/pool.go new file mode 100644 index 000000000..e078e9019 --- /dev/null +++ b/core/clientpool/pool.go @@ -0,0 +1,33 @@ +package clientpool + +import ( + "errors" + "sync/atomic" +) + +func New[T any](size int) (*Pool[T], error) { + if size <= 0 { + return nil, errors.New("pool size must be greater than zero") + } + return &Pool[T]{ + pool: make([]T, 0, size), + }, nil +} + +type Pool[T any] struct { + pool []T + i atomic.Uint64 +} + +func (p *Pool[T]) Add(conn T) { + p.pool = append(p.pool, conn) +} + +func (p *Pool[T]) Next() T { + if len(p.pool) == 0 { + var zero T + return zero + } + i := p.i.Add(1) + return p.pool[int(i)%len(p.pool)] +} diff --git a/core/config/config.go b/core/config/config.go index 37f24d2ea..06f630ac4 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/config_test.go b/core/config/config_test.go index e788b7e2f..185ea0724 100644 --- a/core/config/config_test.go +++ b/core/config/config_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/doc.go b/core/config/doc.go index 70796cc3c..bd2ae49c5 100644 --- a/core/config/doc.go +++ b/core/config/doc.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // Package config provides advanced framework to decode and validate // configuration structs. package config diff --git a/core/config/hooks.go b/core/config/hooks.go index 0fe69096b..e79b64fe5 100644 --- a/core/config/hooks.go +++ b/core/config/hooks.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/hooks_test.go b/core/config/hooks_test.go index 1694084c0..8bafa0907 100644 --- a/core/config/hooks_test.go +++ b/core/config/hooks_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/validations.go b/core/config/validations.go index 6dd310584..c912c5a47 100644 --- a/core/config/validations.go +++ b/core/config/validations.go @@ -1,6 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/validations_test.go b/core/config/validations_test.go index 0d74acc89..e3dfd3045 100644 --- a/core/config/validations_test.go +++ b/core/config/validations_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/validator.go b/core/config/validator.go index 6134d3981..8f482b3b7 100644 --- a/core/config/validator.go +++ b/core/config/validator.go @@ -1,6 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Author: Vladimir Skipor - package config import ( diff --git a/core/config/validator_test.go b/core/config/validator_test.go index bbfc6fd35..8836ccac5 100644 --- a/core/config/validator_test.go +++ b/core/config/validator_test.go @@ -1,6 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Author: Vladimir Skipor - package config import ( diff --git a/core/core.go b/core/core.go index 2fbf676a1..2006d68f6 100644 --- a/core/core.go +++ b/core/core.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // package core defines pandora engine extension points. // Core interfaces implementations MAY be used for custom engine creation and using as a library, // or MAY be registered in pandora plugin system (look at core/plugin package), for creating engine @@ -108,6 +103,8 @@ type GunDeps struct { InstanceID int PoolID string + Shared any + // TODO(skipor): https://github.com/yandex/pandora/issues/71 // Pass parallelism value. InstanceId MUST be -1 if parallelism > 1. } diff --git a/core/coretest/config.go b/core/coretest/config.go index 33204b753..d24a783a3 100644 --- a/core/coretest/config.go +++ b/core/coretest/config.go @@ -1,24 +1,30 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coretest import ( - "github.com/onsi/gomega" + "strings" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" "github.com/yandex/pandora/core/config" - "github.com/yandex/pandora/lib/ginkgoutil" ) -func Decode(data string, result interface{}) { - conf := ginkgoutil.ParseYAML(data) +func DecodeT(t *testing.T, data string, result interface{}) { + conf := ParseYAML(t, data) err := config.Decode(conf, result) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) + require.NoError(t, err) } -func DecodeAndValidate(data string, result interface{}) { - Decode(data, result) +func DecodeAndValidateT(t *testing.T, data string, result interface{}) { + DecodeT(t, data, result) err := config.Validate(result) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) + require.NoError(t, err) +} + +func ParseYAML(t *testing.T, data string) map[string]interface{} { + v := viper.New() + v.SetConfigType("yaml") + err := v.ReadConfig(strings.NewReader(data)) + require.NoError(t, err) + return v.AllSettings() } diff --git a/core/coretest/schedule.go b/core/coretest/schedule.go index 73d9511d2..11ebfaff2 100644 --- a/core/coretest/schedule.go +++ b/core/coretest/schedule.go @@ -1,36 +1,45 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coretest import ( + "testing" "time" - "github.com/onsi/gomega" + "github.com/stretchr/testify/require" "github.com/yandex/pandora/core" ) -func ExpectScheduleNextsStartAt(sched core.Schedule, startAt time.Time, nexts ...time.Duration) { +func ExpectScheduleNextsStartAt(t *testing.T, sched core.Schedule, startAt time.Time, nexts ...time.Duration) { + beforeStartLeft := sched.Left() + tokensExpected := len(nexts) - 1 // Last next is finish time. + require.Equal(t, tokensExpected, beforeStartLeft) + sched.Start(startAt) + actualNexts := DrainScheduleDuration(t, sched, startAt) + require.Equal(t, nexts, actualNexts) +} + +func ExpectScheduleNexts(t *testing.T, sched core.Schedule, nexts ...time.Duration) { + ExpectScheduleNextsStartAt(t, sched, time.Now(), nexts...) +} + +func ExpectScheduleNextsStartAtT(t *testing.T, sched core.Schedule, startAt time.Time, nexts ...time.Duration) { beforeStartLeft := sched.Left() tokensExpected := len(nexts) - 1 // Last next is finish time. - gomega.Expect(beforeStartLeft).To(gomega.Equal(tokensExpected)) + require.Equal(t, tokensExpected, beforeStartLeft) sched.Start(startAt) - actualNexts := DrainScheduleDuration(sched, startAt) - gomega.Expect(actualNexts).To(gomega.Equal(nexts)) + actualNexts := DrainScheduleDuration(t, sched, startAt) + require.Equal(t, nexts, actualNexts) } -func ExpectScheduleNexts(sched core.Schedule, nexts ...time.Duration) { - ExpectScheduleNextsStartAt(sched, time.Now(), nexts...) +func ExpectScheduleNextsT(t *testing.T, sched core.Schedule, nexts ...time.Duration) { + ExpectScheduleNextsStartAtT(t, sched, time.Now(), nexts...) } const drainLimit = 1000000 // DrainSchedule starts schedule and takes all tokens from it. // Returns all tokens and finish time relative to start -func DrainScheduleDuration(sched core.Schedule, startAt time.Time) []time.Duration { - nexts := DrainSchedule(sched) +func DrainScheduleDuration(t *testing.T, sched core.Schedule, startAt time.Time) []time.Duration { + nexts := DrainSchedule(t, sched) durations := make([]time.Duration, len(nexts)) for i, next := range nexts { durations[i] = next.Sub(startAt) @@ -40,18 +49,18 @@ func DrainScheduleDuration(sched core.Schedule, startAt time.Time) []time.Durati // DrainSchedule takes all tokens from passed schedule. // Returns all tokens and finish time. -func DrainSchedule(sched core.Schedule) []time.Time { +func DrainSchedule(t *testing.T, sched core.Schedule) []time.Time { expectedLeft := sched.Left() var nexts []time.Time for len(nexts) < drainLimit { next, ok := sched.Next() nexts = append(nexts, next) if !ok { - gomega.Expect(sched.Left()).To(gomega.Equal(0)) + require.Equal(t, 0, sched.Left()) return nexts } expectedLeft-- - gomega.Expect(sched.Left()).To(gomega.Equal(expectedLeft)) + require.Equal(t, expectedLeft, sched.Left()) } panic("drain limit reached") } diff --git a/core/coretest/sink.go b/core/coretest/sink.go index 191c44111..ffe364f81 100644 --- a/core/coretest/sink.go +++ b/core/coretest/sink.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coretest import ( diff --git a/core/coretest/source.go b/core/coretest/source.go index 9aa94bdb7..6df3d692b 100644 --- a/core/coretest/source.go +++ b/core/coretest/source.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coretest import ( diff --git a/core/coreutil/ammo.go b/core/coreutil/ammo.go index a2ce8a667..7b06a1900 100644 --- a/core/coreutil/ammo.go +++ b/core/coreutil/ammo.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/buffer_size_config.go b/core/coreutil/buffer_size_config.go index a7fac9293..a5eecf170 100644 --- a/core/coreutil/buffer_size_config.go +++ b/core/coreutil/buffer_size_config.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/buffer_size_config_test.go b/core/coreutil/buffer_size_config_test.go index aeb8fbd13..4cea80ebe 100644 --- a/core/coreutil/buffer_size_config_test.go +++ b/core/coreutil/buffer_size_config_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/data.go b/core/coreutil/data.go index 241e8ca31..f57a14a77 100644 --- a/core/coreutil/data.go +++ b/core/coreutil/data.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/doc.go b/core/coreutil/doc.go index 6ee897e82..5c7045ec8 100644 --- a/core/coreutil/doc.go +++ b/core/coreutil/doc.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // package coreutil provides utilities for core interfaces, that can be useful // not only for engine, but other core importers too. // coreutil MUST NOT depend on any core subpackage, because they may import coreutil. diff --git a/core/coreutil/sample.go b/core/coreutil/sample.go index 5c56c5c1b..7b1e29218 100644 --- a/core/coreutil/sample.go +++ b/core/coreutil/sample.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import "github.com/yandex/pandora/core" diff --git a/core/coreutil/schedule.go b/core/coreutil/schedule.go index f56537858..1a663865b 100644 --- a/core/coreutil/schedule.go +++ b/core/coreutil/schedule.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/schedule_test.go b/core/coreutil/schedule_test.go index 196f5a0d6..b1ebd831c 100644 --- a/core/coreutil/schedule_test.go +++ b/core/coreutil/schedule_test.go @@ -1,7 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. - package coreutil import ( diff --git a/core/coreutil/waiter.go b/core/coreutil/waiter.go index 972e80c12..867a10ef8 100644 --- a/core/coreutil/waiter.go +++ b/core/coreutil/waiter.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreutil import ( diff --git a/core/coreutil/waiter_test.go b/core/coreutil/waiter_test.go index 5a94ba40c..7d1fb8cf8 100644 --- a/core/coreutil/waiter_test.go +++ b/core/coreutil/waiter_test.go @@ -1,7 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. - package coreutil import ( diff --git a/core/datasink/file.go b/core/datasink/file.go index 610cff137..0341e7cb0 100644 --- a/core/datasink/file.go +++ b/core/datasink/file.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasink import ( diff --git a/core/datasink/file_test.go b/core/datasink/file_test.go index df6ef0dd9..bf4ce215e 100644 --- a/core/datasink/file_test.go +++ b/core/datasink/file_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasink import ( diff --git a/core/datasink/std.go b/core/datasink/std.go index cfae62f7d..be5d83c40 100644 --- a/core/datasink/std.go +++ b/core/datasink/std.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasink import ( diff --git a/core/datasource/file.go b/core/datasource/file.go index f5a885521..8cf662d2e 100644 --- a/core/datasource/file.go +++ b/core/datasource/file.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasource import ( diff --git a/core/datasource/file_test.go b/core/datasource/file_test.go index 714655a27..0d75952e2 100644 --- a/core/datasource/file_test.go +++ b/core/datasource/file_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasource import ( diff --git a/core/datasource/std.go b/core/datasource/std.go index a9f0aa222..2edb06953 100644 --- a/core/datasource/std.go +++ b/core/datasource/std.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package datasource import ( diff --git a/core/engine/engine.go b/core/engine/engine.go index 893528b9f..6d5476d86 100644 --- a/core/engine/engine.go +++ b/core/engine/engine.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package engine import ( @@ -113,7 +108,7 @@ func (e *Engine) Wait() { func newPool(log *zap.Logger, m Metrics, onWaitDone func(), conf InstancePoolConfig) *instancePool { log = log.With(zap.String("pool", conf.ID)) - return &instancePool{log, m, onWaitDone, conf, nil} + return &instancePool{log: log, metrics: m, onWaitDone: onWaitDone, InstancePoolConfig: conf} } type instancePool struct { @@ -121,7 +116,7 @@ type instancePool struct { metrics Metrics onWaitDone func() InstancePoolConfig - gunWarmUpResult interface{} + sharedGunDeps any } // Run start instance pool. Run blocks until fail happen, or all instances finish. @@ -174,10 +169,7 @@ func (p *instancePool) warmUpGun(ctx context.Context) error { return fmt.Errorf("can't initiate a gun: %w", err) } if gunWithWarmUp, ok := gun.(warmup.WarmedUp); ok { - p.gunWarmUpResult, err = gunWithWarmUp.WarmUp(&warmup.Options{ - Log: p.log, - Ctx: ctx, - }) + p.sharedGunDeps, err = gunWithWarmUp.WarmUp(&warmup.Options{Log: p.log, Ctx: ctx}) if err != nil { return fmt.Errorf("gun warm up failed: %w", err) } @@ -367,7 +359,7 @@ func (p *instancePool) startInstances( instanceSharedDeps: instanceSharedDeps{ provider: p.Provider, metrics: p.metrics, - gunWarmUpResult: p.gunWarmUpResult, + gunDeps: p.sharedGunDeps, aggregator: p.Aggregator, discardOverflow: p.DiscardOverflow, }, diff --git a/core/engine/instance.go b/core/engine/instance.go index da8e4ad3e..965f64512 100644 --- a/core/engine/instance.go +++ b/core/engine/instance.go @@ -1,20 +1,13 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package engine import ( "context" - "fmt" "io" "github.com/pkg/errors" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" "github.com/yandex/pandora/core/coreutil" - "github.com/yandex/pandora/core/warmup" "github.com/yandex/pandora/lib/tag" "go.uber.org/zap" ) @@ -29,7 +22,7 @@ type instance struct { func newInstance(ctx context.Context, log *zap.Logger, poolID string, id int, deps instanceDeps) (*instance, error) { log = log.With(zap.Int("instance", id)) - gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: poolID, InstanceID: id} + gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: poolID, InstanceID: id, Shared: deps.gunDeps} sched, err := deps.newSchedule() if err != nil { return nil, err @@ -38,16 +31,12 @@ func newInstance(ctx context.Context, log *zap.Logger, poolID string, id int, de if err != nil { return nil, err } - if warmedUp, ok := gun.(warmup.WarmedUp); ok { - if err := warmedUp.AcceptWarmUpResult(deps.gunWarmUpResult); err != nil { - return nil, fmt.Errorf("gun failed to accept warmup result: %w", err) - } - } + err = gun.Bind(deps.aggregator, gunDeps) if err != nil { return nil, err } - inst := &instance{log, id, gun, sched, deps.instanceSharedDeps} + inst := &instance{log: log, id: id, gun: gun, schedule: sched, instanceSharedDeps: deps.instanceSharedDeps} return inst, nil } @@ -60,7 +49,7 @@ type instanceDeps struct { type instanceSharedDeps struct { provider core.Provider metrics Metrics - gunWarmUpResult interface{} + gunDeps any aggregator core.Aggregator discardOverflow bool } diff --git a/core/engine/instance_test.go b/core/engine/instance_test.go index 9f6adf4c3..fa02cadd0 100644 --- a/core/engine/instance_test.go +++ b/core/engine/instance_test.go @@ -47,15 +47,13 @@ func Test_Instance(t *testing.T) { var justBeforeEach = func() { deps := instanceDeps{ - - newSchedule, - newGun, - instanceSharedDeps{ - provider, - metrics, - nil, - aggregator, - false, + newSchedule: newSchedule, + newGun: newGun, + instanceSharedDeps: instanceSharedDeps{ + provider: provider, + metrics: metrics, + aggregator: aggregator, + discardOverflow: false, }, } ins, insCreateErr = newInstance(ctx, newNopLogger(), "pool_0", 0, deps) diff --git a/core/import/import.go b/core/import/import.go index 8397bcad3..3659b26a9 100644 --- a/core/import/import.go +++ b/core/import/import.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coreimport import ( diff --git a/core/import/import_suite_test.go b/core/import/import_test.go similarity index 84% rename from core/import/import_suite_test.go rename to core/import/import_test.go index a83785439..49a16ec9f 100644 --- a/core/import/import_suite_test.go +++ b/core/import/import_test.go @@ -6,27 +6,22 @@ import ( "testing" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "github.com/spf13/afero" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/config" "github.com/yandex/pandora/core/coretest" "github.com/yandex/pandora/core/plugin" - "github.com/yandex/pandora/lib/ginkgoutil" "github.com/yandex/pandora/lib/testutil" "go.uber.org/zap" ) -func TestImport(t *testing.T) { +func Test_PluginConfig(t *testing.T) { defer resetGlobals() Import(afero.NewOsFs()) - ginkgoutil.RunSuite(t, "Import Suite") -} -var _ = Describe("plugin decode", func() { - Context("composite schedule", func() { + t.Run("composite schedule", func(t *testing.T) { input := func() map[string]interface{} { return map[string]interface{}{ "schedule": []map[string]interface{}{ @@ -36,27 +31,27 @@ var _ = Describe("plugin decode", func() { } } - It("plugin", func() { + t.Run("plugin", func(t *testing.T) { var conf struct { Schedule core.Schedule } err := config.Decode(input(), &conf) - Expect(err).NotTo(HaveOccurred()) - coretest.ExpectScheduleNexts(conf.Schedule, 0, 0, time.Second) + assert.NoError(t, err) + coretest.ExpectScheduleNextsT(t, conf.Schedule, 0, 0, time.Second) }) - It("plugin factory", func() { + t.Run("plugin factory", func(t *testing.T) { var conf struct { Schedule func() (core.Schedule, error) } err := config.Decode(input(), &conf) - Expect(err).NotTo(HaveOccurred()) + assert.NoError(t, err) sched, err := conf.Schedule() - Expect(err).NotTo(HaveOccurred()) - coretest.ExpectScheduleNexts(sched, 0, 0, time.Second) + assert.NoError(t, err) + coretest.ExpectScheduleNextsT(t, sched, 0, 0, time.Second) }) }) -}) +} func TestSink(t *testing.T) { defer resetGlobals() diff --git a/core/mocks/extras.go b/core/mocks/extras.go index f0cae4c83..543f3d9d3 100644 --- a/core/mocks/extras.go +++ b/core/mocks/extras.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package coremock import ( diff --git a/core/plugin/constructor.go b/core/plugin/constructor.go index 399befd7d..f3a01d20f 100644 --- a/core/plugin/constructor.go +++ b/core/plugin/constructor.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( diff --git a/core/plugin/constructor_test.go b/core/plugin/constructor_test.go index f51b94401..318d5a443 100644 --- a/core/plugin/constructor_test.go +++ b/core/plugin/constructor_test.go @@ -1,338 +1,329 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( "errors" "fmt" "reflect" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -var _ = Describe("plugin constructor", func() { - DescribeTable("expectations failed", - func(newPlugin interface{}) { - defer recoverExpectationFail() - newPluginConstructor(ptestType(), newPlugin) - }, - Entry("not func", - errors.New("that is not constructor")), - Entry("not implements", - func() struct{} { panic("") }), - Entry("too many args", - func(_, _ ptestConfig) ptestPlugin { panic("") }), - Entry("too many return valued", - func() (_ ptestPlugin, _, _ error) { panic("") }), - Entry("second return value is not error", - func() (_, _ ptestPlugin) { panic("") }), - ) - - Context("new plugin", func() { - newPlugin := func(newPlugin interface{}, maybeConf []reflect.Value) (interface{}, error) { - testee := newPluginConstructor(ptestType(), newPlugin) - return testee.NewPlugin(maybeConf) - } - - It("", func() { - plugin, err := newPlugin(ptestNew, nil) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) +func Test_PluginConstructor_ExpectationsFailed(t *testing.T) { + tests := []struct { + name string + newPlugin any + }{ + {"not func", errors.New("that is not constructor")}, + {"not implements", func() struct{} { panic("") }}, + {"too many args", func(_, _ ptestConfig) ptestPlugin { panic("") }}, + {"too many return valued", func() (_ ptestPlugin, _, _ error) { panic("") }}, + {"second return value is not error", func() (_, _ ptestPlugin) { panic("") }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer recoverExpectationFail(t) + newPluginConstructor(ptestType(), tt.newPlugin) }) + } +} - It("more that plugin", func() { - plugin, err := newPlugin(ptestNewMoreThan, nil) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) +func Test_PluginConstructor_NewPlugin(t *testing.T) { + newPlugin := func(newPlugin interface{}, maybeConf []reflect.Value) (interface{}, error) { + testee := newPluginConstructor(ptestType(), newPlugin) + return testee.NewPlugin(maybeConf) + } - It("config", func() { - plugin, err := newPlugin(ptestNewConf, confToMaybe(ptestDefaultConf())) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestDefaultValue) - }) + t.Run("", func(t *testing.T) { + plugin, err := newPlugin(ptestNew, nil) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("failed", func() { - plugin, err := newPlugin(ptestNewErrFailing, nil) - Expect(err).To(Equal(ptestCreateFailedErr)) - Expect(plugin).To(BeNil()) - }) + t.Run("more that plugin", func(t *testing.T) { + plugin, err := newPlugin(ptestNewMoreThan, nil) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) }) - Context("new factory", func() { - newFactoryOK := func(newPlugin interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) interface{} { - testee := newPluginConstructor(ptestType(), newPlugin) - factory, err := testee.NewFactory(factoryType, getMaybeConf) - Expect(err).NotTo(HaveOccurred()) - return factory - } - - It("same type - no wrap", func() { - factory := newFactoryOK(ptestNew, ptestNewType(), nil) - expectSameFunc(factory, ptestNew) - }) + t.Run("config", func(t *testing.T) { + plugin, err := newPlugin(ptestNewConf, confToMaybe(ptestDefaultConf())) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestDefaultValue) + }) - It(" new impl", func() { - factory := newFactoryOK(ptestNewImpl, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("failed", func(t *testing.T) { + plugin, err := newPlugin(ptestNewErrFailing, nil) + assert.ErrorIs(t, err, ptestCreateFailedErr) + assert.Nil(t, plugin) + }) - It("more than", func() { - factory := newFactoryOK(ptestNewMoreThan, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) +} - It("add err", func() { - factory := newFactoryOK(ptestNew, ptestNewErrType(), nil) - f, ok := factory.(func() (ptestPlugin, error)) - Expect(ok).To(BeTrue()) - plugin, err := f() - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) +func Test_PluginConstructor_NewFactory(t *testing.T) { + newFactoryOK := func(newPlugin interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) interface{} { + testee := newPluginConstructor(ptestType(), newPlugin) + factory, err := testee.NewFactory(factoryType, getMaybeConf) + require.NoError(t, err) + return factory + } - It("trim nil err", func() { - factory := newFactoryOK(ptestNewErr, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("same type - no wrap", func(t *testing.T) { + factory := newFactoryOK(ptestNew, ptestNewType(), nil) + expectSameFunc(t, factory, ptestNew) + }) - It("config", func() { - factory := newFactoryOK(ptestNewConf, ptestNewType(), confToGetMaybe(ptestDefaultConf())) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestDefaultValue) - }) + t.Run(" new impl", func(t *testing.T) { + factory := newFactoryOK(ptestNewImpl, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("new factory, get config failed", func() { - factory := newFactoryOK(ptestNewConf, ptestNewErrType(), errToGetMaybe(ptestConfigurationFailedErr)) - f, ok := factory.(func() (ptestPlugin, error)) - Expect(ok).To(BeTrue()) - plugin, err := f() - Expect(err).To(Equal(ptestConfigurationFailedErr)) - Expect(plugin).To(BeNil()) - }) + t.Run("more than", func(t *testing.T) { + factory := newFactoryOK(ptestNewMoreThan, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) + + t.Run("add err", func(t *testing.T) { + factory := newFactoryOK(ptestNew, ptestNewErrType(), nil) + f, ok := factory.(func() (ptestPlugin, error)) + assert.True(t, ok) + plugin, err := f() + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) + + t.Run("trim nil err", func(t *testing.T) { + factory := newFactoryOK(ptestNewErr, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) + + t.Run("config", func(t *testing.T) { + factory := newFactoryOK(ptestNewConf, ptestNewType(), confToGetMaybe(ptestDefaultConf())) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestDefaultValue) + }) + + t.Run("new factory, get config failed", func(t *testing.T) { + factory := newFactoryOK(ptestNewConf, ptestNewErrType(), errToGetMaybe(ptestConfigurationFailedErr)) + f, ok := factory.(func() (ptestPlugin, error)) + assert.True(t, ok) + plugin, err := f() + assert.ErrorIs(t, err, ptestConfigurationFailedErr) + assert.Nil(t, plugin) + }) - It("no err, get config failed, throw panic", func() { - factory := newFactoryOK(ptestNewConf, ptestNewType(), errToGetMaybe(ptestConfigurationFailedErr)) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - func() { - defer func() { - r := recover() - Expect(r).To(Equal(ptestConfigurationFailedErr)) - }() - f() + t.Run("no err, get config failed, throw panic", func(t *testing.T) { + factory := newFactoryOK(ptestNewConf, ptestNewType(), errToGetMaybe(ptestConfigurationFailedErr)) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + func() { + defer func() { + r := recover() + assert.Equal(t, ptestConfigurationFailedErr, r) }() - }) + f() + }() + }) - It("panic on trim non nil err", func() { - factory := newFactoryOK(ptestNewErrFailing, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - func() { - defer func() { - r := recover() - Expect(r).To(Equal(ptestCreateFailedErr)) - }() - f() + t.Run("panic on trim non nil err", func(t *testing.T) { + factory := newFactoryOK(ptestNewErrFailing, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + func() { + defer func() { + r := recover() + assert.Equal(t, ptestCreateFailedErr, r) }() + f() + }() + }) + +} + +func TestFactoryConstructorExpectationsFailed(t *testing.T) { + tests := []struct { + name string + newPlugin any + }{ + {"not func", errors.New("that is not constructor")}, + {"returned not func", func() error { panic("") }}, + {"too many args", func(_, _ ptestConfig) func() ptestPlugin { panic("") }}, + {"too many return valued", func() (func() ptestPlugin, error, error) { panic("") }}, + {"second return value is not error", func() (func() ptestPlugin, ptestPlugin) { panic("") }}, + {"factory accepts conf", func() func(config ptestConfig) ptestPlugin { panic("") }}, + {"not implements", func() func() struct{} { panic("") }}, + {"factory too many args", func() func(_, _ ptestConfig) ptestPlugin { panic("") }}, + {"factory too many return valued", func() func() (_ ptestPlugin, _, _ error) { panic("") }}, + {"factory second return value is not error", func() func() (_, _ ptestPlugin) { panic("") }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer recoverExpectationFail(t) + newFactoryConstructor(ptestType(), tt.newPlugin) }) + } +} + +func TestFactoryConstructorNewPlugin(t *testing.T) { + newPlugin := func(newFactory interface{}, maybeConf []reflect.Value) (interface{}, error) { + testee := newFactoryConstructor(ptestType(), newFactory) + return testee.NewPlugin(maybeConf) + } + t.Run("", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactory, nil) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) }) -}) - -var _ = Describe("factory constructor", func() { - DescribeTable("expectations failed", - func(newPlugin interface{}) { - defer recoverExpectationFail() - newFactoryConstructor(ptestType(), newPlugin) - }, - Entry("not func", - errors.New("that is not constructor")), - Entry("returned not func", - func() error { panic("") }), - Entry("too many args", - func(_, _ ptestConfig) func() ptestPlugin { panic("") }), - Entry("too many return valued", - func() (func() ptestPlugin, error, error) { panic("") }), - Entry("second return value is not error", - func() (func() ptestPlugin, ptestPlugin) { panic("") }), - Entry("factory accepts conf", - func() func(config ptestConfig) ptestPlugin { panic("") }), - Entry("not implements", - func() func() struct{} { panic("") }), - Entry("factory too many args", - func() func(_, _ ptestConfig) ptestPlugin { panic("") }), - Entry("factory too many return valued", - func() func() (_ ptestPlugin, _, _ error) { panic("") }), - Entry("factory second return value is not error", - func() func() (_, _ ptestPlugin) { panic("") }), - ) - - Context("new plugin", func() { - newPlugin := func(newFactory interface{}, maybeConf []reflect.Value) (interface{}, error) { - testee := newFactoryConstructor(ptestType(), newFactory) - return testee.NewPlugin(maybeConf) - } - - It("", func() { - plugin, err := newPlugin(ptestNewFactory, nil) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) - It("impl", func() { - plugin, err := newPlugin(ptestNewFactoryImpl, nil) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("impl", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactoryImpl, nil) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("impl more than", func() { - plugin, err := newPlugin(ptestNewFactoryMoreThan, nil) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("impl more than", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactoryMoreThan, nil) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("config", func() { - plugin, err := newPlugin(ptestNewFactoryConf, confToMaybe(ptestDefaultConf())) - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestDefaultValue) - }) + t.Run("config", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactoryConf, confToMaybe(ptestDefaultConf())) + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestDefaultValue) + }) - It("failed", func() { - plugin, err := newPlugin(ptestNewFactoryErrFailing, nil) - Expect(err).To(Equal(ptestCreateFailedErr)) - Expect(plugin).To(BeNil()) - }) + t.Run("failed", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactoryErrFailing, nil) + assert.ErrorIs(t, err, ptestCreateFailedErr) + assert.Nil(t, plugin) + }) - It("factory failed", func() { - plugin, err := newPlugin(ptestNewFactoryFactoryErrFailing, nil) - Expect(err).To(Equal(ptestCreateFailedErr)) - Expect(plugin).To(BeNil()) - }) + t.Run("factory failed", func(t *testing.T) { + plugin, err := newPlugin(ptestNewFactoryFactoryErrFailing, nil) + assert.ErrorIs(t, err, ptestCreateFailedErr) + assert.Nil(t, plugin) }) +} - Context("new factory", func() { - newFactory := func(newFactory interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) (interface{}, error) { - testee := newFactoryConstructor(ptestType(), newFactory) - return testee.NewFactory(factoryType, getMaybeConf) - } - newFactoryOK := func(newF interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) interface{} { - factory, err := newFactory(newF, factoryType, getMaybeConf) - Expect(err).NotTo(HaveOccurred()) - return factory - } - - It("no err, same type - no wrap", func() { - factory := newFactoryOK(ptestNewFactory, ptestNewType(), nil) - expectSameFunc(factory, ptestNew) - }) +func TestFactoryConstructorNewFactory(t *testing.T) { + newFactory := func(newFactory interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) (interface{}, error) { + testee := newFactoryConstructor(ptestType(), newFactory) + return testee.NewFactory(factoryType, getMaybeConf) + } + newFactoryOK := func(newF interface{}, factoryType reflect.Type, getMaybeConf func() ([]reflect.Value, error)) interface{} { + factory, err := newFactory(newF, factoryType, getMaybeConf) + require.NoError(t, err) + return factory + } - It("has err, same type - no wrap", func() { - factory := newFactoryOK(ptestNewFactoryFactoryErr, ptestNewErrType(), nil) - expectSameFunc(factory, ptestNewErr) - }) + t.Run("no err, same type - no wrap", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactory, ptestNewType(), nil) + expectSameFunc(t, factory, ptestNew) + }) - It("from new impl", func() { - factory := newFactoryOK(ptestNewFactoryImpl, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("has err, same type - no wrap", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryFactoryErr, ptestNewErrType(), nil) + expectSameFunc(t, factory, ptestNewErr) + }) - It("from new impl", func() { - factory := newFactoryOK(ptestNewFactoryMoreThan, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("from new impl", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryImpl, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("add err", func() { - factory := newFactoryOK(ptestNewFactory, ptestNewErrType(), nil) - f, ok := factory.(func() (ptestPlugin, error)) - Expect(ok).To(BeTrue()) - plugin, err := f() - Expect(err).NotTo(HaveOccurred()) - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("from new impl", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryMoreThan, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("factory construction not failed", func() { - factory := newFactoryOK(ptestNewFactoryErr, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("add err", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactory, ptestNewErrType(), nil) + f, ok := factory.(func() (ptestPlugin, error)) + assert.True(t, ok) + plugin, err := f() + assert.NoError(t, err) + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("trim nil err", func() { - factory := newFactoryOK(ptestNewFactoryFactoryErr, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestInitValue) - }) + t.Run("factory construction not failed", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryErr, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("config", func() { - factory := newFactoryOK(ptestNewFactoryConf, ptestNewType(), confToGetMaybe(ptestDefaultConf())) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - plugin := f() - ptestExpectConfigValue(plugin, ptestDefaultValue) - }) + t.Run("trim nil err", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryFactoryErr, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestInitValue) + }) - It("get config failed", func() { - factory, err := newFactory(ptestNewFactoryConf, ptestNewErrType(), errToGetMaybe(ptestConfigurationFailedErr)) - Expect(err).To(Equal(ptestConfigurationFailedErr)) - Expect(factory).To(BeNil()) - }) + t.Run("config", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryConf, ptestNewType(), confToGetMaybe(ptestDefaultConf())) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + plugin := f() + ptestExpectConfigValue(t, plugin, ptestDefaultValue) + }) - It("factory create failed", func() { - factory, err := newFactory(ptestNewFactoryErrFailing, ptestNewErrType(), nil) - Expect(err).To(Equal(ptestCreateFailedErr)) - Expect(factory).To(BeNil()) - }) + t.Run("get config failed", func(t *testing.T) { + factory, err := newFactory(ptestNewFactoryConf, ptestNewErrType(), errToGetMaybe(ptestConfigurationFailedErr)) + assert.Error(t, err, ptestConfigurationFailedErr) + assert.Nil(t, factory) + }) - It("plugin create failed", func() { - factory := newFactoryOK(ptestNewFactoryFactoryErrFailing, ptestNewErrType(), nil) - f, ok := factory.(func() (ptestPlugin, error)) - Expect(ok).To(BeTrue()) - plugin, err := f() - Expect(err).To(Equal(ptestCreateFailedErr)) - Expect(plugin).To(BeNil()) - }) + t.Run("factory create failed", func(t *testing.T) { + factory, err := newFactory(ptestNewFactoryErrFailing, ptestNewErrType(), nil) + assert.ErrorIs(t, err, ptestCreateFailedErr) + assert.Nil(t, factory) + }) - It("panic on trim non nil err", func() { - factory := newFactoryOK(ptestNewFactoryFactoryErrFailing, ptestNewType(), nil) - f, ok := factory.(func() ptestPlugin) - Expect(ok).To(BeTrue()) - func() { - defer func() { - r := recover() - Expect(r).To(Equal(ptestCreateFailedErr)) - }() - f() - }() - }) + t.Run("plugin create failed", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryFactoryErrFailing, ptestNewErrType(), nil) + f, ok := factory.(func() (ptestPlugin, error)) + assert.True(t, ok) + plugin, err := f() + assert.ErrorIs(t, err, ptestCreateFailedErr) + assert.Nil(t, plugin) + }) + t.Run("panic on trim non nil err", func(t *testing.T) { + factory := newFactoryOK(ptestNewFactoryFactoryErrFailing, ptestNewType(), nil) + f, ok := factory.(func() ptestPlugin) + assert.True(t, ok) + func() { + defer func() { + r := recover() + assert.Equal(t, ptestCreateFailedErr, r) + }() + f() + }() }) -}) + +} func confToMaybe(conf interface{}) []reflect.Value { if conf != nil { @@ -353,8 +344,8 @@ func errToGetMaybe(err error) func() ([]reflect.Value, error) { } } -func expectSameFunc(f1, f2 interface{}) { +func expectSameFunc(t *testing.T, f1, f2 interface{}) { s1 := fmt.Sprint(f1) s2 := fmt.Sprint(f2) - Expect(s1).To(Equal(s2)) + assert.Equal(t, s1, s2) } diff --git a/core/plugin/doc.go b/core/plugin/doc.go index 59b90d8df..fbb84e19c 100644 --- a/core/plugin/doc.go +++ b/core/plugin/doc.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // Package plugin provides a generic inversion of control model for making // extensible Go packages, libraries, and applications. Like // github.com/progrium/go-extpoints, but reflect based: doesn't require code diff --git a/core/plugin/example_test.go b/core/plugin/example_test.go index b2f1954cb..0f7cbed18 100644 --- a/core/plugin/example_test.go +++ b/core/plugin/example_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin_test import "github.com/yandex/pandora/core/plugin" diff --git a/core/plugin/plugin.go b/core/plugin/plugin.go index 1d7f00619..80bf03185 100644 --- a/core/plugin/plugin.go +++ b/core/plugin/plugin.go @@ -1,8 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( diff --git a/core/plugin/plugin_suite_test.go b/core/plugin/plugin_suite_test.go deleted file mode 100644 index 0cb918eb6..000000000 --- a/core/plugin/plugin_suite_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - -package plugin - -import ( - "testing" - - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestPlugin(t *testing.T) { - ginkgoutil.RunSuite(t, "Plugin Suite") -} diff --git a/core/plugin/plugin_test.go b/core/plugin/plugin_test.go index a3c9e37b5..e94f64d04 100644 --- a/core/plugin/plugin_test.go +++ b/core/plugin/plugin_test.go @@ -1,48 +1,49 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" ) -var _ = Describe("Default registry", func() { - BeforeEach(func() { - Register(ptestType(), ptestPluginName, ptestNewImpl) - }) - AfterEach(func() { - defaultRegistry = NewRegistry() - }) - It("lookup", func() { - Expect(Lookup(ptestType())).To(BeTrue()) - }) - It("lookup factory", func() { - Expect(LookupFactory(ptestNewErrType())).To(BeTrue()) - }) - It("new", func() { +func TestDefaultRegistry(t *testing.T) { + beforeEach := func() { Register(ptestType(), ptestPluginName, ptestNewImpl) } + afterEach := func() { defaultRegistry = NewRegistry() } + + t.Run("lookup", func(t *testing.T) { + defer afterEach() + beforeEach() + assert.True(t, Lookup(ptestType())) + }) + t.Run("lookup factory", func(t *testing.T) { + defer afterEach() + beforeEach() + assert.True(t, LookupFactory(ptestNewErrType())) + }) + t.Run("new", func(t *testing.T) { + defer afterEach() + beforeEach() plugin, err := New(ptestType(), ptestPluginName) - Expect(err).NotTo(HaveOccurred()) - Expect(plugin).NotTo(BeNil()) + assert.NoError(t, err) + assert.NotNil(t, plugin) }) - It("new factory", func() { + t.Run("new factory", func(t *testing.T) { + defer afterEach() + beforeEach() pluginFactory, err := NewFactory(ptestNewErrType(), ptestPluginName) - Expect(err).NotTo(HaveOccurred()) - Expect(pluginFactory).NotTo(BeNil()) + assert.NoError(t, err) + assert.NotNil(t, pluginFactory) }) -}) +} -var _ = Describe("type helpers", func() { - It("ptr type", func() { +func TestTypeHelpers(t *testing.T) { + t.Run("ptr type", func(t *testing.T) { var plugin ptestPlugin - Expect(PtrType(&plugin)).To(Equal(ptestType())) + assert.Equal(t, ptestType(), PtrType(&plugin)) }) - It("factory plugin type ok", func() { + t.Run("factory plugin type ok", func(t *testing.T) { factoryPlugin, ok := FactoryPluginType(ptestNewErrType()) - Expect(ok).To(BeTrue()) - Expect(factoryPlugin).To(Equal(ptestType())) + assert.True(t, ok) + assert.Equal(t, ptestType(), factoryPlugin) }) -}) +} diff --git a/core/plugin/pluginconfig/hooks.go b/core/plugin/pluginconfig/hooks.go index 4c3e44106..6b7de7e52 100644 --- a/core/plugin/pluginconfig/hooks.go +++ b/core/plugin/pluginconfig/hooks.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - // Package pluginconfig contains integration plugin with config packages. // Doing such integration in different package allows to config and plugin packages // not depend on each other, and set hooks when their are really needed. diff --git a/core/plugin/pluginconfig/hooks_test.go b/core/plugin/pluginconfig/hooks_test.go index 7237d31e2..8a909f2dd 100644 --- a/core/plugin/pluginconfig/hooks_test.go +++ b/core/plugin/pluginconfig/hooks_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package pluginconfig import ( diff --git a/core/plugin/ptest_test.go b/core/plugin/ptest_test.go index 69850d285..d76e1de1f 100644 --- a/core/plugin/ptest_test.go +++ b/core/plugin/ptest_test.go @@ -1,15 +1,11 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( "reflect" + "testing" - . "github.com/onsi/gomega" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" "github.com/yandex/pandora/core/config" ) @@ -93,16 +89,16 @@ func ptestFillConf(conf interface{}) error { return config.Decode(map[string]interface{}{"Value": ptestFilledValue}, conf) } -func ptestExpectConfigValue(conf interface{}, val string) { - conf.(ptestConfChecker).expectConfValue(val) +func ptestExpectConfigValue(t *testing.T, conf interface{}, val string) { + conf.(ptestConfChecker).expectConfValue(t, val) } type ptestConfChecker interface { - expectConfValue(string) + expectConfValue(t *testing.T, val string) } var _ ptestConfChecker = ptestConfig{} var _ ptestConfChecker = &ptestImpl{} -func (c ptestConfig) expectConfValue(val string) { Expect(c.Value).To(Equal(val)) } -func (p *ptestImpl) expectConfValue(val string) { Expect(p.Value).To(Equal(val)) } +func (c ptestConfig) expectConfValue(t *testing.T, val string) { assert.Equal(t, val, c.Value) } +func (p *ptestImpl) expectConfValue(t *testing.T, val string) { assert.Equal(t, val, p.Value) } diff --git a/core/plugin/registry.go b/core/plugin/registry.go index 4ad36d59f..0d2f3bf57 100644 --- a/core/plugin/registry.go +++ b/core/plugin/registry.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( diff --git a/core/plugin/registry_test.go b/core/plugin/registry_test.go index 5eb1da323..692fe0b48 100644 --- a/core/plugin/registry_test.go +++ b/core/plugin/registry_test.go @@ -1,301 +1,421 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package plugin import ( "io" "reflect" + "testing" "github.com/mitchellh/mapstructure" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -var _ = Describe("new default config container", func() { - DescribeTable("expectation fail", - func(constructor interface{}, newDefaultConfigOptional ...interface{}) { - newDefaultConfig := getNewDefaultConfig(newDefaultConfigOptional) - defer recoverExpectationFail() - newDefaultConfigContainer(reflect.TypeOf(constructor), newDefaultConfig) - }, - Entry("invalid type", - func(int) ptestPlugin { return nil }), - Entry("invalid ptr type", - func(*int) ptestPlugin { return nil }), - Entry("to many args", - func(_, _ ptestConfig) ptestPlugin { return nil }), - Entry("default without config", - func() ptestPlugin { return nil }, func() *ptestConfig { return nil }), - Entry("invalid default config", - func(ptestConfig) ptestPlugin { return nil }, func() *ptestConfig { return nil }), - Entry("default config accepts args", - func(*ptestConfig) ptestPlugin { return nil }, func(int) *ptestConfig { return nil }), - ) +func TestNewDefaultConfigContainerExpectationFail(t *testing.T) { + tests := []struct { + name string + constructor any + newDefaultConfigOptional []any + }{ + { + name: "invalid type", + constructor: func(int) ptestPlugin { return nil }, + }, + { + name: "invalid ptr type", + constructor: func(*int) ptestPlugin { return nil }, + }, + { + name: "to many args", + constructor: func(_, _ ptestConfig) ptestPlugin { return nil }, + }, + { + name: "default without config", + constructor: func() ptestPlugin { return nil }, + newDefaultConfigOptional: []any{func() *ptestConfig { return nil }}}, + { + name: "invalid default config", + constructor: func(ptestConfig) ptestPlugin { return nil }, + newDefaultConfigOptional: []any{func() *ptestConfig { return nil }}}, + { + name: "default config accepts args", + constructor: func(*ptestConfig) ptestPlugin { return nil }, + newDefaultConfigOptional: []any{func(int) *ptestConfig { return nil }}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newDefaultConfig := getNewDefaultConfig(tt.newDefaultConfigOptional) + defer recoverExpectationFail(t) + newDefaultConfigContainer(reflect.TypeOf(tt.constructor), newDefaultConfig) + }) + } +} + +func TestNewDefaultConfigContainerExpectationOk(t *testing.T) { + tests := []struct { + name string + constructor any + newDefaultConfigOptional []any + }{ - DescribeTable("expectation ok", - func(constructor interface{}, newDefaultConfigOptional ...interface{}) { - newDefaultConfig := getNewDefaultConfig(newDefaultConfigOptional) - container := newDefaultConfigContainer(reflect.TypeOf(constructor), newDefaultConfig) + { + name: "no default config", + constructor: ptestNewConf}, + { + name: "no default ptr config", + constructor: ptestNewPtrConf}, + { + name: "default config", + constructor: ptestNewConf, + newDefaultConfigOptional: []any{ptestDefaultConf}}, + { + name: "default ptr config", + constructor: ptestNewPtrConf, + newDefaultConfigOptional: []any{ptestNewDefaultPtrConf}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + newDefaultConfig := getNewDefaultConfig(tt.newDefaultConfigOptional) + container := newDefaultConfigContainer(reflect.TypeOf(tt.constructor), newDefaultConfig) conf, err := container.Get(ptestFillConf) - Expect(err).NotTo(HaveOccurred()) - Expect(conf).To(HaveLen(1)) - ptestExpectConfigValue(conf[0].Interface(), ptestFilledValue) - }, - Entry("no default config", - ptestNewConf), - Entry("no default ptr config", - ptestNewPtrConf), - Entry("default config", - ptestNewConf, ptestDefaultConf), - Entry("default ptr config", - ptestNewPtrConf, ptestNewDefaultPtrConf), - ) + assert.NoError(t, err) + assert.Len(t, conf, 1) + ptestExpectConfigValue(t, conf[0].Interface(), ptestFilledValue) + }) + } +} - It("fill no config failed", func() { - container := newDefaultConfigContainer(ptestNewErrType(), nil) - _, err := container.Get(ptestFillConf) - Expect(err).To(HaveOccurred()) - }) -}) +// new default config container fill no config failed +func TestNewDefault(t *testing.T) { + container := newDefaultConfigContainer(ptestNewErrType(), nil) + _, err := container.Get(ptestFillConf) + assert.Error(t, err) +} -var _ = Describe("registry", func() { - It("register name collision panics", func() { +func TestRegistry(t *testing.T) { + t.Run("register name collision panics", func(t *testing.T) { r := NewRegistry() r.ptestRegister(ptestNewImpl) - defer recoverExpectationFail() + defer recoverExpectationFail(t) r.ptestRegister(ptestNewImpl) }) - It("lookup", func() { + t.Run("lookup", func(t *testing.T) { r := NewRegistry() r.ptestRegister(ptestNewImpl) - Expect(r.Lookup(ptestType())).To(BeTrue()) - Expect(r.Lookup(reflect.TypeOf(0))).To(BeFalse()) - Expect(r.Lookup(reflect.TypeOf(&ptestImpl{}))).To(BeFalse()) - Expect(r.Lookup(reflect.TypeOf((*io.Writer)(nil)).Elem())).To(BeFalse()) + assert.True(t, r.Lookup(ptestType())) + assert.False(t, r.Lookup(reflect.TypeOf(0))) + assert.False(t, r.Lookup(reflect.TypeOf(&ptestImpl{}))) + assert.False(t, r.Lookup(reflect.TypeOf((*io.Writer)(nil)).Elem())) }) - It("lookup factory", func() { + t.Run("lookup factory", func(t *testing.T) { r := NewRegistry() r.ptestRegister(ptestNewImpl) - Expect(r.LookupFactory(ptestNewType())).To(BeTrue()) - Expect(r.LookupFactory(ptestNewErrType())).To(BeTrue()) + assert.True(t, r.LookupFactory(ptestNewType())) + assert.True(t, r.LookupFactory(ptestNewErrType())) - Expect(r.LookupFactory(reflect.TypeOf(0))).To(BeFalse()) - Expect(r.LookupFactory(reflect.TypeOf(&ptestImpl{}))).To(BeFalse()) - Expect(r.LookupFactory(reflect.TypeOf((*io.Writer)(nil)).Elem())).To(BeFalse()) + assert.False(t, r.LookupFactory(reflect.TypeOf(0))) + assert.False(t, r.LookupFactory(reflect.TypeOf(&ptestImpl{}))) + assert.False(t, r.LookupFactory(reflect.TypeOf((*io.Writer)(nil)).Elem())) }) +} -}) - -var _ = Describe("new", func() { +func TestNew(t *testing.T) { type New func(r *Registry, fillConfOptional ...func(conf interface{}) error) (interface{}, error) - var ( - r *Registry - testNew New - testNewOk = func(fillConfOptional ...func(conf interface{}) error) (pluginVal string) { - plugin, err := testNew(r, fillConfOptional...) - Expect(err).NotTo(HaveOccurred()) - return plugin.(*ptestImpl).Value - } - ) - BeforeEach(func() { r = NewRegistry() }) - runTestCases := func() { - Context("plugin constructor", func() { - It("no conf", func() { + + testNewOk := func(t *testing.T, r *Registry, testNew New, fillConfOptional ...func(conf interface{}) error) (pluginVal string) { + plugin, err := testNew(r, fillConfOptional...) + require.NoError(t, err) + return plugin.(*ptestImpl).Value + } + + tests := []struct { + name string + + assert func(t *testing.T, r *Registry, testNew New) + }{ + { + name: "plugin constructor. no conf", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewImpl) - Expect(testNewOk()).To(Equal(ptestInitValue)) - }) - It("nil error", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestInitValue, got) + }, + }, + { + name: "plugin conf: nil error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewErr) - Expect(testNewOk()).To(Equal(ptestInitValue)) - }) - It("non-nil error", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestInitValue, got) + }, + }, + { + name: "plugin conf: non-nil error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewErrFailing) _, err := testNew(r) - Expect(err).To(HaveOccurred()) - err = errors.Cause(err) - Expect(ptestCreateFailedErr).To(Equal(err)) - }) - It("no conf, fill conf error", func() { + assert.Error(t, err) + assert.ErrorIs(t, err, ptestCreateFailedErr) + }, + }, + { + name: "plugin conf: no conf, fill conf error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewImpl) expectedErr := errors.New("fill conf err") _, err := testNew(r, func(_ interface{}) error { return expectedErr }) - Expect(expectedErr).To(Equal(err)) - }) - It("no default", func() { + assert.ErrorIs(t, err, expectedErr) + }, + }, + { + name: "plugin conf: no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewConf) - Expect(testNewOk()).To(Equal("")) - }) - It("default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "plugin conf: default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewConf, ptestDefaultConf) - Expect(testNewOk()).To(Equal(ptestDefaultValue)) - }) - It("fill conf default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestDefaultValue, got) + }, + }, + { + name: "plugin conf: fill conf default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewConf, ptestDefaultConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("fill conf no default", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "plugin conf: fill conf no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("fill ptr conf no default", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "plugin conf: fill ptr conf no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewPtrConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("no default ptr conf not nil", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "plugin conf: no default ptr conf not nil", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewPtrConf) - Expect("").To(Equal(testNewOk())) - }) - It("nil default, conf not nil", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "plugin conf: nil default, conf not nil", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewPtrConf, func() *ptestConfig { return nil }) - Expect(testNewOk()).To(Equal("")) - }) - It("fill nil default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "plugin conf: fill nil default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewPtrConf, func() *ptestConfig { return nil }) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("more than one fill conf panics", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "plugin conf: more than one fill conf panics", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewPtrConf) - defer recoverExpectationFail() + defer recoverExpectationFail(t) testNew(r, ptestFillConf, ptestFillConf) - }) - }) - - Context("factory constructor", func() { - It("no conf", func() { + }, + }, + { + name: "factory constructor; no conf", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactory) - Expect(testNewOk()).To(Equal(ptestInitValue)) - }) - It("nil error", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestInitValue, got) + }, + }, + { + name: "factory constructor; nil error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(func() (ptestPlugin, error) { return ptestNewImpl(), nil }) - Expect(testNewOk()).To(Equal(ptestInitValue)) - }) - It("non-nil error", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestInitValue, got) + }, + }, + { + name: "factory constructor; non-nil error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryFactoryErrFailing) _, err := testNew(r) - Expect(err).To(HaveOccurred()) - err = errors.Cause(err) - Expect(ptestCreateFailedErr).To(Equal(err)) - }) - It("no conf, fill conf error", func() { + assert.Error(t, err) + assert.ErrorIs(t, err, ptestCreateFailedErr) + }, + }, + { + name: "factory constructor; no conf, fill conf error", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactory) expectedErr := errors.New("fill conf err") _, err := testNew(r, func(_ interface{}) error { return expectedErr }) - Expect(expectedErr).To(Equal(err)) - }) - It("no default", func() { + assert.ErrorIs(t, err, expectedErr) + }, + }, + { + name: "factory constructor; no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryConf) - Expect(testNewOk()).To(Equal("")) - }) - It("default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "factory constructor; default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryConf, ptestDefaultConf) - Expect(testNewOk()).To(Equal(ptestDefaultValue)) - }) - It("fill conf default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, ptestDefaultValue, got) + }, + }, + { + name: "factory constructor; fill conf default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryConf, ptestDefaultConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("fill conf no default", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "factory constructor; fill conf no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("fill ptr conf no default", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "factory constructor; fill ptr conf no default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryPtrConf) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("no default ptr conf not nil", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "factory constructor; no default ptr conf not nil", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryPtrConf) - Expect("").To(Equal(testNewOk())) - }) - It("nil default, conf not nil", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "factory constructor; nil default, conf not nil", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryPtrConf, func() *ptestConfig { return nil }) - Expect(testNewOk()).To(Equal("")) - }) - It("fill nil default", func() { + got := testNewOk(t, r, testNew) + assert.Equal(t, "", got) + }, + }, + { + name: "factory constructor; fill nil default", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryPtrConf, func() *ptestConfig { return nil }) - Expect(testNewOk(ptestFillConf)).To(Equal(ptestFilledValue)) - }) - It("more than one fill conf panics", func() { + got := testNewOk(t, r, testNew, ptestFillConf) + assert.Equal(t, ptestFilledValue, got) + }, + }, + { + name: "factory constructor; more than one fill conf panics", + assert: func(t *testing.T, r *Registry, testNew New) { r.ptestRegister(ptestNewFactoryPtrConf) - defer recoverExpectationFail() + defer recoverExpectationFail(t) testNew(r, ptestFillConf, ptestFillConf) - }) - }) + }, + }, } - Context("use New", func() { - BeforeEach(func() { testNew = (*Registry).ptestNew }) - runTestCases() - - }) - Context("use NewFactory", func() { - BeforeEach(func() { testNew = (*Registry).ptestNewFactory }) - runTestCases() - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegistry() + testNew := (*Registry).ptestNew + tt.assert(t, r, testNew) -}) + r = NewRegistry() + testNew = (*Registry).ptestNewFactory + tt.assert(t, r, testNew) + }) + } +} -var _ = Describe("decode", func() { - It("ok", func() { - r := NewRegistry() - const nameKey = "type" +func TestDecode(t *testing.T) { + r := NewRegistry() + const nameKey = "type" - var hook mapstructure.DecodeHookFunc - decode := func(input, result interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - DecodeHook: hook, - ErrorUnused: true, - Result: result, - }) - if err != nil { - return err - } - return decoder.Decode(input) + var hook mapstructure.DecodeHookFunc + decode := func(input, result interface{}) error { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: hook, + ErrorUnused: true, + Result: result, + }) + if err != nil { + return err } - hook = mapstructure.ComposeDecodeHookFunc( - func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { - if !r.Lookup(to) { - return data, nil - } - // NOTE: could be map[interface{}]interface{} here. - input := data.(map[string]interface{}) - // NOTE: should be case insensitive behaviour. - pluginName := input[nameKey].(string) - delete(input, nameKey) - return r.New(to, pluginName, func(conf interface{}) error { - // NOTE: should error, if conf has "type" field. - return decode(input, conf) - }) + return decoder.Decode(input) + } + hook = mapstructure.ComposeDecodeHookFunc( + func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + if !r.Lookup(to) { + return data, nil + } + // NOTE: could be map[interface{}]interface{} here. + input := data.(map[string]interface{}) + // NOTE: should be case insensitive behaviour. + pluginName := input[nameKey].(string) + delete(input, nameKey) + return r.New(to, pluginName, func(conf interface{}) error { + // NOTE: should error, if conf has "type" field. + return decode(input, conf) }) + }) - r.Register(ptestType(), "my-plugin", ptestNewConf, ptestDefaultConf) - input := map[string]interface{}{ - "plugin": map[string]interface{}{ - nameKey: "my-plugin", - "value": ptestFilledValue, - }, - } - type Config struct { - Plugin ptestPlugin - } - var conf Config - err := decode(input, &conf) - Expect(err).NotTo(HaveOccurred()) - actualValue := conf.Plugin.(*ptestImpl).Value - Expect(actualValue).To(Equal(ptestFilledValue)) - }) - -}) + r.Register(ptestType(), "my-plugin", ptestNewConf, ptestDefaultConf) + input := map[string]interface{}{ + "plugin": map[string]interface{}{ + nameKey: "my-plugin", + "value": ptestFilledValue, + }, + } + type Config struct { + Plugin ptestPlugin + } + var conf Config + err := decode(input, &conf) + assert.NoError(t, err) + actualValue := conf.Plugin.(*ptestImpl).Value + assert.Equal(t, ptestFilledValue, actualValue) +} -func recoverExpectationFail() { +func recoverExpectationFail(t *testing.T) { r := recover() - Expect(r).NotTo(BeNil()) - Expect(r).To(ContainSubstring("expectation failed")) + assert.NotNil(t, r) + assert.Contains(t, r, "expectation failed") } diff --git a/core/provider/chunk_decoder.go b/core/provider/chunk_decoder.go index 2fd851ca7..0e8edab36 100644 --- a/core/provider/chunk_decoder.go +++ b/core/provider/chunk_decoder.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( diff --git a/core/provider/decoder.go b/core/provider/decoder.go index 01a923c38..ad7321b5c 100644 --- a/core/provider/decoder.go +++ b/core/provider/decoder.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( diff --git a/core/provider/json.go b/core/provider/json.go index 820a991f0..2b7646a8f 100644 --- a/core/provider/json.go +++ b/core/provider/json.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( diff --git a/core/provider/json_test.go b/core/provider/json_test.go index 1690d0086..d44a510c1 100644 --- a/core/provider/json_test.go +++ b/core/provider/json_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( @@ -15,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/datasource" + "github.com/yandex/pandora/lib/testutil" ) type testJSONAmmo struct { @@ -166,3 +162,7 @@ func TestDecoderReset(t *testing.T) { assert.Equal(t, "first", val.Data) assert.Zero(t, val.ID) } + +func testDeps() core.ProviderDeps { + return core.ProviderDeps{Log: testutil.NewLogger()} +} diff --git a/core/provider/num.go b/core/provider/num.go index 772153b76..607706b3a 100644 --- a/core/provider/num.go +++ b/core/provider/num.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( diff --git a/core/provider/num_test.go b/core/provider/num_test.go index caf7242f6..a21e4ccf6 100644 --- a/core/provider/num_test.go +++ b/core/provider/num_test.go @@ -2,13 +2,13 @@ package provider import ( "context" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" "github.com/yandex/pandora/core" ) -var _ = Describe("Num", func() { +func Test_Num(t *testing.T) { var ( limit int @@ -17,47 +17,52 @@ var _ = Describe("Num", func() { cancel context.CancelFunc runRes chan error ) - BeforeEach(func() { + beforeEach := func() { limit = 0 runRes = make(chan error) - }) - JustBeforeEach(func() { + } + justBeforeEach := func() { ctx, cancel = context.WithCancel(context.Background()) p = NewNumConf(NumConfig{limit}) go func() { runRes <- p.Run(ctx, core.ProviderDeps{}) }() - }) + } + + t.Run("unlimited", func(t *testing.T) { + beforeEach() + justBeforeEach() - It("unlimited", func() { for i := 0; i < 100; i++ { a, ok := p.Acquire() - Expect(ok).To(BeTrue()) - Expect(a).To(Equal(i)) + assert.True(t, ok) + assert.Equal(t, i, a) } cancel() - Expect(<-runRes).To(BeNil()) + + res := <-runRes + assert.NoError(t, res) + a, ok := p.Acquire() - Expect(ok).To(BeFalse()) - Expect(a).To(BeNil()) - }, 1) - - Context("unlimited", func() { - BeforeEach(func() { - limit = 50 - }) - It("", func() { - for i := 0; i < limit; i++ { - a, ok := p.Acquire() - Expect(ok).To(BeTrue()) - Expect(a).To(Equal(i)) - } + assert.False(t, ok) + assert.Nil(t, a) + }) + + t.Run("unlimited", func(t *testing.T) { + beforeEach() + limit = 50 + justBeforeEach() + + for i := 0; i < limit; i++ { a, ok := p.Acquire() - Expect(ok).To(BeFalse()) - Expect(a).To(BeNil()) - Expect(<-runRes).To(BeNil()) - }, 1) + assert.True(t, ok) + assert.Equal(t, i, a) + } + res := <-runRes + assert.NoError(t, res) + a, ok := p.Acquire() + assert.False(t, ok) + assert.Nil(t, a) }) - -}) +} diff --git a/core/provider/provider_suite_test.go b/core/provider/provider_suite_test.go deleted file mode 100644 index 1de38db7a..000000000 --- a/core/provider/provider_suite_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package provider - -import ( - "testing" - - "github.com/yandex/pandora/core" - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestProvider(t *testing.T) { - ginkgoutil.RunSuite(t, "AmmoQueue Suite") -} - -func testDeps() core.ProviderDeps { - return core.ProviderDeps{Log: ginkgoutil.NewLogger()} -} diff --git a/core/provider/queue.go b/core/provider/queue.go index ef208d5d6..aa337ab3e 100644 --- a/core/provider/queue.go +++ b/core/provider/queue.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package provider import ( diff --git a/core/register/register.go b/core/register/register.go index 3fd301d0e..e0e97db91 100644 --- a/core/register/register.go +++ b/core/register/register.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package register import ( diff --git a/core/schedule/composite.go b/core/schedule/composite.go index 249ab70c7..fd02f3f95 100644 --- a/core/schedule/composite.go +++ b/core/schedule/composite.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/composite_test.go b/core/schedule/composite_test.go index b2df0f0c5..ec1b65f71 100644 --- a/core/schedule/composite_test.go +++ b/core/schedule/composite_test.go @@ -1,33 +1,28 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( "sync" + "testing" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/coretest" "go.uber.org/atomic" ) -var _ = Describe("composite", func() { - It("empty", func() { +func Test_composite(t *testing.T) { + t.Run("empty", func(t *testing.T) { testee := NewComposite() - coretest.ExpectScheduleNexts(testee, 0) + coretest.ExpectScheduleNexts(t, testee, 0) }) - It("only", func() { + t.Run("only", func(t *testing.T) { testee := NewComposite(NewConst(1, time.Second)) - coretest.ExpectScheduleNexts(testee, 0, time.Second) + coretest.ExpectScheduleNexts(t, testee, 0, time.Second) }) - It("composite", func() { + t.Run("composite", func(t *testing.T) { testee := NewComposite( NewConst(1, 2*time.Second), NewOnce(2), @@ -35,7 +30,7 @@ var _ = Describe("composite", func() { NewOnce(0), NewOnce(1), ) - coretest.ExpectScheduleNexts(testee, + coretest.ExpectScheduleNexts(t, testee, 0, time.Second, @@ -48,7 +43,7 @@ var _ = Describe("composite", func() { }) // Load concurrently, and let race detector do it's work. - It("race", func() { + t.Run("race", func(t *testing.T) { var ( nexts []core.Schedule tokensGot atomic.Int64 @@ -80,10 +75,10 @@ var _ = Describe("composite", func() { }() } wg.Wait() - Expect(tokensGot.Load()).To(Equal(tokensExpected)) + assert.Equal(t, tokensExpected, tokensGot.Load()) }) - It("left with unknown", func() { + t.Run("left with unknown", func(t *testing.T) { unlimitedDuration := time.Second testee := NewComposite( NewUnlimited(unlimitedDuration), @@ -91,23 +86,23 @@ var _ = Describe("composite", func() { NewConst(1, 2*time.Second), NewOnce(1), ) - Expect(testee.Left()).To(Equal(-1)) + assert.Equal(t, -1, testee.Left()) startAt := time.Now().Add(-unlimitedDuration) testee.Start(startAt) unlimitedFinish := startAt.Add(unlimitedDuration) sched := testee.(*compositeSchedule).scheds[0] - Expect(sched.(*unlimitedSchedule).finish.Load()).To(Equal(unlimitedFinish)) + assert.Equal(t, unlimitedFinish, sched.(*unlimitedSchedule).finish.Load()) - Expect(testee.Left()).To(Equal(3)) + assert.Equal(t, 3, testee.Left()) - actualNexts := coretest.DrainScheduleDuration(testee, unlimitedFinish) + actualNexts := coretest.DrainScheduleDuration(t, testee, unlimitedFinish) expectedNests := []time.Duration{ 0, time.Second, 2 * time.Second, 2 * time.Second, // Finish. } - Expect(actualNexts).To(Equal(expectedNests)) + assert.Equal(t, expectedNests, actualNexts) }) -}) +} diff --git a/core/schedule/const.go b/core/schedule/const.go index c7dc92fb0..c09a422e4 100644 --- a/core/schedule/const.go +++ b/core/schedule/const.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/do_at.go b/core/schedule/do_at.go index dddcaf696..099451585 100644 --- a/core/schedule/do_at.go +++ b/core/schedule/do_at.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/instance_step.go b/core/schedule/instance_step.go index 9724854ef..10c9c9c74 100644 --- a/core/schedule/instance_step.go +++ b/core/schedule/instance_step.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/line.go b/core/schedule/line.go index 4e9860ebe..c49bbb42f 100644 --- a/core/schedule/line.go +++ b/core/schedule/line.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/once.go b/core/schedule/once.go index 67e3be2e9..12d35e258 100644 --- a/core/schedule/once.go +++ b/core/schedule/once.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/schedule_suite_test.go b/core/schedule/schedule_suite_test.go deleted file mode 100644 index aea7fe558..000000000 --- a/core/schedule/schedule_suite_test.go +++ /dev/null @@ -1,263 +0,0 @@ -package schedule - -import ( - "sort" - "testing" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/yandex/pandora/core" - "github.com/yandex/pandora/core/coretest" - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestSchedule(t *testing.T) { - ginkgoutil.RunSuite(t, "Schedule Suite") -} - -var _ = Describe("unlimited", func() { - It("", func() { - conf := UnlimitedConfig{50 * time.Millisecond} - testee := NewUnlimitedConf(conf) - start := time.Now() - finish := start.Add(conf.Duration) - Expect(testee.Left()).To(BeEquivalentTo(-1)) - testee.Start(start) - var i int - for prev := time.Now(); ; i++ { - left := testee.Left() - x, ok := testee.Next() - if !ok { - break - } - Expect(left).To(BeEquivalentTo(-1)) - Expect(x).To(BeTemporally(">", prev)) - Expect(x).To(BeTemporally("<", finish)) - } - Expect(testee.Left()).To(BeEquivalentTo(0)) - Expect(i).To(BeNumerically(">", 50)) - }) -}) - -var _ = Describe("once", func() { - It("started", func() { - testee := NewOnce(1) - coretest.ExpectScheduleNexts(testee, 0, 0) - }) - - It("unstarted", func() { - testee := NewOnce(1) - start := time.Now() - x1, ok := testee.Next() - threshold := time.Since(start) - - Expect(ok).To(BeTrue()) - Expect(x1).To(BeTemporally("~", start, threshold)) - - x2, ok := testee.Next() - Expect(ok).To(BeFalse()) - Expect(x2).To(Equal(x1)) - }) - -}) - -var _ = Describe("const", func() { - var ( - conf ConstConfig - testee core.Schedule - underlying *doAtSchedule - ) - - JustBeforeEach(func() { - testee = NewConstConf(conf) - underlying = testee.(*doAtSchedule) - }) - - Context("non-zero ops", func() { - BeforeEach(func() { - conf = ConstConfig{ - Ops: 1, - Duration: 2 * time.Second, - } - }) - It("", func() { - Expect(underlying.n).To(BeEquivalentTo(2)) - coretest.ExpectScheduleNexts(testee, 0, time.Second, 2*time.Second) - }) - }) - - Context("zero ops", func() { - BeforeEach(func() { - conf = ConstConfig{ - Ops: 0, - Duration: 2 * time.Second, - } - }) - It("", func() { - Expect(underlying.n).To(BeEquivalentTo(0)) - coretest.ExpectScheduleNexts(testee, 2*time.Second) - }) - }) -}) - -var _ = Describe("line", func() { - var ( - conf LineConfig - testee core.Schedule - underlying *doAtSchedule - ) - - JustBeforeEach(func() { - testee = NewLineConf(conf) - underlying = testee.(*doAtSchedule) - }) - - Context("too small ops", func() { - BeforeEach(func() { - conf = LineConfig{ - From: 0, - To: 1.999, - Duration: time.Second, - } - }) - It("", func() { - // Too small ops, so should not do anything. - Expect(underlying.n).To(BeEquivalentTo(0)) - coretest.ExpectScheduleNexts(testee, time.Second) - }) - }) - - Context("const ops", func() { - BeforeEach(func() { - conf = LineConfig{ - From: 1, - To: 1, - Duration: 2 * time.Second, - } - }) - - It("", func() { - Expect(underlying.n).To(BeEquivalentTo(2)) - coretest.ExpectScheduleNexts(testee, 0, time.Second, 2*time.Second) - }) - }) - - Context("zero start", func() { - BeforeEach(func() { - conf = LineConfig{ - From: 0, - To: 1, - Duration: 2 * time.Second, - } - }) - - It("", func() { - Expect(underlying.n).To(BeEquivalentTo(1)) - coretest.ExpectScheduleNexts(testee, 0, 2*time.Second) - }) - }) - - Context("non zero start", func() { - BeforeEach(func() { - conf = LineConfig{ - From: 2, - To: 8, - Duration: 2 * time.Second, - } - }) - - It("", func() { - Expect(underlying.n).To(BeEquivalentTo(10)) - start := time.Now() - testee.Start(start) - - var ( - i int - xs []time.Time - x time.Time - ) - for ok := true; ok; i++ { - x, ok = testee.Next() - xs = append(xs, x) - } - Expect(i).To(Equal(11)) - Expect(sort.SliceIsSorted(xs, func(i, j int) bool { - return xs[i].Before(xs[j]) - })).To(BeTrue()) - Expect(start.Add(conf.Duration)).To(Equal(xs[len(xs)-1])) - }) - }) - -}) - -var _ = Describe("step", func() { - It("", func() { - conf := StepConfig{ - From: 1, - To: 2, - Step: 1, - Duration: 2 * time.Second, - } - testee := NewStepConf(conf) - Expect(testee.Left()).To(Equal(6)) - - }) - -}) - -var _ = Describe("instance_step", func() { - It("", func() { - conf := InstanceStepConfig{ - From: 1, - To: 3, - Step: 1, - StepDuration: 2 * time.Second, - } - testee := NewInstanceStepConf(conf) - Expect(testee.Left()).To(Equal(3)) - - }) - -}) - -func BenchmarkLineSchedule(b *testing.B) { - schedule := NewLine(0, float64(b.N), 2*time.Second) - benchmarkScheduleNext(b, schedule) -} - -func BenchmarkLineScheduleParallel(b *testing.B) { - schedule := NewLine(0, float64(b.N), 2*time.Second) - benchmarkScheduleNextParallel(b, schedule) -} - -func BenchmarkUnlimitedSchedule(b *testing.B) { - schedule := NewUnlimited(time.Minute) - benchmarkScheduleNext(b, schedule) -} - -func BenchmarkUnlimitedScheduleParallel(b *testing.B) { - schedule := NewUnlimited(time.Minute) - benchmarkScheduleNextParallel(b, schedule) -} - -func benchmarkScheduleNextParallel(b *testing.B, schedule core.Schedule) { - run := func(pb *testing.PB) { - for pb.Next() { - schedule.Next() - } - } - schedule.Start(time.Now()) - b.ReportAllocs() - b.ResetTimer() - b.RunParallel(run) -} - -func benchmarkScheduleNext(b *testing.B, schedule core.Schedule) { - schedule.Start(time.Now()) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - schedule.Next() - } -} diff --git a/core/schedule/schedule_test.go b/core/schedule/schedule_test.go new file mode 100644 index 000000000..ca2ab1fe1 --- /dev/null +++ b/core/schedule/schedule_test.go @@ -0,0 +1,239 @@ +package schedule + +import ( + "sort" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/yandex/pandora/core" + "github.com/yandex/pandora/core/coretest" +) + +func Test_unlimited(t *testing.T) { + conf := UnlimitedConfig{50 * time.Millisecond} + testee := NewUnlimitedConf(conf) + start := time.Now() + finish := start.Add(conf.Duration) + assert.Equal(t, -1, testee.Left()) + testee.Start(start) + var i int + for prev := time.Now(); ; i++ { + left := testee.Left() + x, ok := testee.Next() + if !ok { + break + } + assert.Equal(t, -1, left) + assert.True(t, x.After(prev)) + assert.True(t, x.Before(finish)) + } + assert.Equal(t, 0, testee.Left()) + assert.Greater(t, i, 50) +} + +func TestOnce(t *testing.T) { + t.Run("started", func(t *testing.T) { + testee := NewOnce(1) + coretest.ExpectScheduleNexts(t, testee, 0, 0) + }) + + t.Run("unstarted", func(t *testing.T) { + testee := NewOnce(1) + start := time.Now() + x1, ok := testee.Next() + threshold := time.Since(start) + + assert.True(t, ok) + assert.WithinDuration(t, start, x1, threshold) + + x2, ok := testee.Next() + assert.False(t, ok) + assert.Equal(t, x1, x2) + }) + +} + +func TestConst(t *testing.T) { + tests := []struct { + name string + conf ConstConfig + wantN int64 + wantAssertNext []time.Duration + }{ + { + name: "non-zero ops", + conf: ConstConfig{ + Ops: 1, + Duration: 2 * time.Second, + }, + wantN: 2, + wantAssertNext: []time.Duration{ + 0, time.Second, 2 * time.Second, + }, + }, + { + name: "zero ops", + conf: ConstConfig{ + Ops: 0, + Duration: 2 * time.Second, + }, + wantN: 0, + wantAssertNext: []time.Duration{ + 2 * time.Second, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + testee := NewConstConf(tt.conf) + underlying := testee.(*doAtSchedule) + + assert.Equal(t, tt.wantN, underlying.n) + coretest.ExpectScheduleNexts(t, testee, tt.wantAssertNext...) + }) + } +} + +func TestLine(t *testing.T) { + tests := []struct { + name string + conf LineConfig + wantN int64 + wantAssertNext []time.Duration + assert func(t *testing.T, testee core.Schedule, underlying *doAtSchedule) + }{ + { + name: "too small ops", + conf: LineConfig{ + From: 0, + To: 1.999, + Duration: time.Second, + }, + wantN: 0, + wantAssertNext: []time.Duration{time.Second}, + }, + { + name: "const ops", + conf: LineConfig{ + From: 1, + To: 1, + Duration: 2 * time.Second, + }, + wantN: 2, + wantAssertNext: []time.Duration{0, time.Second, 2 * time.Second}, + }, + { + name: "zero start", + conf: LineConfig{ + From: 0, + To: 1, + Duration: 2 * time.Second, + }, + wantN: 1, + wantAssertNext: []time.Duration{0, 2 * time.Second}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testee := NewLineConf(tt.conf) + underlying := testee.(*doAtSchedule) + + assert.Equal(t, tt.wantN, underlying.n) + coretest.ExpectScheduleNexts(t, testee, tt.wantAssertNext...) + }) + } +} + +func TestLineNonZeroStart(t *testing.T) { + testee := NewLineConf(LineConfig{ + From: 2, + To: 8, + Duration: 2 * time.Second, + }) + underlying := testee.(*doAtSchedule) + + assert.Equal(t, int64(10), underlying.n) + + start := time.Now() + testee.Start(start) + + var ( + i int + xs []time.Time + x time.Time + ) + for ok := true; ok; i++ { + x, ok = testee.Next() + xs = append(xs, x) + } + assert.Equal(t, 11, i) + assert.True(t, sort.SliceIsSorted(xs, func(i, j int) bool { + return xs[i].Before(xs[j]) + })) + assert.Equal(t, xs[len(xs)-1], start.Add(2*time.Second)) +} + +func TestStep(t *testing.T) { + conf := StepConfig{ + From: 1, + To: 2, + Step: 1, + Duration: 2 * time.Second, + } + testee := NewStepConf(conf) + assert.Equal(t, 6, testee.Left()) +} + +func TestInstanceStep(t *testing.T) { + conf := InstanceStepConfig{ + From: 1, + To: 3, + Step: 1, + StepDuration: 2 * time.Second, + } + testee := NewInstanceStepConf(conf) + assert.Equal(t, 3, testee.Left()) +} + +func BenchmarkLineSchedule(b *testing.B) { + schedule := NewLine(0, float64(b.N), 2*time.Second) + benchmarkScheduleNext(b, schedule) +} + +func BenchmarkLineScheduleParallel(b *testing.B) { + schedule := NewLine(0, float64(b.N), 2*time.Second) + benchmarkScheduleNextParallel(b, schedule) +} + +func BenchmarkUnlimitedSchedule(b *testing.B) { + schedule := NewUnlimited(time.Minute) + benchmarkScheduleNext(b, schedule) +} + +func BenchmarkUnlimitedScheduleParallel(b *testing.B) { + schedule := NewUnlimited(time.Minute) + benchmarkScheduleNextParallel(b, schedule) +} + +func benchmarkScheduleNextParallel(b *testing.B, schedule core.Schedule) { + run := func(pb *testing.PB) { + for pb.Next() { + schedule.Next() + } + } + schedule.Start(time.Now()) + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(run) +} + +func benchmarkScheduleNext(b *testing.B, schedule core.Schedule) { + schedule.Start(time.Now()) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + schedule.Next() + } +} diff --git a/core/schedule/start_sync.go b/core/schedule/start_sync.go index 43f35682b..a525eef7a 100644 --- a/core/schedule/start_sync.go +++ b/core/schedule/start_sync.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/step.go b/core/schedule/step.go index 7f48598ca..9898cda57 100644 --- a/core/schedule/step.go +++ b/core/schedule/step.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/schedule/unlilmited.go b/core/schedule/unlilmited.go index 680e8ec66..990c4d3a2 100644 --- a/core/schedule/unlilmited.go +++ b/core/schedule/unlilmited.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package schedule import ( diff --git a/core/warmup/interface.go b/core/warmup/interface.go index 6438074bf..e794d8021 100644 --- a/core/warmup/interface.go +++ b/core/warmup/interface.go @@ -2,5 +2,4 @@ package warmup type WarmedUp interface { WarmUp(*Options) (interface{}, error) - AcceptWarmUpResult(interface{}) error } diff --git a/docs/eng/architecture.md b/docs/eng/architecture.md index 5e3ff3de1..1f0bf1206 100644 --- a/docs/eng/architecture.md +++ b/docs/eng/architecture.md @@ -28,11 +28,9 @@ Pandora is a set of components talking to each other through Go channels. There Ammo Provider knows how to make an ammo object from an ammo file or other external resource. Instances get ammo objects from Ammo Provider. -### Instances Pool +### Instance Pool -Instances Pool manages the creation of Instances. You can think of one Instance as a single user that sends requests to -a server sequentially. All Instances from one Instances Pool get their ammo from one Ammo Provider. Instances creation -times are controlled by Startup Scheduler. All Instances from one Instances Pool also have Guns of the same type. +Instance Pool manages the creation of Instances. You can think of one Instance as a single user that sends requests to a server sequentially. All Instances from one Instance Pool get their ammo from one Ammo Provider. Instances creation times are controlled by Startup Scheduler. All Instances from one Instance Pool also have Guns of the same type. ### Scheduler diff --git a/docs/eng/best-practices.md b/docs/eng/best-practices.md new file mode 100644 index 000000000..ae2721152 --- /dev/null +++ b/docs/eng/best-practices.md @@ -0,0 +1,4 @@ +# Практики использования + +- [RPS per instance](./best_practices/rps-per-instance.md) +- [Shared client](best_practices/shared-client.md) \ No newline at end of file diff --git a/docs/eng/best_practices/rps-per-instance.md b/docs/eng/best_practices/rps-per-instance.md new file mode 100644 index 000000000..575d8ac1b --- /dev/null +++ b/docs/eng/best_practices/rps-per-instance.md @@ -0,0 +1,48 @@ +[Home](../../index.md) + +--- + +# RPS per instance + +Usually in tests, when we increase the speed of requests submitted to the target service by specifying the `line`, `const`, `step` scheme in the rps section, +then in the `startup` section we specify the `once` scheme, because we want all instances to be available from the very beginning of the test in order to generate the load we need. + +In tests with scenario load, when we have each instance describing virtual user behavior, then in the `startup` section we can specify a smooth growth of the number of users, for example, with the scheme `instance_step`, increasing their number step by step, or `const`, increasing the number of users at a constant rate. +The `rps-per-instance` instance pool setting can be used for this purpose. It is useful for the script generator when we want to limit the speed of each user in rps. + +For example we specify `const` and enable `rps-per-instance`, then by increasing users via `instance_step` we simulate the real user load. + + +Example: + +```yaml +pools: + - id: "scenario" + ammo: + type: http/scenario + file: http_payload.hcl + result: + type: discard + gun: + target: localhost:443 + type: http/scenario + answlog: + enabled: false + rps-per-instance: true + rps: + - type: const + duration: 1m + ops: 4 + startup: + type: instance_step + from: 10 + to: 100 + step: 10 + stepduration: 10s +log: + level: error +``` + +--- + +[Home](../../index.md) diff --git a/docs/eng/best_practices/shared-client.md b/docs/eng/best_practices/shared-client.md new file mode 100644 index 000000000..4e4ab842c --- /dev/null +++ b/docs/eng/best_practices/shared-client.md @@ -0,0 +1,37 @@ +[Home](../../index.md) + +--- + +# Use shared client + +## General principle + +### Transport client + +By default, Pandora components automatically assign it a transport client, such as for http, grpc, or tcp, when creating +a new Instance. When the Instance starts, it opens a connection, such as a tcp connection. +Normally, clients can use multiple connections at the same time, but in the case of Pandora, +each Instance opens only one connection as the Instance makes requests one after the other. + +It's interesting to note that creating a connection doesn't mean that requests will go through that connection. Why? +In the test configuration, you can specify a large number of instances and a small number of RPSs. +The Pandora provider generates requests with the frequency specified in the RPS settings +and sends them to a random instance so that the instance will execute the request. + +## `shared-client`. +In the [http](../http-generator.md) and [grpc](../grpc-generator.md) generator settings, +you can specify the `shared-client.enabled=true` parameter. If you enable this setting, +all instances will use a shared transport client and each will not have to create its own. + +## `shared-client.client-number`. + +The transport client uses to connect the connection. For example, HTTP and gRPC use a tcp connection. +A single client uses multiple connections and can create additional connections if needed. + +But under heavy loads there may be a situation when the client does not have time to create connections. +You can increase the speed of connection creation by a common client by increasing the `shared-client.client-number` parameter. +By default `shared-client.client-number=1`. + +--- + +[Home](../../index.md) diff --git a/docs/eng/config.md b/docs/eng/config.md index 87f8e2e08..23007c302 100644 --- a/docs/eng/config.md +++ b/docs/eng/config.md @@ -26,6 +26,8 @@ pools: type: phout # report format (phout is compatible with Yandex.Tank) destination: ./phout.log # report file name + rps-per-instance: false # rps section is counted for each instance or for the whole test. false - for the whole test + rps: # shooting schedule type: line # linear growth from: 1 # from 1 response per second diff --git a/docs/eng/grpc-generator.md b/docs/eng/grpc-generator.md index 31313a0a7..fc13bdca1 100644 --- a/docs/eng/grpc-generator.md +++ b/docs/eng/grpc-generator.md @@ -8,19 +8,55 @@ Full gRPC generator config ```yaml gun: - type: http + type: grpc target: '[hostname]:443' - timeout: 15s - tls: true + timeout: 15s # Grpc request timeout. Default: 15s + tls: false # If true, Pandora accepts any certificate presented by the server and any host name in that certificate. Default: false + shared-client: + enabled: false # If TRUE, the generator will use a common transport client for all instances + client-number: 1 # The number of shared clients can be increased. The default is 1 dial_options: - timeout: 1s - authority: string + authority: some.host # Specifies the value to be used as the :authority pseudo-header and as the server name in authentication handshake + timeout: 1s # Timeout for dialing GRPC connect. Default: 1s answlog: enabled: true path: ./answ.log filter: all # all - all http codes, warning - log 4xx and 5xx, error - log only 5xx. Default: error ``` +## Mapping Response Codes + +Pandora uses the gRPC client from google.golang.org/grpc as a client (https://github.com/grpc/grpc-go) + +But to unify reports it converts them into HTTP codes. + +### Mapping table gPRC StatusCode -> HTTP StatusCode + +| gRPC Status Name | gRPC Status Code | HTTP Status Code | +|--------------------|------------------|------------------| +| OK | 0 | 200 | +| Canceled | 1 | 499 | +| InvalidArgument | 3 | 400 | +| DeadlineExceeded | 4 | 504 | +| NotFound | 5 | 404 | +| AlreadyExists | 6 | 409 | +| PermissionDenied | 7 | 403 | +| ResourceExhausted | 8 | 429 | +| FailedPrecondition | 9 | 400 | +| Aborted | 10 | 409 | +| OutOfRange | 11 | 400 | +| Unimplemented | 12 | 501 | +| Unavailable 14 | 14 | 503 | +| Unauthenticated 16 | 16 | 401 | +| unknown | - | 500 | + + +# References + +- Best practices + - [RPS per instance](best_practices/rps-per-instance.md) + - [Shared client](best_practices/shared-client.md) + --- [Home](index.md) diff --git a/docs/eng/http-generator.md b/docs/eng/http-generator.md index bd73b2a09..fd64ff4e3 100644 --- a/docs/eng/http-generator.md +++ b/docs/eng/http-generator.md @@ -20,6 +20,9 @@ gun: idle-conn-timeout: 90s # Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit. Default: 90s response-header-timeout: 0 # Amount of time to wait for a server's response headers after fully writing the request (including its body, if any). Zero means no timeout. Default: 0 expect-continue-timeout: 1s # Amount of time to wait for a server's first response headers after fully writing the request headers if the request has an "Expect: 100-continue" header. Zero means no timeout. Default: 1s + shared-client: + enabled: false # If TRUE, the generator will use a common transport client for all instances + client-number: 1 # The number of shared clients can be increased. The default is 1 dial: timeout: 1s # TCP connect timeout. Default: 3s dns-cache: true # Enable DNS cache, remember remote address on first try, and use it in the future. Default: true @@ -39,6 +42,12 @@ gun: trace: true # calculate different request stages: connect time, send time, latency, request bytes ``` +# References + +- Best practices + - [RPS per instance](best_practices/rps-per-instance.md) + - [Shared client](best_practices/shared-client.md) + --- [Home](../index.md) diff --git a/docs/eng/load-profile.md b/docs/eng/load-profile.md index 61c3dc29d..bc89d1b16 100644 --- a/docs/eng/load-profile.md +++ b/docs/eng/load-profile.md @@ -6,27 +6,36 @@ To determine what load to create on the server, use a load profile. It sets how the load will be changed and maintained. -Most tools have their own formats for defining load profiles. When configuring a test via the UI, you can get the final profile by adding sections in the correct order. - - -## line +## const -Linearly increases the load in a given range over a certain period of time. +Maintains the specified load for a certain time. Example: -``` -{duration: 180s, type: line, from: 1, to: 10000} # the load increases from 1 to 10000 requests per second over 180 seconds +generates 10000 requests per second for 300 seconds + +```yaml +rps: + type: const + duration: 300s + from: 1 + ops: 10000 ``` -## const +## line -Maintains the specified load for a certain time. +Linearly increases the load in a given range over a certain period of time. Example: -``` -{duration: 300s, type: const, ops: 10000} # generates 10000 requests per second for 300 seconds +the load increases from 1 to 10000 requests per second over 180 seconds + +```yaml +rps: + type: line + duration: 180s + from: 1 + to: 10000 ``` ## step @@ -35,8 +44,15 @@ Increases the load with the specified increment size from one value to another f Example: -``` -{duration: 30s, type: step, from: 10, to: 100, step: 5} # the load increases from 10 to 100 requests per second in increments of 5 and with a step duration of 30 seconds +the load increases from 10 to 100 requests per second in increments of 5 and with a step duration of 30 seconds + +```yaml +rps: + type: step + duration: 30s + from: 10 + to: 100 + step: 5 ``` ## once @@ -45,8 +61,12 @@ Sends the specified number of requests once and completes the test. There are no Example: -``` -{type: once, times: 133} # sends 133 requests at the start of this test section and completes the test +sends 133 requests at the start of this test section and completes the test + +```yaml +rps: + type: once + times: 133 ``` ## unlimited @@ -55,8 +75,12 @@ Sends as many requests as the target can accept within the established connectio Example: -``` -{type: unlimited, duration: 30s} # unlimited load for 30 seconds +unlimited load for 30 seconds + +```yaml +rps: + type: unlimited + duration: 30s ``` --- diff --git a/docs/eng/scenario-grpc-generator.md b/docs/eng/scenario-grpc-generator.md index fd68578f4..b0b78ca4e 100644 --- a/docs/eng/scenario-grpc-generator.md +++ b/docs/eng/scenario-grpc-generator.md @@ -21,6 +21,7 @@ - [assert/response](#assertresponse) - [Scenarios](#scenarios) - [Sources](#sources) +- [References](#references) ## Configuration @@ -43,8 +44,8 @@ The minimum generator configuration is as follows ```yaml gun: - type: http/scenario - target: localhost:80 + type: grpc/scenario + target: localhost:8888 ``` For a scenario gRPC generator, all settings of a regular gRPC generator are supported [gRPC generator](grpc-generator.md) @@ -284,6 +285,13 @@ More - [scenario in HTTP generator](./scenario-http-generator.md#scenarios) Follow - [Variable sources](scenario/variable_source.md) +# References + +- [HTTP generator](http-generator.md) +- Best practices + - [RPS per instance](best_practices/rps-per-instance.md) + - [Shared client](best_practices/shared-client.md) + --- [Home](../index.md) diff --git a/docs/eng/scenario-http-generator.md b/docs/eng/scenario-http-generator.md index 301090145..16fedb3ec 100644 --- a/docs/eng/scenario-http-generator.md +++ b/docs/eng/scenario-http-generator.md @@ -23,6 +23,7 @@ - [assert/response](#assertresponse) - [Scenarios](#scenarios) - [Sources](#sources) +- [References](#references) ## Configuration @@ -403,6 +404,14 @@ scenario "scenario_second" { Follow - [Variable sources](scenario/variable_source.md) +# References + +- [HTTP generator](http-generator.md) +- Best practices + - [RPS per instance](best_practices/rps-per-instance.md) + - [Shared client](best_practices/shared-client.md) + + --- [Home](../index.md) diff --git a/docs/eng/startup.md b/docs/eng/startup.md new file mode 100644 index 000000000..5660cc812 --- /dev/null +++ b/docs/eng/startup.md @@ -0,0 +1,95 @@ +[Home](index.md) + +--- + +# Instance startup profile + +You can control the profile of instance starts. + +This section can be thought of as how many instances you need, and how quickly they will be available to you. + +Types of Instance startup profile: + +- [once](#once) +- [const](#const) +- [instance_step](#instance_step) +- [composite](#composite) + +#### Note: you cannot reduce the number of running instances + +The startup profile only works to create instances. That is, Pandora does not delete instances until the test is complete. + +## once + +A specified number of instances are created once. + +**Example**: + +creating 10 instances at the start of this test section + +```yaml +startup: + type: once + times: 10 +``` + +## const + +Creating instances at a certain speed. + +**Example**: + +creating 5 instances every second for 60 seconds. As a result, 300 instances will be created after 60 seconds + +```yaml +startup: + type: const + duration: 60s + ops: 5 +``` + +## instance_step + +Creates instances in periodic increments. + +**Example**: + +10 instances are created every 10 seconds. As a result, 100 instances will be created after 100 seconds + +```yaml +startup: + type: instance_step + from: 10 + to: 100 + step: 10 + stepduration: 10s +``` + +## composite + +Composite startup profile is a possibility of arbitrary combination of the above described profiles. + +**Example**: + +Implement a single step [instance_step](#instance_step) using once and const. +- 10 instances are created +- No instances are created within 10 seconds(_ops: 0_) +- 10 instances are created. +- As a result, 20 instances will be created and will run until the entire test is complete + +```yaml +startup: + - type: once + times: 10 + - type: const + ops: 0 + duration: 10s + - type: once + times: 10 +``` + + + +--- + +[Home](index.md) diff --git a/docs/eng/tuturial.md b/docs/eng/tutorial.md similarity index 100% rename from docs/eng/tuturial.md rename to docs/eng/tutorial.md diff --git a/docs/index.md b/docs/index.md index bdf9fccb7..4c08062f3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,13 +7,16 @@ write your own load scenarios in Go, compiling them just before your test. - [Installation](eng/install.md) - [Configuration](eng/config.md) -- [Your first test](eng/tuturial.md) +- [Your first test](eng/tutorial.md) - [Load profile](eng/load-profile.md) +- [Instance startup profile](eng/startup.md) - [HTTP providers](eng/providers.md) - [HTTP generators](eng/http-generator.md) - [Scenario generator / HTTP](eng/scenario-http-generator.md) +- [gRPC generators](eng/grpc-generator.md) - [Scenario generator / gRPC](eng/scenario-grpc-generator.md) - [Custom guns](eng/custom.md) +- [Best practices](eng/best-practices.md) - [Pandora’s performance](eng/performance.md) - [Architectural overview](eng/architecture.md) diff --git a/docs/rus/architecture.md b/docs/rus/architecture.md index 874482291..76139543d 100644 --- a/docs/rus/architecture.md +++ b/docs/rus/architecture.md @@ -28,11 +28,12 @@ Pandora - это набор компонентов, взаимодействую Ammo Provider знает, как создать объект Payload из payload файла (ammo file) или другого внешнего ресурса. И их задача передать Payload Instance'у. См метод `func (p *Provider) Acquire() (core.Ammo, bool)` -### Instances Pool +### Instance Pool -Instances Pool manages the creation of Instances. You can think of one Instance as a single user that sends requests to -a server sequentially. All Instances from one Instances Pool get their ammo from one Ammo Provider. Instances creation -times are controlled by Startup Scheduler. All Instances from one Instances Pool also have Guns of the same type. +**Пул инстансов** управляет созданием **инстансов**. Один инстанс можно представить как одного пользователя, который +**последовательно** отправляет запросы на сервер. Все инстансы из одного пула инстансов получают данные от одного +**провайдера**. Время создания инстанса контролируется **планировщиком**. Все инстансы из одного пула инстансов имеют генераторы +одного типа. ### Scheduler diff --git a/docs/rus/best-practices.md b/docs/rus/best-practices.md new file mode 100644 index 000000000..d4294e564 --- /dev/null +++ b/docs/rus/best-practices.md @@ -0,0 +1,4 @@ +# Практики использования + +- [RPS на инстанс](best_practices/rps-per-instance.md) +- [Общий транспорт](best_practices/shared-client.md) \ No newline at end of file diff --git a/docs/rus/best_practices.md b/docs/rus/best_practices.md new file mode 100644 index 000000000..65bc489bb --- /dev/null +++ b/docs/rus/best_practices.md @@ -0,0 +1,3 @@ +# Практики использования + +- [RPS per instance](./best_practices/rps_per_instance.md) diff --git a/docs/rus/best_practices/rps-per-instance.md b/docs/rus/best_practices/rps-per-instance.md new file mode 100644 index 000000000..1e53e985b --- /dev/null +++ b/docs/rus/best_practices/rps-per-instance.md @@ -0,0 +1,47 @@ +[Домой](../index.md) + +--- + +# RPS на инстанс + +Обычно в тестах, когда мы увеличиваем скорость запросов, подаваемых на тестируемый севис, указывая схему `line`, `const`, `step` в секции rps, +то в секции `startup` мы указываем схему `once`, т.к. хотим, чтобы с самого начала теста нам были доступны все инстансы для того, чтобы сгенерить нужную нам нагрузку. + +В тестах со сценарной нагрузкой, когда у нас каждый инстанс описыает поведение пользователя, то в секции `startup` можно указывать плавный рост количества пользователей, например схемой `instance_step`, увеличивая ступенчато их кол-во, или `const`, увеличивая пользователей с постоянной скоростью. +Для этого можно использовать настройку пула инстансов `rps-per-instance`. Она полезна для сценарного генератора, когда мы хотим ограничить скорость каждого пользователя в rps. + +Например укажем `const` и включим `rps-per-instance`, то потом увеличивая пользователей через `instance_step`, мы имитируем реальную пользовательскую нагрузку. + +Пример: + +```yaml +pools: + - id: "scenario" + ammo: + type: http/scenario + file: http_payload.hcl + result: + type: discard + gun: + target: localhost:443 + type: http/scenario + answlog: + enabled: false + rps-per-instance: true + rps: + - type: const + duration: 1m + ops: 4 + startup: + type: instance_step + from: 10 + to: 100 + step: 10 + stepduration: 10s +log: + level: error +``` + +--- + +[Домой](../index.md) diff --git a/docs/rus/best_practices/shared-client.md b/docs/rus/best_practices/shared-client.md new file mode 100644 index 000000000..186012d38 --- /dev/null +++ b/docs/rus/best_practices/shared-client.md @@ -0,0 +1,37 @@ +[Домой](../index.md) + +--- +# Использовать общий транспорт + +## Общий принцип + +### Транспортный клиент + +По умолчанию, компоненты Pandora при создании нового Инстанса автоматически назначают ему транспортного клиента, +например, для http, grpc или tcp. Когда Инстанс запускается, он открывает соединение, например, tcp соединение. +Обычно, клиенты могут использовать несколько соединений одновременно, но в случае с Pandora, +каждый Инстанс открывает только одно соединение, так как Инстанс выполняет запросы один за другим. + +Стоит заметить, что создание соединения не означает, что запросы будут идти через это соединение. Почему? +В настройках теста можно указать большое кол-во инстансов и маленькое кол-во RPS. +Провайдер Пандора генерирует запросы с частотой указаной в настройках RPS и передает на случайный инстанс, чтобы +инстанс выполнил этот запрос. + +## `shared-client` + +В настройках генераторов [http](../http-generator.md) и [grpc](../grpc-generator.md) можно указать параметр `shared-client.enabled=true`. +Если включить данную настройку, то все инстансы будут использовать общий транспортный клиент и каждый не будет +создавать собственный. + +## `shared-client.client-number` + +Транспортный клиент использует для подключения соединения. Например, HTTP и gRPC используют tcp соединение. +Один клиент использует нескоклько соединений. И может создавать дополнительные при непобходимости. + +Но при больших нагрузках может возникнуть ситуация, когда клиент не успевает создать соединения. +Можно увеличить, скорость создания соединений общим клиентом, увеличив параметро `shared-client.client-number`. +По умолчанию `shared-client.client-number=1` + +--- + +[Домой](../index.md) diff --git a/docs/rus/config.md b/docs/rus/config.md index ec31523eb..6fdb54c8f 100644 --- a/docs/rus/config.md +++ b/docs/rus/config.md @@ -15,26 +15,28 @@ Pandora поддерживает файлы конфигурации в форм ```yaml pools: - - id: HTTP pool # pool name (for your choice) + - id: HTTP pool # идентификатор инстанс пула gun: - type: http # gun type - target: example.com:80 # gun target + type: http # тип генератора + target: example.com:80 # ... далее идут настройки генератора. Зависят от его типа ammo: - type: uri # ammo format - file: ./ammo.uri # ammo File + type: uri # тип провайдера + file: ./ammo.uri # ... далее идут настройки провайдера. Зависят от его типа result: - type: phout # report format (phout is compatible with Yandex.Tank) + type: phout # тип агрегатора (phout - совместим Yandex.Tank) destination: ./phout.log # report file name - rps: # shooting schedule - type: line # linear growth - from: 1 # from 1 response per second - to: 5 # to 5 responses per second - duration: 60s # for 60 seconds + rps-per-instance: false # секция rps считается для каждого инстанса или для всего теста. false - для всего теста - startup: # instances startup schedule - type: once # start 10 instances - times: 10 + rps: # планировщик нагрузки + type: line # тип планировщика + from: 1 # ... далее идут настройки планировщика. Зависят от его типа + to: 5 + duration: 60s + + startup: # запуск инстансов + type: once # тип профиля запуска инстансов + times: 10 # ... далее идут настройки планировщика. Зависят от его типа ``` ## Мониторинг и логирование diff --git a/docs/rus/custom.md b/docs/rus/custom.md index 85be8f633..770b920a7 100644 --- a/docs/rus/custom.md +++ b/docs/rus/custom.md @@ -1,4 +1,4 @@ -[Home](index.md) +[К содержанию](index.md) --- @@ -452,4 +452,4 @@ func main() { --- -[Home](index.md) \ No newline at end of file +[К содержанию](index.md) \ No newline at end of file diff --git a/docs/rus/grpc-generator.md b/docs/rus/grpc-generator.md index 1ec3d6e77..55b6e022d 100644 --- a/docs/rus/grpc-generator.md +++ b/docs/rus/grpc-generator.md @@ -8,19 +8,55 @@ ```yaml gun: - type: http + type: grpc target: '[hostname]:443' - timeout: 15s - tls: true + timeout: 15s # Таймаут для запросов gRPC. По умолчанию: 15s + tls: false # Если true, Pandora принимает любой сертификат, представленный сервером, и любое имя хоста в этом сертификате. По умолчанию: false + shared-client: + enabled: true # Если TRUE, генератор будет использовать общий транспортный клиент для всех инстансов + client-number: 1 # Количество общих клиентов можно увеличить. По умолчанию 1 dial_options: - timeout: 1s - authority: string + authority: some.host # Указывает значение, которое будет использоваться в качестве псевдозаголовка :authority и имени сервера в процессе аутентификации. + timeout: 1s # Таймаут установки gRPC соединения. По умолчанию: 1s answlog: enabled: true path: ./answ.log - filter: all # all - all http codes, warning - log 4xx and 5xx, error - log only 5xx. Default: error + filter: all # all - все http-коды, warning - логировать 4xx и 5xx, error - логировать только 5xx. По умолчанию: error ``` +## Маппинг кодов ответа + +В качестве клиента Пандора использует gRPC клиент от google.golang.org/grpc (https://github.com/grpc/grpc-go) + +Но для унификации графиков преобразует их в HTTP коды. + +### Таблица маппинга gPRC StatusCode -> HTTP StatusCode + +| gRPC Status Name | gRPC Status Code | HTTP Status Code | +|--------------------|------------------|------------------| +| OK | 0 | 200 | +| Canceled | 1 | 499 | +| InvalidArgument | 3 | 400 | +| DeadlineExceeded | 4 | 504 | +| NotFound | 5 | 404 | +| AlreadyExists | 6 | 409 | +| PermissionDenied | 7 | 403 | +| ResourceExhausted | 8 | 429 | +| FailedPrecondition | 9 | 400 | +| Aborted | 10 | 409 | +| OutOfRange | 11 | 400 | +| Unimplemented | 12 | 501 | +| Unavailable 14 | 14 | 503 | +| Unauthenticated 16 | 16 | 401 | +| unknown | - | 500 | + + +# Смотри так же + +- Практики использования + - [RPS на инстанс](best_practices/rps-per-instance.md) + - [Общий транспорт](best_practices/shared-client.md) + --- [Домой](index.md) diff --git a/docs/rus/http-generator.md b/docs/rus/http-generator.md index 80aedbd52..7523e786e 100644 --- a/docs/rus/http-generator.md +++ b/docs/rus/http-generator.md @@ -20,6 +20,9 @@ gun: idle-conn-timeout: 90s # Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. Zero means no limit. Default: 90s response-header-timeout: 0 # Amount of time to wait for a server's response headers after fully writing the request (including its body, if any). Zero means no timeout. Default: 0 expect-continue-timeout: 1s # Amount of time to wait for a server's first response headers after fully writing the request headers if the request has an "Expect: 100-continue" header. Zero means no timeout. Default: 1s + shared-client: + enabled: true # Если TRUE, генератор будет использовать общий транспортный клиент для всех инстансов + client-number: 1 # Количество общих клиентов можно увеличить. По умолчанию 1 dial: timeout: 1s # TCP connect timeout. Default: 3s dns-cache: true # Enable DNS cache, remember remote address on first try, and use it in the future. Default: true @@ -39,6 +42,12 @@ gun: trace: true # calculate different request stages: connect time, send time, latency, request bytes ``` +# Смотри так же + +- Практики использования + - [RPS на инстанс](best_practices/rps-per-instance.md) + - [Общий транспорт](best_practices/shared-client.md) + --- [Домой](index.md) diff --git a/docs/rus/index.md b/docs/rus/index.md index 33159a117..d47e81d92 100644 --- a/docs/rus/index.md +++ b/docs/rus/index.md @@ -6,13 +6,16 @@ Pandora - это высокопроизводительный генератор - [Установка](install.md) - [Конфигурация](config.md) -- [Первый тест](tuturial.md) +- [Первый тест](tutorial.md) - [Профиль нагрузки](load-profile.md) +- [Профиль создание инстансов](startup.md) - [HTTP providers](providers.md) - [HTTP генератор](http-generator.md) - [Сценарный генератор / HTTP](scenario-http-generator.md) +- [gRPC генератор](grpc-generator.md) - [Сценарный генератор / gRPC](scenario-grpc-generator.md) - [Custom](custom.md) +- [Практики использования](best-practices.md) - [Производительность Pandora](performance.md) - [Архитектура](architecture.md) diff --git a/docs/rus/load-profile.md b/docs/rus/load-profile.md index 2488978b6..76c54a8e3 100644 --- a/docs/rus/load-profile.md +++ b/docs/rus/load-profile.md @@ -1,4 +1,4 @@ -[Home](index.md) +[К содержанию](index.md) --- @@ -6,27 +6,36 @@ Чтобы определить, какую нагрузку подавать на сервер, используется профиль нагрузки. Профиль определяет, как будет изменяться и поддерживаться нагрузка. -В большинстве инструментов есть свои форматы для определения профиля нагрузки. При конфигурации теста через UI можно собрать итоговый профиль, добавляя участки в правильном порядке. - -## line +## const -Линейно увеличивает нагрузку в заданном диапазоне за определенный период времени. +Поддерживает указанную нагрузку определенное время. Пример: -``` -{duration: 180s, type: line, from: 1, to: 10000} # увеличение нагрузки от 1 до 10 000 запросов в секунду за 180 секунд +подача 10 000 запросов в секунду в течение 300 секунд + +```yaml +rps: + type: const + duration: 300s + from: 1 + ops: 10000 ``` -## const +## line -Поддерживает указанную нагрузку определенное время. +Линейно увеличивает нагрузку в заданном диапазоне за определенный период времени. Пример: -``` -{duration: 300s, type: const, ops: 10000} # подача 10 000 запросов в секунду в течение 300 секунд +увеличение нагрузки от 1 до 10 000 запросов в секунду за 180 секунд +```yaml +rps: + type: line + duration: 180s + from: 1 + to: 10000 ``` ## step @@ -35,8 +44,15 @@ Пример: -``` -{duration: 30s, type: step, from: 10, to: 100, step: 5} # увеличение нагрузки от 10 до 100 запросов в секунду с шагом 5 и длительностью шага 30 секунд +увеличение нагрузки от 10 до 100 запросов в секунду с шагом 5 и длительностью шага 30 секунд + +```yaml +rps: + type: step + duration: 30s + from: 10 + to: 100 + step: 5 ``` ## once @@ -45,19 +61,28 @@ Пример: -``` -{type: once, times: 133} # отправка 133 запросов на старте этого участка теста и завершение теста +отправка 133 запросов на старте этого участка теста и завершение теста + +```yaml +rps: + type: once + times: 133 ``` ## unlimited + Передает столько запросов, сколько может принять цель в рамках установленных соединений без ограничений в течение указанного времени. Пример: -``` -Передает столько запросов, сколько может принять цель в рамках установленных соединений без ограничений в течение указанного времени +максимальное кол-во rps втечение 30 секунд + +```yaml +rps: + type: unlimited + duration: 30s ``` --- -[Home](index.md) +[К содержанию](index.md) diff --git a/docs/rus/performance.md b/docs/rus/performance.md index 3d49e66da..e92954248 100644 --- a/docs/rus/performance.md +++ b/docs/rus/performance.md @@ -1,4 +1,4 @@ -[Home](index.md) +[К содержанию](index.md) --- @@ -102,4 +102,4 @@ to do this. --- -[Home](index.md) +[К содержанию](index.md) diff --git a/docs/rus/providers.md b/docs/rus/providers.md index a6269a41e..c9ad1a6bb 100644 --- a/docs/rus/providers.md +++ b/docs/rus/providers.md @@ -1,4 +1,4 @@ -[Home](index.md) +[К содержанию](index.md) --- @@ -234,4 +234,4 @@ pools: --- -[Home](index.md) +[К содержанию](index.md) diff --git a/docs/rus/scenario-grpc-generator.md b/docs/rus/scenario-grpc-generator.md index e1be97652..7cb7730c3 100644 --- a/docs/rus/scenario-grpc-generator.md +++ b/docs/rus/scenario-grpc-generator.md @@ -21,6 +21,7 @@ - [assert/response](#assertresponse) - [Scenarios](#scenarios) - [Sources](#sources) +- [Смотри так же](#cмотри-так-же) ## Конфигурация @@ -43,8 +44,8 @@ pools: ```yaml gun: - type: http/scenario - target: localhost:80 + type: grpc/scenario + target: localhost:8888 ``` Для сценарного генератора поддерживаются все настройки обычного [gRPC генератора](grpc-generator.md) @@ -285,6 +286,14 @@ scenario "scenario_name" { См документ - [Источники переменных](scenario/variable_source.md) +# Смотри так же + +- [HTTP генератор](http-generator.md) +- Практики использования + - [RPS на инстанс](best_practices/rps-per-instance.md) + - [Общий транспорт](best_practices/shared-client.md) + + --- [Домой](index.md) diff --git a/docs/rus/scenario-http-generator.md b/docs/rus/scenario-http-generator.md index bb2889606..79c3d08c3 100644 --- a/docs/rus/scenario-http-generator.md +++ b/docs/rus/scenario-http-generator.md @@ -23,6 +23,7 @@ - [assert/response](#assertresponse) - [Scenarios](#scenarios) - [Sources](#sources) +- [Смотри так же](#cмотри-так-же) ## Конфигурация @@ -406,6 +407,15 @@ scenario "scenario_second" { См документ - [Источники переменных](scenario/variable_source.md) +# Смотри так же + +- [HTTP генератор](http-generator.md) +- Практики использования + - [RPS на инстанс](best_practices/rps-per-instance.md) + - [Общий транспорт](best_practices/shared-client.md) + + + --- [Домой](index.md) diff --git a/docs/rus/startup.md b/docs/rus/startup.md new file mode 100644 index 000000000..4177dec8d --- /dev/null +++ b/docs/rus/startup.md @@ -0,0 +1,95 @@ +[К содержанию](index.md) + +--- + +# Профиль создание инстансов + +Вы можете контролировать профиль создания инстансов. + +Данную секцию можно воспринимать как то, сколько инстансов вам необходимо, и как быстро они будут вам доступны. + +Варианты правил создания Инстансов: + +- [once](#once) +- [const](#const) +- [instance_step](#instance_step) +- [composite](#composite) + +#### Замечание: нельзя уменьшить количество работающих инстансов + +Профили создания инстансов работают только на создание, то есть Пандора не удаляет инстансы до окончания теста. + +## once + +Разово создается указанное количество инстансов. + +**Пример**: + +создание 10 инстансов на старте этого участка теста + +```yaml +startup: + type: once + times: 10 +``` + +## const + +Создание инстансов с определенной скоростью. + +**Пример**: + +создание 5 инстансов каждую секунду на протяжении 60 секунд. В результате через 60 секунд будет создано 300 инстансов + +```yaml +startup: + type: const + duration: 60s + ops: 5 +``` + +## instance_step + +Создает инстансы с периодическим шагом. + +**Пример**: + +каждые 10 секунд создается 10 инстансов. В результате через 100 секунд будет создано 100 инстансов + +```yaml +startup: + type: instance_step + from: 10 + to: 100 + step: 10 + stepduration: 10s +``` + +## composite + +Композитное создание инстансов - это возможность произвольной комбинации вышеописанных профилей. + +**Пример**: + +Реализация одного шага [instance_step](#instance_step) с помощью once и const. +- Создается 10 инстансов +- В течение 10 секунд инстансы не создаются (_ops: 0_) +- Создается 10 инстансов. +- Итого 20, которые будут работать до окончания всего теста + +```yaml +startup: + - type: once + times: 10 + - type: const + ops: 0 + duration: 10s + - type: once + times: 10 +``` + + + +--- + +[К содержанию](index.md) diff --git a/docs/rus/tuturial.md b/docs/rus/tutorial.md similarity index 95% rename from docs/rus/tuturial.md rename to docs/rus/tutorial.md index 0c66d4576..47e09ccd7 100644 --- a/docs/rus/tuturial.md +++ b/docs/rus/tutorial.md @@ -1,4 +1,4 @@ -[Home](index.md) +[К содержанию](index.md) --- @@ -47,4 +47,4 @@ The results are in `phout.log`. Use [Yandex.Tank](https://yandextank.readthedocs --- -[Home](index.md) +[К содержанию](index.md) diff --git a/examples/custom_pandora/custom_main.go b/examples/custom_pandora/custom_main.go index c25569f85..1b08f75fc 100644 --- a/examples/custom_pandora/custom_main.go +++ b/examples/custom_pandora/custom_main.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package main import ( diff --git a/examples/grpc/server/proto/target.proto b/examples/grpc/server/proto/target.proto index fafb61620..ad4fd60e9 100644 --- a/examples/grpc/server/proto/target.proto +++ b/examples/grpc/server/proto/target.proto @@ -5,6 +5,7 @@ package target; option go_package = "server.v1;server"; service TargetService { + rpc Hello(HelloRequest) returns (HelloResponse); rpc Auth(AuthRequest) returns (AuthResponse); rpc List(ListRequest) returns (ListResponse); rpc Order(OrderRequest) returns (OrderResponse); @@ -12,6 +13,13 @@ service TargetService { rpc Reset(ResetRequest) returns (ResetResponse); } +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string hello = 1; +} message AuthRequest { string login = 1; string pass = 2; @@ -51,6 +59,7 @@ message StatsResponse { StatisticBodyResponse Auth = 1; StatisticBodyResponse List = 2; StatisticBodyResponse Order = 3; + int64 Hello = 4; } message StatisticBodyResponse { diff --git a/examples/grpc/server/server.go b/examples/grpc/server/server.go index bb944ebbf..d26c6d2db 100644 --- a/examples/grpc/server/server.go +++ b/examples/grpc/server/server.go @@ -48,6 +48,13 @@ type GRPCServer struct { var _ TargetServiceServer = (*GRPCServer)(nil) +func (s *GRPCServer) Hello(ctx context.Context, request *HelloRequest) (*HelloResponse, error) { + s.stats.IncHello() + return &HelloResponse{ + Hello: fmt.Sprintf("Hello %s!", request.Name), + }, nil +} + func (s *GRPCServer) Auth(ctx context.Context, request *AuthRequest) (*AuthResponse, error) { userID, token, err := s.checkLoginPass(request.GetLogin(), request.GetPass()) if err != nil { @@ -114,6 +121,7 @@ func (s *GRPCServer) Order(ctx context.Context, request *OrderRequest) (*OrderRe func (s *GRPCServer) Stats(ctx context.Context, _ *StatsRequest) (*StatsResponse, error) { result := &StatsResponse{ + Hello: int64(s.stats.hello.Load()), Auth: &StatisticBodyResponse{ Code200: s.stats.auth200, Code400: s.stats.auth400.Load(), diff --git a/examples/grpc/server/stats.go b/examples/grpc/server/stats.go index 02517b1da..b45f0c044 100644 --- a/examples/grpc/server/stats.go +++ b/examples/grpc/server/stats.go @@ -24,6 +24,7 @@ func newStats(capacity int) *Stats { } type Stats struct { + hello atomic.Uint64 auth200 map[int64]uint64 auth200Mutex sync.Mutex auth400 atomic.Uint64 @@ -38,6 +39,10 @@ type Stats struct { order500 atomic.Uint64 } +func (s *Stats) IncHello() { + s.hello.Add(1) +} + func (s *Stats) IncAuth400() { s.auth400.Add(1) } diff --git a/examples/grpc/server/target.pb.go b/examples/grpc/server/target.pb.go index 5bf14d910..17b3bb5d5 100644 --- a/examples/grpc/server/target.pb.go +++ b/examples/grpc/server/target.pb.go @@ -7,11 +7,10 @@ package server import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( @@ -21,6 +20,100 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_target_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_target_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_target_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hello string `protobuf:"bytes,1,opt,name=hello,proto3" json:"hello,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_target_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_target_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_target_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloResponse) GetHello() string { + if x != nil { + return x.Hello + } + return "" +} + type AuthRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -33,7 +126,7 @@ type AuthRequest struct { func (x *AuthRequest) Reset() { *x = AuthRequest{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[0] + mi := &file_target_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -46,7 +139,7 @@ func (x *AuthRequest) String() string { func (*AuthRequest) ProtoMessage() {} func (x *AuthRequest) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[0] + mi := &file_target_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -59,7 +152,7 @@ func (x *AuthRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthRequest.ProtoReflect.Descriptor instead. func (*AuthRequest) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{0} + return file_target_proto_rawDescGZIP(), []int{2} } func (x *AuthRequest) GetLogin() string { @@ -88,7 +181,7 @@ type AuthResponse struct { func (x *AuthResponse) Reset() { *x = AuthResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[1] + mi := &file_target_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -101,7 +194,7 @@ func (x *AuthResponse) String() string { func (*AuthResponse) ProtoMessage() {} func (x *AuthResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[1] + mi := &file_target_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -114,7 +207,7 @@ func (x *AuthResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthResponse.ProtoReflect.Descriptor instead. func (*AuthResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{1} + return file_target_proto_rawDescGZIP(), []int{3} } func (x *AuthResponse) GetUserId() int64 { @@ -143,7 +236,7 @@ type ListRequest struct { func (x *ListRequest) Reset() { *x = ListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[2] + mi := &file_target_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -156,7 +249,7 @@ func (x *ListRequest) String() string { func (*ListRequest) ProtoMessage() {} func (x *ListRequest) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[2] + mi := &file_target_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -169,7 +262,7 @@ func (x *ListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. func (*ListRequest) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{2} + return file_target_proto_rawDescGZIP(), []int{4} } func (x *ListRequest) GetToken() string { @@ -197,7 +290,7 @@ type ListResponse struct { func (x *ListResponse) Reset() { *x = ListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[3] + mi := &file_target_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -210,7 +303,7 @@ func (x *ListResponse) String() string { func (*ListResponse) ProtoMessage() {} func (x *ListResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[3] + mi := &file_target_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -223,7 +316,7 @@ func (x *ListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. func (*ListResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{3} + return file_target_proto_rawDescGZIP(), []int{5} } func (x *ListResponse) GetResult() []*ListItem { @@ -244,7 +337,7 @@ type ListItem struct { func (x *ListItem) Reset() { *x = ListItem{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[4] + mi := &file_target_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -257,7 +350,7 @@ func (x *ListItem) String() string { func (*ListItem) ProtoMessage() {} func (x *ListItem) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[4] + mi := &file_target_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -270,7 +363,7 @@ func (x *ListItem) ProtoReflect() protoreflect.Message { // Deprecated: Use ListItem.ProtoReflect.Descriptor instead. func (*ListItem) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{4} + return file_target_proto_rawDescGZIP(), []int{6} } func (x *ListItem) GetItemId() int64 { @@ -293,7 +386,7 @@ type OrderRequest struct { func (x *OrderRequest) Reset() { *x = OrderRequest{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[5] + mi := &file_target_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -306,7 +399,7 @@ func (x *OrderRequest) String() string { func (*OrderRequest) ProtoMessage() {} func (x *OrderRequest) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[5] + mi := &file_target_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -319,7 +412,7 @@ func (x *OrderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OrderRequest.ProtoReflect.Descriptor instead. func (*OrderRequest) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{5} + return file_target_proto_rawDescGZIP(), []int{7} } func (x *OrderRequest) GetToken() string { @@ -354,7 +447,7 @@ type OrderResponse struct { func (x *OrderResponse) Reset() { *x = OrderResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[6] + mi := &file_target_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -367,7 +460,7 @@ func (x *OrderResponse) String() string { func (*OrderResponse) ProtoMessage() {} func (x *OrderResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[6] + mi := &file_target_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -380,7 +473,7 @@ func (x *OrderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use OrderResponse.ProtoReflect.Descriptor instead. func (*OrderResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{6} + return file_target_proto_rawDescGZIP(), []int{8} } func (x *OrderResponse) GetOrderId() int64 { @@ -399,7 +492,7 @@ type StatsRequest struct { func (x *StatsRequest) Reset() { *x = StatsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[7] + mi := &file_target_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -412,7 +505,7 @@ func (x *StatsRequest) String() string { func (*StatsRequest) ProtoMessage() {} func (x *StatsRequest) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[7] + mi := &file_target_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -425,7 +518,7 @@ func (x *StatsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead. func (*StatsRequest) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{7} + return file_target_proto_rawDescGZIP(), []int{9} } type StatsResponse struct { @@ -436,12 +529,13 @@ type StatsResponse struct { Auth *StatisticBodyResponse `protobuf:"bytes,1,opt,name=Auth,proto3" json:"Auth,omitempty"` List *StatisticBodyResponse `protobuf:"bytes,2,opt,name=List,proto3" json:"List,omitempty"` Order *StatisticBodyResponse `protobuf:"bytes,3,opt,name=Order,proto3" json:"Order,omitempty"` + Hello int64 `protobuf:"varint,4,opt,name=Hello,proto3" json:"Hello,omitempty"` } func (x *StatsResponse) Reset() { *x = StatsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[8] + mi := &file_target_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -454,7 +548,7 @@ func (x *StatsResponse) String() string { func (*StatsResponse) ProtoMessage() {} func (x *StatsResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[8] + mi := &file_target_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -467,7 +561,7 @@ func (x *StatsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead. func (*StatsResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{8} + return file_target_proto_rawDescGZIP(), []int{10} } func (x *StatsResponse) GetAuth() *StatisticBodyResponse { @@ -491,6 +585,13 @@ func (x *StatsResponse) GetOrder() *StatisticBodyResponse { return nil } +func (x *StatsResponse) GetHello() int64 { + if x != nil { + return x.Hello + } + return 0 +} + type StatisticBodyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -504,7 +605,7 @@ type StatisticBodyResponse struct { func (x *StatisticBodyResponse) Reset() { *x = StatisticBodyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[9] + mi := &file_target_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -517,7 +618,7 @@ func (x *StatisticBodyResponse) String() string { func (*StatisticBodyResponse) ProtoMessage() {} func (x *StatisticBodyResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[9] + mi := &file_target_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -530,7 +631,7 @@ func (x *StatisticBodyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatisticBodyResponse.ProtoReflect.Descriptor instead. func (*StatisticBodyResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{9} + return file_target_proto_rawDescGZIP(), []int{11} } func (x *StatisticBodyResponse) GetCode200() map[int64]uint64 { @@ -563,7 +664,7 @@ type ResetRequest struct { func (x *ResetRequest) Reset() { *x = ResetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[10] + mi := &file_target_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -576,7 +677,7 @@ func (x *ResetRequest) String() string { func (*ResetRequest) ProtoMessage() {} func (x *ResetRequest) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[10] + mi := &file_target_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -589,7 +690,7 @@ func (x *ResetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetRequest.ProtoReflect.Descriptor instead. func (*ResetRequest) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{10} + return file_target_proto_rawDescGZIP(), []int{12} } type ResetResponse struct { @@ -605,7 +706,7 @@ type ResetResponse struct { func (x *ResetResponse) Reset() { *x = ResetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_target_proto_msgTypes[11] + mi := &file_target_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -618,7 +719,7 @@ func (x *ResetResponse) String() string { func (*ResetResponse) ProtoMessage() {} func (x *ResetResponse) ProtoReflect() protoreflect.Message { - mi := &file_target_proto_msgTypes[11] + mi := &file_target_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -631,7 +732,7 @@ func (x *ResetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetResponse.ProtoReflect.Descriptor instead. func (*ResetResponse) Descriptor() ([]byte, []int) { - return file_target_proto_rawDescGZIP(), []int{11} + return file_target_proto_rawDescGZIP(), []int{13} } func (x *ResetResponse) GetAuth() *StatisticBodyResponse { @@ -659,88 +760,98 @@ var File_target_proto protoreflect.FileDescriptor var file_target_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x37, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, - 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x73, 0x73, 0x22, - 0x3d, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3c, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x06, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x23, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, - 0x65, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x0c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, - 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x69, 0x74, 0x65, - 0x6d, 0x49, 0x64, 0x22, 0x2a, 0x0a, 0x0d, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, - 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xaa, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, - 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, - 0x41, 0x75, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x52, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0xcd, 0x01, 0x0a, - 0x15, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, - 0x30, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x12, 0x18, 0x0a, 0x07, - 0x43, 0x6f, 0x64, 0x65, 0x34, 0x30, 0x30, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x43, - 0x6f, 0x64, 0x65, 0x34, 0x30, 0x30, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x35, 0x30, - 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x35, 0x30, 0x30, - 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x0e, 0x0a, 0x0c, - 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xaa, 0x01, 0x0a, - 0x0d, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, - 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, - 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x41, 0x75, 0x74, - 0x68, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, - 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x52, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x32, 0x97, 0x02, 0x0a, 0x0d, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x41, - 0x75, 0x74, 0x68, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, - 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x34, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x15, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, - 0x05, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, - 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x12, 0x5a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x25, 0x0a, 0x0d, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x68, + 0x65, 0x6c, 0x6c, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x68, 0x65, 0x6c, 0x6c, + 0x6f, 0x22, 0x37, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x73, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x73, 0x73, 0x22, 0x3d, 0x0a, 0x0c, 0x41, 0x75, + 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3c, 0x0a, 0x0b, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x17, + 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0x23, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x17, 0x0a, + 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x0c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x17, 0x0a, 0x07, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x2a, + 0x0a, 0x0d, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc0, 0x01, 0x0a, 0x0d, 0x53, + 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x04, + 0x41, 0x75, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, + 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, + 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x33, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x52, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0xcd, 0x01, + 0x0a, 0x15, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x32, + 0x30, 0x30, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x12, 0x18, 0x0a, + 0x07, 0x43, 0x6f, 0x64, 0x65, 0x34, 0x30, 0x30, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x43, 0x6f, 0x64, 0x65, 0x34, 0x30, 0x30, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x35, + 0x30, 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x43, 0x6f, 0x64, 0x65, 0x35, 0x30, + 0x30, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x30, 0x30, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x0e, 0x0a, + 0x0c, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xaa, 0x01, + 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, + 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x41, 0x75, + 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, + 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x52, 0x05, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x32, 0xcd, 0x02, 0x0a, 0x0d, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x05, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, + 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x14, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x14, 0x2e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x73, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x12, 0x5a, 0x10, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -755,43 +866,47 @@ func file_target_proto_rawDescGZIP() []byte { return file_target_proto_rawDescData } -var file_target_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_target_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_target_proto_goTypes = []interface{}{ - (*AuthRequest)(nil), // 0: target.AuthRequest - (*AuthResponse)(nil), // 1: target.AuthResponse - (*ListRequest)(nil), // 2: target.ListRequest - (*ListResponse)(nil), // 3: target.ListResponse - (*ListItem)(nil), // 4: target.ListItem - (*OrderRequest)(nil), // 5: target.OrderRequest - (*OrderResponse)(nil), // 6: target.OrderResponse - (*StatsRequest)(nil), // 7: target.StatsRequest - (*StatsResponse)(nil), // 8: target.StatsResponse - (*StatisticBodyResponse)(nil), // 9: target.StatisticBodyResponse - (*ResetRequest)(nil), // 10: target.ResetRequest - (*ResetResponse)(nil), // 11: target.ResetResponse - nil, // 12: target.StatisticBodyResponse.Code200Entry + (*HelloRequest)(nil), // 0: target.HelloRequest + (*HelloResponse)(nil), // 1: target.HelloResponse + (*AuthRequest)(nil), // 2: target.AuthRequest + (*AuthResponse)(nil), // 3: target.AuthResponse + (*ListRequest)(nil), // 4: target.ListRequest + (*ListResponse)(nil), // 5: target.ListResponse + (*ListItem)(nil), // 6: target.ListItem + (*OrderRequest)(nil), // 7: target.OrderRequest + (*OrderResponse)(nil), // 8: target.OrderResponse + (*StatsRequest)(nil), // 9: target.StatsRequest + (*StatsResponse)(nil), // 10: target.StatsResponse + (*StatisticBodyResponse)(nil), // 11: target.StatisticBodyResponse + (*ResetRequest)(nil), // 12: target.ResetRequest + (*ResetResponse)(nil), // 13: target.ResetResponse + nil, // 14: target.StatisticBodyResponse.Code200Entry } var file_target_proto_depIdxs = []int32{ - 4, // 0: target.ListResponse.result:type_name -> target.ListItem - 9, // 1: target.StatsResponse.Auth:type_name -> target.StatisticBodyResponse - 9, // 2: target.StatsResponse.List:type_name -> target.StatisticBodyResponse - 9, // 3: target.StatsResponse.Order:type_name -> target.StatisticBodyResponse - 12, // 4: target.StatisticBodyResponse.Code200:type_name -> target.StatisticBodyResponse.Code200Entry - 9, // 5: target.ResetResponse.Auth:type_name -> target.StatisticBodyResponse - 9, // 6: target.ResetResponse.List:type_name -> target.StatisticBodyResponse - 9, // 7: target.ResetResponse.Order:type_name -> target.StatisticBodyResponse - 0, // 8: target.TargetService.Auth:input_type -> target.AuthRequest - 2, // 9: target.TargetService.List:input_type -> target.ListRequest - 5, // 10: target.TargetService.Order:input_type -> target.OrderRequest - 7, // 11: target.TargetService.Stats:input_type -> target.StatsRequest - 10, // 12: target.TargetService.Reset:input_type -> target.ResetRequest - 1, // 13: target.TargetService.Auth:output_type -> target.AuthResponse - 3, // 14: target.TargetService.List:output_type -> target.ListResponse - 6, // 15: target.TargetService.Order:output_type -> target.OrderResponse - 8, // 16: target.TargetService.Stats:output_type -> target.StatsResponse - 11, // 17: target.TargetService.Reset:output_type -> target.ResetResponse - 13, // [13:18] is the sub-list for method output_type - 8, // [8:13] is the sub-list for method input_type + 6, // 0: target.ListResponse.result:type_name -> target.ListItem + 11, // 1: target.StatsResponse.Auth:type_name -> target.StatisticBodyResponse + 11, // 2: target.StatsResponse.List:type_name -> target.StatisticBodyResponse + 11, // 3: target.StatsResponse.Order:type_name -> target.StatisticBodyResponse + 14, // 4: target.StatisticBodyResponse.Code200:type_name -> target.StatisticBodyResponse.Code200Entry + 11, // 5: target.ResetResponse.Auth:type_name -> target.StatisticBodyResponse + 11, // 6: target.ResetResponse.List:type_name -> target.StatisticBodyResponse + 11, // 7: target.ResetResponse.Order:type_name -> target.StatisticBodyResponse + 0, // 8: target.TargetService.Hello:input_type -> target.HelloRequest + 2, // 9: target.TargetService.Auth:input_type -> target.AuthRequest + 4, // 10: target.TargetService.List:input_type -> target.ListRequest + 7, // 11: target.TargetService.Order:input_type -> target.OrderRequest + 9, // 12: target.TargetService.Stats:input_type -> target.StatsRequest + 12, // 13: target.TargetService.Reset:input_type -> target.ResetRequest + 1, // 14: target.TargetService.Hello:output_type -> target.HelloResponse + 3, // 15: target.TargetService.Auth:output_type -> target.AuthResponse + 5, // 16: target.TargetService.List:output_type -> target.ListResponse + 8, // 17: target.TargetService.Order:output_type -> target.OrderResponse + 10, // 18: target.TargetService.Stats:output_type -> target.StatsResponse + 13, // 19: target.TargetService.Reset:output_type -> target.ResetResponse + 14, // [14:20] is the sub-list for method output_type + 8, // [8:14] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name @@ -804,7 +919,7 @@ func file_target_proto_init() { } if !protoimpl.UnsafeEnabled { file_target_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthRequest); i { + switch v := v.(*HelloRequest); i { case 0: return &v.state case 1: @@ -816,7 +931,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthResponse); i { + switch v := v.(*HelloResponse); i { case 0: return &v.state case 1: @@ -828,7 +943,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListRequest); i { + switch v := v.(*AuthRequest); i { case 0: return &v.state case 1: @@ -840,7 +955,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListResponse); i { + switch v := v.(*AuthResponse); i { case 0: return &v.state case 1: @@ -852,7 +967,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListItem); i { + switch v := v.(*ListRequest); i { case 0: return &v.state case 1: @@ -864,7 +979,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrderRequest); i { + switch v := v.(*ListResponse); i { case 0: return &v.state case 1: @@ -876,7 +991,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrderResponse); i { + switch v := v.(*ListItem); i { case 0: return &v.state case 1: @@ -888,7 +1003,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatsRequest); i { + switch v := v.(*OrderRequest); i { case 0: return &v.state case 1: @@ -900,7 +1015,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatsResponse); i { + switch v := v.(*OrderResponse); i { case 0: return &v.state case 1: @@ -912,7 +1027,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatisticBodyResponse); i { + switch v := v.(*StatsRequest); i { case 0: return &v.state case 1: @@ -924,7 +1039,7 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResetRequest); i { + switch v := v.(*StatsResponse); i { case 0: return &v.state case 1: @@ -936,6 +1051,30 @@ func file_target_proto_init() { } } file_target_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatisticBodyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_target_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_target_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResetResponse); i { case 0: return &v.state @@ -954,7 +1093,7 @@ func file_target_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_target_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/examples/grpc/server/target_grpc.pb.go b/examples/grpc/server/target_grpc.pb.go index cffbcd30e..7386b7c05 100644 --- a/examples/grpc/server/target_grpc.pb.go +++ b/examples/grpc/server/target_grpc.pb.go @@ -8,7 +8,6 @@ package server import ( context "context" - grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -23,6 +22,7 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type TargetServiceClient interface { + Hello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) Order(ctx context.Context, in *OrderRequest, opts ...grpc.CallOption) (*OrderResponse, error) @@ -38,6 +38,15 @@ func NewTargetServiceClient(cc grpc.ClientConnInterface) TargetServiceClient { return &targetServiceClient{cc} } +func (c *targetServiceClient) Hello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { + out := new(HelloResponse) + err := c.cc.Invoke(ctx, "/target.TargetService/Hello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *targetServiceClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) { out := new(AuthResponse) err := c.cc.Invoke(ctx, "/target.TargetService/Auth", in, out, opts...) @@ -87,6 +96,7 @@ func (c *targetServiceClient) Reset(ctx context.Context, in *ResetRequest, opts // All implementations must embed UnimplementedTargetServiceServer // for forward compatibility type TargetServiceServer interface { + Hello(context.Context, *HelloRequest) (*HelloResponse, error) Auth(context.Context, *AuthRequest) (*AuthResponse, error) List(context.Context, *ListRequest) (*ListResponse, error) Order(context.Context, *OrderRequest) (*OrderResponse, error) @@ -99,6 +109,9 @@ type TargetServiceServer interface { type UnimplementedTargetServiceServer struct { } +func (UnimplementedTargetServiceServer) Hello(context.Context, *HelloRequest) (*HelloResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented") +} func (UnimplementedTargetServiceServer) Auth(context.Context, *AuthRequest) (*AuthResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Auth not implemented") } @@ -127,6 +140,24 @@ func RegisterTargetServiceServer(s grpc.ServiceRegistrar, srv TargetServiceServe s.RegisterService(&TargetService_ServiceDesc, srv) } +func _TargetService_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TargetServiceServer).Hello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/target.TargetService/Hello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TargetServiceServer).Hello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _TargetService_Auth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AuthRequest) if err := dec(in); err != nil { @@ -224,6 +255,10 @@ var TargetService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "target.TargetService", HandlerType: (*TargetServiceServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "Hello", + Handler: _TargetService_Hello_Handler, + }, { MethodName: "Auth", Handler: _TargetService_Auth_Handler, diff --git a/examples/http/server/server.go b/examples/http/server/server.go index 03db043bb..24f7d9d23 100644 --- a/examples/http/server/server.go +++ b/examples/http/server/server.go @@ -202,7 +202,7 @@ func (s *Server) statisticHandler(w http.ResponseWriter, r *http.Request) { Code500: s.stats.auth500.Load(), }, List: StatisticBodyResponse{ - Code200: s.stats.list200, + Code200: s.stats.List200, Code400: s.stats.list400.Load(), Code500: s.stats.list500.Load(), }, diff --git a/examples/http/server/stats.go b/examples/http/server/stats.go index d11d62962..2c0f10475 100644 --- a/examples/http/server/stats.go +++ b/examples/http/server/stats.go @@ -11,7 +11,7 @@ func newStats(capacity int) *Stats { auth200Mutex: sync.Mutex{}, auth400: atomic.Uint64{}, auth500: atomic.Uint64{}, - list200: make(map[int64]uint64, capacity), + List200: make(map[int64]uint64, capacity), list200Mutex: sync.Mutex{}, list400: atomic.Uint64{}, list500: atomic.Uint64{}, @@ -28,7 +28,7 @@ type Stats struct { auth200Mutex sync.Mutex auth400 atomic.Uint64 auth500 atomic.Uint64 - list200 map[int64]uint64 + List200 map[int64]uint64 list200Mutex sync.Mutex list400 atomic.Uint64 list500 atomic.Uint64 @@ -62,7 +62,7 @@ func (s *Stats) IncList500() { func (s *Stats) IncList200(userID int64) { s.list200Mutex.Lock() - s.list200[userID]++ + s.List200[userID]++ s.list200Mutex.Unlock() } @@ -88,7 +88,7 @@ func (s *Stats) Reset() { s.auth500.Store(0) s.list200Mutex.Lock() - s.list200 = map[int64]uint64{} + s.List200 = map[int64]uint64{} s.list200Mutex.Unlock() s.list400.Store(0) s.list500.Store(0) diff --git a/go.mod b/go.mod index c961bdfc8..7651c7fc8 100644 --- a/go.mod +++ b/go.mod @@ -13,11 +13,9 @@ require ( github.com/golang/protobuf v1.5.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/hcl/v2 v2.16.2 - github.com/jhump/protoreflect v1.15.1 + github.com/jhump/protoreflect v1.15.6 github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 - github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.27.10 github.com/pkg/errors v0.9.1 github.com/spf13/afero v1.9.5 github.com/spf13/viper v1.16.0 @@ -25,42 +23,34 @@ require ( go.uber.org/atomic v1.11.0 go.uber.org/zap v1.26.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.19.0 + golang.org/x/net v0.20.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 - google.golang.org/grpc v1.58.2 - google.golang.org/protobuf v1.31.0 + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 gopkg.in/bluesuncorp/validator.v9 v9.10.0 gopkg.in/yaml.v2 v2.4.0 ) require ( - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver v1.5.0 // indirect github.com/PaesslerAG/gval v1.2.1 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/bufbuild/protocompile v0.5.1 // indirect + github.com/bufbuild/protocompile v0.8.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/frankban/quicktest v1.14.5 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-task/slim-sprig v2.20.0+incompatible // indirect github.com/go-test/deep v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect - github.com/google/uuid v1.4.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nxadm/tail v1.4.8 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect @@ -72,17 +62,14 @@ require ( github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/zclconf/go-cty v1.13.2 // indirect - go.uber.org/goleak v1.2.1 // indirect + go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c37200882..e7a36317d 100644 --- a/go.sum +++ b/go.sum @@ -38,10 +38,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= github.com/PaesslerAG/gval v1.2.1 h1:Ggwtej1xCyt1994VuDCSjycybIDo3duDCDghK/xc/A0= github.com/PaesslerAG/gval v1.2.1/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= @@ -58,8 +54,8 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg= -github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY8+wjsYLR/lc40= +github.com/bufbuild/protocompile v0.8.0 h1:9Kp1q6OkS9L4nM3FYbr8vlJnEwtbpDPQlQOVXfR+78s= +github.com/bufbuild/protocompile v0.8.0/go.mod h1:+Etjg4guZoAqzVk2czwEQP12yaxLJ8DxuqCJ9qHdH94= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -86,22 +82,15 @@ github.com/facebookgo/stackerr v0.0.0-20150612192056-c2fcf88613f4 h1:fP04zlkPjAG github.com/facebookgo/stackerr v0.0.0-20150612192056-c2fcf88613f4/go.mod h1:SBHk9aNQtiw4R4bEuzHjVmZikkUKCnO1v3lPQ21HZGk= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v2.20.0+incompatible h1:4Xh3bDzO29j4TWNOI+24ubc0vbVFMg2PMnXKxK54/CA= -github.com/go-task/slim-sprig v2.20.0+incompatible/go.mod h1:N/mhXZITr/EQAOErEHciKvO1bFei2Lld2Ym6h96pdy0= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -163,12 +152,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -183,15 +168,10 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1a0= github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= -github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/jhump/protoreflect v1.15.6 h1:WMYJbw2Wo+KOWwZFvgY0jMoVHM6i4XIvRs2RcBj5VmI= +github.com/jhump/protoreflect v1.15.6/go.mod h1:jCHoyYQIJnaabEYnbGwyo9hUqfyUMTbJw/tAut5t97E= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 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= @@ -219,19 +199,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -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.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -291,8 +258,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -304,8 +271,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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= @@ -343,7 +308,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -365,7 +329,6 @@ golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -376,8 +339,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 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= @@ -397,10 +360,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -409,10 +371,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -433,15 +392,14 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w 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-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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -500,12 +458,9 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 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= @@ -592,8 +547,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= 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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -606,8 +561,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/bluesuncorp/validator.v9 v9.10.0 h1:eyhz/IzFglUqngYr1p7WCfKVAAQh9E/IsqJcnwG/OWg= gopkg.in/bluesuncorp/validator.v9 v9.10.0/go.mod h1:sz1RrKEIYJCpC5S6ruDsBWo5vYV69E+bEZ86LbUsSZ8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -616,20 +571,14 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -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= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/lib/errutil/errutil.go b/lib/errutil/errutil.go index abee5482d..b429c1cd8 100644 --- a/lib/errutil/errutil.go +++ b/lib/errutil/errutil.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package errutil import ( diff --git a/lib/errutil/errutil_suite_test.go b/lib/errutil/errutil_suite_test.go deleted file mode 100644 index 48e59214d..000000000 --- a/lib/errutil/errutil_suite_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package errutil - -import ( - "context" - "fmt" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/pkg/errors" - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestErrutil(t *testing.T) { - ginkgoutil.RunSuite(t, "Errutil Suite") -} - -var _ = Describe("Iscoreutil.IsCtxErroror", func() { - canceledContext, cancel := context.WithCancel(context.Background()) - cancel() - - It("nil error", func() { - Expect(IsCtxError(context.Background(), nil)).To(BeTrue()) - Expect(IsCtxError(canceledContext, nil)).To(BeTrue()) - }) - - It("context error", func() { - Expect(IsCtxError(context.Background(), context.Canceled)).To(BeFalse()) - Expect(IsCtxError(canceledContext, context.Canceled)).To(BeTrue()) - }) - - It("caused by context error", func() { - Expect(IsCtxError(context.Background(), errors.Wrap(context.Canceled, "new err"))).To(BeFalse()) - Expect(IsCtxError(canceledContext, errors.Wrap(context.Canceled, "new err"))).To(BeTrue()) - }) - - It("default error wrapping has defferent result", func() { - Expect(IsCtxError(context.Background(), fmt.Errorf("new err %w", context.Canceled))).To(BeFalse()) - Expect(IsCtxError(canceledContext, fmt.Errorf("new err %w", context.Canceled))).To(BeFalse()) - }) - - It("usual error", func() { - err := errors.New("new err") - Expect(IsCtxError(canceledContext, err)).To(BeFalse()) - Expect(IsCtxError(context.Background(), err)).To(BeFalse()) - }) -}) diff --git a/lib/errutil/errutil_test.go b/lib/errutil/errutil_test.go index 9fc9cb673..4f9c49ee6 100644 --- a/lib/errutil/errutil_test.go +++ b/lib/errutil/errutil_test.go @@ -7,9 +7,41 @@ import ( "testing" pkgerrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestIscoreutilIsCtxErroror(t *testing.T) { + canceledContext, cancel := context.WithCancel(context.Background()) + cancel() + + t.Run("nil error", func(t *testing.T) { + assert.True(t, IsCtxError(context.Background(), nil)) + assert.True(t, IsCtxError(canceledContext, nil)) + }) + + t.Run("context error", func(t *testing.T) { + assert.False(t, IsCtxError(context.Background(), context.Canceled)) + assert.True(t, IsCtxError(canceledContext, context.Canceled)) + }) + + t.Run("caused by context error", func(t *testing.T) { + assert.False(t, IsCtxError(context.Background(), pkgerrors.Wrap(context.Canceled, "new err"))) + assert.True(t, IsCtxError(canceledContext, pkgerrors.Wrap(context.Canceled, "new err"))) + }) + + t.Run("default error wrapping has defferent result", func(t *testing.T) { + assert.False(t, IsCtxError(context.Background(), fmt.Errorf("new err %w", context.Canceled))) + assert.False(t, IsCtxError(canceledContext, fmt.Errorf("new err %w", context.Canceled))) + }) + + t.Run("usual error", func(t *testing.T) { + err := errors.New("new err") + assert.False(t, IsCtxError(canceledContext, err)) + assert.False(t, IsCtxError(context.Background(), err)) + }) +} + func TestJoin(t *testing.T) { type args struct { } diff --git a/lib/ginkgoutil/ginkgo.go b/lib/ginkgoutil/ginkgo.go deleted file mode 100644 index 2d1fac0c0..000000000 --- a/lib/ginkgoutil/ginkgo.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - -package ginkgoutil - -import ( - "strings" - "testing" - - "github.com/onsi/ginkgo" - "github.com/onsi/gomega" - "github.com/onsi/gomega/format" - "github.com/spf13/viper" - "github.com/stretchr/testify/mock" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -func SetupSuite() { - format.UseStringerRepresentation = true // Otherwise error stacks have binary format. - ReplaceGlobalLogger() - gomega.RegisterFailHandler(ginkgo.Fail) -} - -func RunSuite(t *testing.T, description string) { - SetupSuite() - ginkgo.RunSpecs(t, description) -} - -func ReplaceGlobalLogger() *zap.Logger { - log := NewLogger() - zap.ReplaceGlobals(log) - zap.RedirectStdLog(log) - return log -} - -func NewLogger() *zap.Logger { - conf := zap.NewDevelopmentConfig() - enc := zapcore.NewConsoleEncoder(conf.EncoderConfig) - core := zapcore.NewCore(enc, zapcore.AddSync(ginkgo.GinkgoWriter), zap.DebugLevel) - log := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.DPanicLevel)) - return log -} - -type Mock interface { - AssertExpectations(t mock.TestingT) bool - AssertNotCalled(t mock.TestingT, methodName string, arguments ...interface{}) bool -} - -func AssertExpectations(mocks ...Mock) { - for _, m := range mocks { - m.AssertExpectations(ginkgo.GinkgoT(1)) - } -} - -func AssertNotCalled(mock Mock, methodName string) { - mock.AssertNotCalled(ginkgo.GinkgoT(1), methodName) -} - -func ParseYAML(data string) map[string]interface{} { - v := viper.New() - v.SetConfigType("yaml") - err := v.ReadConfig(strings.NewReader(data)) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - return v.AllSettings() -} diff --git a/lib/ginkgoutil/matchers.go b/lib/ginkgoutil/matchers.go deleted file mode 100644 index 8049198d2..000000000 --- a/lib/ginkgoutil/matchers.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - -package ginkgoutil - -import ( - "reflect" - - "github.com/onsi/gomega" -) - -func ExpectFuncsEqual(f1, f2 interface{}) { - val1 := reflect.ValueOf(f1) - val2 := reflect.ValueOf(f2) - gomega.Expect(val1.Pointer()).To(gomega.Equal(val2.Pointer())) -} diff --git a/lib/ioutil2/closer.go b/lib/ioutil2/closer.go index 3fcfea9be..df8d7a0c2 100644 --- a/lib/ioutil2/closer.go +++ b/lib/ioutil2/closer.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ioutil2 // NopCloser may be embedded to any struct to implement io.Closer doing nothing on closer. diff --git a/lib/ioutil2/funcs.go b/lib/ioutil2/funcs.go index 4b654bb33..342dbcbab 100644 --- a/lib/ioutil2/funcs.go +++ b/lib/ioutil2/funcs.go @@ -1,6 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Author: Vladimir Skipor - package ioutil2 type ReaderFunc func(p []byte) (n int, err error) diff --git a/lib/ioutil2/io_mocks_test.go b/lib/ioutil2/io_mocks_test.go index 8cc3547b5..309dfe7ff 100644 --- a/lib/ioutil2/io_mocks_test.go +++ b/lib/ioutil2/io_mocks_test.go @@ -1,6 +1,3 @@ -// Copyright (c) 2016 Yandex LLC. All rights reserved. -// Author: Vladimir Skipor - package ioutil2 import "io" diff --git a/lib/ioutil2/reader.go b/lib/ioutil2/reader.go index c70bf25f4..b671bb5f5 100644 --- a/lib/ioutil2/reader.go +++ b/lib/ioutil2/reader.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ioutil2 import "io" diff --git a/lib/ioutil2/writer.go b/lib/ioutil2/writer.go index 477dbc85c..3518fcdf2 100644 --- a/lib/ioutil2/writer.go +++ b/lib/ioutil2/writer.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package ioutil2 import ( diff --git a/lib/monitoring/counter.go b/lib/monitoring/counter.go index 6c8744014..a1610ea9d 100644 --- a/lib/monitoring/counter.go +++ b/lib/monitoring/counter.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package monitoring import ( diff --git a/lib/mp/iterator.go b/lib/mp/iterator.go index 15215854d..d09e71300 100644 --- a/lib/mp/iterator.go +++ b/lib/mp/iterator.go @@ -29,11 +29,11 @@ func (n *NextIterator) Rand(length int) int { } func (n *NextIterator) Next(segment string) int { + n.mx.Lock() + defer n.mx.Unlock() a, ok := n.gs[segment] if !ok { - n.mx.Lock() n.gs[segment] = &atomic.Uint64{} - n.mx.Unlock() return 0 } add := a.Add(1) diff --git a/lib/netutil/dial.go b/lib/netutil/dial.go index 7aa55704a..a79750738 100644 --- a/lib/netutil/dial.go +++ b/lib/netutil/dial.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package netutil import ( diff --git a/lib/netutil/netutil_suite_test.go b/lib/netutil/netutil_test.go similarity index 60% rename from lib/netutil/netutil_suite_test.go rename to lib/netutil/netutil_test.go index a397d4121..92a7c9e68 100644 --- a/lib/netutil/netutil_suite_test.go +++ b/lib/netutil/netutil_test.go @@ -7,31 +7,24 @@ import ( "testing" "time" - "github.com/onsi/ginkgo" - "github.com/onsi/gomega" "github.com/pkg/errors" - "github.com/yandex/pandora/lib/ginkgoutil" + "github.com/stretchr/testify/assert" netmock "github.com/yandex/pandora/lib/netutil/mocks" ) -func TestNetutil(t *testing.T) { - ginkgoutil.RunSuite(t, "Netutil Suite") -} - -var _ = ginkgo.Describe("DNS", func() { - - ginkgo.It("lookup reachable", func() { +func Test_DNS(t *testing.T) { + t.Run("lookup reachable", func(t *testing.T) { listener, err := net.ListenTCP("tcp4", nil) defer func() { _ = listener.Close() }() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) + assert.NoError(t, err) port := strconv.Itoa(listener.Addr().(*net.TCPAddr).Port) addr := "localhost:" + port expectedResolved := "127.0.0.1:" + port resolved, err := LookupReachable(addr, time.Second) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - gomega.Expect(resolved).To(gomega.Equal(expectedResolved)) + assert.NoError(t, err) + assert.Equal(t, expectedResolved, resolved) }) const ( @@ -39,19 +32,19 @@ var _ = ginkgo.Describe("DNS", func() { resolved = "[::1]:8888" ) - ginkgo.It("cache", func() { + t.Run("cache", func(t *testing.T) { cache := &SimpleDNSCache{} got, ok := cache.Get(addr) - gomega.Expect(ok).To(gomega.BeFalse()) - gomega.Expect(got).To(gomega.BeEmpty()) + assert.False(t, ok) + assert.Equal(t, "", got) cache.Add(addr, resolved) got, ok = cache.Get(addr) - gomega.Expect(ok).To(gomega.BeTrue()) - gomega.Expect(got).To(gomega.Equal(resolved)) + assert.True(t, ok) + assert.Equal(t, resolved, got) }) - ginkgo.It("Dialer cache miss", func() { + t.Run("Dialer cache miss", func(t *testing.T) { ctx := context.Background() mockConn := &netmock.Conn{} mockConn.On("RemoteAddr").Return(&net.TCPAddr{ @@ -66,13 +59,15 @@ var _ = ginkgo.Describe("DNS", func() { testee := NewDNSCachingDialer(dialer, cache) conn, err := testee.DialContext(ctx, "tcp", addr) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - gomega.Expect(conn).To(gomega.Equal(mockConn)) + assert.NoError(t, err) + assert.Equal(t, mockConn, conn) - ginkgoutil.AssertExpectations(mockConn, cache, dialer) + mockConn.AssertExpectations(t) + cache.AssertExpectations(t) + dialer.AssertExpectations(t) }) - ginkgo.It("Dialer cache hit", func() { + t.Run("Dialer cache hit", func(t *testing.T) { ctx := context.Background() mockConn := &netmock.Conn{} cache := &netmock.DNSCache{} @@ -82,13 +77,15 @@ var _ = ginkgo.Describe("DNS", func() { testee := NewDNSCachingDialer(dialer, cache) conn, err := testee.DialContext(ctx, "tcp", addr) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - gomega.Expect(conn).To(gomega.Equal(mockConn)) + assert.NoError(t, err) + assert.Equal(t, mockConn, conn) - ginkgoutil.AssertExpectations(mockConn, cache, dialer) + mockConn.AssertExpectations(t) + cache.AssertExpectations(t) + dialer.AssertExpectations(t) }) - ginkgo.It("Dialer cache miss err", func() { + t.Run("Dialer cache miss err", func(t *testing.T) { ctx := context.Background() expectedErr := errors.New("dial failed") cache := &netmock.DNSCache{} @@ -98,10 +95,11 @@ var _ = ginkgo.Describe("DNS", func() { testee := NewDNSCachingDialer(dialer, cache) conn, err := testee.DialContext(ctx, "tcp", addr) - gomega.Expect(err).To(gomega.Equal(expectedErr)) - gomega.Expect(conn).To(gomega.BeNil()) + assert.ErrorIs(t, err, expectedErr) + assert.Nil(t, conn) - ginkgoutil.AssertExpectations(cache, dialer) + cache.AssertExpectations(t) + dialer.AssertExpectations(t) }) -}) +} diff --git a/lib/tag/debug.go b/lib/tag/debug.go index ee36c25b0..9ac1c051b 100644 --- a/lib/tag/debug.go +++ b/lib/tag/debug.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - //go:build debug // +build debug diff --git a/lib/tag/no_degug.go b/lib/tag/no_degug.go index 697fb8ef2..2692a2362 100644 --- a/lib/tag/no_degug.go +++ b/lib/tag/no_degug.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - //go:build !debug // +build !debug diff --git a/lib/tag/no_race.go b/lib/tag/no_race.go index 7ccebb392..a261be15a 100644 --- a/lib/tag/no_race.go +++ b/lib/tag/no_race.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - //go:build !race // +build !race diff --git a/lib/tag/race.go b/lib/tag/race.go index 0cdaf0afb..bf83a325a 100644 --- a/lib/tag/race.go +++ b/lib/tag/race.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - //go:build race // +build race diff --git a/lib/testutil/afero.go b/lib/testutil/afero.go index da164b382..dd06ee24d 100644 --- a/lib/testutil/afero.go +++ b/lib/testutil/afero.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package testutil import ( diff --git a/lib/testutil/matchers.go b/lib/testutil/matchers.go index 78d6d268d..d2ac22bd6 100644 --- a/lib/testutil/matchers.go +++ b/lib/testutil/matchers.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package testutil import ( diff --git a/lib/testutil/matchers_test.go b/lib/testutil/matchers_test.go index 35f1565de..72a771110 100644 --- a/lib/testutil/matchers_test.go +++ b/lib/testutil/matchers_test.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package testutil import ( diff --git a/lib/testutil/util.go b/lib/testutil/util.go index d8b2be43e..25a3d2e24 100644 --- a/lib/testutil/util.go +++ b/lib/testutil/util.go @@ -1,12 +1,9 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package testutil import ( "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" ) func ReplaceGlobalLogger() *zap.Logger { @@ -19,9 +16,15 @@ func ReplaceGlobalLogger() *zap.Logger { func NewLogger() *zap.Logger { conf := zap.NewDevelopmentConfig() conf.OutputPaths = []string{"stdout"} + conf.Level.SetLevel(zapcore.ErrorLevel) log, err := conf.Build(zap.AddCaller(), zap.AddStacktrace(zap.PanicLevel)) if err != nil { - panic(err) + zap.L().Fatal("Logger build failed", zap.Error(err)) } return log } + +func NewNullLogger() *zap.Logger { + c, _ := observer.New(zap.InfoLevel) + return zap.New(c) +} diff --git a/lib/zaputil/stack_extract_core.go b/lib/zaputil/stack_extract_core.go index e596912b9..e73eafeaa 100644 --- a/lib/zaputil/stack_extract_core.go +++ b/lib/zaputil/stack_extract_core.go @@ -1,8 +1,3 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package zaputil import ( diff --git a/lib/zaputil/stack_extract_core_test.go b/lib/zaputil/stack_extract_core_test.go index 1f09ee8a3..d5c26a41e 100644 --- a/lib/zaputil/stack_extract_core_test.go +++ b/lib/zaputil/stack_extract_core_test.go @@ -1,16 +1,11 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package zaputil import ( "fmt" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" @@ -28,20 +23,19 @@ func noStackFields2() []zapcore.Field { } } -var _ = Describe("stack_extract_core", func() { - - It("check integration", func() { +func Test_StackExtractCore(t *testing.T) { + t.Run("check integration", func(t *testing.T) { nested, logs := observer.New(zap.DebugLevel) log := zap.New(NewStackExtractCore(nested)) log.Debug("test", noStackFields1()...) - Expect(logs.Len()).To(Equal(1)) + assert.Equal(t, 1, logs.Len()) entry := logs.All()[0] - Expect(entry.Message).To(Equal("test")) - Expect(entry.Context).To(Equal(noStackFields1())) + assert.Equal(t, "test", entry.Message) + assert.Equal(t, noStackFields1(), entry.Context) }) - It("no stacks", func() { + t.Run("no stacks", func(t *testing.T) { nested, logs := observer.New(zap.DebugLevel) testee := NewStackExtractCore(nested) @@ -49,13 +43,15 @@ var _ = Describe("stack_extract_core", func() { entry := zapcore.Entry{Message: "test"} _ = testee.Write(entry, noStackFields2()) - Expect(logs.Len()).To(Equal(1)) - Expect(logs.All()[0]).To(Equal( + assert.Equal(t, 1, logs.Len()) + assert.Equal( + t, observer.LoggedEntry{Entry: entry, Context: append(noStackFields1(), noStackFields2()...)}, - )) + logs.All()[0], + ) }) - It("stack in write", func() { + t.Run("stack in write", func(t *testing.T) { const sampleErrMsg = "stacked error msg" sampleErr := errors.New(sampleErrMsg) sampleStack := fmt.Sprintf("%+v", sampleErr.(stackedErr).StackTrace()) @@ -71,17 +67,19 @@ var _ = Describe("stack_extract_core", func() { expectedEntry := entry expectedEntry.Stack = "error stacktrace:" + sampleStack - Expect(logs.Len()).To(Equal(1)) - Expect(logs.All()[0]).To(Equal( + assert.Equal(t, 1, logs.Len()) + assert.Equal( + t, observer.LoggedEntry{ Entry: expectedEntry, Context: append(noStackFields1(), zap.String("error", sampleErrMsg)), }, - )) - Expect(fields).To(Equal(fieldsCopy)) + logs.All()[0], + ) + assert.Equal(t, fieldsCopy, fields) }) - It("stack in with", func() { + t.Run("stack in with", func(t *testing.T) { const sampleErrMsg = "stacked error msg" sampleCause := fmt.Errorf(sampleErrMsg) sampleErr := errors.WithStack(sampleCause) @@ -99,17 +97,19 @@ var _ = Describe("stack_extract_core", func() { expectedEntry := entry expectedEntry.Stack = "error stacktrace:" + sampleStack - Expect(logs.Len()).To(Equal(1)) - Expect(logs.All()[0]).To(Equal( + assert.Equal(t, 1, logs.Len()) + assert.Equal( + t, observer.LoggedEntry{ Entry: expectedEntry, Context: append(noStackFields1(), zap.Error(sampleCause)), }, - )) - Expect(fields).To(Equal(fieldsCopy)) + logs.All()[0], + ) + assert.Equal(t, fieldsCopy, fields) }) - It("stacks join", func() { + t.Run("stacks join", func(t *testing.T) { const sampleErrMsg = "stacked error msg" sampleErr := errors.New(sampleErrMsg) sampleStack := fmt.Sprintf("%+v", sampleErr.(stackedErr).StackTrace()) @@ -124,13 +124,15 @@ var _ = Describe("stack_extract_core", func() { expectedEntry := entry expectedEntry.Stack = entryStack + "\n" + customKey + " stacktrace:" + sampleStack - Expect(logs.Len()).To(Equal(1)) - Expect(logs.All()[0]).To(Equal( + assert.Equal(t, 1, logs.Len()) + + assert.Equal( + t, observer.LoggedEntry{ Entry: expectedEntry, Context: []zapcore.Field{zap.String(customKey, sampleErrMsg)}, }, - )) + logs.All()[0], + ) }) - -}) +} diff --git a/lib/zaputil/zaputil_suite_test.go b/lib/zaputil/zaputil_suite_test.go deleted file mode 100644 index fbeff291d..000000000 --- a/lib/zaputil/zaputil_suite_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2018 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - -package zaputil - -import ( - "testing" - - "github.com/yandex/pandora/lib/ginkgoutil" -) - -func TestZaputilSuite(t *testing.T) { - ginkgoutil.RunSuite(t, "Zaputil Suite") -} diff --git a/main.go b/main.go index 9d258e747..8c25937e8 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,3 @@ -// Copyright (c) 2017 Yandex LLC. All rights reserved. -// Use of this source code is governed by a MPL 2.0 -// license that can be found in the LICENSE file. -// Author: Vladimir Skipor - package main import ( diff --git a/tests/acceptance/common.go b/tests/acceptance/common.go new file mode 100644 index 000000000..ca774bf49 --- /dev/null +++ b/tests/acceptance/common.go @@ -0,0 +1,72 @@ +package acceptance + +import ( + "bytes" + "context" + "os" + "sync" + "testing" + "text/template" + + "github.com/stretchr/testify/require" + "github.com/yandex/pandora/cli" + "github.com/yandex/pandora/core" + "github.com/yandex/pandora/core/config" + "github.com/yandex/pandora/core/engine" + "github.com/yandex/pandora/lib/monitoring" + "gopkg.in/yaml.v2" +) + +func parseConfigFile(t *testing.T, filename string, serverAddr string) *cli.CliConfig { + t.Helper() + mapCfg := unmarshalConfigFile(t, filename, serverAddr) + conf := decodeConfig(t, mapCfg) + return conf +} + +func decodeConfig(t *testing.T, mapCfg map[string]any) *cli.CliConfig { + t.Helper() + conf := cli.DefaultConfig() + err := config.DecodeAndValidate(mapCfg, conf) + require.NoError(t, err) + return conf +} + +func unmarshalConfigFile(t *testing.T, filename string, serverAddr string) map[string]any { + t.Helper() + f, err := os.ReadFile(filename) + require.NoError(t, err) + tmpl, err := template.New("x").Parse(string(f)) + require.NoError(t, err) + b := &bytes.Buffer{} + err = tmpl.Execute(b, map[string]string{"target": serverAddr}) + require.NoError(t, err) + mapCfg := map[string]any{} + err = yaml.Unmarshal(b.Bytes(), &mapCfg) + require.NoError(t, err) + return mapCfg +} + +func newEngineMetrics(prefix string) engine.Metrics { + return engine.Metrics{ + Request: monitoring.NewCounter(prefix + "_Requests"), + Response: monitoring.NewCounter(prefix + "_Responses"), + InstanceStart: monitoring.NewCounter(prefix + "_UsersStarted"), + InstanceFinish: monitoring.NewCounter(prefix + "_UsersFinished"), + } +} + +type aggregator struct { + mx sync.Mutex + samples []core.Sample +} + +func (a *aggregator) Run(ctx context.Context, deps core.AggregatorDeps) error { + return nil +} + +func (a *aggregator) Report(s core.Sample) { + a.mx.Lock() + defer a.mx.Unlock() + a.samples = append(a.samples, s) +} diff --git a/tests/acceptance/config_model.go b/tests/acceptance/config_model.go new file mode 100644 index 000000000..a0ccd9e15 --- /dev/null +++ b/tests/acceptance/config_model.go @@ -0,0 +1,51 @@ +package acceptance + +type PandoraConfigLog struct { + Level string `yaml:"level"` +} +type PandoraConfigMonitoringExpVar struct { + Enabled bool `yaml:"enabled"` + Port int `yaml:"port"` +} +type PandoraConfigMonitoring struct { + ExpVar PandoraConfigMonitoringExpVar `yaml:"expvar"` +} +type PandoraConfigGRPCGun struct { + Type string `yaml:"type"` + Target string `yaml:"target"` + TLS bool `yaml:"tls"` + ReflectPort *int64 `yaml:"reflect_port,omitempty"` + SharedClient struct { + ClientNumber int `yaml:"client-number,omitempty"` + Enabled bool `yaml:"enabled"` + } `yaml:"shared-client,omitempty"` +} +type PandoraConfigAmmo struct { + Type string `yaml:"type"` + File string `yaml:"file"` +} +type PandoraConfigResult struct { + Type string `yaml:"type"` +} +type PandoraConfigRps struct { + Type string `yaml:"type"` + Duration string `yaml:"duration"` + Ops int `yaml:"ops"` +} +type PandoraConfigStartup struct { + Type string `yaml:"type"` + Times int `yaml:"times"` +} +type PandoraConfigGRPCPool struct { + ID string `yaml:"id"` + Gun PandoraConfigGRPCGun `yaml:"gun"` + Ammo PandoraConfigAmmo `yaml:"ammo"` + Result PandoraConfigResult `yaml:"result"` + Rps []PandoraConfigRps `yaml:"rps"` + Startup PandoraConfigStartup `yaml:"startup"` +} +type PandoraConfigGRPC struct { + Pools []PandoraConfigGRPCPool `yaml:"pools"` + Log *PandoraConfigLog `yaml:"log"` + Monitoring *PandoraConfigMonitoring `yaml:"monitoring"` +} diff --git a/tests/acceptance/connect_test.go b/tests/acceptance/connect_test.go new file mode 100644 index 000000000..56c285dde --- /dev/null +++ b/tests/acceptance/connect_test.go @@ -0,0 +1,120 @@ +package acceptance + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/suite" + grpc "github.com/yandex/pandora/components/grpc/import" + phttpimport "github.com/yandex/pandora/components/phttp/import" + "github.com/yandex/pandora/core/engine" + coreimport "github.com/yandex/pandora/core/import" + "github.com/yandex/pandora/lib/testutil" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +func TestConnectGunSuite(t *testing.T) { + suite.Run(t, new(ConnectGunSuite)) +} + +type ConnectGunSuite struct { + suite.Suite + fs afero.Fs + log *zap.Logger + metrics engine.Metrics +} + +func (s *ConnectGunSuite) SetupSuite() { + s.fs = afero.NewOsFs() + testOnce.Do(func() { + coreimport.Import(s.fs) + phttpimport.Import(s.fs) + grpc.Import(s.fs) + }) + + s.log = testutil.NewNullLogger() + s.metrics = newEngineMetrics("connect_suite") +} + +func (s *ConnectGunSuite) Test_Connect() { + tests := []struct { + name string + filecfg string + isTLS bool + preStartSrv func(srv *httptest.Server) + wantErrContain string + wantCnt int + }{ + { + name: "http", + filecfg: "testdata/connect/connect.yaml", + isTLS: false, + wantCnt: 4, + }, + { + name: "http-check-limits", + filecfg: "testdata/connect/connect-check-limit.yaml", + isTLS: false, + wantCnt: 8, + }, + { + name: "http-check-passes", + filecfg: "testdata/connect/connect-check-passes.yaml", + isTLS: false, + wantCnt: 15, + }, + { + name: "connect-ssl", + filecfg: "testdata/connect/connect-ssl.yaml", + isTLS: true, + wantCnt: 4, + }, + { + name: "connect-shared-client", + filecfg: "testdata/connect/connect-shared-client.yaml", + isTLS: false, + wantCnt: 4, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + var requetsCount atomic.Int64 // Request served by test server. + requetsCount.Store(0) + srv := httptest.NewUnstartedServer(http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + requetsCount.Inc() + rw.WriteHeader(http.StatusOK) + })) + defer srv.Close() + + conf := parseConfigFile(s.T(), tt.filecfg, srv.Listener.Addr().String()) + s.Require().Equal(1, len(conf.Engine.Pools)) + aggr := &aggregator{} + conf.Engine.Pools[0].Aggregator = aggr + pandora := engine.New(s.log, s.metrics, conf.Engine) + + if tt.preStartSrv != nil { + tt.preStartSrv(srv) + } + if tt.isTLS { + srv.StartTLS() + } else { + srv.Start() + } + err := pandora.Run(context.Background()) + if tt.wantErrContain != "" { + s.Assert().Equal(int64(0), requetsCount.Load()) + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContain) + return + } + s.Require().NoError(err) + s.Require().Equal(int64(tt.wantCnt), int64(len(aggr.samples))) + s.Assert().GreaterOrEqual(requetsCount.Load(), int64(len(aggr.samples))) // requetsCount more than shoots + }) + } +} diff --git a/tests/acceptance/grpc_test.go b/tests/acceptance/grpc_test.go new file mode 100644 index 000000000..491b327c9 --- /dev/null +++ b/tests/acceptance/grpc_test.go @@ -0,0 +1,213 @@ +package acceptance + +import ( + "context" + "log/slog" + "net" + "os" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/yandex/pandora/cli" + grpcimport "github.com/yandex/pandora/components/grpc/import" + phttpimport "github.com/yandex/pandora/components/phttp/import" + "github.com/yandex/pandora/core/engine" + coreimport "github.com/yandex/pandora/core/import" + "github.com/yandex/pandora/examples/grpc/server" + "github.com/yandex/pandora/lib/pointer" + "github.com/yandex/pandora/lib/testutil" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + "gopkg.in/yaml.v2" +) + +func TestCheckGRPCReflectServer(t *testing.T) { + fs := afero.NewOsFs() + testOnce.Do(func() { + coreimport.Import(fs) + phttpimport.Import(fs) + grpcimport.Import(fs) + }) + pandoraLogger := testutil.NewNullLogger() + pandoraMetrics := newEngineMetrics("reflect") + baseFile, err := os.ReadFile("testdata/grpc/base.yaml") + require.NoError(t, err) + + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) + + t.Run("reflect not found", func(t *testing.T) { + grpcServer := grpc.NewServer() + srv := server.NewServer(logger, time.Now().UnixNano()) + server.RegisterTargetServiceServer(grpcServer, srv) + grpcAddress := ":18888" + // Don't register reflection handler + // reflection.Register(grpcServer) + l, err := net.Listen("tcp", grpcAddress) + require.NoError(t, err) + go func() { + err = grpcServer.Serve(l) + require.NoError(t, err) + }() + + defer func() { + grpcServer.Stop() + }() + + conf := parseFileContentToCliConfig(t, baseFile, nil) + + require.Equal(t, 1, len(conf.Engine.Pools)) + aggr := &aggregator{} + conf.Engine.Pools[0].Aggregator = aggr + pandora := engine.New(pandoraLogger, pandoraMetrics, conf.Engine) + err = pandora.Run(context.Background()) + require.Error(t, err) + require.Contains(t, err.Error(), "gun warm up failed") + require.Contains(t, err.Error(), "unknown service grpc.reflection.v1alpha.ServerReflection") + + st, err := srv.Stats(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, int64(0), st.Hello) + }) + + t.Run("reflect on another port", func(t *testing.T) { + grpcServer := grpc.NewServer() + srv := server.NewServer(logger, time.Now().UnixNano()) + server.RegisterTargetServiceServer(grpcServer, srv) + grpcAddress := ":18888" + l, err := net.Listen("tcp", grpcAddress) + require.NoError(t, err) + go func() { + err := grpcServer.Serve(l) + require.NoError(t, err) + }() + + reflectionGrpcServer := grpc.NewServer() + reflectionSrv := server.NewServer(logger, time.Now().UnixNano()) + server.RegisterTargetServiceServer(reflectionGrpcServer, reflectionSrv) + grpcAddress = ":18889" + reflection.Register(reflectionGrpcServer) + listenRef, err := net.Listen("tcp", grpcAddress) + require.NoError(t, err) + go func() { + err := reflectionGrpcServer.Serve(listenRef) + require.NoError(t, err) + }() + defer func() { + grpcServer.Stop() + reflectionGrpcServer.Stop() + }() + + conf := parseFileContentToCliConfig(t, baseFile, func(c *PandoraConfigGRPC) { + c.Pools[0].Gun.ReflectPort = pointer.ToInt64(18889) + }) + + require.Equal(t, 1, len(conf.Engine.Pools)) + aggr := &aggregator{} + conf.Engine.Pools[0].Aggregator = aggr + pandora := engine.New(pandoraLogger, pandoraMetrics, conf.Engine) + err = pandora.Run(context.Background()) + require.NoError(t, err) + + st, err := srv.Stats(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, int64(8), st.Hello) + }) +} + +func TestGrpcGunSuite(t *testing.T) { + suite.Run(t, new(GrpcGunSuite)) +} + +type GrpcGunSuite struct { + suite.Suite + fs afero.Fs + log *zap.Logger + metrics engine.Metrics +} + +func (s *GrpcGunSuite) SetupSuite() { + s.fs = afero.NewOsFs() + testOnce.Do(func() { + coreimport.Import(s.fs) + phttpimport.Import(s.fs) + grpcimport.Import(s.fs) + }) + + s.log = testutil.NewNullLogger() + s.metrics = newEngineMetrics("grpc_suite") +} + +func (s *GrpcGunSuite) Test_Run() { + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) + baseFile, err := os.ReadFile("testdata/grpc/base.yaml") + s.Require().NoError(err) + + tests := []struct { + name string + overwrite func(c *PandoraConfigGRPC) + wantCnt int64 + }{ + { + name: "default testdata/grpc/base.yaml", + wantCnt: 8, + }, + { + name: "add pool-size testdata/grpc/base.yaml", + overwrite: func(c *PandoraConfigGRPC) { + c.Pools[0].Gun.SharedClient.Enabled = true + c.Pools[0].Gun.SharedClient.ClientNumber = 2 + }, + wantCnt: 8, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + + grpcServer := grpc.NewServer() + srv := server.NewServer(logger, time.Now().UnixNano()) + server.RegisterTargetServiceServer(grpcServer, srv) + reflection.Register(grpcServer) + l, err := net.Listen("tcp", ":18888") + s.Require().NoError(err) + go func() { + err = grpcServer.Serve(l) + s.Require().NoError(err) + }() + defer func() { + grpcServer.Stop() + }() + + conf := parseFileContentToCliConfig(s.T(), baseFile, tt.overwrite) + + aggr := &aggregator{} + conf.Engine.Pools[0].Aggregator = aggr + pandora := engine.New(s.log, s.metrics, conf.Engine) + + err = pandora.Run(context.Background()) + s.Require().NoError(err) + stats, err := srv.Stats(context.Background(), nil) + s.Require().NoError(err) + s.Require().Equal(tt.wantCnt, stats.Hello) + }) + } +} + +func parseFileContentToCliConfig(t *testing.T, baseFile []byte, overwrite func(c *PandoraConfigGRPC)) *cli.CliConfig { + cfg := PandoraConfigGRPC{} + err := yaml.Unmarshal(baseFile, &cfg) + require.NoError(t, err) + if overwrite != nil { + overwrite(&cfg) + } + b, err := yaml.Marshal(cfg) + require.NoError(t, err) + mapCfg := map[string]any{} + err = yaml.Unmarshal(b, &mapCfg) + require.NoError(t, err) + + return decodeConfig(t, mapCfg) +} diff --git a/tests/acceptance/http_scenario_test.go b/tests/acceptance/http_scenario_test.go new file mode 100644 index 000000000..154a4525a --- /dev/null +++ b/tests/acceptance/http_scenario_test.go @@ -0,0 +1,109 @@ +package acceptance + +import ( + "context" + "log/slog" + "os" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/stretchr/testify/suite" + grpc "github.com/yandex/pandora/components/grpc/import" + phttpimport "github.com/yandex/pandora/components/phttp/import" + "github.com/yandex/pandora/core/engine" + coreimport "github.com/yandex/pandora/core/import" + "github.com/yandex/pandora/examples/http/server" + "github.com/yandex/pandora/lib/testutil" + "go.uber.org/zap" +) + +func TestHTTPScenarioSuite(t *testing.T) { + suite.Run(t, new(HTTPScenarioSuite)) +} + +type HTTPScenarioSuite struct { + suite.Suite + fs afero.Fs + log *zap.Logger + metrics engine.Metrics + addr string + srv *server.Server +} + +func (s *HTTPScenarioSuite) SetupSuite() { + s.fs = afero.NewOsFs() + testOnce.Do(func() { + coreimport.Import(s.fs) + phttpimport.Import(s.fs) + grpc.Import(s.fs) + }) + + s.log = testutil.NewNullLogger() + s.metrics = newEngineMetrics("http_scenario_suite") + + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) + port := os.Getenv("PORT") // TODO: how to set free port in CI? + if port == "" { + port = "8886" + } + + s.addr = "localhost:" + port + s.srv = server.NewServer(s.addr, logger, time.Now().UnixNano()) + s.srv.ServeAsync() + + go func() { + err := <-s.srv.Err() + s.NoError(err) + }() +} + +func (s *HTTPScenarioSuite) TearDownSuite() { + err := s.srv.Shutdown(context.Background()) + s.NoError(err) +} + +func (s *HTTPScenarioSuite) SetupTest() { + s.srv.Stats().Reset() +} + +func (s *HTTPScenarioSuite) Test_Http_Check_Passes() { + tests := []struct { + name string + filecfg string + wantErrContain string + wantCnt int + wantStats *server.Stats + }{ + { + name: "base", + filecfg: "testdata/http_scenario/scenario.yaml", + wantCnt: 4, + wantStats: &server.Stats{ + Auth200: map[int64]uint64{1: 2, 2: 1, 3: 1}, + List200: map[int64]uint64{1: 2, 2: 1, 3: 1}, + Order200: map[int64]uint64{1: 6, 2: 3, 3: 3}, + }, + }, + } + for _, tt := range tests { + s.Run(tt.name, func() { + conf := parseConfigFile(s.T(), tt.filecfg, s.addr) + s.Require().Equal(1, len(conf.Engine.Pools)) + aggr := &aggregator{} + conf.Engine.Pools[0].Aggregator = aggr + + pandora := engine.New(s.log, s.metrics, conf.Engine) + + err := pandora.Run(context.Background()) + if tt.wantErrContain != "" { + s.Require().Error(err) + s.Require().Contains(err.Error(), tt.wantErrContain) + return + } + + s.Require().NoError(err) + s.Require().Equal(tt.wantStats, s.srv.Stats()) + }) + } +} diff --git a/tests/acceptance/http_test.go b/tests/acceptance/http_test.go index e34f7bfa9..fc2fe8fac 100644 --- a/tests/acceptance/http_test.go +++ b/tests/acceptance/http_test.go @@ -1,32 +1,22 @@ -package httphttp2 +package acceptance import ( - "bytes" "context" "net/http" "net/http/httptest" - "os" "sync" "testing" - "text/template" "github.com/spf13/afero" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/yandex/pandora/cli" grpc "github.com/yandex/pandora/components/grpc/import" phttpimport "github.com/yandex/pandora/components/phttp/import" - "github.com/yandex/pandora/core" - "github.com/yandex/pandora/core/config" "github.com/yandex/pandora/core/engine" coreimport "github.com/yandex/pandora/core/import" - "github.com/yandex/pandora/lib/monitoring" + "github.com/yandex/pandora/lib/testutil" "go.uber.org/atomic" "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "go.uber.org/zap/zaptest/observer" "golang.org/x/net/http2" - "gopkg.in/yaml.v2" ) var testOnce = &sync.Once{} @@ -44,13 +34,14 @@ type PandoraSuite struct { func (s *PandoraSuite) SetupSuite() { s.fs = afero.NewOsFs() - coreimport.Import(s.fs) - phttpimport.Import(s.fs) - grpc.Import(s.fs) - - s.log = newNullLogger() - // s.log = newLogger() - s.metrics = newEngineMetrics() + testOnce.Do(func() { + coreimport.Import(s.fs) + phttpimport.Import(s.fs) + grpc.Import(s.fs) + }) + + s.log = testutil.NewNullLogger() + s.metrics = newEngineMetrics("http_suite") } func (s *PandoraSuite) Test_Http_Check_Passes() { @@ -106,6 +97,16 @@ func (s *PandoraSuite) Test_Http_Check_Passes() { isTLS: false, wantCnt: 15, }, + { + name: "http2-shared-client", + filecfg: "testdata/http/http2-shared-client.yaml", + isTLS: true, + preStartSrv: func(srv *httptest.Server) { + _ = http2.ConfigureServer(srv.Config, nil) + srv.TLS = srv.Config.TLSConfig + }, + wantCnt: 8, + }, } for _, tt := range tests { s.Run(tt.name, func() { @@ -145,69 +146,3 @@ func (s *PandoraSuite) Test_Http_Check_Passes() { }) } } - -func parseConfigFile(t *testing.T, filename string, serverAddr string) *cli.CliConfig { - mapCfg := unmarshalConfigFile(t, filename, serverAddr) - conf := decodeConfig(t, mapCfg) - return conf -} - -func decodeConfig(t *testing.T, mapCfg map[string]any) *cli.CliConfig { - conf := cli.DefaultConfig() - err := config.DecodeAndValidate(mapCfg, conf) - require.NoError(t, err) - return conf -} - -func unmarshalConfigFile(t *testing.T, filename string, serverAddr string) map[string]any { - f, err := os.ReadFile(filename) - require.NoError(t, err) - tmpl, err := template.New("x").Parse(string(f)) - require.NoError(t, err) - b := &bytes.Buffer{} - err = tmpl.Execute(b, map[string]string{"target": serverAddr}) - require.NoError(t, err) - mapCfg := map[string]any{} - err = yaml.Unmarshal(b.Bytes(), &mapCfg) - require.NoError(t, err) - return mapCfg -} - -func newNullLogger() *zap.Logger { - c, _ := observer.New(zap.InfoLevel) - return zap.New(c) -} - -func newLogger() *zap.Logger { - zapConf := zap.NewDevelopmentConfig() - zapConf.Level.SetLevel(zapcore.DebugLevel) - log, err := zapConf.Build(zap.AddCaller()) - if err != nil { - zap.L().Fatal("Logger build failed", zap.Error(err)) - } - return log -} - -func newEngineMetrics() engine.Metrics { - return engine.Metrics{ - Request: monitoring.NewCounter("engine_Requests"), - Response: monitoring.NewCounter("engine_Responses"), - InstanceStart: monitoring.NewCounter("engine_UsersStarted"), - InstanceFinish: monitoring.NewCounter("engine_UsersFinished"), - } -} - -type aggregator struct { - mx sync.Mutex - samples []core.Sample -} - -func (a *aggregator) Run(ctx context.Context, deps core.AggregatorDeps) error { - return nil -} - -func (a *aggregator) Report(s core.Sample) { - a.mx.Lock() - defer a.mx.Unlock() - a.samples = append(a.samples, s) -} diff --git a/tests/acceptance/testdata/connect/connect-check-limit.yaml b/tests/acceptance/testdata/connect/connect-check-limit.yaml new file mode 100644 index 000000000..cbc77ecac --- /dev/null +++ b/tests/acceptance/testdata/connect/connect-check-limit.yaml @@ -0,0 +1,23 @@ +pools: + - id: "" + ammo: + file: testdata/http/payload5.uri + type: uri + limit: 8 + result: + type: discard + gun: + target: {{.target}} + type: connect + answlog: + enabled: false + rps-per-instance: false + rps: + - duration: 5s + ops: 10 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/connect/connect-check-passes.yaml b/tests/acceptance/testdata/connect/connect-check-passes.yaml new file mode 100644 index 000000000..42aa9f66b --- /dev/null +++ b/tests/acceptance/testdata/connect/connect-check-passes.yaml @@ -0,0 +1,23 @@ +pools: + - id: "" + ammo: + file: testdata/http/payload5.uri + type: uri + passes: 3 + result: + type: discard + gun: + target: {{.target}} + type: connect + answlog: + enabled: false + rps-per-instance: false + rps: + - duration: 5s + ops: 10 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/connect/connect-shared-client.yaml b/tests/acceptance/testdata/connect/connect-shared-client.yaml new file mode 100644 index 000000000..5ae8b9482 --- /dev/null +++ b/tests/acceptance/testdata/connect/connect-shared-client.yaml @@ -0,0 +1,25 @@ +pools: + - id: "" + ammo: + file: testdata/http/payload.uri + type: uri + result: + type: discard + gun: + target: {{.target}} + type: connect + shared-client: + enabled: true + client-number: 1 + answlog: + enabled: false + rps-per-instance: false + rps: + - duration: 1s + ops: 4 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/connect/connect-ssl.yaml b/tests/acceptance/testdata/connect/connect-ssl.yaml new file mode 100644 index 000000000..276a0f8bc --- /dev/null +++ b/tests/acceptance/testdata/connect/connect-ssl.yaml @@ -0,0 +1,26 @@ +pools: + - id: "" + ammo: + file: testdata/http/payload.uri + type: uri + result: + type: discard + gun: + target: {{.target}} + type: connect + ssl: true + connect-ssl: true # first record does not look like a TLS handshake. Check https://go.dev/src/crypto/tls/conn.go + answlog: + enabled: false + rps-per-instance: false + rps: + - times: 2 + type: once + - duration: 0.5s + ops: 4 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/connect/connect.yaml b/tests/acceptance/testdata/connect/connect.yaml new file mode 100644 index 000000000..323a9cfd2 --- /dev/null +++ b/tests/acceptance/testdata/connect/connect.yaml @@ -0,0 +1,24 @@ +pools: + - id: "" + ammo: + file: testdata/http/payload.uri + type: uri + result: + type: discard + gun: + target: {{.target}} + type: connect + answlog: + enabled: false + rps-per-instance: false + rps: + - times: 2 + type: once + - duration: 0.5s + ops: 4 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/connect/payload.uri b/tests/acceptance/testdata/connect/payload.uri new file mode 100644 index 000000000..35ec3b9d7 --- /dev/null +++ b/tests/acceptance/testdata/connect/payload.uri @@ -0,0 +1 @@ +/ \ No newline at end of file diff --git a/tests/acceptance/testdata/connect/payload5.uri b/tests/acceptance/testdata/connect/payload5.uri new file mode 100644 index 000000000..760465f78 --- /dev/null +++ b/tests/acceptance/testdata/connect/payload5.uri @@ -0,0 +1,5 @@ +/a +/b +/c +/d +/e diff --git a/tests/acceptance/testdata/grpc/base.yaml b/tests/acceptance/testdata/grpc/base.yaml new file mode 100644 index 000000000..3c984e272 --- /dev/null +++ b/tests/acceptance/testdata/grpc/base.yaml @@ -0,0 +1,25 @@ +pools: + - id: GRPC + gun: + type: grpc + target: localhost:18888 + tls: false + use-shared-client: false + ammo: + type: grpc/json + file: testdata/grpc/grpc.payload + result: + type: discard + rps: + - duration: 2s + ops: 4 + type: const + startup: + type: once + times: 2 +log: + level: error +monitoring: + expvar: + enabled: true + port: 1234 diff --git a/tests/acceptance/testdata/grpc/grpc.payload b/tests/acceptance/testdata/grpc/grpc.payload new file mode 100644 index 000000000..7be8432e3 --- /dev/null +++ b/tests/acceptance/testdata/grpc/grpc.payload @@ -0,0 +1 @@ +{"tag": "auth", "call": "target.TargetService.Hello", "payload": {"name": "test"}} diff --git a/tests/acceptance/testdata/http/http2-shared-client.yaml b/tests/acceptance/testdata/http/http2-shared-client.yaml new file mode 100644 index 000000000..89284c4ff --- /dev/null +++ b/tests/acceptance/testdata/http/http2-shared-client.yaml @@ -0,0 +1,28 @@ +pools: + - id: "" + ammo: + type: uri + headers: + - '[Content-Type: application/json]' + uris: + - / + result: + type: discard + gun: + target: {{.target}} + type: http2 + answlog: + enabled: false + shared-client: + enabled: true + client-number: 1 + rps-per-instance: false + rps: + - duration: 1s + ops: 8 + type: const + startup: + - times: 2 + type: once +log: + level: debug diff --git a/tests/acceptance/testdata/http_scenario/filter.json b/tests/acceptance/testdata/http_scenario/filter.json new file mode 100644 index 000000000..f047d711c --- /dev/null +++ b/tests/acceptance/testdata/http_scenario/filter.json @@ -0,0 +1,3 @@ +{ + "name": "Spiral 4v4 NS" +} \ No newline at end of file diff --git a/tests/acceptance/testdata/http_scenario/http_payload.hcl b/tests/acceptance/testdata/http_scenario/http_payload.hcl new file mode 100644 index 000000000..4796cd288 --- /dev/null +++ b/tests/acceptance/testdata/http_scenario/http_payload.hcl @@ -0,0 +1,123 @@ + +variable_source "users" "file/csv" { + file = "testdata/http_scenario/users.csv" + fields = ["user_id", "name", "pass"] + ignore_first_line = true + delimiter = "," +} +variable_source "filter_src" "file/json" { + file = "testdata/http_scenario/filter.json" +} +request "auth_req" { + method = "POST" + uri = "/auth" + headers = { + Content-Type = "application/json" + Useragent = "Yandex" + } + tag = "auth" + body = <